Files
dify/api/controllers/console/dify_builder_policy.py

121 lines
5.3 KiB
Python

"""Authenticated Console Workspace policy and actor/App availability APIs."""
from flask import request
from flask_restx import Resource
from pydantic import ValidationError
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
from configs import dify_config
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.flask_admission import console_account_admission
from core.dify_builder.models import Actor
from extensions.application_services.dify_builder_cognition_policy import (
build_cognition_policy_service,
policy_projection,
)
from libs.exception import BaseHTTPException
from libs.external_api import error_responses
from libs.helper import dump_response
from machinery.context import RequestContext
from services.dify_builder.application.cognition_policy import CognitionPolicyDeniedError
from services.dify_builder.service import AppAccess
from services.dify_builder.wiring import _authorize_app
from .dify_builder_fields import DifyBuilderFeatureUnavailableResponse
from .dify_builder_policy_fields import (
DifyBuilderAvailabilityResponse,
DifyBuilderPolicyPayload,
DifyBuilderPolicyResponse,
)
class BuilderPolicyConflictError(BaseHTTPException):
error_code = "builder_policy_conflict"
code = 409
description = "Workspace Builder policy changed; reload before saving."
register_schema_models(console_ns, DifyBuilderPolicyPayload)
register_response_schema_models(
console_ns, DifyBuilderPolicyResponse, DifyBuilderAvailabilityResponse, DifyBuilderFeatureUnavailableResponse
)
def _actor(context: RequestContext) -> Actor:
return Actor(tenant_id=context.active_workspace_id, account_id=context.account_id)
def _raise_policy_error(error: CognitionPolicyDeniedError):
if error.reason == "policy_revision_conflict":
raise BuilderPolicyConflictError() from error
raise Forbidden("Workspace administrator permission is required.") from error
@console_ns.route("/workspaces/current/dify-builder/policy")
class DifyBuilderPolicyApi(Resource):
@console_ns.response(200, "Success", console_ns.models[DifyBuilderPolicyResponse.__name__])
@console_ns.response(
403, "App Builder is disabled", console_ns.models[DifyBuilderFeatureUnavailableResponse.__name__]
)
@console_account_admission()
def get(self, context: RequestContext):
"""Read policy for an admitted workspace administrator when Builder is enabled."""
if not dify_config.DIFY_BUILDER_ENABLED:
return {"code": "feature_unavailable"}, 403
actor = _actor(context)
service = build_cognition_policy_service()
try:
result = policy_projection(service.availability(actor, admin=True))
except CognitionPolicyDeniedError as error:
_raise_policy_error(error)
return dump_response(DifyBuilderPolicyResponse, result), 200
@error_responses(BuilderPolicyConflictError)
@console_ns.expect(console_ns.models[DifyBuilderPolicyPayload.__name__])
@console_ns.response(200, "Success", console_ns.models[DifyBuilderPolicyResponse.__name__])
@console_ns.response(
403, "App Builder is disabled", console_ns.models[DifyBuilderFeatureUnavailableResponse.__name__]
)
@console_account_admission()
def put(self, context: RequestContext):
"""Validate and save policy for an admitted workspace administrator when Builder is enabled."""
if not dify_config.DIFY_BUILDER_ENABLED:
return {"code": "feature_unavailable"}, 403
actor = _actor(context)
try:
payload = DifyBuilderPolicyPayload.model_validate(request.get_json(silent=True))
service = build_cognition_policy_service()
result = policy_projection(
service.put_status(actor, payload.policy(), expected_revision=payload.expected_revision)
)
except (ValidationError, ValueError) as error:
raise BadRequest("Invalid Workspace Builder policy.") from error
except CognitionPolicyDeniedError as error:
_raise_policy_error(error)
return dump_response(DifyBuilderPolicyResponse, result), 200
@console_ns.route("/apps/<uuid:app_id>/dify-builder/availability")
class DifyBuilderAvailabilityApi(Resource):
@console_ns.response(200, "Success", console_ns.models[DifyBuilderAvailabilityResponse.__name__])
@console_ns.response(
403, "App Builder is disabled", console_ns.models[DifyBuilderFeatureUnavailableResponse.__name__]
)
@console_account_admission()
def get(self, context: RequestContext, app_id):
"""Inspect model readiness for an admitted app editor when Builder is enabled."""
if not dify_config.DIFY_BUILDER_ENABLED:
return {"code": "feature_unavailable"}, 403
actor = _actor(context)
from core.dify_builder.errors import NotFoundError
try:
_authorize_app(actor, str(app_id), AppAccess.EDIT)
result = policy_projection(build_cognition_policy_service().availability(actor))
except NotFoundError as error:
raise NotFound() from error
except CognitionPolicyDeniedError as error:
_raise_policy_error(error)
return dump_response(DifyBuilderAvailabilityResponse, result), 200