You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

186 regels
6.2 KiB

  1. from __future__ import unicode_literals
  2. import frappe, json
  3. from frappe.oauth import OAuthWebRequestValidator, WebApplicationServer
  4. from oauthlib.oauth2 import FatalClientError, OAuth2Error
  5. from urllib import quote, urlencode
  6. from werkzeug import url_fix
  7. from urlparse import urlparse
  8. from frappe.integrations.doctype.oauth_provider_settings.oauth_provider_settings import get_oauth_settings
  9. from frappe import _
  10. #Variables required across requests
  11. oauth_validator = OAuthWebRequestValidator()
  12. oauth_server = WebApplicationServer(oauth_validator)
  13. credentials = None
  14. def get_urlparams_from_kwargs(param_kwargs):
  15. arguments = param_kwargs
  16. if arguments.get("data"):
  17. arguments.pop("data")
  18. if arguments.get("cmd"):
  19. arguments.pop("cmd")
  20. return urlencode(arguments)
  21. @frappe.whitelist()
  22. def approve(*args, **kwargs):
  23. r = frappe.request
  24. uri = url_fix(r.url.replace("+"," "))
  25. http_method = r.method
  26. body = r.get_data()
  27. headers = r.headers
  28. try:
  29. scopes, credentials = oauth_server.validate_authorization_request(uri, http_method, body, headers)
  30. headers, body, status = oauth_server.create_authorization_response(uri=credentials['redirect_uri'], \
  31. body=body, headers=headers, scopes=scopes, credentials=credentials)
  32. uri = headers.get('Location', None)
  33. frappe.local.response["type"] = "redirect"
  34. frappe.local.response["location"] = uri
  35. except FatalClientError as e:
  36. return e
  37. except OAuth2Error as e:
  38. return e
  39. @frappe.whitelist(allow_guest=True)
  40. def authorize(*args, **kwargs):
  41. #Fetch provider URL from settings
  42. oauth_settings = get_oauth_settings()
  43. params = get_urlparams_from_kwargs(kwargs)
  44. request_url = urlparse(frappe.request.url)
  45. success_url = request_url.scheme + "://" + request_url.netloc + "/api/method/frappe.integration_broker.oauth2.approve?" + params
  46. failure_url = frappe.form_dict["redirect_uri"] + "?error=access_denied"
  47. if frappe.session['user']=='Guest':
  48. #Force login, redirect to preauth again.
  49. frappe.local.response["type"] = "redirect"
  50. frappe.local.response["location"] = "/login?redirect-to=/api/method/frappe.integration_broker.oauth2.authorize?" + quote(params)
  51. elif frappe.session['user']!='Guest':
  52. try:
  53. r = frappe.request
  54. uri = url_fix(r.url)
  55. http_method = r.method
  56. body = r.get_data()
  57. headers = r.headers
  58. scopes, credentials = oauth_server.validate_authorization_request(uri, http_method, body, headers)
  59. skip_auth = frappe.db.get_value("OAuth Client", credentials['client_id'], "skip_authorization")
  60. unrevoked_tokens = frappe.get_all("OAuth Bearer Token", filters={"status":"Active"})
  61. if skip_auth or (oauth_settings["skip_authorization"] == "Auto" and len(unrevoked_tokens)):
  62. frappe.local.response["type"] = "redirect"
  63. frappe.local.response["location"] = success_url
  64. else:
  65. #Show Allow/Deny screen.
  66. response_html_params = frappe._dict({
  67. "client_id": frappe.db.get_value("OAuth Client", kwargs['client_id'], "app_name"),
  68. "success_url": success_url,
  69. "failure_url": failure_url,
  70. "details": scopes
  71. })
  72. resp_html = frappe.render_template("templates/includes/oauth_confirmation.html", response_html_params)
  73. frappe.respond_as_web_page("Confirm Access", resp_html)
  74. except FatalClientError as e:
  75. return e
  76. except OAuth2Error as e:
  77. return e
  78. @frappe.whitelist(allow_guest=True)
  79. def get_token(*args, **kwargs):
  80. r = frappe.request
  81. uri = url_fix(r.url)
  82. http_method = r.method
  83. body = r.form
  84. headers = r.headers
  85. #Check whether frappe server URL is set
  86. frappe_server_url = frappe.db.get_value("Social Login Keys", None, "frappe_server_url") or None
  87. if not frappe_server_url:
  88. frappe.throw(_("Define Frappe Server URL in Social Login Keys"))
  89. try:
  90. headers, body, status = oauth_server.create_token_response(uri, http_method, body, headers, credentials)
  91. out = frappe._dict(json.loads(body))
  92. if not out.error and "openid" in out.scope:
  93. token_user = frappe.db.get_value("OAuth Bearer Token", out.access_token, "user")
  94. token_client = frappe.db.get_value("OAuth Bearer Token", out.access_token, "client")
  95. client_secret = frappe.db.get_value("OAuth Client", token_client, "client_secret")
  96. if token_user in ["Guest", "Administrator"]:
  97. frappe.throw(_("Logged in as Guest or Administrator"))
  98. import hashlib
  99. id_token_header = {
  100. "typ":"jwt",
  101. "alg":"HS256"
  102. }
  103. id_token = {
  104. "aud": token_client,
  105. "exp": int((frappe.db.get_value("OAuth Bearer Token", out.access_token, "expiration_time") - frappe.utils.datetime.datetime(1970, 1, 1)).total_seconds()),
  106. "sub": frappe.db.get_value("User", token_user, "frappe_userid"),
  107. "iss": frappe_server_url,
  108. "at_hash": frappe.oauth.calculate_at_hash(out.access_token, hashlib.sha256)
  109. }
  110. import jwt
  111. id_token_encoded = jwt.encode(id_token, client_secret, algorithm='HS256', headers=id_token_header)
  112. out.update({"id_token":id_token_encoded})
  113. frappe.local.response = out
  114. except FatalClientError as e:
  115. return e
  116. @frappe.whitelist(allow_guest=True)
  117. def revoke_token(*args, **kwargs):
  118. r = frappe.request
  119. uri = url_fix(r.url)
  120. http_method = r.method
  121. body = r.form
  122. headers = r.headers
  123. headers, body, status = oauth_server.create_revocation_response(uri, headers=headers, body=body, http_method=http_method)
  124. frappe.local.response['http_status_code'] = status
  125. if status == 200:
  126. return "success"
  127. else:
  128. return "bad request"
  129. @frappe.whitelist()
  130. def openid_profile(*args, **kwargs):
  131. picture = None
  132. first_name, last_name, avatar, name, frappe_userid = frappe.db.get_value("User", frappe.session.user, ["first_name", "last_name", "user_image", "name", "frappe_userid"])
  133. request_url = urlparse(frappe.request.url)
  134. if avatar:
  135. if validate_url(avatar):
  136. picture = avatar
  137. else:
  138. picture = request_url.scheme + "://" + request_url.netloc + avatar
  139. user_profile = frappe._dict({
  140. "sub": frappe_userid,
  141. "name": " ".join(filter(None, [first_name, last_name])),
  142. "given_name": first_name,
  143. "family_name": last_name,
  144. "email": name,
  145. "picture": picture
  146. })
  147. frappe.local.response = user_profile
  148. def validate_url(url_string):
  149. from urlparse import urlparse
  150. try:
  151. result = urlparse(url_string)
  152. if result.scheme and result.scheme in ["http", "https", "ftp", "ftps"]:
  153. return True
  154. else:
  155. return False
  156. except:
  157. return False