|
|
|
|
@ -9,6 +9,7 @@ from flask import current_app, request
|
|
|
|
|
from flask_login import user_logged_in # type: ignore
|
|
|
|
|
from flask_restful import Resource # type: ignore
|
|
|
|
|
from libs.login import _get_user
|
|
|
|
|
from libs.passport import PassportService
|
|
|
|
|
from models.account import Account, Tenant, TenantAccountJoin, TenantStatus
|
|
|
|
|
from models.model import ApiToken, App, EndUser
|
|
|
|
|
from pydantic import BaseModel # type: ignore
|
|
|
|
|
@ -33,16 +34,34 @@ class FetchUserArg(BaseModel):
|
|
|
|
|
required: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: add auth jwt token check
|
|
|
|
|
def validate_app_token(
|
|
|
|
|
view: Optional[Callable] = None, *, fetch_user_arg: Optional[FetchUserArg] = None
|
|
|
|
|
):
|
|
|
|
|
def validate_app_token(view: Optional[Callable] = None):
|
|
|
|
|
def decorator(view_func):
|
|
|
|
|
@wraps(view_func)
|
|
|
|
|
def decorated_view(*args, **kwargs):
|
|
|
|
|
api_token = validate_and_get_api_token("app")
|
|
|
|
|
# Extract user info from Bearer token
|
|
|
|
|
auth_header = request.headers.get("Authorization")
|
|
|
|
|
if auth_header is None or " " not in auth_header:
|
|
|
|
|
raise Unauthorized("Authorization header must be provided and start with 'Bearer'")
|
|
|
|
|
|
|
|
|
|
auth_scheme, auth_token = auth_header.split(None, 1)
|
|
|
|
|
auth_scheme = auth_scheme.lower()
|
|
|
|
|
|
|
|
|
|
if auth_scheme != "bearer":
|
|
|
|
|
raise Unauthorized("Authorization scheme must be 'Bearer'")
|
|
|
|
|
|
|
|
|
|
# Decode the JWT token to extract user info
|
|
|
|
|
try:
|
|
|
|
|
decoded = PassportService().verify(auth_token)
|
|
|
|
|
user_id = decoded.get("user_id")
|
|
|
|
|
if not user_id:
|
|
|
|
|
raise Unauthorized("Invalid token: missing user_id")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise Unauthorized(f"Failed to extract user_id from token: {str(e)}")
|
|
|
|
|
|
|
|
|
|
# Get app model using hardcoded ID
|
|
|
|
|
app_id = "b278ba96-fa8e-48a8-b3e9-debe34468be0" # TODO: ytqh Replace with your actual hardcoded app ID
|
|
|
|
|
app_model = db.session.query(App).filter(App.id == app_id).first()
|
|
|
|
|
|
|
|
|
|
app_model = db.session.query(App).filter(App.id == api_token.app_id).first()
|
|
|
|
|
if not app_model:
|
|
|
|
|
raise Forbidden("The app no longer exists.")
|
|
|
|
|
|
|
|
|
|
@ -52,11 +71,7 @@ def validate_app_token(
|
|
|
|
|
if not app_model.enable_api:
|
|
|
|
|
raise Forbidden("The app's API service has been disabled.")
|
|
|
|
|
|
|
|
|
|
tenant = (
|
|
|
|
|
db.session.query(Tenant)
|
|
|
|
|
.filter(Tenant.id == app_model.tenant_id)
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
tenant = db.session.query(Tenant).filter(Tenant.id == app_model.tenant_id).first()
|
|
|
|
|
if tenant is None:
|
|
|
|
|
raise ValueError("Tenant does not exist.")
|
|
|
|
|
if tenant.status == TenantStatus.ARCHIVE:
|
|
|
|
|
@ -64,26 +79,7 @@ def validate_app_token(
|
|
|
|
|
|
|
|
|
|
kwargs["app_model"] = app_model
|
|
|
|
|
|
|
|
|
|
if fetch_user_arg:
|
|
|
|
|
if fetch_user_arg.fetch_from == WhereisUserArg.QUERY:
|
|
|
|
|
user_id = request.args.get("user")
|
|
|
|
|
elif fetch_user_arg.fetch_from == WhereisUserArg.JSON:
|
|
|
|
|
user_id = request.get_json().get("user")
|
|
|
|
|
elif fetch_user_arg.fetch_from == WhereisUserArg.FORM:
|
|
|
|
|
user_id = request.form.get("user")
|
|
|
|
|
else:
|
|
|
|
|
# use default-user
|
|
|
|
|
user_id = None
|
|
|
|
|
|
|
|
|
|
if not user_id and fetch_user_arg.required:
|
|
|
|
|
raise ValueError("Arg user must be provided.")
|
|
|
|
|
|
|
|
|
|
if user_id:
|
|
|
|
|
user_id = str(user_id)
|
|
|
|
|
|
|
|
|
|
kwargs["end_user"] = create_or_update_end_user_for_user_id(
|
|
|
|
|
app_model, user_id
|
|
|
|
|
)
|
|
|
|
|
kwargs["end_user"] = create_or_update_end_user_for_user_id(app_model, user_id)
|
|
|
|
|
|
|
|
|
|
return view_func(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
@ -108,27 +104,13 @@ def cloud_edition_billing_resource_check(resource: str, api_token_type: str):
|
|
|
|
|
documents_upload_quota = features.documents_upload_quota
|
|
|
|
|
|
|
|
|
|
if resource == "members" and 0 < members.limit <= members.size:
|
|
|
|
|
raise Forbidden(
|
|
|
|
|
"The number of members has reached the limit of your subscription."
|
|
|
|
|
)
|
|
|
|
|
raise Forbidden("The number of members has reached the limit of your subscription.")
|
|
|
|
|
elif resource == "apps" and 0 < apps.limit <= apps.size:
|
|
|
|
|
raise Forbidden(
|
|
|
|
|
"The number of apps has reached the limit of your subscription."
|
|
|
|
|
)
|
|
|
|
|
elif (
|
|
|
|
|
resource == "vector_space"
|
|
|
|
|
and 0 < vector_space.limit <= vector_space.size
|
|
|
|
|
):
|
|
|
|
|
raise Forbidden(
|
|
|
|
|
"The capacity of the vector space has reached the limit of your subscription."
|
|
|
|
|
)
|
|
|
|
|
elif (
|
|
|
|
|
resource == "documents"
|
|
|
|
|
and 0 < documents_upload_quota.limit <= documents_upload_quota.size
|
|
|
|
|
):
|
|
|
|
|
raise Forbidden(
|
|
|
|
|
"The number of documents has reached the limit of your subscription."
|
|
|
|
|
)
|
|
|
|
|
raise Forbidden("The number of apps has reached the limit of your subscription.")
|
|
|
|
|
elif resource == "vector_space" and 0 < vector_space.limit <= vector_space.size:
|
|
|
|
|
raise Forbidden("The capacity of the vector space has reached the limit of your subscription.")
|
|
|
|
|
elif resource == "documents" and 0 < documents_upload_quota.limit <= documents_upload_quota.size:
|
|
|
|
|
raise Forbidden("The number of documents has reached the limit of your subscription.")
|
|
|
|
|
else:
|
|
|
|
|
return view(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
@ -204,9 +186,7 @@ def validate_and_get_api_token(scope: str | None = None):
|
|
|
|
|
"""
|
|
|
|
|
auth_header = request.headers.get("Authorization")
|
|
|
|
|
if auth_header is None or " " not in auth_header:
|
|
|
|
|
raise Unauthorized(
|
|
|
|
|
"Authorization header must be provided and start with 'Bearer'"
|
|
|
|
|
)
|
|
|
|
|
raise Unauthorized("Authorization header must be provided and start with 'Bearer'")
|
|
|
|
|
|
|
|
|
|
auth_scheme, auth_token = auth_header.split(None, 1)
|
|
|
|
|
auth_scheme = auth_scheme.lower()
|
|
|
|
|
@ -221,10 +201,7 @@ def validate_and_get_api_token(scope: str | None = None):
|
|
|
|
|
update(ApiToken)
|
|
|
|
|
.where(
|
|
|
|
|
ApiToken.token == auth_token,
|
|
|
|
|
(
|
|
|
|
|
ApiToken.last_used_at.is_(None)
|
|
|
|
|
| (ApiToken.last_used_at < cutoff_time)
|
|
|
|
|
),
|
|
|
|
|
(ApiToken.last_used_at.is_(None) | (ApiToken.last_used_at < cutoff_time)),
|
|
|
|
|
ApiToken.type == scope,
|
|
|
|
|
)
|
|
|
|
|
.values(last_used_at=current_time)
|
|
|
|
|
@ -234,9 +211,7 @@ def validate_and_get_api_token(scope: str | None = None):
|
|
|
|
|
api_token = result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
if not api_token:
|
|
|
|
|
stmt = select(ApiToken).where(
|
|
|
|
|
ApiToken.token == auth_token, ApiToken.type == scope
|
|
|
|
|
)
|
|
|
|
|
stmt = select(ApiToken).where(ApiToken.token == auth_token, ApiToken.type == scope)
|
|
|
|
|
api_token = session.scalar(stmt)
|
|
|
|
|
if not api_token:
|
|
|
|
|
raise Unauthorized("Access token is invalid")
|
|
|
|
|
@ -246,9 +221,7 @@ def validate_and_get_api_token(scope: str | None = None):
|
|
|
|
|
return api_token
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_or_update_end_user_for_user_id(
|
|
|
|
|
app_model: App, user_id: Optional[str] = None
|
|
|
|
|
) -> EndUser:
|
|
|
|
|
def create_or_update_end_user_for_user_id(app_model: App, user_id: Optional[str] = None) -> EndUser:
|
|
|
|
|
"""
|
|
|
|
|
Create or update session terminal based on user ID.
|
|
|
|
|
"""
|
|
|
|
|
|