- 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
107 lines
6.3 KiB
Markdown
107 lines
6.3 KiB
Markdown
# 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` (optional `unit`, `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 and `api/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.yml` disables H2 console, sets `ddl-auto: validate`, Flyway on, stricter logging.
|
||
- **Done:** `org.postgresql:postgresql` on the runtime classpath; set `SPRING_DATASOURCE_*` (see `application-prod.yml` and 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:** ✅ `ShallowEtagHeaderFilter` on `/api/v1/**` (see `WebConfig`).
|
||
- **Request ID:** ✅ `RequestIdFilter` assigns/propagates `X-Request-Id`.
|
||
- **API versioning:** Keep /api/v1; when introducing breaking changes, add /api/v2 and document deprecation.
|
||
- **Credential types:** ✅ `@Pattern` on `CredentialSyncRequest.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`.
|