You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
9.8 KiB
Markdown
248 lines
9.8 KiB
Markdown
# 2.2 User Service (Port 8082) - Detailed Implementation Plan
|
|
|
|
## Summary
|
|
Implement the User Service as the source of truth for player profile and compliance data (registration, profile updates, email verification status, GDPR export/deletion), aligned with the implementation decisions already used in `backend/services/question-bank-service`.
|
|
|
|
This service will run on Fiber, persist in PostgreSQL, expose health/readiness/metrics, and use shared packages for auth, validation, error mapping, logging, tracing, and service bootstrapping.
|
|
|
|
## Decisions Reused from 2.1 Question Bank Service
|
|
1. Use the same service composition style as `question-bank-service/cmd/main.go`:
|
|
- `internal/infra/config.FromEnv()` for service-specific env parsing.
|
|
- Shared logger/metrics/tracer initialization from `backend/shared`.
|
|
- Repository initialization + `EnsureSchema(ctx)` at startup.
|
|
- `/health`, `/ready`, and `/metrics` endpoints.
|
|
- Route registration through `internal/interfaces/http/RegisterRoutes`.
|
|
2. Use the same auth approach:
|
|
- Public routes can remain open when Zitadel env is missing (local development fallback).
|
|
- Admin routes use Zitadel JWT middleware when configured.
|
|
3. Use the same persistence style:
|
|
- PostgreSQL with `pgxpool` and SQL DDL in repository `EnsureSchema`, matching current pragmatic pattern.
|
|
4. Follow the same testing pyramid:
|
|
- Domain/application unit tests.
|
|
- HTTP integration tests with in-memory doubles.
|
|
- Optional DB-backed integration tests gated by environment.
|
|
|
|
## Objectives
|
|
1. Provide player registration and profile management APIs.
|
|
2. Keep local profile data synchronized with authenticated identity claims from Zitadel.
|
|
3. Support GDPR rights: data export and account deletion.
|
|
4. Enforce RBAC on admin endpoints and ownership checks on user-scoped endpoints.
|
|
5. Deliver production-ready observability and baseline test coverage.
|
|
|
|
## API Endpoints
|
|
- `POST /users/register`
|
|
- `GET /users/:id`
|
|
- `PUT /users/:id`
|
|
- `DELETE /users/:id`
|
|
- `POST /users/verify-email`
|
|
- `GET /admin/users`
|
|
- `POST /admin/users/:id/export`
|
|
|
|
## Domain Model
|
|
Aggregate:
|
|
- `User`
|
|
|
|
Value objects:
|
|
- `Email` (normalized lowercase, RFC-like format validation)
|
|
- `DisplayName` (2-50 chars, allowed chars policy aligned with project guidelines)
|
|
- `ConsentRecord` (terms/privacy/version + timestamp + source)
|
|
|
|
Repository contract (`UserRepository`):
|
|
- `Create(ctx, user)`
|
|
- `GetByID(ctx, id)`
|
|
- `GetByEmail(ctx, email)`
|
|
- `UpdateProfile(ctx, id, patch)`
|
|
- `MarkEmailVerified(ctx, id, verifiedAt)`
|
|
- `SoftDelete(ctx, id, deletedAt)`
|
|
- `List(ctx, pagination, filters)`
|
|
- `ExportBundle(ctx, id)`
|
|
|
|
## Data Model (PostgreSQL)
|
|
Primary table: `users`
|
|
- `id UUID PRIMARY KEY`
|
|
- `zitadel_user_id VARCHAR(128) UNIQUE NULL`
|
|
- `email VARCHAR(320) UNIQUE NOT NULL`
|
|
- `email_verified BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- `display_name VARCHAR(50) NOT NULL`
|
|
- `consent_version VARCHAR(32) NOT NULL`
|
|
- `consent_given_at TIMESTAMPTZ NOT NULL`
|
|
- `consent_source VARCHAR(32) NOT NULL DEFAULT 'web'`
|
|
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
|
- `updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
|
|
- `deleted_at TIMESTAMPTZ NULL`
|
|
|
|
Indexes:
|
|
- `UNIQUE(email)`
|
|
- `UNIQUE(zitadel_user_id) WHERE zitadel_user_id IS NOT NULL`
|
|
- `INDEX(deleted_at)`
|
|
- `INDEX(created_at DESC)`
|
|
|
|
Audit table: `user_audit_log`
|
|
- Track admin exports and delete actions for compliance/auditability.
|
|
- Fields: `id`, `actor_user_id`, `target_user_id`, `action`, `metadata_json`, `created_at`.
|
|
|
|
## Authorization and Identity Rules
|
|
1. `GET/PUT/DELETE /users/:id`:
|
|
- Allowed for the same authenticated user (`sub` mapped to local user) or `admin` role.
|
|
2. `POST /users/register`:
|
|
- Requires authenticated token.
|
|
- If user already exists by `zitadel_user_id` or email, return idempotent success (`200`) with existing profile.
|
|
3. `POST /users/verify-email`:
|
|
- Requires authenticated token.
|
|
- Resolve email verification from trusted source (token claim and/or Zitadel userinfo).
|
|
- Never accept arbitrary email-verified flag from client body.
|
|
4. Admin routes (`/admin/*`):
|
|
- Require admin role + MFA via shared Zitadel middleware behavior.
|
|
|
|
## Endpoint Behavior
|
|
1. `POST /users/register`
|
|
- Input: `display_name`, `consent_version`, `consent_source`.
|
|
- Identity fields (`email`, `zitadel_user_id`) come from token / Zitadel client, not request body.
|
|
- Output: canonical user profile.
|
|
|
|
2. `GET /users/:id`
|
|
- Return sanitized profile (no internal audit metadata).
|
|
- `404` for missing or soft-deleted user.
|
|
|
|
3. `PUT /users/:id`
|
|
- Allow updates to `display_name` and consent refresh fields.
|
|
- Reject immutable field updates (`email`, `id`, `zitadel_user_id`).
|
|
|
|
4. `DELETE /users/:id` (GDPR)
|
|
- Soft-delete local profile.
|
|
- Trigger best-effort anonymization policy for user-facing displays.
|
|
- Write compliance audit event.
|
|
|
|
5. `POST /users/verify-email`
|
|
- Refresh verification status from Zitadel and persist local `email_verified=true` with timestamp/audit event.
|
|
|
|
6. `GET /admin/users`
|
|
- Paginated listing with filters: `email`, `display_name`, `created_after`, `created_before`, `include_deleted`.
|
|
|
|
7. `POST /admin/users/:id/export`
|
|
- Produce structured JSON export bundle (profile + consents + audit entries).
|
|
- Return payload inline for now (later storage handoff can be added).
|
|
|
|
## Package and File Layout
|
|
Target structure (mirrors 2.1 service organization):
|
|
- `backend/services/user-service/cmd/main.go`
|
|
- `backend/services/user-service/internal/infra/config/config.go`
|
|
- `backend/services/user-service/internal/domain/user/`
|
|
- `backend/services/user-service/internal/application/user/`
|
|
- `backend/services/user-service/internal/infra/persistence/ent/`
|
|
- `backend/services/user-service/internal/interfaces/http/`
|
|
- `backend/services/user-service/tests/`
|
|
|
|
## Implementation Work Breakdown
|
|
|
|
### Workstream A - Bootstrap and Configuration
|
|
1. Add `internal/infra/config/config.go` with:
|
|
- `USER_SERVICE_PORT` (default `8082`)
|
|
- `USER_ADMIN_LIST_DEFAULT_LIMIT` (default `50`)
|
|
- `USER_ADMIN_LIST_MAX_LIMIT` (default `200`)
|
|
- shared `POSTGRES_*`, `TRACING_*`, `METRICS_*`, `LOG_LEVEL`, `ZITADEL_*`
|
|
2. Update `cmd/main.go` to match question-bank startup pattern:
|
|
- logger/metrics/tracer creation
|
|
- postgres client + schema ensure
|
|
- readiness check endpoint
|
|
- metrics endpoint
|
|
- route registration with optional admin middleware
|
|
|
|
### Workstream B - Domain and Application Layer
|
|
1. Define domain entity, value objects, domain errors.
|
|
2. Define application DTOs for request/response mapping.
|
|
3. Implement application service methods:
|
|
- `Register`
|
|
- `GetProfile`
|
|
- `UpdateProfile`
|
|
- `DeleteUser`
|
|
- `VerifyEmail`
|
|
- `AdminListUsers`
|
|
- `AdminExportUser`
|
|
4. Reuse shared error codes and transport error mapping style from question-bank handlers.
|
|
|
|
### Workstream C - Persistence Layer
|
|
1. Implement `Client` and `UserRepository` under `internal/infra/persistence/ent`.
|
|
2. Implement `EnsureSchema(ctx)` for `users` and `user_audit_log` tables/indexes.
|
|
3. Add SQL queries for pagination/filtering and soft-delete aware reads.
|
|
4. Add mappers between SQL rows and domain entity.
|
|
|
|
### Workstream D - HTTP Interface
|
|
1. Add request/response models with validation tags.
|
|
2. Implement handler methods with consistent JSON envelope and status codes.
|
|
3. Register routes in `routes.go` with admin group and middleware support.
|
|
4. Enforce ownership checks using user id from auth context.
|
|
|
|
### Workstream E - Compliance and Audit
|
|
1. Implement export builder (deterministic JSON schema).
|
|
2. Implement delete flow with audit logging and redaction policy hooks.
|
|
3. Ensure admin export/delete actions are auditable.
|
|
|
|
### Workstream F - Testing
|
|
1. Unit tests:
|
|
- email/display-name/consent validation
|
|
- idempotent registration logic
|
|
- ownership and admin authorization guard behavior
|
|
2. HTTP integration tests (in-memory repo + test middleware):
|
|
- register/get/update/delete happy paths
|
|
- unauthorized/forbidden cases
|
|
- admin list/export auth behavior
|
|
- `/metrics` availability
|
|
3. Repository integration tests (optional env-gated):
|
|
- schema creation
|
|
- unique constraints
|
|
- pagination/filter correctness
|
|
|
|
## Error Handling Contract
|
|
Use project shared domain errors and consistent mappings:
|
|
- `400`: validation failures
|
|
- `401`: missing/invalid token
|
|
- `403`: non-owner/non-admin access
|
|
- `404`: user not found
|
|
- `409`: duplicate email or identity conflict
|
|
- `500`: unexpected errors
|
|
|
|
## Observability
|
|
1. Structured logs:
|
|
- include `service=user-service`, request id, actor id (when available), and route.
|
|
- never log raw tokens or PII-heavy payloads.
|
|
2. Metrics:
|
|
- request count/latency/status via shared HTTP metrics.
|
|
- dedicated counters for `user_registration_total`, `user_deletion_total`, `gdpr_export_total`.
|
|
3. Tracing:
|
|
- ensure startup/shutdown tracer lifecycle identical to question-bank.
|
|
- instrument service/repository boundaries for registration/export/delete paths.
|
|
|
|
## Security and Privacy Controls
|
|
1. Input validation and sanitization on all mutable fields.
|
|
2. No trust of client-supplied identity attributes when token context exists.
|
|
3. Soft-delete as default deletion strategy; avoid hard delete in initial implementation.
|
|
4. Minimize data in responses and logs (data minimization principle).
|
|
5. Audit every admin export and delete action.
|
|
|
|
## Delivery Sequence (3-4 Days)
|
|
1. Day 1: bootstrap/config + domain/application skeleton + schema DDL.
|
|
2. Day 2: repository + register/get/update endpoints + auth/ownership checks.
|
|
3. Day 3: delete/verify-email/admin list/export + observability wiring.
|
|
4. Day 4: tests, bug fixes, and readiness verification.
|
|
|
|
## Verification Commands
|
|
From `backend/services/user-service`:
|
|
```bash
|
|
go test ./...
|
|
go vet ./...
|
|
```
|
|
|
|
From `backend` (optional full workspace check):
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
## Definition of Done
|
|
1. All endpoints implemented and route-protected per spec.
|
|
2. PostgreSQL schema auto-created on startup and validated in tests.
|
|
3. Health, readiness, and metrics endpoints functional.
|
|
4. AuthN/AuthZ and ownership checks covered by tests.
|
|
5. GDPR export/delete flows implemented with audit records.
|
|
6. `go test ./...` and `go vet ./...` pass for `user-service`.
|