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.

data_source_oauth.py 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import logging
  2. import requests
  3. from flask import current_app, redirect, request
  4. from flask_login import current_user
  5. from flask_restx import Resource, fields
  6. from werkzeug.exceptions import Forbidden
  7. from configs import dify_config
  8. from controllers.console import api, console_ns
  9. from libs.login import login_required
  10. from libs.oauth_data_source import NotionOAuth
  11. from ..wraps import account_initialization_required, setup_required
  12. logger = logging.getLogger(__name__)
  13. def get_oauth_providers():
  14. with current_app.app_context():
  15. notion_oauth = NotionOAuth(
  16. client_id=dify_config.NOTION_CLIENT_ID or "",
  17. client_secret=dify_config.NOTION_CLIENT_SECRET or "",
  18. redirect_uri=dify_config.CONSOLE_API_URL + "/console/api/oauth/data-source/callback/notion",
  19. )
  20. OAUTH_PROVIDERS = {"notion": notion_oauth}
  21. return OAUTH_PROVIDERS
  22. @console_ns.route("/oauth/data-source/<string:provider>")
  23. class OAuthDataSource(Resource):
  24. @api.doc("oauth_data_source")
  25. @api.doc(description="Get OAuth authorization URL for data source provider")
  26. @api.doc(params={"provider": "Data source provider name (notion)"})
  27. @api.response(
  28. 200,
  29. "Authorization URL or internal setup success",
  30. api.model(
  31. "OAuthDataSourceResponse",
  32. {"data": fields.Raw(description="Authorization URL or 'internal' for internal setup")},
  33. ),
  34. )
  35. @api.response(400, "Invalid provider")
  36. @api.response(403, "Admin privileges required")
  37. def get(self, provider: str):
  38. # The role of the current user in the table must be admin or owner
  39. if not current_user.is_admin_or_owner:
  40. raise Forbidden()
  41. OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
  42. with current_app.app_context():
  43. oauth_provider = OAUTH_DATASOURCE_PROVIDERS.get(provider)
  44. if not oauth_provider:
  45. return {"error": "Invalid provider"}, 400
  46. if dify_config.NOTION_INTEGRATION_TYPE == "internal":
  47. internal_secret = dify_config.NOTION_INTERNAL_SECRET
  48. if not internal_secret:
  49. return ({"error": "Internal secret is not set"},)
  50. oauth_provider.save_internal_access_token(internal_secret)
  51. return {"data": "internal"}
  52. else:
  53. auth_url = oauth_provider.get_authorization_url()
  54. return {"data": auth_url}, 200
  55. @console_ns.route("/oauth/data-source/callback/<string:provider>")
  56. class OAuthDataSourceCallback(Resource):
  57. @api.doc("oauth_data_source_callback")
  58. @api.doc(description="Handle OAuth callback from data source provider")
  59. @api.doc(
  60. params={
  61. "provider": "Data source provider name (notion)",
  62. "code": "Authorization code from OAuth provider",
  63. "error": "Error message from OAuth provider",
  64. }
  65. )
  66. @api.response(302, "Redirect to console with result")
  67. @api.response(400, "Invalid provider")
  68. def get(self, provider: str):
  69. OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
  70. with current_app.app_context():
  71. oauth_provider = OAUTH_DATASOURCE_PROVIDERS.get(provider)
  72. if not oauth_provider:
  73. return {"error": "Invalid provider"}, 400
  74. if "code" in request.args:
  75. code = request.args.get("code")
  76. return redirect(f"{dify_config.CONSOLE_WEB_URL}?type=notion&code={code}")
  77. elif "error" in request.args:
  78. error = request.args.get("error")
  79. return redirect(f"{dify_config.CONSOLE_WEB_URL}?type=notion&error={error}")
  80. else:
  81. return redirect(f"{dify_config.CONSOLE_WEB_URL}?type=notion&error=Access denied")
  82. @console_ns.route("/oauth/data-source/binding/<string:provider>")
  83. class OAuthDataSourceBinding(Resource):
  84. @api.doc("oauth_data_source_binding")
  85. @api.doc(description="Bind OAuth data source with authorization code")
  86. @api.doc(
  87. params={"provider": "Data source provider name (notion)", "code": "Authorization code from OAuth provider"}
  88. )
  89. @api.response(
  90. 200,
  91. "Data source binding success",
  92. api.model("OAuthDataSourceBindingResponse", {"result": fields.String(description="Operation result")}),
  93. )
  94. @api.response(400, "Invalid provider or code")
  95. def get(self, provider: str):
  96. OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
  97. with current_app.app_context():
  98. oauth_provider = OAUTH_DATASOURCE_PROVIDERS.get(provider)
  99. if not oauth_provider:
  100. return {"error": "Invalid provider"}, 400
  101. if "code" in request.args:
  102. code = request.args.get("code", "")
  103. if not code:
  104. return {"error": "Invalid code"}, 400
  105. try:
  106. oauth_provider.get_access_token(code)
  107. except requests.HTTPError as e:
  108. logger.exception(
  109. "An error occurred during the OAuthCallback process with %s: %s", provider, e.response.text
  110. )
  111. return {"error": "OAuth data source process failed"}, 400
  112. return {"result": "success"}, 200
  113. @console_ns.route("/oauth/data-source/<string:provider>/<uuid:binding_id>/sync")
  114. class OAuthDataSourceSync(Resource):
  115. @api.doc("oauth_data_source_sync")
  116. @api.doc(description="Sync data from OAuth data source")
  117. @api.doc(params={"provider": "Data source provider name (notion)", "binding_id": "Data source binding ID"})
  118. @api.response(
  119. 200,
  120. "Data source sync success",
  121. api.model("OAuthDataSourceSyncResponse", {"result": fields.String(description="Operation result")}),
  122. )
  123. @api.response(400, "Invalid provider or sync failed")
  124. @setup_required
  125. @login_required
  126. @account_initialization_required
  127. def get(self, provider, binding_id):
  128. provider = str(provider)
  129. binding_id = str(binding_id)
  130. OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
  131. with current_app.app_context():
  132. oauth_provider = OAUTH_DATASOURCE_PROVIDERS.get(provider)
  133. if not oauth_provider:
  134. return {"error": "Invalid provider"}, 400
  135. try:
  136. oauth_provider.sync_data_source(binding_id)
  137. except requests.HTTPError as e:
  138. logger.exception(
  139. "An error occurred during the OAuthCallback process with %s: %s", provider, e.response.text
  140. )
  141. return {"error": "OAuth data source process failed"}, 400
  142. return {"result": "success"}, 200