dify/api/controllers/files/wraps.py
Wu Tianwei 550196e3b8
feat: app deployment v2 (#41444)
Co-authored-by: zhangx1n <zhangxin@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-09-04 02:01:35 +00:00

58 lines
1.7 KiB
Python

"""Bearer authentication for the AppDeploy file grant endpoints."""
from collections.abc import Callable
from functools import wraps
from flask import request
from extensions.ext_application_services import application_services
from libs.exception import BaseHTTPException
from services.entities.file_grant_entities import FileGrantClaims, FileGrantScope
class FileGrantInvalidError(BaseHTTPException):
error_code = "grant_invalid"
description = "The file grant is missing, malformed, or expired."
code = 401
class FileGrantScopeDeniedError(BaseHTTPException):
error_code = "grant_scope_denied"
description = "The file grant does not carry the required scope."
code = 403
class GrantedFileNotFoundError(BaseHTTPException):
error_code = "file_not_found"
description = "File not found."
code = 404
def file_grant_required[**P, R](scope: FileGrantScope) -> Callable[[Callable[P, R]], Callable[P, R]]:
"""Require a valid file grant carrying ``scope`` and inject its claims."""
def decorator(view: Callable[P, R]) -> Callable[P, R]:
@wraps(view)
def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
kwargs["grant"] = _authenticated_claims(scope)
return view(*args, **kwargs)
return decorated
return decorator
def _authenticated_claims(scope: FileGrantScope) -> FileGrantClaims:
scheme, _, token = request.headers.get("Authorization", "").partition(" ")
if scheme.lower() != "bearer" or not token:
raise FileGrantInvalidError()
claims = application_services().file_grants.decode_grant(token)
if claims is None:
raise FileGrantInvalidError()
if scope not in claims.scopes:
raise FileGrantScopeDeniedError()
return claims