feat: add new table of end user oauth (#28351)

This commit is contained in:
Charles Yao 2025-11-24 01:28:40 -06:00 committed by GitHub
commit 26dc4d43bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 148 additions and 0 deletions

View File

@ -0,0 +1,91 @@
"""add enduser authentication provider
Revision ID: a7b4e8f2c9d1
Revises: 132392a2635f
Create Date: 2025-11-18 14:00:00.000000
"""
import models as models
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "a7b4e8f2c9d1"
down_revision = "132392a2635f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"tool_enduser_authentication_providers",
sa.Column(
"id",
models.types.StringUUID(),
nullable=False,
),
sa.Column(
"name",
sa.String(length=256),
server_default="API KEY 1",
nullable=False,
),
sa.Column("tenant_id", models.types.StringUUID(), nullable=False),
sa.Column("end_user_id", models.types.StringUUID(), nullable=False),
sa.Column("provider", sa.Text(), nullable=False),
sa.Column("encrypted_credentials", sa.Text(), default="", nullable=False),
sa.Column(
"created_at",
sa.DateTime(),
server_default=sa.func.current_timestamp(),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(),
server_default=sa.func.current_timestamp(),
nullable=False,
),
sa.Column(
"credential_type",
sa.String(length=32),
server_default="api-key",
nullable=False,
),
sa.Column("expires_at", sa.BigInteger(), server_default=sa.text("-1"), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"end_user_id",
"provider",
),
)
op.create_index(
op.f("ix_tool_enduser_authentication_providers_end_user_id"),
"tool_enduser_authentication_providers",
["end_user_id"],
unique=False,
)
op.create_index(
op.f("ix_tool_enduser_authentication_providers_provider"),
"tool_enduser_authentication_providers",
["provider"],
unique=False,
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
op.f("ix_tool_enduser_authentication_providers_provider"),
table_name="tool_enduser_authentication_providers",
)
op.drop_index(
op.f("ix_tool_enduser_authentication_providers_end_user_id"),
table_name="tool_enduser_authentication_providers",
)
op.drop_table("tool_enduser_authentication_providers")
# ### end Alembic commands ###

View File

@ -80,6 +80,7 @@ from .task import CeleryTask, CeleryTaskSet
from .tools import (
ApiToolProvider,
BuiltinToolProvider,
EndUserAuthenticationProvider,
ToolConversationVariables,
ToolFile,
ToolLabelBinding,
@ -149,6 +150,7 @@ __all__ = [
"DocumentSegment",
"Embedding",
"EndUser",
"EndUserAuthenticationProvider",
"ExternalKnowledgeApis",
"ExternalKnowledgeBindings",
"IconType",

View File

@ -9,9 +9,11 @@ from deprecated import deprecated
from sqlalchemy import ForeignKey, String, func
from sqlalchemy.orm import Mapped, mapped_column
from core.plugin.entities.plugin_daemon import CredentialType
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_bundle import ApiToolBundle
from core.tools.entities.tool_entities import ApiProviderSchemaType, WorkflowToolParameterConfiguration
from libs.uuid_utils import uuidv7
from .base import TypeBase
from .engine import db
@ -109,6 +111,59 @@ class BuiltinToolProvider(TypeBase):
return cast(dict[str, Any], json.loads(self.encrypted_credentials))
class EndUserAuthenticationProvider(TypeBase):
"""
This table stores the authentication credentials for end users in tools.
Mimics the BuiltinToolProvider structure but for end users instead of tenants.
"""
__tablename__ = "tool_enduser_authentication_providers"
__table_args__ = (
sa.UniqueConstraint("end_user_id", "provider"),
)
# id of the authentication provider
id: Mapped[str] = mapped_column(StringUUID, primary_key=True, default=lambda: str(uuid4()), init=False)
name: Mapped[str] = mapped_column(
String(256),
nullable=False,
default="API KEY 1",
)
# id of the tenant
tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# id of the end user
end_user_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
# name of the tool provider
provider: Mapped[str] = mapped_column(LongText, nullable=False)
# encrypted credentials for the end user
encrypted_credentials: Mapped[str] = mapped_column(LongText, nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(
sa.DateTime, nullable=False, server_default=func.current_timestamp(), init=False
)
updated_at: Mapped[datetime] = mapped_column(
sa.DateTime,
nullable=False,
server_default=func.current_timestamp(),
onupdate=func.current_timestamp(),
init=False,
)
# credential type, e.g., "api-key", "oauth2"
credential_type: Mapped[CredentialType] = mapped_column(
String(32), nullable=False, default=CredentialType.API_KEY
)
# Unix timestamp in seconds since epoch (1970-01-01 UTC); -1 indicates no expiration
expires_at: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, default=-1)
@property
def credentials(self) -> dict[str, Any]:
if not self.encrypted_credentials:
return {}
try:
return cast(dict[str, Any], json.loads(self.encrypted_credentials))
except json.JSONDecodeError:
return {}
class ApiToolProvider(TypeBase):
"""
The table stores the api providers.