dify/api/controllers/web/wraps.py

93 lines
3.4 KiB
Python
Raw Normal View History

2023-05-15 08:51:32 +08:00
from functools import wraps
from flask import request
2023-05-15 08:51:32 +08:00
from flask_restful import Resource
2024-05-15 16:14:49 +08:00
from werkzeug.exceptions import BadRequest, NotFound, Unauthorized
2024-05-15 16:14:49 +08:00
from controllers.web.error import WebSSOAuthRequiredError
from extensions.ext_database import db
from libs.passport import PassportService
from models.model import App, EndUser, Site
2024-08-25 18:47:02 +08:00
from services.enterprise.enterprise_service import EnterpriseService
2024-05-15 16:14:49 +08:00
from services.feature_service import FeatureService
2023-05-15 08:51:32 +08:00
def validate_jwt_token(view=None):
2023-05-15 08:51:32 +08:00
def decorator(view):
@wraps(view)
def decorated(*args, **kwargs):
app_model, end_user = decode_jwt_token()
2023-05-15 08:51:32 +08:00
return view(app_model, end_user, *args, **kwargs)
2023-05-15 08:51:32 +08:00
return decorated
2023-05-15 08:51:32 +08:00
if view:
return decorator(view)
return decorator
2024-05-15 16:14:49 +08:00
def decode_jwt_token():
2024-05-15 16:14:49 +08:00
system_features = FeatureService.get_system_features()
app_code = request.headers.get("X-App-Code")
2024-05-15 16:14:49 +08:00
try:
auth_header = request.headers.get("Authorization")
2024-05-15 16:14:49 +08:00
if auth_header is None:
raise Unauthorized("Authorization header is missing.")
2024-05-15 16:14:49 +08:00
if " " not in auth_header:
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
2024-05-15 16:14:49 +08:00
auth_scheme, tk = auth_header.split(None, 1)
auth_scheme = auth_scheme.lower()
if auth_scheme != "bearer":
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
2024-05-15 16:14:49 +08:00
decoded = PassportService().verify(tk)
app_code = decoded.get("app_code")
app_model = db.session.query(App).filter(App.id == decoded["app_id"]).first()
2024-05-15 16:14:49 +08:00
site = db.session.query(Site).filter(Site.code == app_code).first()
if not app_model:
raise NotFound()
if not app_code or not site:
raise BadRequest("Site URL is no longer valid.")
2024-05-15 16:14:49 +08:00
if app_model.enable_site is False:
raise BadRequest("Site is disabled.")
end_user = db.session.query(EndUser).filter(EndUser.id == decoded["end_user_id"]).first()
2024-05-15 16:14:49 +08:00
if not end_user:
raise NotFound()
2024-08-25 18:47:02 +08:00
_validate_web_sso_token(decoded, system_features, app_code)
2024-05-15 16:14:49 +08:00
return app_model, end_user
except Unauthorized as e:
if system_features.sso_enforced_for_web:
app_web_sso_enabled = EnterpriseService.get_app_web_sso_enabled(app_code).get("enabled", False)
2024-08-25 18:47:02 +08:00
if app_web_sso_enabled:
raise WebSSOAuthRequiredError()
2024-05-15 16:14:49 +08:00
raise Unauthorized(e.description)
2024-08-25 18:47:02 +08:00
def _validate_web_sso_token(decoded, system_features, app_code):
app_web_sso_enabled = False
2024-05-15 16:14:49 +08:00
# Check if SSO is enforced for web, and if the token source is not SSO, raise an error and redirect to SSO login
if system_features.sso_enforced_for_web:
app_web_sso_enabled = EnterpriseService.get_app_web_sso_enabled(app_code).get("enabled", False)
2024-08-25 18:47:02 +08:00
if app_web_sso_enabled:
source = decoded.get("token_source")
if not source or source != "sso":
2024-08-25 18:47:02 +08:00
raise WebSSOAuthRequiredError()
2024-05-15 16:14:49 +08:00
# Check if SSO is not enforced for web, and if the token source is SSO,
# raise an error and redirect to normal passport login
2024-08-25 18:47:02 +08:00
if not system_features.sso_enforced_for_web or not app_web_sso_enabled:
source = decoded.get("token_source")
if source and source == "sso":
raise Unauthorized("sso token expired.")
2024-05-15 16:14:49 +08:00
2023-05-15 08:51:32 +08:00
class WebApiResource(Resource):
method_decorators = [validate_jwt_token]