From b85f7e31e2647087eac1ceee0cfad191408d8a95 Mon Sep 17 00:00:00 2001 From: StringKE Date: Sat, 2 May 2026 16:33:16 +0400 Subject: [PATCH 1/2] feat(auth): add enterprise SSO and RBAC --- .../src/components/settings-sidebar/index.vue | 3 +- apps/web/src/i18n/locales/en.json | 49 + apps/web/src/i18n/locales/zh.json | 49 + apps/web/src/pages/iam/index.vue | 1091 +++ apps/web/src/pages/login/index.vue | 69 +- apps/web/src/pages/login/sso-callback.vue | 41 + apps/web/src/pages/profile/index.vue | 1 - apps/web/src/router.ts | 16 + apps/web/src/store/user.ts | 2 - cmd/agent/app.go | 54 +- cmd/agent/module.go | 2 + .../migrations/0077_iam_sso_rbac.down.sql | 64 + .../migrations/0077_iam_sso_rbac.up.sql | 304 + db/postgres/queries/acl.sql | 4 +- db/postgres/queries/bind.sql | 8 +- db/postgres/queries/channel_identities.sql | 24 +- db/postgres/queries/channels.sql | 6 +- db/postgres/queries/iam.sql | 282 + db/postgres/queries/messages.sql | 22 +- db/postgres/queries/user_provider_oauth.sql | 10 +- db/postgres/queries/users.sql | 79 +- .../migrations/0003_iam_sso_rbac.down.sql | 50 + db/sqlite/migrations/0003_iam_sso_rbac.up.sql | 342 + db/sqlite/queries/acl.sql | 4 +- db/sqlite/queries/bind.sql | 8 +- db/sqlite/queries/channel_identities.sql | 24 +- db/sqlite/queries/channels.sql | 6 +- db/sqlite/queries/iam.sql | 318 + db/sqlite/queries/messages.sql | 22 +- db/sqlite/queries/user_provider_oauth.sql | 10 +- db/sqlite/queries/users.sql | 107 +- docs/sdlc/2026-05-02/iam-sso-rbac/plan.md | 548 ++ docs/sdlc/2026-05-02/iam-sso-rbac/spec.md | 850 ++ go.mod | 33 +- go.sum | 60 + internal/accounts/service.go | 56 - internal/accounts/types.go | 3 - internal/bind/service.go | 2 +- internal/bind/service_test.go | 4 +- internal/bots/service.go | 91 +- internal/bots/service_test.go | 31 +- internal/channel/identities/service.go | 4 +- internal/channel/inbound/channel.go | 41 +- internal/channel/inbound/channel_test.go | 106 + internal/channel/service.go | 2 +- internal/command/access.go | 4 +- internal/command/handler.go | 51 +- internal/command/handler_test.go | 67 +- internal/command/registry.go | 6 +- internal/db/postgres/sqlc/acl.sql.go | 6 +- internal/db/postgres/sqlc/bind.sql.go | 26 +- internal/db/postgres/sqlc/bots.sql.go | 2 +- .../db/postgres/sqlc/browser_contexts.sql.go | 2 +- .../postgres/sqlc/channel_identities.sql.go | 56 +- .../db/postgres/sqlc/channel_routes.sql.go | 2 +- internal/db/postgres/sqlc/channels.sql.go | 22 +- .../db/postgres/sqlc/compaction_logs.sql.go | 2 +- internal/db/postgres/sqlc/containers.sql.go | 2 +- .../db/postgres/sqlc/conversations.sql.go | 2 +- internal/db/postgres/sqlc/db.go | 2 +- .../db/postgres/sqlc/email_bindings.sql.go | 2 +- .../postgres/sqlc/email_oauth_tokens.sql.go | 2 +- internal/db/postgres/sqlc/email_outbox.sql.go | 2 +- .../db/postgres/sqlc/email_providers.sql.go | 2 +- internal/db/postgres/sqlc/events.sql.go | 2 +- .../db/postgres/sqlc/heartbeat_logs.sql.go | 2 +- internal/db/postgres/sqlc/iam.sql.go | 1195 +++ internal/db/postgres/sqlc/mcp.sql.go | 2 +- internal/db/postgres/sqlc/mcp_oauth.sql.go | 2 +- internal/db/postgres/sqlc/media.sql.go | 2 +- .../db/postgres/sqlc/memory_providers.sql.go | 2 +- internal/db/postgres/sqlc/messages.sql.go | 24 +- internal/db/postgres/sqlc/models.go | 254 +- internal/db/postgres/sqlc/models.sql.go | 2 +- internal/db/postgres/sqlc/network.sql.go | 2 +- .../db/postgres/sqlc/provider_oauth.sql.go | 2 +- internal/db/postgres/sqlc/schedule.sql.go | 2 +- .../db/postgres/sqlc/schedule_logs.sql.go | 2 +- .../db/postgres/sqlc/search_providers.sql.go | 2 +- .../db/postgres/sqlc/session_events.sql.go | 2 +- internal/db/postgres/sqlc/session_info.sql.go | 2 +- internal/db/postgres/sqlc/sessions.sql.go | 2 +- internal/db/postgres/sqlc/settings.sql.go | 2 +- internal/db/postgres/sqlc/snapshots.sql.go | 2 +- internal/db/postgres/sqlc/token_usage.sql.go | 2 +- .../db/postgres/sqlc/tool_approval.sql.go | 2 +- .../postgres/sqlc/user_provider_oauth.sql.go | 24 +- internal/db/postgres/sqlc/users.sql.go | 231 +- internal/db/postgres/sqlc/versions.sql.go | 2 +- internal/db/postgres/store/accounts.go | 84 +- internal/db/sqlite/sqlc/acl.sql.go | 6 +- internal/db/sqlite/sqlc/bind.sql.go | 26 +- internal/db/sqlite/sqlc/bots.sql.go | 2 +- .../db/sqlite/sqlc/browser_contexts.sql.go | 2 +- .../db/sqlite/sqlc/channel_identities.sql.go | 56 +- internal/db/sqlite/sqlc/channel_routes.sql.go | 2 +- internal/db/sqlite/sqlc/channels.sql.go | 22 +- .../db/sqlite/sqlc/compaction_logs.sql.go | 2 +- internal/db/sqlite/sqlc/containers.sql.go | 2 +- internal/db/sqlite/sqlc/conversations.sql.go | 2 +- internal/db/sqlite/sqlc/db.go | 2 +- internal/db/sqlite/sqlc/email_bindings.sql.go | 2 +- .../db/sqlite/sqlc/email_oauth_tokens.sql.go | 2 +- internal/db/sqlite/sqlc/email_outbox.sql.go | 2 +- .../db/sqlite/sqlc/email_providers.sql.go | 2 +- internal/db/sqlite/sqlc/events.sql.go | 2 +- internal/db/sqlite/sqlc/heartbeat_logs.sql.go | 2 +- internal/db/sqlite/sqlc/iam.sql.go | 1262 +++ internal/db/sqlite/sqlc/mcp.sql.go | 2 +- internal/db/sqlite/sqlc/mcp_oauth.sql.go | 2 +- internal/db/sqlite/sqlc/media.sql.go | 2 +- .../db/sqlite/sqlc/memory_providers.sql.go | 2 +- internal/db/sqlite/sqlc/messages.sql.go | 24 +- internal/db/sqlite/sqlc/models.go | 254 +- internal/db/sqlite/sqlc/models.sql.go | 2 +- internal/db/sqlite/sqlc/network.sql.go | 2 +- internal/db/sqlite/sqlc/provider_oauth.sql.go | 2 +- internal/db/sqlite/sqlc/schedule.sql.go | 2 +- internal/db/sqlite/sqlc/schedule_logs.sql.go | 2 +- .../db/sqlite/sqlc/search_providers.sql.go | 2 +- internal/db/sqlite/sqlc/session_events.sql.go | 2 +- internal/db/sqlite/sqlc/session_info.sql.go | 2 +- internal/db/sqlite/sqlc/sessions.sql.go | 2 +- internal/db/sqlite/sqlc/settings.sql.go | 2 +- internal/db/sqlite/sqlc/snapshots.sql.go | 2 +- internal/db/sqlite/sqlc/token_usage.sql.go | 2 +- internal/db/sqlite/sqlc/tool_approval.sql.go | 2 +- .../db/sqlite/sqlc/user_provider_oauth.sql.go | 24 +- internal/db/sqlite/sqlc/users.sql.go | 257 +- internal/db/sqlite/sqlc/versions.sql.go | 2 +- internal/db/sqlite/store/accounts.go | 90 +- internal/db/sqlite/store/iam_queries.go | 414 + internal/db/sqlite/store/queries.go | 320 +- internal/db/store/contracts.go | 3 - internal/db/store/queries.go | 93 +- internal/handlers/auth.go | 485 +- internal/handlers/auth_session_store.go | 81 + internal/handlers/auth_test.go | 357 + internal/handlers/handler_helpers.go | 8 +- internal/handlers/iam.go | 973 +++ internal/handlers/message.go | 15 +- internal/handlers/skills_test.go | 15 +- internal/handlers/users.go | 74 +- internal/iam/accounts/service.go | 380 + internal/iam/accounts/service_test.go | 286 + internal/iam/accounts/types.go | 112 + internal/iam/auth/jwt.go | 236 + internal/iam/auth/jwt_test.go | 103 + internal/iam/auth/session_middleware.go | 37 + internal/iam/auth/session_middleware_test.go | 111 + internal/iam/rbac/bootstrap.go | 69 + internal/iam/rbac/bootstrap_test.go | 65 + internal/iam/rbac/service.go | 115 + internal/iam/rbac/service_test.go | 148 + internal/iam/rbac/store.go | 47 + internal/iam/rbac/types.go | 52 + internal/iam/sso/cookie.go | 142 + internal/iam/sso/cookie_test.go | 66 + internal/iam/sso/db_service.go | 473 ++ internal/iam/sso/login_code.go | 22 + internal/iam/sso/login_code_test.go | 22 + internal/iam/sso/oidc.go | 188 + internal/iam/sso/oidc_test.go | 83 + internal/iam/sso/saml.go | 100 + internal/iam/sso/saml_test.go | 42 + internal/iam/sso/service.go | 132 + internal/iam/sso/service_test.go | 195 + internal/iam/sso/types.go | 171 + internal/memory/adapters/builtin/builtin.go | 11 +- internal/providers/oauth.go | 2 +- internal/server/server.go | 3 + internal/tui/api.go | 1 - packages/sdk/src/@pinia/colada.gen.ts | 447 +- packages/sdk/src/index.ts | 4 +- packages/sdk/src/sdk.gen.ts | 243 +- packages/sdk/src/types.gen.ts | 1401 +++- spec/docs.go | 7318 ++++++++++------- spec/swagger.json | 7318 ++++++++++------- spec/swagger.yaml | 7230 +++++++++------- 179 files changed, 29239 insertions(+), 10071 deletions(-) create mode 100644 apps/web/src/pages/iam/index.vue create mode 100644 apps/web/src/pages/login/sso-callback.vue create mode 100644 db/postgres/migrations/0077_iam_sso_rbac.down.sql create mode 100644 db/postgres/migrations/0077_iam_sso_rbac.up.sql create mode 100644 db/postgres/queries/iam.sql create mode 100644 db/sqlite/migrations/0003_iam_sso_rbac.down.sql create mode 100644 db/sqlite/migrations/0003_iam_sso_rbac.up.sql create mode 100644 db/sqlite/queries/iam.sql create mode 100644 docs/sdlc/2026-05-02/iam-sso-rbac/plan.md create mode 100644 docs/sdlc/2026-05-02/iam-sso-rbac/spec.md create mode 100644 internal/db/postgres/sqlc/iam.sql.go create mode 100644 internal/db/sqlite/sqlc/iam.sql.go create mode 100644 internal/db/sqlite/store/iam_queries.go create mode 100644 internal/handlers/auth_session_store.go create mode 100644 internal/handlers/auth_test.go create mode 100644 internal/handlers/iam.go create mode 100644 internal/iam/accounts/service.go create mode 100644 internal/iam/accounts/service_test.go create mode 100644 internal/iam/accounts/types.go create mode 100644 internal/iam/auth/jwt.go create mode 100644 internal/iam/auth/jwt_test.go create mode 100644 internal/iam/auth/session_middleware.go create mode 100644 internal/iam/auth/session_middleware_test.go create mode 100644 internal/iam/rbac/bootstrap.go create mode 100644 internal/iam/rbac/bootstrap_test.go create mode 100644 internal/iam/rbac/service.go create mode 100644 internal/iam/rbac/service_test.go create mode 100644 internal/iam/rbac/store.go create mode 100644 internal/iam/rbac/types.go create mode 100644 internal/iam/sso/cookie.go create mode 100644 internal/iam/sso/cookie_test.go create mode 100644 internal/iam/sso/db_service.go create mode 100644 internal/iam/sso/login_code.go create mode 100644 internal/iam/sso/login_code_test.go create mode 100644 internal/iam/sso/oidc.go create mode 100644 internal/iam/sso/oidc_test.go create mode 100644 internal/iam/sso/saml.go create mode 100644 internal/iam/sso/saml_test.go create mode 100644 internal/iam/sso/service.go create mode 100644 internal/iam/sso/service_test.go create mode 100644 internal/iam/sso/types.go diff --git a/apps/web/src/components/settings-sidebar/index.vue b/apps/web/src/components/settings-sidebar/index.vue index b03b97af9..aa678c401 100644 --- a/apps/web/src/components/settings-sidebar/index.vue +++ b/apps/web/src/components/settings-sidebar/index.vue @@ -63,7 +63,7 @@ import { computed, inject, type Component } from 'vue' import { storeToRefs } from 'pinia' import { useRouter, useRoute } from 'vue-router' import { useI18n } from 'vue-i18n' -import { ChevronLeft, Bot, Boxes, Globe, Brain, Volume2, AudioLines, Mail, AppWindow, ChartLine, User, Store, Info } from 'lucide-vue-next' +import { ChevronLeft, Bot, Boxes, Globe, Brain, Volume2, AudioLines, Mail, AppWindow, ShieldCheck, ChartLine, User, Store, Info } from 'lucide-vue-next' import { useChatSelectionStore } from '@/store/chat-selection' import { Sidebar, @@ -119,6 +119,7 @@ const allNavItems: { title: string; name: string; icon: Component }[] = [ { title: t('sidebar.transcription'), name: 'transcription', icon: AudioLines }, { title: t('sidebar.email'), name: 'email', icon: Mail }, { title: t('sidebar.browser'), name: 'browser', icon: AppWindow }, + { title: t('sidebar.iam'), name: 'iam', icon: ShieldCheck }, { title: t('sidebar.supermarket'), name: 'supermarket', icon: Store }, { title: t('sidebar.usage'), name: 'usage', icon: ChartLine }, { title: t('sidebar.profile'), name: 'profile', icon: User }, diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json index 18879d85e..af4f7fa76 100644 --- a/apps/web/src/i18n/locales/en.json +++ b/apps/web/src/i18n/locales/en.json @@ -12,6 +12,8 @@ "loading": "Loading...", "operation": "Actions", "enable": "Enable", + "enabled": "Enabled", + "disabled": "Disabled", "optional": "optional", "advanced": "Advanced", "import": "Import", @@ -75,6 +77,7 @@ "platform": "Platform", "usage": "Usage", "browser": "Browser", + "iam": "IAM", "supermarket": "Supermarket", "about": "About" }, @@ -1607,6 +1610,52 @@ "colInputTokens": "Input", "colOutputTokens": "Output" }, + "iam": { + "title": "Identity and Access", + "subtitle": "Manage SSO providers, groups, role assignments, and built-in roles.", + "roles": "Roles", + "groups": "Groups", + "sso": "SSO", + "botPermissions": "Bot permissions", + "key": "Key", + "displayName": "Display name", + "source": "Source", + "actions": "Actions", + "members": "Members", + "createGroup": "Create group", + "editGroup": "Edit group", + "externalId": "External ID", + "metadata": "Metadata JSON", + "groupMembers": "Group members", + "selectGroup": "Select group", + "selectUser": "Select user", + "deleteGroupConfirm": "Delete this group?", + "name": "Name", + "type": "Type", + "enabled": "Enabled", + "mappings": "Mappings", + "createProvider": "Create provider", + "editProvider": "Edit provider", + "trustEmail": "Trust email", + "emailLinkingPolicy": "Email linking policy", + "configJson": "Config JSON", + "attributeMappingJson": "Attribute mapping JSON", + "deleteProviderConfirm": "Delete this SSO provider?", + "groupMappings": "Group mappings", + "selectProvider": "Select provider", + "externalGroup": "External group", + "assignBotRole": "Assign bot role", + "bot": "Bot", + "selectBot": "Select bot", + "principalType": "Principal type", + "principal": "Principal", + "selectPrincipal": "Select principal", + "role": "Role", + "assign": "Assign", + "scope": "Scope", + "system": "System", + "empty": "No records" + }, "supermarket": { "title": "Supermarket", "searchPlaceholder": "Search MCPs and Skills...", diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json index 797c590af..dd25d7393 100644 --- a/apps/web/src/i18n/locales/zh.json +++ b/apps/web/src/i18n/locales/zh.json @@ -12,6 +12,8 @@ "loading": "加载中...", "operation": "操作", "enable": "启用", + "enabled": "已启用", + "disabled": "已禁用", "optional": "可选", "advanced": "高级选项", "import": "导入", @@ -75,6 +77,7 @@ "platform": "接入平台", "usage": "用量统计", "browser": "浏览器", + "iam": "身份权限", "supermarket": "市场", "about": "关于" }, @@ -1603,6 +1606,52 @@ "colInputTokens": "输入", "colOutputTokens": "输出" }, + "iam": { + "title": "身份与权限", + "subtitle": "管理 SSO 提供方、用户组、角色分配和内置角色。", + "roles": "角色", + "groups": "用户组", + "sso": "SSO", + "botPermissions": "Bot 权限", + "key": "Key", + "displayName": "显示名称", + "source": "来源", + "actions": "操作", + "members": "成员", + "createGroup": "创建用户组", + "editGroup": "编辑用户组", + "externalId": "外部 ID", + "metadata": "Metadata JSON", + "groupMembers": "用户组成员", + "selectGroup": "选择用户组", + "selectUser": "选择用户", + "deleteGroupConfirm": "删除这个用户组?", + "name": "名称", + "type": "类型", + "enabled": "启用", + "mappings": "映射", + "createProvider": "创建提供方", + "editProvider": "编辑提供方", + "trustEmail": "信任邮箱", + "emailLinkingPolicy": "邮箱合并策略", + "configJson": "Config JSON", + "attributeMappingJson": "Attribute mapping JSON", + "deleteProviderConfirm": "删除这个 SSO 提供方?", + "groupMappings": "用户组映射", + "selectProvider": "选择提供方", + "externalGroup": "外部用户组", + "assignBotRole": "分配 Bot 角色", + "bot": "Bot", + "selectBot": "选择 Bot", + "principalType": "主体类型", + "principal": "主体", + "selectPrincipal": "选择主体", + "role": "角色", + "assign": "分配", + "scope": "Scope", + "system": "系统", + "empty": "暂无记录" + }, "supermarket": { "title": "市场", "searchPlaceholder": "搜索 MCP 和 Skills...", diff --git a/apps/web/src/pages/iam/index.vue b/apps/web/src/pages/iam/index.vue new file mode 100644 index 000000000..caabbfd53 --- /dev/null +++ b/apps/web/src/pages/iam/index.vue @@ -0,0 +1,1091 @@ +