# Backend TODO

Findings from architectural review. Ordered roughly by severity.

---

## Bugs

### `@Value` annotation missing closing brace
**File:** `config/DataInitializer.java:33–37`
```java
// Current (broken — Spring may silently fail to resolve the property)
@Value("${app.admin.username:admin")
@Value("${app.admin.password:admin123")

// Fix
@Value("${app.admin.username:admin}")
@Value("${app.admin.password:admin123}")
```
Spring's `@Value` SpEL parser requires the closing `}`. Without it, the literal string `"${app.admin.username:admin"` is injected instead of the resolved value. The default credentials will appear to work but env-var overrides will silently be ignored.

---

### Default credentials logged in plaintext
**File:** `config/DataInitializer.java:64`
```java
log.info("Default Admin erstellt: User='admin', Pass='admin123'");
```
The password appears in application logs. Remove the password from the log statement entirely. Log only the username.

---

## Design Issues

### Test data runs in every environment
**File:** `config/DataInitializer.java:69–143`

`initData` seeds 500 fake documents and 4 random persons whenever the database is empty. This runs unconditionally — including in production — and the log message at line 141 claims "50 Personen" when only 4 are created (copy/paste error).

**Fix:** Guard the bean with a Spring profile so it only runs locally:
```java
@Bean
@Profile("dev")
public CommandLineRunner initData(...) { ... }
```
Set `spring.profiles.active=dev` in `application.properties` for local use, and ensure the production environment does not set that profile.

---

### Two redundant permission groups created on startup
**File:** `config/DataInitializer.java:49–53` and `101–105`

`initAdminUser` creates group `"Administrators"` with `{ADMIN, READ_ALL, WRITE_ALL}`.
`initData` creates group `"Admins"` with `{READ_ALL, WRITE_ALL, ADMIN}` — identical permissions, different name, different bean.

Both beans run when the DB is empty, resulting in two duplicate admin groups and the admin user only belonging to the first one. `initData` is also guarded by `personRepo.count() > 0` — so if persons already exist it won't create the second group, making startup behaviour inconsistent.

**Fix:** Consolidate into a single `CommandLineRunner` bean or extract group creation into a shared `@PostConstruct` method. Remove `initData`'s group creation entirely (it belongs in `initAdminUser`).

---

### CSRF protection disabled with no plan to re-enable it
**File:** `config/SecurityConfig.java:39`
```java
.csrf(csrf -> csrf.disable())
```
The comment says "for development". HTTP Basic Auth over a cookie-authenticated SvelteKit frontend is still vulnerable to CSRF for mutating endpoints (PUT, DELETE, POST).

**Fix:** Either:
- Re-enable CSRF with `CookieCsrfTokenRepository` and pass the token to the frontend, or
- Adopt a stateless JWT/session approach and verify `Origin`/`Referer` headers

At minimum, document this as a known gap that must be addressed before any external deployment.

---

### Spring Session tables are created but never used
**File:** `db/migration/V1__initial_schema.sql` (Spring Session tables), `config/SecurityConfig.java`

The schema includes `spring_session` and `spring_session_attributes` tables, but the auth model is stateless HTTP Basic — credentials are re-validated from the database on every request. Spring Session JDBC is wiring itself up but managing nothing meaningful.

**Fix:** Decide on one model:
- **Keep Basic Auth (stateless):** Remove the Spring Session JDBC dependency and tables. Each request re-authenticates — which is fine for a small internal app.
- **Switch to session-based auth:** Replace Basic Auth with form login, issue a server-side session ID, and let Spring Session manage it. This reduces per-request DB hits.

---

### `Permission` enum not used consistently — permissions are plain strings
**File:** `security/Permission.java`, `security/RequirePermission.java`, `security/PermissionAspect.java`

`@RequirePermission` takes a `Permission` enum value, but user group permissions are stored and compared as raw `String` values (e.g., `"ADMIN"`, `"READ_ALL"`). The comparison in `PermissionAspect` calls `permission.name()` to convert the enum back to a string for matching — so type-safety is only at the annotation call site, not end-to-end.

**Fix:** Make the entire stack type-safe: store permissions as the enum's name (which is already happening) but also parse them back into `Permission` enum values when loading `UserDetails` authorities. This way a typo in the DB is caught at load time rather than silently failing permission checks.

---

## Missing Capabilities

### No test coverage
**File:** `src/test/java/org/raddatz/familienarchiv/FamilienarchivApplicationTests.java`

The only test is a Spring context load test. No unit or integration tests exist for any service, repository, or controller logic.

**Suggested starting points (highest value for effort):**
1. `DocumentSpecifications` — pure logic, easy to unit test with an in-memory H2 or Testcontainers PostgreSQL
2. Canonical import loaders (`CanonicalSheetReader`, `DocumentImporter`, etc.) — parsing/upsert logic, test with fixture canonical `.xlsx` files
3. `PermissionAspect` — security logic should be tested; use `@WithMockUser` from Spring Security Test

---

### No API documentation
There is no OpenAPI/Swagger endpoint. The only documentation is the `.http` files in `backend/api_tests/`.

**Fix:** Add `springdoc-openapi-starter-webmvc-ui` to `pom.xml`. It generates `/swagger-ui.html` and `/v3/api-docs` automatically from existing controller annotations with zero additional code.

---

### `FileService` content-type detection is fragile
File extension is inferred from the S3 key string. Many document types (TIFF scans, HEIC photos, Word documents) are unhandled and fall back to `application/octet-stream`, forcing a download instead of inline display.

**Fix:** Use `java.nio.file.Files.probeContentType()` or Apache Tika for robust MIME detection. Alternatively, store the content-type at upload time in the `Document` entity and retrieve it on download.

---

### Generic error response is hardcoded in German
**File:** `controller/GlobalExceptionHandler.java:36`
```java
.body(new ErrorResponse("Ein Fehler ist aufgetreten"))
```
The fallback error message is hardcoded in German. It should use a locale-aware message source or at minimum be in English as the lingua franca of APIs.
