test(04-01): add failing tests for security services

- JwtService: 7 tests for access/refresh token creation and validation
- Ed25519SigningService: 5 tests for keypair, signing, verification
- BootstrapTokenValidator: 6 tests for token matching and rotation
- Core interfaces and stub implementations (all throw UnsupportedOperationException)
- Added nimbus-jose-jwt and spring-boot-starter-security dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-11 19:58:59 +01:00
parent b7c35037e6
commit 51a02700dd
11 changed files with 482 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
package com.cameleer3.server.core.security;
/**
* Service for Ed25519 digital signatures.
* <p>
* Used to sign configuration and command payloads pushed to agents via SSE,
* allowing agents to verify the authenticity of received data.
* The keypair is ephemeral (generated at startup); agents receive the public
* key during registration.
*/
public interface Ed25519SigningService {
/**
* Signs the given payload using the server's Ed25519 private key.
*
* @param payload the string payload to sign
* @return Base64-encoded signature bytes
*/
String sign(String payload);
/**
* Returns the server's Ed25519 public key as a Base64-encoded string
* (X.509 SubjectPublicKeyInfo DER format).
*
* @return Base64-encoded public key
*/
String getPublicKeyBase64();
}

View File

@@ -0,0 +1,15 @@
package com.cameleer3.server.core.security;
/**
* Thrown when a JWT token is invalid, expired, or of the wrong type.
*/
public class InvalidTokenException extends RuntimeException {
public InvalidTokenException(String message) {
super(message);
}
public InvalidTokenException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,48 @@
package com.cameleer3.server.core.security;
/**
* Service for creating and validating JSON Web Tokens (JWT).
* <p>
* Access tokens are short-lived (default 1 hour) and used for API authentication.
* Refresh tokens are longer-lived (default 7 days) and used to obtain new access tokens.
*/
public interface JwtService {
/**
* Creates a signed access JWT with the given agent ID and group.
*
* @param agentId the agent identifier (becomes the {@code sub} claim)
* @param group the agent group (becomes the {@code group} claim)
* @return a signed JWT string
*/
String createAccessToken(String agentId, String group);
/**
* Creates a signed refresh JWT with the given agent ID and group.
*
* @param agentId the agent identifier (becomes the {@code sub} claim)
* @param group the agent group (becomes the {@code group} claim)
* @return a signed JWT string
*/
String createRefreshToken(String agentId, String group);
/**
* Validates an access token and extracts the agent ID.
* Rejects expired tokens and tokens that are not of type "access".
*
* @param token the JWT string to validate
* @return the agent ID from the {@code sub} claim
* @throws InvalidTokenException if the token is invalid, expired, or not an access token
*/
String validateAndExtractAgentId(String token);
/**
* Validates a refresh token and extracts the agent ID.
* Rejects expired tokens and tokens that are not of type "refresh".
*
* @param token the JWT string to validate
* @return the agent ID from the {@code sub} claim
* @throws InvalidTokenException if the token is invalid, expired, or not a refresh token
*/
String validateRefreshToken(String token);
}