dev.fitko.fitconnect.core.cases.SecurityEventTokenService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of client Show documentation
Show all versions of client Show documentation
Library that provides client access to the FIT-Connect api-endpoints for sending, subscribing and
routing
The newest version!
package dev.fitko.fitconnect.core.cases;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.event.Event;
import dev.fitko.fitconnect.api.domain.model.event.EventClaimFields;
import dev.fitko.fitconnect.api.domain.model.event.EventHeaderFields;
import dev.fitko.fitconnect.api.domain.model.event.EventPayload;
import dev.fitko.fitconnect.api.domain.model.event.authtags.AuthenticationTags;
import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
import dev.fitko.fitconnect.api.exceptions.internal.EventCreationException;
import dev.fitko.fitconnect.api.exceptions.internal.ValidationException;
import dev.fitko.fitconnect.api.services.events.SecurityEventService;
import dev.fitko.fitconnect.api.services.validation.ValidationService;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static dev.fitko.fitconnect.api.domain.model.event.Event.ACCEPT_SUBMISSION;
import static dev.fitko.fitconnect.api.domain.model.event.Event.REJECT_SUBMISSION;
public class SecurityEventTokenService implements SecurityEventService {
private final RSAKey signingKey;
private final ValidationService validationService;
private final ApplicationConfig config;
public SecurityEventTokenService(final ApplicationConfig config, final ValidationService validationService, final RSAKey signingKey) {
this.config = config;
this.signingKey = signingKey;
this.validationService = validationService;
}
@Override
public SignedJWT createAcceptSubmissionEvent(final EventPayload eventPayload) {
return signJWT(ACCEPT_SUBMISSION, eventPayload);
}
@Override
public SignedJWT createRejectSubmissionEvent(final EventPayload eventPayload) {
return signJWT(REJECT_SUBMISSION, eventPayload);
}
private SignedJWT signJWT(final Event event, final EventPayload eventPayload) {
final String transactionId = "case:" + eventPayload.getCaseId();
final String subject = "submission:" + eventPayload.getSubmissionId();
final JWSSigner signer = getJwsSigner(signingKey);
final JWSHeader header = buildJwsHeader(signingKey.getKeyID());
final JWTClaimsSet claimsSet = buildJwtClaimsSet(event, eventPayload, transactionId, subject);
final SignedJWT signedJWT = new SignedJWT(header, claimsSet);
try {
signedJWT.sign(signer);
validateEventSchema(signedJWT.getPayload().toString());
return signedJWT;
} catch (final JOSEException | ValidationException e) {
throw new EventCreationException("Creating " + event + " event JWT failed", e.getCause());
}
}
private JWSSigner getJwsSigner(final RSAKey rsaKey) {
try {
return new RSASSASigner(rsaKey);
} catch (final JOSEException e) {
throw new EventCreationException("Error creating the RSASigner", e);
}
}
private JWSHeader buildJwsHeader(final String keyId) {
try {
return JWSHeader.parse(Map.of(
EventHeaderFields.TYPE, "secevent+jwt",
EventHeaderFields.KEY_ID, keyId,
EventHeaderFields.ALGORITHM, "PS512"
));
} catch (final ParseException e) {
throw new EventCreationException("Parsing JWS header failed", e);
}
}
private JWTClaimsSet buildJwtClaimsSet(final Event event, final EventPayload eventPayload, final String transactionId, final String subject) {
return new JWTClaimsSet.Builder()
.claim(EventClaimFields.CLAIM_SCHEMA, config.getSetSchemaWriteVersion())
.issuer(eventPayload.getDestinationId().toString())
.issueTime(new Date())
.jwtID(UUID.randomUUID().toString())
.subject(subject)
.claim(EventClaimFields.CLAIM_TXN, transactionId)
.claim(EventClaimFields.CLAIM_EVENTS, buildEventsClaim(event, eventPayload))
.build();
}
private Map buildEventsClaim(final Event event, final EventPayload eventPayload) {
final Map events = new HashMap<>();
if (eventPayload.getProblems() != null && !eventPayload.getProblems().isEmpty()) {
events.put("problems", eventPayload.getProblems());
}
if (event.equals(ACCEPT_SUBMISSION)) {
final String dataAuthTag = eventPayload.getDataAuthTag();
final String metadataAuthTag = eventPayload.getMetadataAuthTag();
final Map attachmentAuthTags = eventPayload.getAttachmentAuthTags();
events.put("authenticationTags", new AuthenticationTags(dataAuthTag, metadataAuthTag, attachmentAuthTags));
}
return Map.of(event.getSchemaUri(), events);
}
private void validateEventSchema(final String eventPayload) {
final ValidationResult validationResult = validationService.validateSetEventSchema(eventPayload);
if (validationResult.hasError()) {
throw new ValidationException("Set event is invalid", validationResult.getError());
}
}
}