00.51.00
Release date: 2026-05-08 Previous release: 00.50.00 (pre-public) Next release: 00.60.00 Maven coordinates (parent):
com.svenruppert:security-for-flow-parent:00.51.00
This release moves the project from a single Vaadin demo of an SPI-based security framework to a small multi-module library with two reference demos — one in-JVM, one REST-authoritative — plus a complete first-run bootstrap mechanism, a central logout flow, and substantially harder test coverage on the three library modules.
There is no breaking API change for consumers that already used
AuthenticationService, AuthorizationService, AccessEvaluator,
LoginListener and LoginView from 00.50.00. New SPI types are
opt-in.
Highlights
- 5 → 7 Maven modules with strict library / adapter / demo separation
- New REST adapter (
security-rest) for protecting REST handlers with the same annotation model as Vaadin routes - Complete first-run bootstrap subsystem (3 modes, atomic POSIX-0600
token files, configurable TTL, fail-fast on
DISABLED + no admin) - Adapter-neutral decision model (
AuthorizationDecision,AccessContext,SecuritySubject) usable from both Vaadin and REST - Generic annotations
@RequiresRole,@RequiresPermission,@ProtectedByshipped with built-in evaluators - Central
LogoutServiceSPI with Vaadin adapter that handlesVaadinSession,HttpSession, and browser-side redirect in the correct order - Reusable building blocks extracted from demos:
PermissionGuard,StaticRolePermissionMapping,RolePermissionResolver,SecuredOperationRegistry,OperationVisibilityService,BootstrapStatus,BootstrapConfigurationLoader - Third demo
demo-vaadin-rest-clientshowing the two-tier picture - Mutation coverage lifted across all three library modules
(
security-core68 % → 86 %,security-rest85 % → 97 %,security-vaadin13 % → 79 %)
Module structure
| Module | Artifact | Purpose |
|---|---|---|
security-core | security-core | Generic, framework-neutral security concepts and decision logic |
security-vaadin | security-vaadin | Vaadin Flow adapter (navigation, listener, session, logout) |
security-rest | security-rest | Framework-light REST adapter (no Spring, no Jakarta Security) |
demo-rest-shared | demo-rest-shared | Tiny module — DemoEndpoints + DemoJson for the REST demos |
demo-vaadin | demo-vaadin | Single-JVM Vaadin reference (WAR) |
demo-rest | demo-rest | REST reference (JAR) — JDK HttpServer, no Spring |
demo-vaadin-rest-client | demo-vaadin-rest-client | Two-tier reference: Vaadin UI consumes a separate REST backend |
Strict dependency rules:
security-core -> (no project deps)
security-vaadin -> security-core
security-rest -> security-core
demo-vaadin -> security-core, security-vaadin
demo-rest -> security-core, security-rest, demo-rest-shared
demo-vaadin-rest-client -> security-core, security-vaadin, demo-rest-sharedFirst-run bootstrap
A fresh installation now does not ship with a hard-coded admin.
| Mode | Where the token lives | Survives restart? |
|---|---|---|
DISABLED | n/a | n/a (fail-fast on startup if no admin exists) |
TRANSIENT_CONSOLE | In-memory only, printed to server console | No |
PERSISTENT_FILE | File on disk (POSIX 0600, atomic creation) | Yes |
Configuration via system property → environment variable → default:
| System property | Environment variable | Default |
|---|---|---|
security.bootstrap.mode | SECURITY_BOOTSTRAP_MODE | TRANSIENT_CONSOLE |
security.bootstrap.token.file | SECURITY_BOOTSTRAP_TOKEN_FILE | ./data/bootstrap.token |
security.bootstrap.token.ttl | SECURITY_BOOTSTRAP_TOKEN_TTL | PT24H (ISO-8601) |
Three setup paths:
- REST —
GET /api/bootstrap/status,POST /api/bootstrap/admin - CLI —
init-admincommand, password viaConsole.readPassword() - Vaadin
/setuproute — both single-JVM and REST-authoritative variants are demonstrated
Tokens are XXXX-XXXX-XXXX-XXXX-XXXX, ~100 bits, ambiguity-free
alphabet via SecureRandom; BootstrapToken.matches(...) is
constant-time; file-mode tokens are created atomically (POSIX
rw-------, no umask window); token values never reach the logger,
HTTP body, or Vaadin notification. Bootstrap admin creation is
serialised under a single ReentrantLock — verified by a 16-thread
parallelism test. See Bootstrap.
Central LogoutService
LogoutService SPI in security-core, Vaadin adapter in
security-vaadin. LogoutPolicy lets the application choose:
| Policy factory | Subject cleared | Vaadin session closed | HTTP session invalidated |
|---|---|---|---|
LogoutPolicy.clearSubjectOnly(target) | ✅ | ❌ | ❌ |
LogoutPolicy.invalidateHttpSession(target) | ✅ | ❌ | ✅ |
LogoutPolicy.fullInvalidate(target) | ✅ | ✅ | ✅ |
The Vaadin adapter calls Page.setLocation(...) for the redirect
before invalidating the session, so the response carries the
redirect to the browser.
The 00.51
LogoutContext+LogoutPolicyshape was replaced in 00.60 with the sharperlogout(SubjectId, LogoutScope)signature. See the 00.60 migration guide.
New demo: demo-vaadin-rest-client
A second Vaadin demo that consumes demo-rest over HTTP instead of
running everything in one JVM.
- WAR running on Jetty port 9090 (parallel to
demo-reston 8080) - All HTTP / JSON / endpoint paths confined to one
backend/package — verified with a single grep BackendExceptionwith semanticKind(Unauthenticated / Forbidden / NotFound / BadRequest / Conflict / ServerError / Transport)- Sealed
LoginResultandBootstrapResultso viewsswitchinstead oftry/catch - REST-authoritative
/setupflow — the Vaadin process never runs a localInitialAdminBootstrapService; it posts to the backend - Three view-protection styles demonstrated side by side:
@RequiresPermission("document:read")(Style A1)@RequiresRole("ROLE_ADMIN")(Style A2)@VisibleForRoles({ADMIN, EDITOR})— project-specific custom annotation backed byProjectRoleAccessEvaluator(Style B)
- 13-case integration test against an in-process
DemoRestServer - New shared module
demo-rest-sharedcarriesDemoEndpoints+DemoJsonfor both the demo server and any client
Bug fixes
VaadinAccessContextFactorynow populatesAccessContext.subject(). In 00.50.00 the compatibility constructor for the Vaadin path always producedsubject = Optional.empty(), so generic@RequiresRole/@RequiresPermissionevaluators always saw “no subject” and returnedUnauthenticated— causing a login loop after successful authentication.AccessContext.vaadinAttributesno longer rejects the empty path. Navigation to the Vaadin root""previously crashed because of an over-zealousrequireNotBlankonpath. Replaced withrequireNonNull+ regression test (AccessContextTest).InitialAdminBootstrapServicecleanup-failure is now visible. When deleting the persistent token file fails after a successful setup, the service emits aWARNINGviajava.util.logging(without the token value).
Mutation coverage
| Module | Killed mutants | Line coverage (mutated classes) | Test strength |
|---|---|---|---|
security-core | 200 → 254 of 294 (68 % → 86 %) | 81 % → 85 % | 83 % → 95 % |
security-rest | 33 → 38 of 39 (85 % → 97 %) | 88 % → 93 % | 92 % → 97 % |
security-vaadin | 16 → 94 of 119 (13 % → 79 %) | 13 % → 73 % | 89 % → 95 % |
Tests run against the original implementation, not against mocks — that’s the project-wide testing policy from this release forward.
mvn -pl :security-core org.pitest:pitest-maven:mutationCoverage -Dpitest-test-classes='com.svenruppert.*'
mvn -pl :security-rest org.pitest:pitest-maven:mutationCoverage -Dpitest-test-classes='com.svenruppert.*'
mvn -pl :security-vaadin org.pitest:pitest-maven:mutationCoverage -Dpitest-test-classes='com.svenruppert.*'The
pitest-test-classesoverride is required — the parent-pom defaultjunit.com.svenruppert.*does not match this project’s test package layout.
Known limitations (closed in 00.60.00)
The following items from Konzept-V00.60.00.md were not yet
implemented in 00.51.00 but all shipped in 00.60.00:
SecurityAuditService(Login / Logout / AccessDenied / ActionDenied events)LoginAttemptPolicy(brute-force throttling)- minimal
SessionPolicy(idle timeout, absolute lifetime, rotation after login) PasswordHasher.needsRehash(...)+ re-hash on loginActionAuthorizationServiceas injectable SPI- Karibu / TestBench-based UI tests (00.60 ships Vaadin Browserless tests instead)
SecurityServiceResolverextensions for the new SPIs
Build
- Java 26
- Maven 3.9.9+
- Vaadin 25.1.1
- Jetty 12.1.8 (EE11)
mvn clean install