2026-05-19 11:00:28 +00:00
|
|
|
|
// Code generated by goctl. DO NOT EDIT.
|
|
|
|
|
|
// goctl 1.10.1
|
|
|
|
|
|
|
|
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-05-21 06:45:35 +00:00
|
|
|
|
auth "gateway/internal/handler/auth"
|
2026-05-20 23:51:22 +00:00
|
|
|
|
member "gateway/internal/handler/member"
|
2026-05-19 11:00:28 +00:00
|
|
|
|
normal "gateway/internal/handler/normal"
|
feat(permission): add RBAC module with Casbin enforcement and policy reload
- Multi-tenant RBAC: permission catalog, roles, role-permission mapping,
user-role assignment, and external IdP role mapping (zitadel/ldap/scim).
- Casbin enforcer with Redis-backed adapter and Pub/Sub reload for
multi-instance policy sync; HTTP middleware enforces (tenant, role,
path, method) with platform admin bypass.
- /api/v1/permissions routes: catalog, me, policy/reload, roles CRUD,
role permissions, user roles, role mappings.
- New error scope (31) for Permission and biz code descriptions.
- Wire Permission module into ServiceContext, config, mongo-index, and
add cmd/permission-seed CLI plus etc/rbac.conf model.
- Redis client gains lazy PubSubClient helper (go-zero wrapper lacks Subscribe).
- Rewrite internal/model/member/README to cover Tenant/Member/Identity.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 08:47:35 +00:00
|
|
|
|
permission "gateway/internal/handler/permission"
|
2026-05-19 11:00:28 +00:00
|
|
|
|
"gateway/internal/svc"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/rest"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
2026-05-20 23:51:22 +00:00
|
|
|
|
server.AddRoutes(
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
2026-05-26 16:55:37 +00:00
|
|
|
|
// Email + 密碼登入(ZITADEL ROPG → CloudEP JWT;若已啟用 TOTP 則回傳 MFA challenge)
|
2026-05-21 06:45:35 +00:00
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/login",
|
|
|
|
|
|
Handler: auth.LoginHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-26 16:55:37 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 確認登入 MFA(TOTP / 備援碼)並核發 JWT
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/login/mfa",
|
|
|
|
|
|
Handler: auth.LoginMfaConfirmHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-21 06:45:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Social 登入 OAuth callback
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/login/social/callback",
|
|
|
|
|
|
Handler: auth.LoginSocialCallbackHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// Social 登入:建立 login session 並回傳 OAuth URL(不含 invite)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/login/social/start",
|
|
|
|
|
|
Handler: auth.LoginSocialStartHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-26 16:55:37 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 忘記密碼:寄送重設 OTP(僅 platform_native 平台帳號)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/password/forgot",
|
|
|
|
|
|
Handler: auth.PasswordForgotHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 忘記密碼:驗證 OTP 並重設密碼(僅 platform_native)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/password/reset",
|
|
|
|
|
|
Handler: auth.PasswordResetHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-21 06:45:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Email 註冊(建立 ZITADEL + member,寄 registration OTP)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/register",
|
|
|
|
|
|
Handler: auth.RegisterHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 確認 registration OTP 並核發 JWT
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/register/confirm",
|
|
|
|
|
|
Handler: auth.RegisterConfirmHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 重寄 registration OTP
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/register/resend",
|
|
|
|
|
|
Handler: auth.RegisterResendHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-26 16:55:37 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 恢復未完成註冊(依 Email 重寄 registration OTP)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/register/resume",
|
|
|
|
|
|
Handler: auth.RegisterResumeHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-21 06:45:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Social 註冊 OAuth callback
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/register/social/callback",
|
|
|
|
|
|
Handler: auth.RegisterSocialCallbackHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// Social 註冊:建立 session 並回傳 OAuth URL
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/register/social/start",
|
|
|
|
|
|
Handler: auth.RegisterSocialStartHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// ZITADEL id_token 換 CloudEP JWT(企業 SSO)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/token/exchange",
|
|
|
|
|
|
Handler: auth.TokenExchangeHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 以 refresh_token 換發新的 access/refresh token
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/token/refresh",
|
|
|
|
|
|
Handler: auth.TokenRefreshHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
rest.WithPrefix("/api/v1/auth"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
server.AddRoutes(
|
2026-05-21 09:30:50 +00:00
|
|
|
|
rest.WithMiddlewares(
|
|
|
|
|
|
[]rest.Middleware{serverCtx.AuthJWT},
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
|
|
|
|
|
// 登出(撤銷 access JWT 及配對 refresh JWT)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/logout",
|
|
|
|
|
|
Handler: auth.LogoutHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
}...,
|
|
|
|
|
|
),
|
|
|
|
|
|
rest.WithPrefix("/api/v1/auth"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
server.AddRoutes(
|
|
|
|
|
|
rest.WithMiddlewares(
|
|
|
|
|
|
[]rest.Middleware{serverCtx.AuthJWT},
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
|
|
|
|
|
// 取得當前會員 profile(Bearer JWT;本機 dev 可 fallback X-Tenant-ID + X-UID)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/me",
|
|
|
|
|
|
Handler: member.GetMemberMeHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新當前會員 profile
|
|
|
|
|
|
Method: http.MethodPatch,
|
|
|
|
|
|
Path: "/me",
|
|
|
|
|
|
Handler: member.UpdateMemberMeHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-26 16:55:37 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 變更登入密碼(僅 platform_native 平台帳號)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/password",
|
|
|
|
|
|
Handler: member.ChangePasswordHandler(serverCtx),
|
|
|
|
|
|
},
|
2026-05-21 09:30:50 +00:00
|
|
|
|
{
|
|
|
|
|
|
// TOTP 狀態
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/me/totp",
|
|
|
|
|
|
Handler: member.GetTOTPStatusHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 解除 TOTP 綁定
|
|
|
|
|
|
Method: http.MethodDelete,
|
|
|
|
|
|
Path: "/me/totp",
|
|
|
|
|
|
Handler: member.DisableTOTPHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 重產 TOTP 備援碼
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/totp/backup-codes",
|
|
|
|
|
|
Handler: member.RegenerateTOTPBackupCodesHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 確認 TOTP 綁定
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/totp/enroll-confirm",
|
|
|
|
|
|
Handler: member.ConfirmTOTPEnrollHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 開始 TOTP 綁定
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/totp/enroll-start",
|
|
|
|
|
|
Handler: member.StartTOTPEnrollHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 驗證 TOTP(step-up 測試)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/totp/verify",
|
|
|
|
|
|
Handler: member.VerifyTOTPHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 確認業務 email 驗證
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/verifications/email/confirm",
|
|
|
|
|
|
Handler: member.ConfirmEmailVerificationHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 開始業務 email 驗證
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/verifications/email/start",
|
|
|
|
|
|
Handler: member.StartEmailVerificationHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 確認業務 phone 驗證
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/verifications/phone/confirm",
|
|
|
|
|
|
Handler: member.ConfirmPhoneVerificationHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 開始業務 phone 驗證
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/me/verifications/phone/start",
|
|
|
|
|
|
Handler: member.StartPhoneVerificationHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
}...,
|
|
|
|
|
|
),
|
2026-05-20 23:51:22 +00:00
|
|
|
|
rest.WithPrefix("/api/v1/members"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-19 11:00:28 +00:00
|
|
|
|
server.AddRoutes(
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
|
|
|
|
|
// Ping
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/health",
|
|
|
|
|
|
Handler: normal.PingHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
rest.WithPrefix("/api/v1"),
|
|
|
|
|
|
rest.WithTimeout(3000*time.Millisecond),
|
|
|
|
|
|
)
|
feat(permission): add RBAC module with Casbin enforcement and policy reload
- Multi-tenant RBAC: permission catalog, roles, role-permission mapping,
user-role assignment, and external IdP role mapping (zitadel/ldap/scim).
- Casbin enforcer with Redis-backed adapter and Pub/Sub reload for
multi-instance policy sync; HTTP middleware enforces (tenant, role,
path, method) with platform admin bypass.
- /api/v1/permissions routes: catalog, me, policy/reload, roles CRUD,
role permissions, user roles, role mappings.
- New error scope (31) for Permission and biz code descriptions.
- Wire Permission module into ServiceContext, config, mongo-index, and
add cmd/permission-seed CLI plus etc/rbac.conf model.
- Redis client gains lazy PubSubClient helper (go-zero wrapper lacks Subscribe).
- Rewrite internal/model/member/README to cover Tenant/Member/Identity.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 08:47:35 +00:00
|
|
|
|
|
|
|
|
|
|
server.AddRoutes(
|
2026-05-21 09:30:50 +00:00
|
|
|
|
rest.WithMiddlewares(
|
|
|
|
|
|
[]rest.Middleware{serverCtx.AuthJWT},
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
|
|
|
|
|
// 取得全局 Permission Catalog(樹狀或扁平;可篩 status/type)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/catalog",
|
|
|
|
|
|
Handler: permission.GetPermissionCatalogHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 取得當前使用者的 role / permission map(前端渲染選單)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/me",
|
|
|
|
|
|
Handler: permission.GetMePermissionsHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
}...,
|
|
|
|
|
|
),
|
|
|
|
|
|
rest.WithPrefix("/api/v1/permissions"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
server.AddRoutes(
|
|
|
|
|
|
rest.WithMiddlewares(
|
|
|
|
|
|
[]rest.Middleware{serverCtx.AuthJWT, serverCtx.CasbinRBAC},
|
|
|
|
|
|
[]rest.Route{
|
|
|
|
|
|
{
|
|
|
|
|
|
// 強制重載 Casbin policy(單租戶或所有租戶;同步 + Pub/Sub broadcast)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/policy/reload",
|
|
|
|
|
|
Handler: permission.ReloadPolicyHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 列出外部來源 → 內部 role 的映射(zitadel / ldap / scim)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/role-mappings",
|
|
|
|
|
|
Handler: permission.ListRoleMappingsHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// Upsert 外部 IdP 群組到內部 role 的映射
|
|
|
|
|
|
Method: http.MethodPut,
|
|
|
|
|
|
Path: "/role-mappings",
|
|
|
|
|
|
Handler: permission.UpsertRoleMappingHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 刪除外部 → 內部 role 映射
|
|
|
|
|
|
Method: http.MethodDelete,
|
|
|
|
|
|
Path: "/role-mappings",
|
|
|
|
|
|
Handler: permission.DeleteRoleMappingHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 列出租戶內所有角色(含 system role)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/roles",
|
|
|
|
|
|
Handler: permission.ListRolesHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 建立租戶自訂角色(key 不可改、不可使用 system./platform_ 開頭)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/roles",
|
|
|
|
|
|
Handler: permission.CreateRoleHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新角色(display_name / status;is_system 角色不可改 status)
|
|
|
|
|
|
Method: http.MethodPatch,
|
|
|
|
|
|
Path: "/roles/:id",
|
|
|
|
|
|
Handler: permission.UpdateRoleHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 刪除角色(is_system 不可刪;存在 user 指派時拒絕)
|
|
|
|
|
|
Method: http.MethodDelete,
|
|
|
|
|
|
Path: "/roles/:id",
|
|
|
|
|
|
Handler: permission.DeleteRoleHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 讀取角色目前勾選的 permission 集合
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/roles/:id/permissions",
|
|
|
|
|
|
Handler: permission.GetRolePermissionsHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 全量取代角色的 permission 勾選(自動補齊父權限;觸發 LoadPolicy + Pub/Sub reload)
|
|
|
|
|
|
Method: http.MethodPut,
|
|
|
|
|
|
Path: "/roles/:id/permissions",
|
|
|
|
|
|
Handler: permission.ReplaceRolePermissionsHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 查詢使用者目前指派的角色(含 RoleKey / DisplayName)
|
|
|
|
|
|
Method: http.MethodGet,
|
|
|
|
|
|
Path: "/users/:uid/roles",
|
|
|
|
|
|
Handler: permission.ListUserRolesHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 指派角色給使用者(預設 source=manual;source 來源由 SyncFromX 自動標)
|
|
|
|
|
|
Method: http.MethodPost,
|
|
|
|
|
|
Path: "/users/:uid/roles",
|
|
|
|
|
|
Handler: permission.AssignUserRoleHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 撤銷使用者的單一角色
|
|
|
|
|
|
Method: http.MethodDelete,
|
|
|
|
|
|
Path: "/users/:uid/roles/:role_id",
|
|
|
|
|
|
Handler: permission.RevokeUserRoleHandler(serverCtx),
|
|
|
|
|
|
},
|
|
|
|
|
|
}...,
|
|
|
|
|
|
),
|
feat(permission): add RBAC module with Casbin enforcement and policy reload
- Multi-tenant RBAC: permission catalog, roles, role-permission mapping,
user-role assignment, and external IdP role mapping (zitadel/ldap/scim).
- Casbin enforcer with Redis-backed adapter and Pub/Sub reload for
multi-instance policy sync; HTTP middleware enforces (tenant, role,
path, method) with platform admin bypass.
- /api/v1/permissions routes: catalog, me, policy/reload, roles CRUD,
role permissions, user roles, role mappings.
- New error scope (31) for Permission and biz code descriptions.
- Wire Permission module into ServiceContext, config, mongo-index, and
add cmd/permission-seed CLI plus etc/rbac.conf model.
- Redis client gains lazy PubSubClient helper (go-zero wrapper lacks Subscribe).
- Rewrite internal/model/member/README to cover Tenant/Member/Identity.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 08:47:35 +00:00
|
|
|
|
rest.WithPrefix("/api/v1/permissions"),
|
|
|
|
|
|
)
|
2026-05-19 11:00:28 +00:00
|
|
|
|
}
|