Files
cameleer-server/cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java
hsiegeln 858975f03f
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 2m57s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped
refactor(license): extract cameleer-license-api module from server-core
Splits the pure license contract types (LicenseInfo, LicenseValidator,
LicenseState, LicenseStateMachine, LicenseLimits, DefaultTierLimits) into a
new cameleer-license-api module under package com.cameleer.license.

Why: cameleer-license-minter previously depended on cameleer-server-core for
these types, dragging cameleer-server-core + cameleer-common onto the
classpath of every minter consumer (notably cameleer-saas). The SaaS
management plane has no business carrying server-runtime types — it only
needs the license contract to mint and verify tokens.

After:
  cameleer-license-minter -> cameleer-license-api  (no server internals)
  cameleer-server-core    -> cameleer-license-api
  cameleer-saas           -> cameleer-license-minter -> cameleer-license-api

Verified: mvn -pl cameleer-license-minter dependency:tree shows the minter
no longer pulls cameleer-server-core or cameleer-common. Full reactor
verify (-DskipITs) green: 371 tests pass.

LicenseGate stays in server-core (server-runtime state holder, not contract).

Closes cameleer/cameleer-server#156

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:06:52 +02:00

140 lines
5.0 KiB
Java

package com.cameleer.server.app;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.license.LicenseInfo;
import com.cameleer.server.core.security.JwtService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Test utility for creating JWT-authenticated requests in integration tests.
* <p>
* Registers a test agent and issues a JWT access token that can be used
* to authenticate against protected endpoints.
*/
@Component
public class TestSecurityHelper {
private final JwtService jwtService;
private final AgentRegistryService agentRegistryService;
private final LicenseGate licenseGate;
@Autowired
public TestSecurityHelper(JwtService jwtService,
AgentRegistryService agentRegistryService,
LicenseGate licenseGate) {
this.jwtService = jwtService;
this.agentRegistryService = agentRegistryService;
this.licenseGate = licenseGate;
}
/**
* Loads a synthetic, signature-bypassing license into {@link LicenseGate} so the test can
* exercise paths that would otherwise be rejected by default-tier caps. The license is
* always-ACTIVE (1 day from now, no grace) and limits are merged over defaults — only
* supply the keys you want to lift. Use this from {@code @BeforeEach} in ITs that need to
* create more than the default-tier allowance of envs/apps/users/etc.
*/
public void installSyntheticUnsignedLicense(Map<String, Integer> caps) {
LicenseInfo info = new LicenseInfo(
UUID.randomUUID(),
"default",
"test-license",
Map.copyOf(caps),
Instant.now(),
Instant.now().plus(1, ChronoUnit.DAYS),
0);
licenseGate.load(info);
}
/** Clears any test license previously installed via {@link #installSyntheticUnsignedLicense}. */
public void clearTestLicense() {
licenseGate.clear();
}
/**
* Registers a test agent and returns a valid JWT access token with AGENT role.
*/
public String registerTestAgent(String instanceId) {
agentRegistryService.register(instanceId, instanceId, "test-group", "default", "1.0", List.of(), Map.of());
return jwtService.createAccessToken(instanceId, "test-group", List.of("AGENT"));
}
/**
* Returns a valid JWT access token with the given roles (no agent registration).
*/
public String createToken(String subject, String application, List<String> roles) {
return jwtService.createAccessToken(subject, application, roles);
}
/**
* Returns a valid JWT access token with OPERATOR role.
*/
public String operatorToken() {
// Subject must start with "user:" for JwtAuthenticationFilter to treat it as a UI user token
return jwtService.createAccessToken("user:test-operator", "user", List.of("OPERATOR"));
}
/**
* Returns a valid JWT access token with ADMIN role.
*/
public String adminToken() {
return jwtService.createAccessToken("user:test-admin", "user", List.of("ADMIN"));
}
/**
* Returns a valid JWT access token with VIEWER role.
*/
public String viewerToken() {
return jwtService.createAccessToken("user:test-viewer", "user", List.of("VIEWER"));
}
/**
* Returns HttpHeaders with JWT Bearer authorization, protocol version, and JSON content type.
*/
public HttpHeaders authHeaders(String jwt) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + jwt);
headers.set("X-Cameleer-Protocol-Version", "1");
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
/**
* Returns HttpHeaders with JWT Bearer authorization and protocol version (no content type).
*/
public HttpHeaders authHeadersNoBody(String jwt) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + jwt);
headers.set("X-Cameleer-Protocol-Version", "1");
return headers;
}
/**
* Returns HttpHeaders with ADMIN JWT Bearer authorization, protocol version, and JSON content type.
*/
public HttpHeaders adminHeaders() {
return authHeaders(adminToken());
}
/**
* Returns HttpHeaders with bootstrap token authorization, protocol version, and JSON content type.
*/
public HttpHeaders bootstrapHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer test-bootstrap-token");
headers.set("X-Cameleer-Protocol-Version", "1");
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
}