- Add Gitea Actions workflow; point README to gitea.d-bis.org/Sankofa_Phoenix/SMOA - Expand .gitignore for Spring H2 data, secrets, Kotlin .kotlin/, tooling - Track docs/api/generated ReDoc bundle; refresh api docs README - Android: network/auth/sync, UI shell, tests; backend credentials/integrity APIs - Docs, scripts (generate-api-docs), modules and core updates Made-with: Cursor
6.3 KiB
6.3 KiB
SMOA Backend – Gap Analysis and Roadmap
Review summary
The backend implements the sync contract expected by the mobile app (POST sync endpoints, SyncResponse with conflict/remoteData), with validation, optional API key auth, OpenAPI, conflict detection, and H2 persistence. Below are covered areas, gaps, and recommendations.
What's in place
| Area | Status |
|---|---|
| Sync API contract | All five sync endpoints (directory, order, evidence, credential, report); request DTOs aligned with app; SyncResponse with success, itemId, serverTimestamp, conflict, remoteData, message. |
| Conflict detection | Directory uses lastUpdated; others use clientUpdatedAt; server returns conflict + remoteData when server has newer version. |
| Validation | @Valid on all sync bodies; NotBlank/NotNull on required fields. |
| Auth | Optional API key via X-API-Key or api_key query; when key is set, all /api/v1/* require it. |
| Security | Stateless; CSRF disabled for API; health/info/docs/h2-console permitted without auth. |
| Persistence | JPA entities and repositories for all five resource types; H2 file DB; ddl-auto: update. |
| OpenAPI | springdoc; /v3/api-docs, /swagger-ui.html; ApiKey scheme documented. |
| Health | GET /health with status, application, timestamp, and DB check (up/down). |
| CORS | Configurable via smoa.cors.allowed-origins (default *). |
| Error handling | Global exception handler: 400 for validation/type errors with JSON body; 500 for other errors. |
Gaps and recommendations
1. Delete / SyncOperation.Delete ✅ Done
- Gap: App has SyncOperation.Create, Update, Delete. Backend only does upsert (create/update).
- Done: DELETE endpoints added:
/api/v1/sync/directory/{id},/order/{orderId},/evidence/{evidenceId},/credential/{credentialId},/report/{reportId}; each returns SyncResponse; audit logged.
2. Pull / GET (initial load or refresh) ✅ Done
- Gap: No GET endpoints. App today only pushes from a queue; for "refresh after coming online" or initial load, pull is often needed.
- Done: GET list endpoints:
/api/v1/directory(optionalunit,X-Unit),/api/v1/orders(since, limit, jurisdiction / X-Unit),/api/v1/evidence,/api/v1/credentials,/api/v1/reports(since, limit, optional filters). See PullController andapi/dto/PullResponse.kt.
3. Enum validation ✅ Done
- Gap: orderType, status (orders), evidenceType, reportType are free strings. App uses enums.
- Done: @Pattern added for orderType (AUTHORIZATION|…|ADMINISTRATIVE), status (DRAFT|…|REVOKED), evidenceType (PHYSICAL|…|DOCUMENT), reportType (OPERATIONAL|…|REGULATORY), report format (PDF|XML|JSON|CSV|EXCEL).
4. SyncResponse.remoteData format ✅ Done
- Gap: Backend returns remoteData as base64; client must decode.
- Done: Documented in OpenAPI description that remoteData is base64-encoded JSON when conflict=true.
5. Production and ops ✅ Done (baseline)
- Done:
application-prod.ymldisables H2 console, setsddl-auto: validate, Flyway on, stricter logging. - Done:
org.postgresql:postgresqlon the runtime classpath; setSPRING_DATASOURCE_*(seeapplication-prod.ymland backend README) for PostgreSQL. Dev/test keep H2 unless overridden.
6. Rate limiting ✅ Done
- Gap: No rate limiting on sync or auth.
- Done: RateLimitFilter on /api/v1/*; per API key or IP; configurable
smoa.rate-limit.requests-per-minute(default 120); 429 when exceeded; disabled in test profile.
7. Audit / logging ✅ Done
- Gap: No structured audit log for "who synced what when".
- Done: SyncAuditLog entity and SyncAuditService; sync and delete operations logged (resourceType, resourceId, operation, success). SyncController calls audit after each sync/delete.
8. Tests ✅ Done
- Gap: No backend unit or integration tests.
- Done: DirectorySyncServiceTest (create, conflict/remoteData, delete); GlobalExceptionHandlerTest (500); SyncControllerIntegrationTest (directory, credential type validation, health); application-test.yml (H2 in-memory, rate limit off); mockk for unit tests.
9. Ids and authorization
- Gap: No tenant/org/unit scoping; any client with a valid API key can read/write any resource.
- Recommendation: If the app is multi-tenant or unit-scoped, add unit/tenant to API key or token and filter queries (e.g. directory by unit, orders by unit).
10. Infrastructure ✅ Done (Dockerfile)
- Gap: No Dockerfile or k8s manifests; no migration strategy beyond ddl-auto.
- Done: backend/Dockerfile (multi-stage); build from repo root:
docker build -f backend/Dockerfile .. Optional: Flyway/Liquibase and ddl-auto: validate in prod.
Optional improvements
- Pagination: Pull endpoints use
limit/since; extend with cursor or page/size if lists grow very large. - ETag / If-None-Match: ✅
ShallowEtagHeaderFilteron/api/v1/**(seeWebConfig). - Request ID: ✅
RequestIdFilterassigns/propagatesX-Request-Id. - API versioning: Keep /api/v1; when introducing breaking changes, add /api/v2 and document deprecation.
- Credential types: ✅
@PatternonCredentialSyncRequest.credentialType(SmoaCredentialType.CREDENTIAL_TYPE_PATTERN).
Quick reference: config
| Property | Default | Purpose |
|---|---|---|
| smoa.api.key | (empty) | API key; empty = no auth |
| smoa.api.key-header | X-API-Key | Header name |
| smoa.cors.allowed-origins | * | CORS origins (comma-separated) |
| smoa.rate-limit.enabled | true | Enable rate limit on /api/v1/* |
| smoa.rate-limit.requests-per-minute | 120 | Max requests per key/IP per minute |
| server.port | 8080 | Port |
| spring.datasource.url | H2 file | DB URL (use PostgreSQL in prod) |
Summary
The backend is ready for mobile sync with: push and delete sync, pull/GET endpoints (with ETag), conflict handling, enum validation (including credential types), rate limiting, audit logging, request IDs, tests, Dockerfile, prod profile, PostgreSQL driver, and Flyway with ddl-auto: validate in prod. Remaining: strong tenant isolation (per-tenant credentials, RLS, or JWT claims) — see docs/security/TENANT-THREAT-MODEL.md.