Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions docs/specs/012-scim-foundation-user-model/steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 →
Expand All @@ -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
Expand Down Expand Up @@ -210,7 +210,7 @@ change.
Set<String> 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`).
Expand All @@ -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,
Expand Down Expand Up @@ -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:**
Expand Down
29 changes: 14 additions & 15 deletions docs/specs/INDEX.md
Original file line number Diff line number Diff line change
@@ -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 |