Security Test¶
Create test principals and deterministic policy decisions for security tests.
Add this module with test scope and construct principals or configure the fake policy port to exercise permit, deny, and indeterminate results. Test fixtures are not production authorization adapters.
| Maven artifact | Packaging | Source project |
|---|---|---|
org.fireflyframework:fireflyframework-security-test |
jar |
modules/security-test |
Part of Security and identity.
In the onboarding service¶
Create applicant/reviewer test principals and deterministic policy outcomes for onboarding authorization tests. These fixtures do not replace a production policy source.
Add the component¶
Installation and version availability explains source builds and published releases.
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-security-test</artifactId>
<version>26.10.20-SNAPSHOT</version>
<scope>test</scope>
</dependency>
Quick start¶
The Java examples in this section are API fragments for application code. Use the stated imports and supply the application-owned collaborators described alongside them.
Add the module as a test-scoped dependency, then build identities and inject policy decisions directly:
import org.fireflyframework.security.api.domain.Decision;
import org.fireflyframework.security.api.domain.SecurityPrincipal;
import org.fireflyframework.security.test.FakePolicyDecisionPort;
import org.fireflyframework.security.test.TestPrincipals;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.Set;
// A principal with authorities and scopes
SecurityPrincipal alice = TestPrincipals.user("alice", Set.of("admin"), Set.of("reports.read"));
// Install an authenticated token into the reactive security context
var auth = TestPrincipals.authentication("alice", "admin");
Mono<String> underTest = handler.handle(request)
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
// Drive a PEP with a configurable PDP and assert what it asked
FakePolicyDecisionPort pdp = FakePolicyDecisionPort.denyAll("reports.read scope required");
StepVerifier.create(pep.check(alice, "read", "/api/reports", Map.of()))
.expectNextMatches(Decision::granted) // ... or assert the deny path
.verifyComplete();
assertThat(pdp.lastAction()).isEqualTo("read");
assertThat(pdp.lastResource()).isEqualTo("/api/reports");
FakePolicyDecisionPort is mutable and volatile-backed, so the same instance can be reconfigured between assertions with returning(...).
Configuration and wiring¶
This module declares no @ConfigurationProperties class of its own. Use the constructor/port contracts above and the configuration of the host or related core module.
Public API and extension points¶
| Type | Contract and responsibility |
|---|---|
FakePolicyDecisionPort |
A configurable PolicyDecisionPort test double that returns a predetermined decision and records the last request it received, so tests can assert PEP wiring without a real policy engine. |
TestPrincipals |
Factory helpers for building SecurityPrincipals and FireflyAuthenticationTokens in tests, so application test code does not hand-roll principals or wrestle with the security context. |
Related components¶
| Related component | Why it belongs nearby |
|---|---|
| Security API | Define product-neutral principals, authorization decisions, driving use cases, and the Secure annotation. |
| Security SPI | Define driven ports for policy, relationships, revocation, keys, secrets, and other external security services. |
| Security WebFlux Binding | Bind the framework principal and authorization policy to reactive Spring Security/WebFlux. |
Build and test¶
Use Java 25. From the repository root, build this component with its required dependencies:
git clone https://github.com/fireflyframework/fireflyframework-jafly.git
cd fireflyframework-jafly
./mvnw -pl :fireflyframework-security-test -am verify
./mvnw -pl :fireflyframework-security-test -am install
verify runs the selected projects' tests. install makes the current build available in your local Maven repository.
Representative test sources:
Test fixture¶
The fakePolicyDecisionPortReturnsConfiguredDecisionAndRecordsRequest test demonstrates one concrete behavior. Excerpt from SecurityTestFixturesTest.java. The test class supplies its fixtures and imports.
@Test
void fakePolicyDecisionPortReturnsConfiguredDecisionAndRecordsRequest() {
FakePolicyDecisionPort permit = FakePolicyDecisionPort.permitAll();
StepVerifier.create(permit.authorize(TestPrincipals.user("u1"), "read", "doc:1", Map.of()))
.expectNextMatches(d -> d.granted())
.verifyComplete();
assertThat(permit.lastAction()).isEqualTo("read");
assertThat(permit.lastResource()).isEqualTo("doc:1");
FakePolicyDecisionPort deny = FakePolicyDecisionPort.denyAll("nope");
StepVerifier.create(deny.authorize(TestPrincipals.user("u1"), "write", "doc:1", Map.of()))
.expectNextMatches(d -> !d.granted())
.verifyComplete();
}
Detailed reference¶
Where it sits in the platform¶
The security platform is layered hexagonally; dependencies point inward, and test fixtures attach as an outboard, test-time adapter:
security-api → security-spi → security-core → security-webflux → security-test
(ports + (driven (neutral (reactive (this module:
domain) ports) engine) Spring Security test doubles for
bindings) domain + ports)
security-apidefines the domain this module builds and returns:SecurityPrincipal,Decision.security-spidefines the driven port this module fakes:PolicyDecisionPort.security-webfluxsupplies the reactive Spring Security token,FireflyAuthenticationToken, whichTestPrincipalsconstructs for installation into the reactive security context.- This module depends on
security-api,security-spi, andsecurity-webflux(plusreactor-core) and contributes no runtime beans. it is meant to be atest-scoped dependency of other modules and of applications.
It imports no vendor SDK and starts no Spring context.
Key types¶
| Type | Role |
|---|---|
TestPrincipals |
Final factory of static helpers building SecurityPrincipals and authenticated FireflyAuthenticationTokens for tests. |
FakePolicyDecisionPort |
Configurable PolicyDecisionPort test double: returns a predetermined Decision and records the last action/resource it was asked to authorize. |
TestPrincipals (all static, the class is non-instantiable):
user(String subject, String... authorities). aSecurityPrincipalwith the given subject and authorities.user(String subject, Set<String> authorities, Set<String> scopes). a principal carrying both authorities and OAuth2 scopes.authentication(String subject, String... authorities). an authenticatedFireflyAuthenticationTokenready forReactiveSecurityContextHolder.
FakePolicyDecisionPort:
permitAll()/denyAll(String reason). factory shortcuts wrappingDecision.permit()/Decision.deny(reason).new FakePolicyDecisionPort(Decision next). start from an explicit decision.returning(Decision decision). fluently swap the decision the double will replay next.lastAction()/lastResource(). read back what the lastauthorize(...)call was asked about.authorize(SecurityPrincipal, String action, String resource, Map<String,Object> context). thePolicyDecisionPortcontract; recordsaction/resourceand returnsMono.just(next).
Domain types consumed (from security-api): SecurityPrincipal, Decision. Driven port faked (from security-spi): PolicyDecisionPort. Reactive token built (from security-webflux): FireflyAuthenticationToken.
Dependencies¶
| Scope | Artifact | Why |
|---|---|---|
| compile | fireflyframework-security-api |
SecurityPrincipal, Decision |
| compile | fireflyframework-security-spi |
PolicyDecisionPort |
| compile | fireflyframework-security-webflux |
FireflyAuthenticationToken |
| compile | io.projectreactor:reactor-core |
Mono return type of authorize(...) |
| test | org.junit.jupiter:junit-jupiter, org.assertj:assertj-core, io.projectreactor:reactor-test |
the module's own self-test |
Versions are managed by the Firefly parent/BOM. Depend on it from a consuming module like:
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-security-test</artifactId>
<scope>test</scope>
</dependency>
If you are not inheriting the Firefly parent, pin the version explicitly:
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-security-test</artifactId>
<version>26.10.20-SNAPSHOT</version>
<scope>test</scope>
</dependency>
Testing¶
The module ships its own unit test, SecurityTestFixturesTest, which exercises both fixtures as plain JUnit 5 tests (no Spring context, StepVerifier for the reactive call):
TestPrincipals.user("u1", "admin").hasAuthority("admin")is true;authentication("u1", "admin").securityPrincipal().subject()equals"u1"and the token reportsisAuthenticated().FakePolicyDecisionPort.permitAll().authorize(...)emits agranted()Decisionand recordslastAction()/lastResource()(read/doc:1);denyAll("nope").authorize(...)emits a non-grantedDecision.
These fixtures themselves back the negative-path integration tests elsewhere in the platform. For example ResourceServerIntegrationTest in fireflyframework-security-resource-server, whose contributed deny rules prove the default-deny / policy-enforcement path independently of authentication.
Continue the application¶
Use the Security Method Policy guide for the next part of this integration. The runnable submission/query service is in Getting started.