-
Notifications
You must be signed in to change notification settings - Fork 18.5k
feat: add new table of end user oauth #28351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/end-user-oauth
Are you sure you want to change the base?
Changes from all commits
c493e08
adf673d
76069b5
5e93a61
39de9e7
6cd7ab4
153609b
bbd466e
8f6937e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,95 @@ | ||||||
| """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(), | ||||||
| server_default=sa.text("uuid_generate_v4()"), | ||||||
| nullable=False, | ||||||
| ), | ||||||
| sa.Column( | ||||||
| "name", | ||||||
| sa.String(length=256), | ||||||
| server_default=sa.text("'API KEY 1'::character varying"), | ||||||
| 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.String(length=256), nullable=False), | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| sa.Column("encrypted_credentials", sa.Text(), nullable=True), | ||||||
| sa.Column( | ||||||
| "created_at", | ||||||
| sa.DateTime(), | ||||||
| server_default=sa.text("CURRENT_TIMESTAMP(0)"), | ||||||
| nullable=False, | ||||||
| ), | ||||||
| sa.Column( | ||||||
| "updated_at", | ||||||
| sa.DateTime(), | ||||||
| server_default=sa.text("CURRENT_TIMESTAMP(0)"), | ||||||
| nullable=False, | ||||||
| ), | ||||||
| sa.Column( | ||||||
| "credential_type", | ||||||
| sa.String(length=32), | ||||||
| server_default=sa.text("'api-key'::character varying"), | ||||||
| nullable=False, | ||||||
| ), | ||||||
| sa.Column("expires_at", sa.BigInteger(), server_default=sa.text("-1"), nullable=False), | ||||||
| sa.PrimaryKeyConstraint("id", name="tool_enduser_authentication_provider_pkey"), | ||||||
| sa.UniqueConstraint( | ||||||
| "tenant_id", | ||||||
| "provider", | ||||||
| "end_user_id", | ||||||
| "name", | ||||||
| name="unique_enduser_authentication_provider", | ||||||
| ), | ||||||
| ) | ||||||
| op.create_index( | ||||||
| "tool_enduser_authentication_provider_tenant_id_idx", | ||||||
| "tool_enduser_authentication_providers", | ||||||
| ["tenant_id"], | ||||||
| unique=False, | ||||||
| ) | ||||||
| op.create_index( | ||||||
| "tool_enduser_authentication_provider_end_user_id_idx", | ||||||
| "tool_enduser_authentication_providers", | ||||||
| ["end_user_id"], | ||||||
| unique=False, | ||||||
| ) | ||||||
| # ### end Alembic commands ### | ||||||
|
|
||||||
|
|
||||||
| def downgrade(): | ||||||
| # ### commands auto generated by Alembic - please adjust! ### | ||||||
| op.drop_index( | ||||||
| "tool_enduser_authentication_provider_end_user_id_idx", | ||||||
| table_name="tool_enduser_authentication_providers", | ||||||
| ) | ||||||
| op.drop_index( | ||||||
| "tool_enduser_authentication_provider_tenant_id_idx", | ||||||
| table_name="tool_enduser_authentication_providers", | ||||||
| ) | ||||||
| op.drop_table("tool_enduser_authentication_providers") | ||||||
| # ### end Alembic commands ### | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,9 +9,11 @@ | |||||||||||||
| 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,56 @@ def credentials(self) -> dict[str, Any]: | |||||||||||||
| 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", "name"), | ||||||||||||||
| ) | ||||||||||||||
|
Comment on lines
+121
to
+123
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The unique constraint in the model definition is
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| # id of the authentication provider | ||||||||||||||
| id: Mapped[str] = mapped_column(StringUUID, primary_key=True, default=uuidv7, init=False) | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||
| name: Mapped[str] = mapped_column( | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, enforcing name uniqueness for a Ideally, we should rely on IDs as the primary identifier and treat names merely as user-facing hints. Therefore, strict uniqueness shouldn't be required. If a user creates duplicate names, they should bear the responsibility for any ambiguity. Enforcing name uniqueness has caused implementation issues in the past, such as with credential management in the EE version. Furthermore, it complicates name generation. That said, I suggest discussing this with the PM. I’ve skimmed the PRD and found no explicit requirement for name uniqueness.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, should we remove the unique constraint at all? Since ID is enough for uniqueness |
||||||||||||||
| String(256), | ||||||||||||||
| nullable=False, | ||||||||||||||
| default="API KEY 1", | ||||||||||||||
| index=True | ||||||||||||||
| ) | ||||||||||||||
| # 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, index=True) | ||||||||||||||
| # name of the tool provider | ||||||||||||||
| provider: Mapped[str] = mapped_column(LongText, nullable=False, index=True) | ||||||||||||||
|
Comment on lines
+127
to
+138
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several inconsistencies between the model definition and the Alembic migration regarding indexes:
These discrepancies can lead to performance issues and schema drift. Please ensure the model definition and the migration are synchronized. It's recommended to make the model the source of truth and adjust the migration accordingly. |
||||||||||||||
| # encrypted credentials for the end user | ||||||||||||||
| encrypted_credentials: Mapped[str] = mapped_column(LongText, nullable=False, default="") | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||
| created_at: Mapped[datetime] = mapped_column( | ||||||||||||||
| sa.DateTime, nullable=False, default=datetime.now, init=False | ||||||||||||||
| ) | ||||||||||||||
|
Comment on lines
+141
to
+143
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||
| updated_at: Mapped[datetime] = mapped_column( | ||||||||||||||
| sa.DateTime, | ||||||||||||||
| nullable=False, | ||||||||||||||
| default=datetime.now, | ||||||||||||||
| 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 | ||||||||||||||
| ) | ||||||||||||||
| expires_at: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, default=-1) | ||||||||||||||
asukaminato0721 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
|
||||||||||||||
| @property | ||||||||||||||
| def credentials(self) -> dict[str, Any]: | ||||||||||||||
| if not self.encrypted_credentials: | ||||||||||||||
| return {} | ||||||||||||||
| return cast(dict[str, Any], json.loads(self.encrypted_credentials)) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class ApiToolProvider(TypeBase): | ||||||||||||||
| """ | ||||||||||||||
| The table stores the api providers. | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would break MySQL support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will edit the script at once after all comments got resolved at the new provider