diff --git a/docs/specs/012-scim-foundation-user-model/steps.md b/docs/specs/012-scim-foundation-user-model/steps.md index b2a11b6..6674a5c 100644 --- a/docs/specs/012-scim-foundation-user-model/steps.md +++ b/docs/specs/012-scim-foundation-user-model/steps.md @@ -144,8 +144,8 @@ the `findBySub` happy path. **Acceptance criteria:** -- [ ] Project builds; full test suite passes. -- [ ] `UserServiceTest` (Mockito unit tests) is extended with: +- [x] Project builds; full test suite passes. +- [x] `UserServiceTest` (Mockito unit tests) is extended with: - First-login scenario: new user → `userProvisioner.provision(...)` called → entity has all four new fields populated. - Pre-foundation backfill: existing row with `sub` set, `externalId/userName` null → @@ -163,15 +163,15 @@ the `findBySub` happy path. AccessDeniedException; `sub` is **not** written onto the row. - Drift on multiple fields runs as a single `save(user)` call. - Brand-new user only inserted after all three lookups miss. -- [ ] `UserServiceConcurrencyTest` still passes — the concurrency-test from spec 011 keeps +- [x] `UserServiceConcurrencyTest` still passes — the concurrency-test from spec 011 keeps the race-recovery regression covered. -- [ ] A new integration test in `UserServiceTest` or a new file +- [x] A new integration test in `UserServiceTest` or a new file `UserServiceActiveGateIntegrationTest` (Testcontainers): persists an inactive `UserEntity` directly and verifies that the next `getCurrentUserEntity()` for that subject throws `AccessDeniedException`. -- [ ] Reactivation regression test: toggle a user `active=false → active=true` between two +- [x] Reactivation regression test: toggle a user `active=false → active=true` between two calls; the second call succeeds. -- [ ] Two-users-same-email-no-preferred_username test: create user A with +- [x] Two-users-same-email-no-preferred_username test: create user A with `userName = email`, then attempt to provision user B with the same email and no `preferred_username` → expect `DataIntegrityViolationException`/`IllegalStateException` (whichever Spring surfaces) → request fails as `500 Internal Server Error`. This test goes in @@ -210,7 +210,7 @@ change. Set groups, String displayName)`. Add Javadoc that describes the contract; mark the whole package as a temporary 012-shipped scaffolding that spec 010 supersedes. **If spec 010 has landed:** skip the port creation; just consume it from its real location. -- [ ] Create `src/main/java/com/openelements/spring/base/services/user/UserEntityPrincipalDirectory.java`: +- [x] Create `src/main/java/com/openelements/spring/base/services/user/UserEntityPrincipalDirectory.java`: - `@Component` implementing `PrincipalDirectory`. - Constructor takes a `UserRepository` (`@Autowired` constructor injection, `Objects.requireNonNull`). @@ -223,8 +223,8 @@ change. **Acceptance criteria:** -- [ ] Project builds. -- [ ] New `UserEntityPrincipalDirectoryTest` (Testcontainers integration test): +- [x] Project builds. +- [x] New `UserEntityPrincipalDirectoryTest` (Testcontainers integration test): - Active user with `externalId = "alice-ext"` → `Optional.of(ResolvedPrincipal(active=true, roles=empty, groups=empty, displayName=user.getName()))`. - Inactive user with `externalId = "bob-ext"` → `Optional.of(ResolvedPrincipal(active=false, @@ -255,23 +255,23 @@ change. - The new `UserInformation` record fields (source-level breaking change). - The Flyway migration template from `design.md` for consuming apps. - The fact that `active` defaults to `true` for all existing rows. -- [ ] `.claude/conventions/project-specific/project-structure.md`: +- [x] `.claude/conventions/project-specific/project-structure.md`: - Add `UserEntityPrincipalDirectory` to the `services/user/` listing. - Add the `services/apitoken/` package (if Step 5 shipped the port here as a temporary measure). -- [ ] `TODO.md`: +- [x] `TODO.md`: - If applicable, mark the "SCIM 2.0 Foundation" entry as superseded (spec 012 is the implementation of that entry). -- [ ] Optional but recommended: a one-paragraph section in +- [x] Optional but recommended: a one-paragraph section in `services/user/package-info.java` describing how `PrincipalDirectory` consumers (the SCIM spec's eventual handlers, opaque-token validation in spec 010, etc.) read `UserEntity`. **Acceptance criteria:** -- [ ] README compiles (markdown-lint clean if linter is configured). -- [ ] Upgrade-notes paragraph mentions all three observable changes: record fields, schema +- [x] README compiles (markdown-lint clean if linter is configured). +- [x] Upgrade-notes paragraph mentions all three observable changes: record fields, schema migration template, active default. -- [ ] No stale references to the old single-`sub` lookup or "JIT-sync of roles" survive in +- [x] No stale references to the old single-`sub` lookup or "JIT-sync of roles" survive in documentation. **Related behaviors:** diff --git a/docs/specs/INDEX.md b/docs/specs/INDEX.md index 1457317..fcbe5d0 100644 --- a/docs/specs/INDEX.md +++ b/docs/specs/INDEX.md @@ -1,19 +1,18 @@ # Spec Index -| ID | Spec-Folder | Name | Areas | Description | GitHub Issue | Status | -|-----|----------------------------------|------------------------------|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|--------| -| 001 | 001-release-pipeline | Release pipeline | backend, build, infrastructure | Maven Central releases via JReleaser, SNAPSHOT publishing to GitHub Packages, CI build validation | — | done | -| 002 | 002-slack-messaging | Slack messaging service | backend, api | Reusable Spring service for sending plain-text messages to Slack channels via bot user | #2 | done | -| 003 | 003-email-service | Email sending service | backend, api | Reusable Spring service for sending plain-text emails via SMTP | #5 | done | -| 004 | 004-avatar-url-from-authentik | Avatar URL from authentik | backend, security, database | Replace manual avatar upload with avatar URL sync from authentik JWT claim | #7 | done | -| 005 | 005-audit-log | Audit log | backend, database | Persist data lifecycle events (create/update/delete) as audit log entries with user tracking | #8 | done | -| 006 | 006-split-security-filter-chains | Split security filter chains | backend, security, architecture | Split single SecurityFilterChain into two isolated chains: API-key for /api/external/**, JWT for everything else | #11 | done | -| 007 | 007-spring-boot-3.5-upgrade | Spring Boot 3.5 upgrade | build, testing, infrastructure | Upgrade Spring Boot 3.3.2 → 3.5.14 and pin Testcontainers 2.0.5 to fix integration tests on Docker Engine 29+ | #13 | done | -| 008 | 008-audit-log-user-fk | Audit log user FK | backend, database, security | Replace AuditLogEntity.userName String with ManyToOne to UserEntity; bootstrap a System User with reserved nil UUID | #16 | done | -| 009 | 009-comment-author-association | Comment author association | backend, database, api | Replace CommentEntity.authorId String with @ManyToOne UserEntity association — adds FK, removes N+1, renames column to author_id (breaking) | #14 | done | -| 010 | 010-api-token-module | API Token Module | backend, security, database | Unified opaque-token module for SERVICE + USER tokens via Spring Security OpaqueTokenIntrospector; live role/group resolution via new PrincipalDirectory port; replaces existing ApiKey infrastructure (breaking change) | — | open | -| 011 | 011-security-config-hygiene | Security config hygiene | backend, security, architecture | Tighten Spring Boot auto-configuration annotations, drop synchronized from UserService hot path, centralise JSON AuthenticationEntryPoint, use SecurityContextHolderStrategy, replace UserInformation sentinel with Optional, lock Roles utility class | — | done | -| 012 | 012-scim-foundation-user-model | SCIM foundation user model | backend, security, database | Relax UserEntity.sub, add externalId / userName / active, JIT-login correlation and backfill, AccessDeniedException gate for deactivated users — prepares the data model for the SCIM-Provider spec without introducing SCIM endpoints | — | open | +| ID | Spec-Folder | Name | Areas | Description | GitHub Issue | Status | +|-----|----------------------------------|------------------------------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|--------| +| 001 | 001-release-pipeline | Release pipeline | backend, build, infrastructure | Maven Central releases via JReleaser, SNAPSHOT publishing to GitHub Packages, CI build validation | — | done | +| 002 | 002-slack-messaging | Slack messaging service | backend, api | Reusable Spring service for sending plain-text messages to Slack channels via bot user | #2 | done | +| 003 | 003-email-service | Email sending service | backend, api | Reusable Spring service for sending plain-text emails via SMTP | #5 | done | +| 004 | 004-avatar-url-from-authentik | Avatar URL from authentik | backend, security, database | Replace manual avatar upload with avatar URL sync from authentik JWT claim | #7 | done | +| 005 | 005-audit-log | Audit log | backend, database | Persist data lifecycle events (create/update/delete) as audit log entries with user tracking | #8 | done | +| 006 | 006-split-security-filter-chains | Split security filter chains | backend, security, architecture | Split single SecurityFilterChain into two isolated chains: API-key for /api/external/**, JWT for everything else | #11 | done | +| 007 | 007-spring-boot-3.5-upgrade | Spring Boot 3.5 upgrade | build, testing, infrastructure | Upgrade Spring Boot 3.3.2 → 3.5.14 and pin Testcontainers 2.0.5 to fix integration tests on Docker Engine 29+ | #13 | done | +| 008 | 008-audit-log-user-fk | Audit log user FK | backend, database, security | Replace AuditLogEntity.userName String with ManyToOne to UserEntity; bootstrap a System User with reserved nil UUID | #16 | done | +| 009 | 009-comment-author-association | Comment author association | backend, database, api | Replace CommentEntity.authorId String with @ManyToOne UserEntity association — adds FK, removes N+1, renames column to author_id (breaking) | #14 | done | +| 010 | 010-api-token-module | API Token Module | backend, security, database | Unified opaque-token module for SERVICE + USER tokens via Spring Security OpaqueTokenIntrospector; live role/group resolution via new PrincipalDirectory port; replaces existing ApiKey infrastructure (breaking change) | — | open | +| 011 | 011-security-config-hygiene | Security config hygiene | backend, security, architecture | Tighten Spring Boot auto-configuration annotations, drop synchronized from UserService hot path, centralise JSON AuthenticationEntryPoint, use SecurityContextHolderStrategy, replace UserInformation sentinel with Optional, lock Roles utility class | — | done | +| 012 | 012-scim-foundation-user-model | SCIM foundation user model | backend, security, database | Relax UserEntity.sub, add externalId / userName / active, JIT-login correlation and backfill, AccessDeniedException gate for deactivated users — prepares the data model for the SCIM-Provider spec without introducing SCIM endpoints | — | done | | 013 | 013-dedicated-schema-and-starter | Dedicated schema + starter | backend, database, architecture, build | Move all 7 library tables into a fixed `oe_spring_services` schema (single persistence unit) and ship a real Spring Boot starter that registers the library package additively via AutoConfigurationPackages — zero-config consumption. Migrations stay consumer-managed; the library ships ready-to-apply per-release SQL in the upgrade doc. Includes the app→lib cross-entity reference contract | — | open | -| 014 | 014-library-owned-flyway | Library-owned Flyway | backend, database, architecture | WITHDRAWN — a library-run Flyway instance proved too complex (no-Flyway-bean hack, enforced ordering, fail-fast coupling, conditional V1, deprecation two-deploy dance). Replaced by consumer-managed migrations + per-release SQL shipped in the upgrade doc (folded into 013 + the release-doc process) | — | withdrawn |