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

# 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`.