Files
familienarchiv/docs/TODO-backend.md
Marcel 09ec2103c8 fix: correct malformed @Value annotations in DataInitializer
Missing closing braces caused Spring to inject the literal placeholder
string instead of resolving the property, silently ignoring any
app.admin.username / app.admin.password env-var overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 12:16:00 +01:00

7.0 KiB
Raw Permalink Blame History

Backend TODO

Findings from architectural review. Ordered roughly by severity.


Bugs

@Value annotation missing closing brace

File: config/DataInitializer.java:3337

// 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

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:69143

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:

@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:4953 and 101105

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

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


MassImportService provides no status or error feedback

File: service/MassImportService.java, controller/AdminController.java

/api/admin/trigger-import returns immediately (async), but there is no way for the admin to know whether the import succeeded, failed, or is still running. Errors during async execution are silently swallowed.

Fix options:

  • Store import job status in a DB table (import_jobs) with state (RUNNING, DONE, FAILED) and expose a GET /api/admin/import-status endpoint
  • Alternatively, make the endpoint synchronous since it already blocks on file I/O — only use async if you need true non-blocking behaviour

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. ExcelService — parsing logic, test with fixture .xlsx files (one exists in api_tests/)
  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

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