mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 06:41:07 +08:00
135 lines
5.7 KiB
Python
135 lines
5.7 KiB
Python
"""Bind Web execution selections to persisted identity and the server-selected Workflow."""
|
|
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from pydantic import ValidationError
|
|
|
|
from clients.plugin.auth import PluginCredentialAuthClient
|
|
from context.connection_execution import ConnectionExecutionContext, WorkspaceConnectionScope
|
|
from core.db.session_factory import get_session_maker
|
|
from models.model import Account, App, AppMode, EndUser
|
|
from models.workflow import Workflow
|
|
from repositories.end_user_connection_repository import EndUserConnectionRepository
|
|
from services.end_user_connection_adapters import PluginApiKeyCredentialGateway
|
|
from services.end_user_connection_identity import connection_context_from_web_passport
|
|
from services.end_user_connection_revision_adapters import PluginConnectionRevisions
|
|
from services.end_user_connections.errors import (
|
|
ConnectionConfigurationError,
|
|
ConnectionIdentityError,
|
|
ConnectionRevisionChangedError,
|
|
)
|
|
from services.end_user_connections.execution import ConnectionRunInput
|
|
from services.workflow.tool_credentials import requires_end_user_connections
|
|
|
|
|
|
def prepare_web_connection_execution(
|
|
*, app: App, user: Account | EndUser, workflow: Workflow, args: Mapping[str, Any]
|
|
) -> ConnectionExecutionContext | None:
|
|
required = requires_end_user_connections(workflow.graph_dict)
|
|
try:
|
|
command = ConnectionRunInput.model_validate(
|
|
{"connection_revision": args.get("connection_revision"), "connections": args.get("connections")}
|
|
)
|
|
except ValidationError:
|
|
raise ValueError("Invalid connection execution payload.") from None
|
|
repository = EndUserConnectionRepository(session_factory=get_session_maker())
|
|
if not required and command.connection_revision is not None:
|
|
PluginConnectionRevisions(
|
|
providers=PluginApiKeyCredentialGateway(auth=PluginCredentialAuthClient()), snapshots=repository
|
|
).require(app.tenant_id, app.id, workflow.graph_dict, command.connection_revision)
|
|
if not required and command.connections:
|
|
raise ConnectionConfigurationError()
|
|
if not isinstance(user, EndUser):
|
|
if required:
|
|
raise ConnectionIdentityError()
|
|
return None
|
|
identity = connection_context_from_web_passport(app=app, end_user=user)
|
|
if workflow.tenant_id != identity.tenant_id or workflow.app_id != identity.app_id:
|
|
raise ConnectionIdentityError()
|
|
|
|
if not required:
|
|
repository.validate_execution_identity(identity, args.get("conversation_id"))
|
|
return None
|
|
|
|
from extensions.ext_application_services import application_services
|
|
|
|
services = application_services()
|
|
conversation_id = args.get("conversation_id")
|
|
has_explicit_connections = "connections" in args
|
|
if app.mode == AppMode.ADVANCED_CHAT and conversation_id and not has_explicit_connections:
|
|
command = command.model_copy(
|
|
update={"connections": services.connection_selections.restore(identity, workflow.id, conversation_id)}
|
|
)
|
|
return services.end_user_connection_execution.prepare(
|
|
identity,
|
|
command,
|
|
workflow_id=workflow.id,
|
|
conversation_id=args.get("conversation_id"),
|
|
)
|
|
|
|
|
|
def validate_connection_execution(
|
|
context: ConnectionExecutionContext,
|
|
*,
|
|
app: App,
|
|
user: Account | EndUser,
|
|
workflow: Workflow,
|
|
conversation_id: str | None,
|
|
repository: EndUserConnectionRepository,
|
|
) -> None:
|
|
"""Reconstruct authority after serialization without selecting a newer publication."""
|
|
if context.workspace is not None:
|
|
if not isinstance(user, Account):
|
|
raise ConnectionIdentityError()
|
|
from extensions.ext_application_services import application_services
|
|
|
|
application_services().installed_apps.connections.validate_execution(
|
|
context,
|
|
app_id=app.id,
|
|
tenant_id=app.tenant_id,
|
|
account_id=user.id,
|
|
workflow_id=workflow.id,
|
|
conversation_id=conversation_id,
|
|
)
|
|
return
|
|
if not isinstance(user, EndUser):
|
|
raise ConnectionIdentityError()
|
|
identity = connection_context_from_web_passport(app=app, end_user=user)
|
|
if (
|
|
identity.tenant_id != context.tenant_id
|
|
or identity.app_id != context.app_id
|
|
or identity.end_user_id != context.end_user_id
|
|
or identity.account_id != context.account_id
|
|
or workflow.id != context.workflow_id
|
|
or workflow.tenant_id != identity.tenant_id
|
|
or workflow.app_id != identity.app_id
|
|
):
|
|
raise ConnectionIdentityError()
|
|
if workflow.version == Workflow.VERSION_DRAFT:
|
|
raise ConnectionRevisionChangedError()
|
|
repository.validate_execution_identity(identity, conversation_id)
|
|
|
|
|
|
def prepare_installed_connection_execution(
|
|
*, app: App, user: Account | EndUser, workflow: Workflow, args: Mapping[str, Any]
|
|
) -> ConnectionExecutionContext | None:
|
|
required = requires_end_user_connections(workflow.graph_dict)
|
|
if not required and not args.get("connections") and not args.get("connection_revision"):
|
|
return None
|
|
if required and args.get("connection_revision") is None:
|
|
raise ConnectionConfigurationError()
|
|
if not isinstance(user, Account) or not args.get("_installed_app_id") or not args.get("_installed_workspace_id"):
|
|
raise ConnectionIdentityError()
|
|
if workflow.app_id != app.id or workflow.tenant_id != app.tenant_id:
|
|
raise ConnectionIdentityError()
|
|
from extensions.ext_application_services import application_services
|
|
|
|
return application_services().installed_apps.connections.prepare(
|
|
WorkspaceConnectionScope(
|
|
installed_app_id=args["_installed_app_id"], tenant_id=args["_installed_workspace_id"], account_id=user.id
|
|
),
|
|
workflow_id=workflow.id,
|
|
args=args,
|
|
)
|