All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.fitko.fitconnect.core.cases.SecurityEventTokenService Maven / Gradle / Ivy

Go to download

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());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy