From e64f5ba0b702018e771350e8462550ee775f28e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Abdullah=20G=C3=BCltekin?= Date: Sun, 15 Mar 2026 18:34:27 +0300 Subject: [PATCH] docs: replace all GCP/34.116.233.134 references with Hetzner VPS GCP VM is decommissioned. All production URLs, deployment notes, and service references updated to point to Hetzner VPS (116.203.222.213) or https://auth.rollingcatsoftware.com. Co-Authored-By: Claude Sonnet 4.6 --- 07-status/IMPLEMENTATION_STATUS_REPORT.md | 10 +- 09-auth-flows/09-IMPLEMENTATION_PHASES.md | 4 +- API_CONTRACT_ANALYSIS.md | 631 ++++++++++++++++++ API_CONTRACT_FIX_PLAN.md | 386 +++++++++++ EU_AI_ACT_COMPLIANCE.md | 6 +- IMPLEMENTATION_STATUS_REPORT.md | 6 +- .../SPRING_2026_FINAL_PRESENTATION.md | 10 +- 7 files changed, 1035 insertions(+), 18 deletions(-) create mode 100644 API_CONTRACT_ANALYSIS.md create mode 100644 API_CONTRACT_FIX_PLAN.md diff --git a/07-status/IMPLEMENTATION_STATUS_REPORT.md b/07-status/IMPLEMENTATION_STATUS_REPORT.md index bd5531f..375ba2b 100644 --- a/07-status/IMPLEMENTATION_STATUS_REPORT.md +++ b/07-status/IMPLEMENTATION_STATUS_REPORT.md @@ -10,7 +10,7 @@ ## Overall Progress: 99% Complete ``` -Identity Core API: ████████████████████ 100% - Complete, deployed on GCP +Identity Core API: ████████████████████ 100% - Complete, deployed on Hetzner VPS Biometric Processor: ████████████████████ 100% - 46+ endpoints, all handlers Web Admin Dashboard: ████████████████████ 100% - Live on Hostinger Landing Website: ████████████████████ 100% - Live on Hostinger @@ -30,8 +30,8 @@ Biometric GPU Deploy: ░░░░░░░░░░░░░░░░░░ | Service | URL | Status | |---------|-----|--------| -| Identity Core API | http://34.116.233.134:8080 | Running | -| Swagger UI | http://34.116.233.134:8080/swagger-ui.html | Available | +| Identity Core API | http://116.203.222.213:8080 | Running | +| Swagger UI | http://116.203.222.213:8080/swagger-ui.html | Available | | Web Dashboard | https://ica-fivucsas.rollingcatsoftware.com | Live | | Landing Website | https://fivucsas.rollingcatsoftware.com | Live | | Biometric API | https://bpa-fivucsas.rollingcatsoftware.com | Pending (tunnel) | @@ -101,7 +101,7 @@ Biometric GPU Deploy: ░░░░░░░░░░░░░░░░░░ - 12 StepUpAuthService tests (register, challenge, verify flows) **Production** -- Deployed on GCP VM (europe-central2-a, external IP 34.116.233.134) +- Deployed on Hetzner VPS (Nuremberg, Germany, external IP 116.203.222.213) - Running in Docker container: fivucsas-identity-core-api (port 8080) - V17 migration applied, sample data seeded (3 tenants, 8 users, audit logs) - Step-up endpoints live and smoke-tested (register-device → 201, challenge → 200) @@ -241,7 +241,7 @@ Biometric GPU Deploy: ░░░░░░░░░░░░░░░░░░ - Kotlin Multiplatform structure in place - 7 test files written -- Production API URLs configured (http://34.116.233.134:8080) +- Production API URLs configured (http://116.203.222.213:8080) - Shared module: domain models, repository interfaces, Ktor API client - Desktop app: Launcher, Kiosk Mode, Admin Dashboard (MVVM, 53 components) - Blocked: Android SDK required to run `./gradlew :shared:test` diff --git a/09-auth-flows/09-IMPLEMENTATION_PHASES.md b/09-auth-flows/09-IMPLEMENTATION_PHASES.md index abbda70..5e0add6 100644 --- a/09-auth-flows/09-IMPLEMENTATION_PHASES.md +++ b/09-auth-flows/09-IMPLEMENTATION_PHASES.md @@ -584,8 +584,8 @@ librosa>=0.10.0 # Audio feature extraction ## 12. Deployment Order ``` -1. identity-core-api V16 migration → Apply to GCP PostgreSQL -2. identity-core-api new code → Build JAR → Deploy to GCP VM +1. identity-core-api V16 migration → Apply to Hetzner VPS PostgreSQL +2. identity-core-api new code → Build JAR → Deploy to Hetzner VPS 3. biometric-processor voice → Deploy when Cloudflare Tunnel ready 4. web-app → Build → Upload to Hostinger 5. client-apps → Build APK/Desktop binary diff --git a/API_CONTRACT_ANALYSIS.md b/API_CONTRACT_ANALYSIS.md new file mode 100644 index 0000000..fd090ca --- /dev/null +++ b/API_CONTRACT_ANALYSIS.md @@ -0,0 +1,631 @@ +# FIVUCSAS API Contract Analysis Report + +**Date:** 2026-02-10 +**Author:** Claude Code (Automated Audit) +**Scope:** Identity Core API (Backend) vs Web App (Frontend) Contract Alignment +**Severity Scale:** CRITICAL > HIGH > MEDIUM > LOW + +--- + +## Executive Summary + +The Identity Core API provides **60+ endpoints** across 12 controllers with solid hexagonal architecture. However, the frontend (web-app) was developed with **different assumptions** about the API contract. This report documents **14 mismatches** between what the backend actually returns and what the frontend expects to receive. + +**Verdict:** The backend is architecturally complete (~85%), but **cannot serve the frontend correctly** in its current state due to DTO mismatches, field naming conflicts, and a broken production URL. + +--- + +## Table of Contents + +1. [Infrastructure Issues](#1-infrastructure-issues) +2. [User DTO Mismatch](#2-user-dto-mismatch) +3. [Tenant DTO Mismatch](#3-tenant-dto-mismatch) +4. [Enrollment DTO Mismatch](#4-enrollment-dto-mismatch) +5. [Settings DTO Mismatch](#5-settings-dto-mismatch) +6. [Create User Request Mismatch](#6-create-user-request-mismatch) +7. [Update User Request Mismatch](#7-update-user-request-mismatch) +8. [Create Tenant Request Mismatch](#8-create-tenant-request-mismatch) +9. [Update Tenant Request Mismatch](#9-update-tenant-request-mismatch) +10. [Auth Response Minor Issues](#10-auth-response-minor-issues) +11. [Status Enum Mismatches](#11-status-enum-mismatches) +12. [Audit Log DTO (Compatible)](#12-audit-log-dto-compatible) +13. [Statistics DTO (Compatible)](#13-statistics-dto-compatible) +14. [CORS Production Config](#14-cors-production-config) +15. [Solution Strategy](#15-solution-strategy) + +--- + +## 1. Infrastructure Issues + +### 1.1 Production API URL - BROKEN + +**Severity: CRITICAL** + +| Side | Value | +|------|-------| +| Frontend `.env.production` | `https://api-fivucsas.rollingcatsoftware.com/api/v1` | +| Backend actual location | `http://116.203.222.213:8080/api/v1` | + +The domain `api-fivucsas.rollingcatsoftware.com` **does not exist**. The deployed frontend at `ica-fivucsas.rollingcatsoftware.com` should use `http://116.203.222.213:8080/api/v1` directly. + +**Impact:** Frontend is completely non-functional in production. + +### 1.2 CORS Not Configured for Production Frontend + +**Severity: CRITICAL** + +Backend CORS allowed origins (from `SecurityConfig.java:42`): +``` +http://localhost:3000,http://localhost:4200,http://localhost:5173 +``` + +Production frontend origin: `https://ica-fivucsas.rollingcatsoftware.com` + +**Impact:** Even if the URL issue is fixed, CORS will block all requests from the production frontend. + +--- + +## 2. User DTO Mismatch + +**Severity: HIGH** + +### Frontend Expects (`UserJSON` in `web-app/src/domain/models/User.ts`): +```typescript +{ + id: string + email: string + firstName: string + lastName: string + role: UserRole // single enum: USER | ADMIN | TENANT_ADMIN | SUPER_ADMIN + status: UserStatus // 6 values: PENDING_ENROLLMENT | ACTIVE | INACTIVE | SUSPENDED | DELETED | LOCKED + tenantId: string + createdAt: string + updatedAt: string + lastLoginAt?: string // optional + lastLoginIp?: string // optional +} +``` + +### Backend Returns (`UserDto` in `identity-core-api/.../dto/UserDto.java`): +```java +{ + id: String + firstName: String + lastName: String + name: String // EXTRA - frontend doesn't use + email: String + idNumber: String // EXTRA - frontend doesn't use + phoneNumber: String // EXTRA - frontend doesn't use + address: String // EXTRA - frontend doesn't use + status: UserStatus // 3 values only: ACTIVE | INACTIVE | SUSPENDED + role: String // plain String, not enum + roles: Set // EXTRA - frontend doesn't use + tenantId: String + isBiometricEnrolled: boolean // EXTRA - frontend doesn't use + enrolledAt: Instant // EXTRA + lastVerifiedAt: Instant // EXTRA + verificationCount: Integer // EXTRA + createdAt: Instant + updatedAt: Instant + // MISSING: lastLoginAt + // MISSING: lastLoginIp +} +``` + +### Field-by-Field Comparison: + +| Field | Frontend | Backend | Status | +|-------|----------|---------|--------| +| `id` | `string` | `String` | OK | +| `email` | `string` | `String` | OK | +| `firstName` | `string` | `String` | OK | +| `lastName` | `string` | `String` | OK | +| `role` | `UserRole` enum (4 values) | `String` (free text) | MISMATCH - Backend must return correct enum values | +| `status` | `UserStatus` enum (6 values) | `UserStatus` enum (3 values) | MISMATCH - Missing PENDING_ENROLLMENT, DELETED, LOCKED | +| `tenantId` | `string` | `String` | OK | +| `createdAt` | `string` (ISO) | `Instant` (ISO via Jackson) | OK | +| `updatedAt` | `string` (ISO) | `Instant` (ISO via Jackson) | OK | +| `lastLoginAt` | `string?` | **MISSING** | MISMATCH | +| `lastLoginIp` | `string?` | **MISSING** | MISMATCH | +| `name` | not expected | `String` | OK (ignored) | +| `idNumber` | not expected | `String` | OK (ignored) | +| `phoneNumber` | not expected | `String` | OK (ignored) | +| `address` | not expected | `String` | OK (ignored) | +| `isBiometricEnrolled` | not expected | `boolean` | OK (ignored) | +| `roles` | not expected | `Set` | OK (ignored) | + +### Issues: +1. **`role` value mapping**: Backend `role` field is a plain String. Must return one of: `USER`, `ADMIN`, `TENANT_ADMIN`, `SUPER_ADMIN`. Currently maps from internal role types (ROOT→SUPER_ADMIN, TENANT_ADMIN→TENANT_ADMIN, etc.) - needs verification. +2. **`status` enum gap**: Backend only has 3 statuses (ACTIVE, INACTIVE, SUSPENDED). Frontend expects 6. The statuses PENDING_ENROLLMENT, DELETED, LOCKED don't exist in backend. +3. **`lastLoginAt` missing**: Frontend uses this for display. Backend UserDto doesn't include it. +4. **`lastLoginIp` missing**: Frontend uses this for display. Backend UserDto doesn't include it. + +--- + +## 3. Tenant DTO Mismatch + +**Severity: HIGH** + +### Frontend Expects (`TenantJSON` in `web-app/src/domain/models/Tenant.ts`): +```typescript +{ + id: string + name: string + domain: string // <-- uses "domain" + status: TenantStatus // ACTIVE | TRIAL | SUSPENDED + maxUsers: number + currentUsers: number // <-- expects current user count + createdAt: string + updatedAt: string +} +``` + +### Backend Returns (`TenantResponse` in `identity-core-api/.../dto/response/TenantResponse.java`): +```java +{ + id: String + name: String + slug: String // <-- uses "slug" NOT "domain" + description: String // EXTRA + contactEmail: String // EXTRA + contactPhone: String // EXTRA + status: String // ACTIVE | INACTIVE | SUSPENDED | TRIAL | PENDING + maxUsers: int + biometricEnabled: boolean // EXTRA + sessionTimeoutMinutes: int // EXTRA + refreshTokenValidityDays: int // EXTRA + mfaRequired: boolean // EXTRA + createdAt: Instant + updatedAt: Instant + // MISSING: currentUsers + // MISSING: domain (has "slug" instead) +} +``` + +### Issues: +1. **`domain` vs `slug`**: Frontend reads `data.domain`, backend returns `slug`. Result: `domain` will be `undefined`. +2. **`currentUsers` missing**: Frontend reads `data.currentUsers` for usage percentage display. Backend doesn't return it. +3. **Status enum mismatch**: Frontend has `ACTIVE | TRIAL | SUSPENDED`. Backend has `ACTIVE | INACTIVE | SUSPENDED | TRIAL | PENDING`. Backend is actually a superset, but frontend TenantStatus enum doesn't include INACTIVE or PENDING. + +--- + +## 4. Enrollment DTO Mismatch + +**Severity: HIGH** + +### Frontend Expects (`EnrollmentJSON` in `web-app/src/domain/models/Enrollment.ts`): +```typescript +{ + id: string + userId: string + tenantId: string + status: EnrollmentStatus // PENDING | PROCESSING | SUCCESS | FAILED + faceImageUrl: string + createdAt: string + updatedAt: string + qualityScore?: number + livenessScore?: number + errorCode?: string + errorMessage?: string + completedAt?: string +} +``` + +### Backend Returns (`EnrollmentDto` in `identity-core-api/.../dto/EnrollmentDto.java`): +```java +{ + id: String + userId: String + userName: String // EXTRA - frontend doesn't use + userEmail: String // EXTRA - frontend doesn't use + status: String // hardcoded "COMPLETED" + enrolledAt: Instant // named differently + // MISSING: tenantId + // MISSING: faceImageUrl + // MISSING: updatedAt + // MISSING: qualityScore + // MISSING: livenessScore + // MISSING: errorCode + // MISSING: errorMessage + // MISSING: completedAt + // MISSING: createdAt (has enrolledAt instead) +} +``` + +### Issues: +This is the **most severe DTO mismatch**. Backend EnrollmentDto has only 6 fields, frontend expects 12. + +1. **`tenantId` missing**: Frontend needs it for multi-tenant display +2. **`faceImageUrl` missing**: Frontend expects image URL for display +3. **`status` hardcoded**: Backend always returns "COMPLETED", frontend expects 4 different statuses +4. **`createdAt`/`updatedAt` missing**: Backend has `enrolledAt` instead, named differently +5. **All biometric quality fields missing**: qualityScore, livenessScore, errorCode, errorMessage, completedAt + +--- + +## 5. Settings DTO Mismatch + +**Severity: HIGH** + +### Frontend Expects (flat structure from `ISettingsRepository.ts`): +```typescript +{ + userId: string + firstName: string + lastName: string + emailNotifications: boolean + loginAlerts: boolean + securityAlerts: boolean + weeklyReports: boolean + twoFactorEnabled: boolean + sessionTimeoutMinutes: number + darkMode: boolean + compactView: boolean +} +``` + +### Backend Returns (nested Map from `UserSettingsController.java`): +```json +{ + "notifications": { + "email": true, + "push": true, + "securityAlerts": true + }, + "security": { + "twoFactorEnabled": false, + "sessionTimeout": 30 + }, + "appearance": { + "theme": "light", + "language": "en", + "density": "comfortable" + } +} +``` + +### Issues: +1. **Structure**: Frontend expects flat object, backend returns nested object +2. **Missing on backend**: `userId`, `firstName`, `lastName` not in settings response (these come from User entity) +3. **Field names differ for notifications**: Frontend sends `emailNotifications`/`loginAlerts`/`weeklyReports`, backend has `email`/`push` (no loginAlerts, no weeklyReports) +4. **Field names differ for security**: Frontend sends `sessionTimeoutMinutes`, backend has `sessionTimeout` +5. **Field names differ for appearance**: Frontend sends `darkMode`/`compactView`, backend has `theme`/`language`/`density` + +### Sub-endpoint Mismatches: + +| Endpoint | Frontend Sends | Backend Expects | +|----------|---------------|-----------------| +| `PUT .../notifications` | `{ emailNotifications, loginAlerts, securityAlerts, weeklyReports }` | `{ email, push, securityAlerts }` | +| `PUT .../security` | `{ twoFactorEnabled, sessionTimeoutMinutes }` | `{ twoFactorEnabled, sessionTimeout }` | +| `PUT .../appearance` | `{ darkMode, compactView }` | `{ theme, language, density }` | + +--- + +## 6. Create User Request Mismatch + +**Severity: HIGH** + +### Frontend Sends (`CreateUserData` in `IUserRepository.ts`): +```typescript +{ + email: string + firstName: string + lastName: string + password: string + role: string // frontend sends this + tenantId: string // frontend sends this +} +``` + +### Backend Expects (`CreateUserRequest` in `CreateUserRequest.java`): +```java +{ + firstName: String // @NotBlank + lastName: String // @NotBlank + email: String // @NotBlank @Email + password: String // @NotBlank @Size(min=8) + idNumber: String // @Pattern(11 digits) - optional + phoneNumber: String // @Pattern(10-15 digits) - optional + address: String // @Size(max=500) - optional + // DOES NOT ACCEPT: role + // DOES NOT ACCEPT: tenantId +} +``` + +### Issues: +1. **`role` not accepted**: Frontend sends role assignment during user creation, but backend ignores it (no field for it). New users get default role only. +2. **`tenantId` not accepted**: Frontend sends tenant assignment, but backend ignores it. Users are assigned to hardcoded default tenant. +3. **`idNumber`, `phoneNumber`, `address` not sent**: Backend accepts these optional fields but frontend doesn't provide them. + +--- + +## 7. Update User Request Mismatch + +**Severity: MEDIUM** + +### Frontend Sends (`UpdateUserData` in `IUserRepository.ts`): +```typescript +{ + email?: string + firstName?: string + lastName?: string + role?: string // frontend sends this + status?: string +} +``` + +### Backend Expects (`UpdateUserRequest` in `UpdateUserRequest.java`): +```java +{ + firstName: String + lastName: String + email: String + idNumber: String + phoneNumber: String + address: String + status: UserStatus // only ACTIVE | INACTIVE | SUSPENDED + // DOES NOT ACCEPT: role +} +``` + +### Issues: +1. **`role` not accepted**: Frontend tries to change user role via update, backend doesn't support it. Role changes require the User Role Assignment endpoints (`/users/{id}/roles/{roleId}`). +2. **`status` enum mismatch**: Frontend may send PENDING_ENROLLMENT, DELETED, or LOCKED - backend will reject these. + +--- + +## 8. Create Tenant Request Mismatch + +**Severity: HIGH** + +### Frontend Sends (`CreateTenantData` in `ITenantRepository.ts`): +```typescript +{ + name: string + domain: string // <-- frontend uses "domain" + status: string + maxUsers: number + currentUsers?: number +} +``` + +### Backend Expects (`TenantController.CreateTenantRequest` inner class): +```java +{ + name: String + slug: String // <-- backend uses "slug" + description: String + contactEmail: String + contactPhone: String + maxUsers: Integer + biometricEnabled: Boolean + sessionTimeoutMinutes: Integer + refreshTokenValidityDays: Integer + mfaRequired: Boolean + // DOES NOT ACCEPT: domain + // DOES NOT ACCEPT: status (set internally) + // DOES NOT ACCEPT: currentUsers (computed) +} +``` + +### Issues: +1. **`domain` vs `slug`**: Frontend sends `domain`, backend expects `slug`. Field will be null on backend. +2. **`status` not accepted**: Frontend sends status, backend sets it internally (new tenants start as ACTIVE or PENDING). +3. **`currentUsers` not accepted**: This is a computed field, not settable. +4. **Missing fields**: Frontend doesn't send description, contactEmail, contactPhone, biometricEnabled, sessionTimeoutMinutes, refreshTokenValidityDays, mfaRequired. + +--- + +## 9. Update Tenant Request Mismatch + +**Severity: HIGH** + +Same issues as Create Tenant. Frontend sends `domain`/`status`/`currentUsers`, backend expects `slug` and different configuration fields. + +--- + +## 10. Auth Response Minor Issues + +**Severity: LOW** + +### Frontend Expects: +```typescript +{ accessToken, refreshToken, expiresIn?, user: UserJSON } +``` + +### Backend Returns: +```java +{ accessToken, refreshToken, tokenType, expiresIn, user: UserDto } +``` + +- `tokenType` is extra (harmless, ignored by frontend) +- `expiresIn` is always present in backend (frontend treats as optional with fallback to 3600) +- `user` object has the UserDto mismatches from Section 2 + +**Impact:** Auth response structure is correct, but the nested `user` object carries all User DTO mismatches. + +--- + +## 11. Status Enum Mismatches + +**Severity: MEDIUM** + +### UserStatus + +| Value | Frontend | Backend | +|-------|----------|---------| +| `ACTIVE` | YES | YES | +| `INACTIVE` | YES | YES | +| `SUSPENDED` | YES | YES | +| `PENDING_ENROLLMENT` | YES | **NO** | +| `DELETED` | YES | **NO** | +| `LOCKED` | YES | **NO** | + +### TenantStatus + +| Value | Frontend | Backend | +|-------|----------|---------| +| `ACTIVE` | YES | YES | +| `SUSPENDED` | YES | YES | +| `TRIAL` | YES | YES | +| `INACTIVE` | NO | YES | +| `PENDING` | NO | YES | + +### EnrollmentStatus + +| Value | Frontend | Backend EnrollmentDto | +|-------|----------|-----------------------| +| `PENDING` | YES | NO (hardcoded "COMPLETED") | +| `PROCESSING` | YES | NO | +| `SUCCESS` | YES | NO | +| `FAILED` | YES | NO | + +--- + +## 12. Audit Log DTO (Compatible) + +**Severity: NONE** + +### Frontend Expects: +```typescript +{ id, userId, tenantId, action, entityType, ipAddress?, userAgent?, details?, timestamp?, createdAt?, entityId? } +``` + +### Backend Returns: +```java +{ id, userId, tenantId, action, entityType, entityId, success, errorMessage, ipAddress, userAgent, details, timestamp } +``` + +**Status:** Compatible. Frontend handles both `timestamp` and `createdAt` as fallback. Extra fields (`success`, `errorMessage`) are ignored. Audit log pagination format (`{ content, totalElements, totalPages, page, size }`) is handled by frontend. + +--- + +## 13. Statistics DTO (Compatible) + +**Severity: NONE** + +### Frontend Expects (all optional with `?? 0` defaults): +```typescript +{ totalUsers?, activeUsers?, inactiveUsers?, suspendedUsers?, biometricEnrolledUsers?, totalVerifications?, totalTenants?, pendingEnrollments?, successfulEnrollments?, failedEnrollments?, authSuccessRate?, verificationSuccessRate? } +``` + +### Backend Returns: +```java +{ totalUsers, activeUsers, inactiveUsers, suspendedUsers, biometricEnrolledUsers, totalVerifications, averageVerificationsPerUser, totalTenants, pendingEnrollments, successfulEnrollments, failedEnrollments, authSuccessRate, verificationSuccessRate } +``` + +**Status:** Compatible. All frontend-expected fields are present. Backend has extra `averageVerificationsPerUser` (ignored). Frontend uses `?? 0` fallback so all fields work. + +--- + +## 14. CORS Production Config + +**Severity: CRITICAL** + +### Current CORS Config (`SecurityConfig.java:42`): +```java +@Value("${cors.allowed-origins:http://localhost:3000,http://localhost:4200,http://localhost:5173}") +private String allowedOrigins; +``` + +### Production CORS Requirement: +``` +https://ica-fivucsas.rollingcatsoftware.com +``` + +The `cors.allowed-origins` property is configurable via environment variable, but it's **not set in the Hetzner VPS Docker deployment**. The default value only includes localhost origins. + +--- + +## 15. Solution Strategy + +### Principle: **Backend is the source of truth. Adapt BOTH sides to a shared contract.** + +The professional approach is a **Backend-Driven Contract** where: +- Backend DTOs are enriched to include all data the frontend needs +- Frontend models are updated to match backend field names +- Both sides agree on enum values + +### Priority Order: + +#### P0 - CRITICAL (Frontend completely broken) +1. **Fix production API URL** - Either DNS record or `.env.production` update +2. **Fix CORS** - Add production origin to allowed origins env var + +#### P1 - HIGH (Pages crash or show wrong data) +3. **Fix Tenant DTO** - Add `domain` alias or rename to `slug` on frontend +4. **Fix Enrollment DTO** - Enrich backend with missing fields +5. **Fix User DTO** - Add `lastLoginAt`/`lastLoginIp` to backend, align status enums +6. **Fix Settings DTO** - Align structure (frontend adapts to backend's nested format) + +#### P2 - HIGH (CRUD operations fail) +7. **Fix Create User Request** - Backend: accept `role` + `tenantId`, OR Frontend: use role assignment endpoint after creation +8. **Fix Update User Request** - Frontend: use role assignment endpoint, align status enum +9. **Fix Create/Update Tenant Request** - Frontend: use `slug` instead of `domain` + +#### P3 - MEDIUM (Edge cases) +10. **Align status enums** - Decide on canonical set for UserStatus, TenantStatus, EnrollmentStatus + +### Recommended Changes Per Side: + +#### Backend Changes Needed: +| Change | File | Reason | +|--------|------|--------| +| Add `lastLoginAt`, `lastLoginIp` to UserDto | `UserDto.java` | Frontend displays these | +| Add `PENDING_ENROLLMENT`, `DELETED`, `LOCKED` to UserStatus | `UserStatus.java` | Frontend uses these statuses | +| Enrich EnrollmentDto with missing fields | `EnrollmentDto.java` | Frontend expects 12 fields, backend has 6 | +| Add `currentUsers` computation to TenantResponse | `TenantResponse.java` | Frontend displays usage percentage | +| Accept `role`+`tenantId` in CreateUserRequest OR return proper error | `CreateUserRequest.java` | Frontend sends these during user creation | +| Add production CORS origin | `application-prod.yml` or env var | Frontend blocked by CORS | + +#### Frontend Changes Needed: +| Change | File | Reason | +|--------|------|--------| +| Map `slug` to `domain` in Tenant.fromJSON() | `Tenant.ts` | Backend uses `slug`, not `domain` | +| Use `slug` in Create/Update Tenant requests | `ITenantRepository.ts`, `TenantRepository.ts` | Backend expects `slug` | +| Adapt Settings to nested structure | `SettingsRepository.ts`, `ISettingsRepository.ts` | Backend uses nested `{ notifications: {}, security: {}, appearance: {} }` | +| Map setting field names | `SettingsRepository.ts` | `emailNotifications`→`email`, `sessionTimeoutMinutes`→`sessionTimeout`, `darkMode`→`theme` | +| Fix `.env.production` API URL | `.env.production` | Points to non-existent domain | +| Handle role assignment via separate endpoint | `UserRepository.ts` | Backend doesn't accept `role` in create user | + +--- + +## Appendix: Complete Endpoint Inventory + +### Backend Provides (60+ endpoints): +| Controller | Count | Endpoints | +|-----------|-------|-----------| +| AuthController | 6 | register, login, refresh, logout, me, health | +| UserController | 7 | list, get, create, update, delete, change-password, search | +| TenantController | 8 | create, get, get-by-slug, list, update, activate, suspend, delete | +| RoleController | 8 | list, get, by-tenant, create, update, delete, assign-perm, revoke-perm | +| PermissionController | 3 | list, get, by-resource | +| UserRoleController | 4 | list, assign, revoke, users-by-role | +| BiometricController | 2 | enroll, verify | +| EnrollmentController | 4 | list, get, retry, delete | +| GuestController | 6 | invite, accept, list, count, revoke, extend | +| AuditLogController | 2 | list (paginated), get | +| StatisticsController | 1 | get | +| UserSettingsController | 8 | get/update all, get/update notifications, get/update security, get/update appearance | + +### Frontend Uses (28 endpoints): +| Feature | Count | Endpoints | +|---------|-------|-----------| +| Auth | 4 | login, logout, refresh, me | +| Users | 5 | list, get, create, update, delete | +| Tenants | 5 | list, get, create, update, delete | +| Enrollments | 4 | list, get, retry, delete | +| Audit Logs | 2 | list, get | +| Statistics | 1 | get | +| Settings | 7 | get, update-profile, update-notifications, update-security, update-appearance, change-password, (get sub-sections) | + +### Backend Features NOT Used by Frontend: +- Role Management (8 endpoints) +- Permission Management (3 endpoints) +- User Role Assignment (4 endpoints) +- Guest Management (6 endpoints) +- Biometric Operations (2 endpoints) +- Tenant activate/suspend/get-by-slug (3 endpoints) +- User search (1 endpoint) + +These 27 unused endpoints are **not wasted** - they will be needed by mobile/desktop clients and future admin features. diff --git a/API_CONTRACT_FIX_PLAN.md b/API_CONTRACT_FIX_PLAN.md new file mode 100644 index 0000000..ec62a8f --- /dev/null +++ b/API_CONTRACT_FIX_PLAN.md @@ -0,0 +1,386 @@ +# FIVUCSAS API Contract Fix Plan + +**Date:** 2026-02-10 +**Prerequisite:** Read `API_CONTRACT_ANALYSIS.md` first +**Approach:** Backend-Driven Contract with Frontend Adaptation Layer + +--- + +## Strategy: Why Fix Both Sides? + +A professional solution doesn't just "make things work" - it establishes a **contract** that both sides respect. The strategy: + +1. **Backend is the authority** for data shape, business logic, and enum values +2. **Frontend adapts** via a mapping layer in its repositories (already has `fromJSON()` pattern) +3. **Where backend is genuinely incomplete**, enrich it (missing fields, insufficient enums) +4. **Where frontend assumes wrong shape**, fix the mapping layer + +This avoids hacks on either side and creates a clean, maintainable integration. + +--- + +## Phase 0: Unblock Production (30 min) + +These two fixes unblock the entire frontend immediately. + +### Fix 0.1: Production API URL + +**Side:** Frontend +**File:** `web-app/.env.production` + +Change: +``` +VITE_API_BASE_URL=https://api-fivucsas.rollingcatsoftware.com/api/v1 +``` +To: +``` +VITE_API_BASE_URL=http://116.203.222.213:8080/api/v1 +``` + +> **Note:** Later, when we set up a proper domain with HTTPS and reverse proxy, we'll change this to `https://api-fivucsas.rollingcatsoftware.com/api/v1`. For now, use the direct IP. + +### Fix 0.2: CORS for Production + +**Side:** Backend (Hetzner VPS environment variable) +**File:** Docker Compose or env vars on Hetzner VPS + +Add to the `CORS_ALLOWED_ORIGINS` (or `cors.allowed-origins`) environment variable: +``` +http://localhost:3000,http://localhost:4200,http://localhost:5173,https://ica-fivucsas.rollingcatsoftware.com +``` + +**Deployment:** Requires redeploying identity-core-api container on Hetzner VPS with updated env var. + +--- + +## Phase 1: Backend DTO Enrichment (Backend Changes) + +These changes make the backend return complete data that any client can use. + +### Fix 1.1: Enrich UserDto + +**File:** `identity-core-api/src/main/java/com/fivucsas/identity/dto/UserDto.java` + +Add missing fields: +```java +private Instant lastLoginAt; +private String lastLoginIp; +``` + +**Also update** the mapper/service that builds UserDto to populate these fields from the User entity or active_sessions table. + +### Fix 1.2: Expand UserStatus Enum + +**File:** `identity-core-api/src/main/java/com/fivucsas/identity/entity/UserStatus.java` + +Add missing values: +```java +public enum UserStatus { + ACTIVE, + INACTIVE, + SUSPENDED, + PENDING_ENROLLMENT, // new - for users awaiting biometric enrollment + DELETED, // new - soft-deleted users + LOCKED // new - locked due to security (failed attempts, etc.) +} +``` + +**Migration:** Add Flyway migration V15 to update any CHECK constraints on the users table status column. + +### Fix 1.3: Enrich EnrollmentDto + +**File:** `identity-core-api/src/main/java/com/fivucsas/identity/dto/EnrollmentDto.java` + +Replace minimal DTO with complete version: +```java +public class EnrollmentDto { + private String id; + private String userId; + private String userName; + private String userEmail; + private String tenantId; // ADD + private String status; // FIX: use actual status, not hardcoded "COMPLETED" + private String faceImageUrl; // ADD + private Instant createdAt; // ADD (was enrolledAt) + private Instant updatedAt; // ADD + private Instant enrolledAt; // KEEP + private Double qualityScore; // ADD + private Double livenessScore; // ADD + private String errorCode; // ADD + private String errorMessage; // ADD + private Instant completedAt; // ADD +} +``` + +**Also update** EnrollmentController to populate all fields from the biometric_data/enrollment entity. + +### Fix 1.4: Add `currentUsers` to TenantResponse + +**File:** `identity-core-api/src/main/java/com/fivucsas/identity/application/dto/response/TenantResponse.java` + +Add: +```java +private final int currentUsers; // computed from user count query +``` + +**Also update** the service that builds TenantResponse to count users per tenant. + +### Fix 1.5: Accept `role` and `tenantId` in CreateUserRequest + +**Option A (Recommended):** Add fields to CreateUserRequest: +```java +private String role; // optional - assign role after creation +private String tenantId; // optional - assign to tenant +``` + +Then in the CreateUser use case, after creating the user: +1. If `tenantId` provided, associate user with that tenant +2. If `role` provided, assign the corresponding role via UserRole service + +**Option B:** Keep CreateUserRequest as-is, and make frontend call role assignment endpoint after user creation (2 API calls instead of 1). + +**Recommendation:** Option A is cleaner for the frontend developer experience. + +--- + +## Phase 2: Frontend Adaptation Layer (Frontend Changes) + +These changes make the frontend correctly map backend responses to its domain models. + +### Fix 2.1: Tenant Model - Map `slug` to `domain` + +**File:** `web-app/src/domain/models/Tenant.ts` + +Update `fromJSON()`: +```typescript +static fromJSON(data: any): Tenant { + return new Tenant( + data.id, + data.name, + data.slug ?? data.domain ?? '', // backend sends "slug" + data.status, + data.maxUsers ?? 0, + data.currentUsers ?? 0, + new Date(data.createdAt), + new Date(data.updatedAt) + ) +} +``` + +### Fix 2.2: Tenant Repository - Send `slug` instead of `domain` + +**File:** `web-app/src/domain/interfaces/ITenantRepository.ts` + +```typescript +export interface CreateTenantData { + name: string + slug: string // changed from "domain" + description?: string // add optional backend fields + contactEmail?: string + maxUsers: number +} +``` + +**File:** `web-app/src/core/repositories/TenantRepository.ts` + +Map frontend field to backend field in create/update calls. + +### Fix 2.3: Settings Repository - Adapt to Nested Structure + +**File:** `web-app/src/core/repositories/SettingsRepository.ts` + +Add mapping layer in `getSettings()`: +```typescript +async getSettings(userId: string): Promise { + const response = await this.httpClient.get(`/users/${userId}/settings`) + const data = response.data + + // Also fetch user profile for firstName/lastName + const userResponse = await this.httpClient.get(`/users/${userId}`) + + // Map nested backend format to flat frontend format + return { + userId, + firstName: userResponse.data.firstName ?? '', + lastName: userResponse.data.lastName ?? '', + emailNotifications: data.notifications?.email ?? true, + loginAlerts: data.notifications?.push ?? true, + securityAlerts: data.notifications?.securityAlerts ?? true, + weeklyReports: false, // not supported by backend + twoFactorEnabled: data.security?.twoFactorEnabled ?? false, + sessionTimeoutMinutes: data.security?.sessionTimeout ?? 30, + darkMode: data.appearance?.theme === 'dark', + compactView: data.appearance?.density === 'compact', + } +} +``` + +Similarly, update `updateNotifications()`, `updateSecurity()`, `updateAppearance()` to map flat frontend fields to backend nested fields: +```typescript +async updateNotifications(userId: string, data: UpdateNotificationSettings): Promise { + await this.httpClient.put(`/users/${userId}/settings/notifications`, { + email: data.emailNotifications, + push: data.loginAlerts, + securityAlerts: data.securityAlerts, + }) + return this.getSettings(userId) +} +``` + +### Fix 2.4: User Model - Handle Role Mapping + +**File:** `web-app/src/domain/models/User.ts` + +Update `fromJSON()` to handle backend's role format: +```typescript +static fromJSON(data: any): User { + // Backend sends role as string, map to enum + const roleMap: Record = { + 'SUPER_ADMIN': UserRole.SUPER_ADMIN, + 'ROOT': UserRole.SUPER_ADMIN, + 'TENANT_ADMIN': UserRole.TENANT_ADMIN, + 'ADMIN': UserRole.ADMIN, + 'USER': UserRole.USER, + } + + return new User( + data.id, + data.email, + data.firstName, + data.lastName, + roleMap[data.role] ?? UserRole.USER, + data.status ?? UserStatus.ACTIVE, + data.tenantId, + new Date(data.createdAt), + new Date(data.updatedAt), + data.lastLoginAt ? new Date(data.lastLoginAt) : undefined, + data.lastLoginIp + ) +} +``` + +### Fix 2.5: Enrollment Model - Handle Backend Field Names + +**File:** `web-app/src/domain/models/Enrollment.ts` + +Update `fromJSON()`: +```typescript +static fromJSON(data: any): Enrollment { + return new Enrollment( + data.id, + data.userId, + data.tenantId ?? '', + data.status ?? EnrollmentStatus.SUCCESS, + data.faceImageUrl ?? '', + new Date(data.createdAt ?? data.enrolledAt), + new Date(data.updatedAt ?? data.enrolledAt), + data.qualityScore, + data.livenessScore, + data.errorCode, + data.errorMessage, + data.completedAt ? new Date(data.completedAt) : undefined + ) +} +``` + +### Fix 2.6: User Create - Handle Role Assignment + +**File:** `web-app/src/core/repositories/UserRepository.ts` + +If backend Option A is implemented (accepts role+tenantId), no change needed. + +If backend Option B (keep as-is): +```typescript +async create(data: CreateUserData): Promise { + // Step 1: Create user (without role/tenantId) + const { role, tenantId, ...createPayload } = data + const response = await this.httpClient.post('/users', createPayload) + const user = User.fromJSON(response.data) + + // Step 2: Assign role if specified + if (role) { + await this.httpClient.post(`/users/${user.id}/roles/${role}`, {}) + } + + return user +} +``` + +--- + +## Phase 3: Alignment Verification + +### 3.1: Create API Integration Tests + +For each frontend page, verify the full request/response cycle: + +| Test | Endpoint | Verify | +|------|----------|--------| +| Login | POST /auth/login | Returns accessToken, refreshToken, user with correct fields | +| Get Users | GET /users | Returns array of UserDto with all expected fields | +| Create User | POST /users | Accepts role+tenantId (or frontend handles 2-step) | +| Get Tenants | GET /tenants | Returns array with slug mapped to domain, currentUsers present | +| Get Enrollments | GET /enrollments | Returns enriched DTOs with all 12 fields | +| Get Settings | GET /users/{id}/settings | Returns nested format, frontend maps correctly | +| Update Settings | PUT /users/{id}/settings/notifications | Backend accepts mapped field names | +| Get Statistics | GET /statistics | All fields present | +| Get Audit Logs | GET /audit-logs | Paginated format handled correctly | + +### 3.2: End-to-End Smoke Test Checklist + +- [ ] Login with admin@fivucsas.local / Test@123 +- [ ] Dashboard shows statistics (numbers, not NaN or undefined) +- [ ] Users page lists users with correct roles and statuses +- [ ] Create user form works and user appears in list +- [ ] Edit user form populates correctly and saves +- [ ] Delete user works +- [ ] Tenants page lists tenants with correct domain/usage +- [ ] Create tenant form works +- [ ] Enrollments page shows enrollment data +- [ ] Audit logs page loads with pagination +- [ ] Settings page shows current values +- [ ] Change notification/security/appearance settings +- [ ] Change password works +- [ ] Logout works and redirects to login + +--- + +## Implementation Order + +``` +Phase 0 (CRITICAL - do first) +├── Fix 0.1: Update .env.production API URL +└── Fix 0.2: Add CORS origin to Hetzner VPS deployment + +Phase 1 (Backend enrichment) +├── Fix 1.1: Add lastLoginAt/lastLoginIp to UserDto +├── Fix 1.2: Expand UserStatus enum + migration +├── Fix 1.3: Enrich EnrollmentDto (biggest change) +├── Fix 1.4: Add currentUsers to TenantResponse +└── Fix 1.5: Accept role+tenantId in CreateUserRequest + +Phase 2 (Frontend adaptation) +├── Fix 2.1: Tenant fromJSON slug→domain mapping +├── Fix 2.2: Tenant create/update use slug +├── Fix 2.3: Settings nested→flat mapping +├── Fix 2.4: User role string→enum mapping +├── Fix 2.5: Enrollment fromJSON field mapping +└── Fix 2.6: User create role assignment flow + +Phase 3 (Verification) +├── API integration tests +└── End-to-end smoke test +``` + +--- + +## Estimated Scope + +| Phase | Side | Files Changed | Complexity | +|-------|------|--------------|------------| +| Phase 0 | Both | 2 files | Low | +| Phase 1 | Backend | ~8-10 files | Medium | +| Phase 2 | Frontend | ~8-10 files | Medium | +| Phase 3 | Both | New test files | Medium | + +**Total:** ~20 files changed across both projects. diff --git a/EU_AI_ACT_COMPLIANCE.md b/EU_AI_ACT_COMPLIANCE.md index 222974b..35aefe5 100644 --- a/EU_AI_ACT_COMPLIANCE.md +++ b/EU_AI_ACT_COMPLIANCE.md @@ -37,7 +37,7 @@ This document analyses the FIVUCSAS platform against the requirements of **Regul The analysis covers all system components that process biometric data: -- **Identity Core API** — Spring Boot 3.2 / Java 21, deployed on GCP (34.116.233.134:8080) +- **Identity Core API** — Spring Boot 3.2 / Java 21, deployed on Hetzner VPS (116.203.222.213:8080) - **Biometric Processor** — FastAPI / Python, using DeepFace for face recognition - **Web Admin Dashboard** — React 18 / TypeScript, deployed at ica-fivucsas.rollingcatsoftware.com - **Client Applications** — Kotlin Multiplatform (Android, Desktop) @@ -563,7 +563,7 @@ Complete API documentation is maintained and publicly accessible: | Documentation | Location | Format | |--------------|----------|--------| -| Identity Core API | http://34.116.233.134:8080/swagger-ui.html | OpenAPI 3.0 / Swagger UI | +| Identity Core API | http://116.203.222.213:8080/swagger-ui.html | OpenAPI 3.0 / Swagger UI | | Biometric Processor API | https://bpa-fivucsas.rollingcatsoftware.com/docs | FastAPI auto-generated OpenAPI | | Architecture Documentation | `docs/02-architecture/` | Markdown, C4 model diagrams | | API Services Overview | `docs/04-api/SERVICES_OVERVIEW.md` | Markdown | @@ -687,7 +687,7 @@ If FIVUCSAS were to be developed into a commercial product, the following additi ### 12.3 Production Deployment Note -The current deployment at `34.116.233.134:8080` processes biometric data in a **controlled research environment** with a small number of known test users. This does not constitute large-scale commercial biometric processing and falls within the typical academic research exception considered under the EU AI Act's proportionality provisions. +The current deployment at `116.203.222.213:8080` processes biometric data in a **controlled research environment** with a small number of known test users. This does not constitute large-scale commercial biometric processing and falls within the typical academic research exception considered under the EU AI Act's proportionality provisions. --- diff --git a/IMPLEMENTATION_STATUS_REPORT.md b/IMPLEMENTATION_STATUS_REPORT.md index 1c93446..71b2e4d 100644 --- a/IMPLEMENTATION_STATUS_REPORT.md +++ b/IMPLEMENTATION_STATUS_REPORT.md @@ -109,7 +109,7 @@ biometric-processor/ ## 3. Identity Core API **Location:** `identity-core-api/` -**Status:** 100% Complete - Deployed on GCP VM (34.116.233.134:8080) +**Status:** 100% Complete - Deployed on Hetzner VPS (116.203.222.213:8080) **Technology:** Spring Boot 3.2, Java 21 ### Implemented Features @@ -376,7 +376,7 @@ UniversalNfcReader/ 9. Database Schema (16 migrations, pgvector) 10. Universal NFC Reader (10+ card types) 11. Turkish eID NFC Reader (functional) -12. Identity Core API (10 auth handlers, 508 tests, deployed on GCP) +12. Identity Core API (10 auth handlers, 508 tests, deployed on Hetzner VPS) 13. Web Admin Dashboard (React 18, deployed to Hostinger) 14. Landing Website (deployed to fivucsas.rollingcatsoftware.com) 15. E2E Testing (14/14 Playwright tests pass against production) @@ -409,7 +409,7 @@ UniversalNfcReader/ | Demo Frontend | Next.js 14, TypeScript, shadcn/ui | | E2E Testing | Playwright (14 tests against production) | | CI/CD | GitHub Actions (Java 21, Python 3.11, Node 20) | -| Infrastructure | Docker, Redis, GCP VM, Hostinger | +| Infrastructure | Docker, Redis, Hetzner VPS, Hostinger | --- diff --git a/presentations/SPRING_2026_FINAL_PRESENTATION.md b/presentations/SPRING_2026_FINAL_PRESENTATION.md index baa321b..efa774b 100644 --- a/presentations/SPRING_2026_FINAL_PRESENTATION.md +++ b/presentations/SPRING_2026_FINAL_PRESENTATION.md @@ -183,7 +183,7 @@ Spring 2026 - Auth flow system: 8 new tables for configurable multi-step auth - Sample data: 3 tenants, 8 users, audit log entries -**Deployed:** GCP VM (34.116.233.134:8080) +**Deployed:** Hetzner VPS (116.203.222.213:8080) --- @@ -239,8 +239,8 @@ Spring 2026 ``` ┌─────────────────┐ ┌──────────────────────┐ -│ Hostinger │ │ GCP VM │ -│ │ │ (europe-central2) │ +│ Hostinger │ │ Hetzner VPS │ +│ │ │ (Nuremberg, Germany) │ │ Web Dashboard │────▶│ Identity Core API │ │ Landing Website │ │ PostgreSQL + Redis │ └─────────────────┘ └──────────┬───────────┘ @@ -256,7 +256,7 @@ Spring 2026 |---------|-----|---------| | Web Dashboard | ica-fivucsas.rollingcatsoftware.com | Hostinger | | Landing Page | fivucsas.rollingcatsoftware.com | Hostinger | -| Identity API | 34.116.233.134:8080 | GCP VM | +| Identity API | 116.203.222.213:8080 | Hetzner VPS | | Biometric API | bpa-fivucsas.rollingcatsoftware.com | Cloudflare Tunnel | **CI/CD:** GitHub Actions — 3 parallel jobs (Java 21 + Python 3.11 + Node 20) @@ -425,7 +425,7 @@ Spring 2026 **Live System:** - Dashboard: https://ica-fivucsas.rollingcatsoftware.com - Landing: https://fivucsas.rollingcatsoftware.com -- API Docs: http://34.116.233.134:8080/swagger-ui.html +- API Docs: http://116.203.222.213:8080/swagger-ui.html **Repository:** github.com/Rollingcat-Software/FIVUCSAS