feat: add Role (owner|admin) to the user domain, persistence model and mapper
This commit is contained in:
1 parent
162fea4afb
commit
461e93e3b5
30 files changed
+791
-48
No files matched your search
@@ -0,0 +1,55 @@
|
||||
# QA procedure: user_roles
|
||||
|
||||
Package: `test` (black-box). Reuse `mockUserRepo`, `stubHasher`, `mockMailRepo`, `mockInviteRepo`.
|
||||
No live server, no real SQLite file, no real SMTP.
|
||||
|
||||
## Setup shared by every case
|
||||
1. Call `validator.InitValidator()` once.
|
||||
2. Use cases: `NewGetUserUseCase`, `NewCreateInviteUseCase`, `NewListUsersAndInvitesUseCase`,
|
||||
`NewCreateUserUseCase`, `NewAcceptInviteUseCase` over the fakes.
|
||||
3. Router mirrors `NewUserRouter`: `jwtauth.Verifier` + `Authenticator` with
|
||||
`jwtauth.New("HS256", []byte("test-secret"), nil)`, `RequireCurrentToken`, `RequireVerifiedUser`,
|
||||
`GET /`, and a group with `RequireOwner(getUser)`, `TrustedProxyRealIP(nil)`,
|
||||
`NewRateLimiter(2, time.Minute)`, `POST /invite`. A second router mirrors `NewAuthRouter` for
|
||||
`/register`, `/invite/accept` and `/me`.
|
||||
4. Seed: owner id 1 "marta" (role owner), admin id 2 "jonas" (role admin), both verified.
|
||||
Cookies carry `user_id` and `token_version`, and never a role.
|
||||
|
||||
## Roles at creation
|
||||
5. Empty repo, `POST /v1/auth/register` -> 200, the stored user has `Role == user.RoleOwner`.
|
||||
6. Pending invite with a known code (stubHasher), `POST /v1/auth/invite/accept` -> 200, the stored
|
||||
user has `Role == user.RoleAdmin`, and user 1 is still owner.
|
||||
7. Use-case level: `CreateUser` returns a user with `RoleOwner`; `AcceptInvite` returns `RoleAdmin`.
|
||||
|
||||
## GET /v1/auth/me
|
||||
8. Table over owner and admin: 200, decode into `map[string]json.RawMessage`, key `role` equals the stored role.
|
||||
9. Unverified owner -> 200, `role` "owner", `is_verified` false.
|
||||
10. The key set of the public user is exactly the eight fields, and `POST /v1/auth/verify` returns the same eight.
|
||||
|
||||
## GET /v1/user/
|
||||
11. Seed three users plus a pending invite. 200; user items have exactly the keys id, kind, username,
|
||||
display_name, email, role, status, created_at; email and role match the seed. The invite item has
|
||||
exactly id, kind, email, status, created_at. No forbidden key appears anywhere.
|
||||
12. Cookie of admin 2 -> 200.
|
||||
|
||||
## POST /v1/user/invite
|
||||
13. Owner cookie, valid body -> 201, one invite, one "invitation" mail.
|
||||
14. Admin cookie, valid body -> 403, body exactly `apperr.OwnerRequiredErrorMessage`, `Content-Type`
|
||||
is not JSON-decoded (plain text), no invite, no mail.
|
||||
15. Admin cookie with `{"email":"not-an-email"}` and with `not json` -> 403 both.
|
||||
16. Rate limit 2: admin sends 3 requests -> 403, 403, 403.
|
||||
17. Flip user 2's stored role from owner to admin between two requests with the same cookie ->
|
||||
second request 403.
|
||||
18. Existing auth table from invitations.qa.md (no cookie 401, unverified 403, stale token 401) is
|
||||
unchanged; the unverified 403 body remains `apperr.VerificationRequiredErrorMessage`.
|
||||
|
||||
## Use case level
|
||||
19. `RequireOwner` with a user id that no longer exists -> 401 `apperr.UnauthorizedErrorMessage`;
|
||||
with an unexpected repository error -> 500.
|
||||
|
||||
## Manual verification (not exercised by the Go tests)
|
||||
- Persistence backfill: copy a SQLite file from before this change with 3 users and no role column.
|
||||
Start the app: the column exists, the lowest id is `owner`, the others `admin`. Restart: nothing
|
||||
changes. Start against a fresh file: registering creates an owner.
|
||||
- Layering check by review: `Role` and the sentinel/constants live in `internal/domain/user`; GORM
|
||||
tags only in `persistence/models`, converted by the mapper.
|
||||
Reference in new issue
Block a user