Commit Graph

5 Commits

Author SHA1 Message Date
王性驊 d845ef45fd feat(auth): 登入 MFA、忘記/改密碼與註冊恢復流程
補齊平台帳號(platform_native)的密碼自助能力,並讓未完成 Email 驗證的使用者可恢復註冊;OIDC/LDAP/SCIM 帳號禁止在本系統變更密碼。登入若已啟用 TOTP 改為兩階段驗證,OTP 重送加入 60 秒冷卻;同步調整 golangci 排除路徑與 zitadel lint 修正。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 00:55:37 +08:00
王性驊 bdeb7e8263 refactor(middleware): wire AuthJWT + CasbinRBAC via .api middleware directive
Stop relying on a global server.Use(CloudEPJWT) that was invisible from
the .api source. Protected routes now declare middleware explicitly in
each @server block and goctl chains them into routes.go — the .api file
is the single source of truth for "who needs Bearer / who needs RBAC".

Concretely:

- Rewrite middleware to go-zero's standard struct + Handle() pattern.
  AuthJWT becomes strict: missing/invalid Bearer returns 28501000 (was
  soft passthrough). CasbinRBAC stays nil-tolerant so dev/test boots
  without a policy.
- Files renamed to goctl's stringx convention (authjwt_middleware.go,
  casbinrbac_middleware.go) so future `make gen-api` runs see them as
  already-generated and skip the empty stub.
- Move actor context helpers (Actor, WithActor, ActorFromContext) into
  internal/library/actor so middleware and BOTH logic packages share
  one context key. Previously each logic package had its own private
  actorKey struct{}, so an actor injected for member was invisible to
  permission — the permission RBAC chain would always see "missing
  actor". member/permission actor.go are now thin type-alias shims.
- .api files declare middleware per group:
    auth.api (public)         → no middleware (register/login/token/...)
    auth.api (logout)         → middleware: AuthJWT
    member.api                → middleware: AuthJWT
    permission.api (catalog,me) → middleware: AuthJWT
    permission.api (admin ops) → middleware: AuthJWT,CasbinRBAC
    normal.api (/health)      → no middleware
- ServiceContext exposes AuthJWT / CasbinRBAC as rest.Middleware; the
  global server.Use(...) in gateway.go is removed.
- Document the pattern in AGENTS.md (cross-agent rules) and
  generate/api/README.md (detailed examples + filename rules) so any
  future AI agent or human follows the same convention.

make gen-api / gen-doc / lint / build all pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 17:30:50 +08:00
王性驊 4590f1c951 docs(api): group OpenAPI by tags + add Chinese field descriptions and enums
Make the generated docs/openapi/gateway.yaml usable by adding three things
go-doc parses out of the .api source:

- @server tags + summary on every block → Swagger UI groups endpoints
  (Auth / Member / Permission / Normal) instead of dumping everything
  under "default".
- backtick end-of-line // 中文 on every Request field → property
  descriptions in the schema. go-doc only reads the trailing comment,
  not the line above, so all comments are placed on the same line as
  the tag.
- options=A|B|C in json/form tags wherever validate:"oneof=..." exists
  → enum dropdowns. The validate tag is kept for runtime validation;
  go-zero also enforces options= at bind time.

Codify the rules in generate/api/README.md (tags / 行末註解 / options=)
and add AGENTS.md at repo root so any AI agent (Claude / Cursor / Codex)
picks them up automatically when working on the project.

types.go regenerated via make gen-api to keep json tags in sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 17:15:25 +08:00
王性驊 713a81f70b feat(auth): add unified registration/login module with Zitadel + lint cleanup
- Introduce auth module: handlers, logic, domain/repository/usecase, JWT
  middleware, and Zitadel OIDC client (password + authorization code +
  userinfo + JWKS verification)
- Wire member rate-limit, structured errors, and refactored member/
  notification usecases (introduce shared errors, drop repo_errors.go)
- Bring the codebase to zero golangci-lint issues:
  * goimports formatting
  * errcheck on io.ReadAll/Unlock cleanup paths
  * contextcheck: HandlerContext now takes (ctx, *http.Request)
  * gocritic: rename shadowed `max`, use http.NoBody
  * goconst: extract test fixtures and bsonOpSet
  * testifylint: switch to assert inside httptest handlers

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 14:45:35 +08:00
王性驊 2ae86e9002 add member totp 2026-05-21 07:51:22 +08:00