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

com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultFakeImpl Maven / Gradle / Ivy

There is a newer version: 2.5.81
Show newest version
package com.github.nagyesta.lowkeyvault.service.key.impl;

import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.AesJsonWebKeyImportRequestConverter;
import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.EcJsonWebKeyImportRequestConverter;
import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.RsaJsonWebKeyImportRequestConverter;
import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest;
import com.github.nagyesta.lowkeyvault.service.common.impl.BaseVaultFakeImpl;
import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId;
import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId;
import com.github.nagyesta.lowkeyvault.service.key.util.PeriodUtil;
import com.github.nagyesta.lowkeyvault.service.vault.VaultFake;
import lombok.NonNull;
import org.springframework.util.Assert;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class KeyVaultFakeImpl
        extends BaseVaultFakeImpl>
        implements KeyVaultFake {

    private final RsaJsonWebKeyImportRequestConverter rsaConverter = new RsaJsonWebKeyImportRequestConverter();
    private final EcJsonWebKeyImportRequestConverter ecConverter = new EcJsonWebKeyImportRequestConverter();
    private final AesJsonWebKeyImportRequestConverter aesConverter = new AesJsonWebKeyImportRequestConverter();
    private final ConcurrentMap rotationPolicies = new ConcurrentHashMap<>();

    public KeyVaultFakeImpl(@org.springframework.lang.NonNull final VaultFake vaultFake,
                            @org.springframework.lang.NonNull final RecoveryLevel recoveryLevel,
                            final Integer recoverableDays) {
        super(vaultFake, recoveryLevel, recoverableDays);
    }

    @Override
    protected VersionedKeyEntityId createVersionedId(final String id, final String version) {
        return new VersionedKeyEntityId(vaultFake().baseUri(), id, version);
    }

    @Override
    public VersionedKeyEntityId createKeyVersion(@NonNull final String keyName, @NonNull final KeyCreateDetailedInput input) {
        Assert.isTrue(!input.isManaged() || (input.getExpiresOn() != null && input.getNotBefore() != null),
                "Managed key (name=" + keyName + ") must have notBefore and expiresOn parameters set!");
        final VersionedKeyEntityId keyEntityId = input.getKey().getKeyType().createKey(this, keyName, input.getKey());
        setKeyOperations(keyEntityId, input.getKeyOperations());
        //avoid overwriting expiry if it was generated by the rotation policy
        if (getEntities().getReadOnlyEntity(keyEntityId).getExpiry().isEmpty()) {
            setExpiry(keyEntityId, input.getNotBefore(), input.getExpiresOn());
        }
        setEnabled(keyEntityId, Objects.requireNonNullElse(input.getEnabled(), true));
        setManaged(keyEntityId, input.isManaged());
        addTags(keyEntityId, input.getTags());
        return keyEntityId;
    }

    @Override
    public VersionedKeyEntityId importKeyVersion(final String keyName, final KeyImportInput input) {
        final VersionedKeyEntityId keyEntityId = new VersionedKeyEntityId(vaultFake().baseUri(), keyName);
        return importKeyVersion(keyEntityId, input);
    }

    @Override
    public VersionedKeyEntityId importKeyVersion(final VersionedKeyEntityId keyEntityId, final KeyImportInput input) {
        Assert.isTrue(input.getHsm() == null || input.getHsm() == input.getKey().getKeyType().isHsm(),
                "When HSM property is set in request, key type must match it.");
        final KeyType keyType = Objects.requireNonNull(input.getKey()).getKeyType();
        keyType.importKey(this, keyEntityId, input.getKey());
        setKeyOperations(keyEntityId, input.getKey().getKeyOps());
        addTags(keyEntityId, input.getTags());
        setExpiry(keyEntityId, input.getNotBefore(), input.getExpiresOn());
        setEnabled(keyEntityId, Objects.requireNonNullElse(input.getEnabled(), true));
        setManaged(keyEntityId, input.isManaged());
        setCreatedAndUpdatedOn(keyEntityId, input.getCreatedOn(), input.getUpdatedOn());
        return keyEntityId;
    }

    @Override
    public VersionedKeyEntityId importRsaKeyVersion(
            final VersionedKeyEntityId keyEntityId, final JsonWebKeyImportRequest key) {
        final KeyType keyType = Objects.requireNonNull(key).getKeyType();
        Assert.isTrue(keyType.isRsa(), "RSA key expected, but found: " + keyType.name());
        final RsaKeyVaultKeyEntity keyEntity = new RsaKeyVaultKeyEntity(keyEntityId, vaultFake(), rsaConverter.convert(key),
                rsaConverter.getKeyParameter(key), keyType.isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public VersionedKeyEntityId importEcKeyVersion(
            final VersionedKeyEntityId keyEntityId, final JsonWebKeyImportRequest key) {
        final KeyType keyType = Objects.requireNonNull(key).getKeyType();
        Assert.isTrue(keyType.isEc(), "EC key expected, but found: " + keyType.name());
        final EcKeyVaultKeyEntity keyEntity = new EcKeyVaultKeyEntity(keyEntityId, vaultFake(), ecConverter.convert(key),
                ecConverter.getKeyParameter(key), keyType.isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public VersionedKeyEntityId importOctKeyVersion(
            final VersionedKeyEntityId keyEntityId, final JsonWebKeyImportRequest key) {
        final KeyType keyType = Objects.requireNonNull(key).getKeyType();
        Assert.isTrue(keyType.isOct(), "OCT key expected, but found: " + keyType.name());
        Assert.isTrue(keyType.isHsm(), "OCT keys are only supported using HSM.");
        final AesKeyVaultKeyEntity keyEntity = new AesKeyVaultKeyEntity(keyEntityId, vaultFake(), aesConverter.convert(key),
                aesConverter.getKeyParameter(key), keyType.isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public VersionedKeyEntityId createRsaKeyVersion(
            @NonNull final String keyName, @NonNull final RsaKeyCreationInput input) {
        final VersionedKeyEntityId keyEntityId = new VersionedKeyEntityId(vaultFake().baseUri(), keyName);
        final RsaKeyVaultKeyEntity keyEntity = new RsaKeyVaultKeyEntity(keyEntityId, vaultFake(),
                input.getKeyParameter(), input.getPublicExponent(), input.getKeyType().isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public VersionedKeyEntityId createEcKeyVersion(
            @NonNull final String keyName, @NonNull final EcKeyCreationInput input) {
        final VersionedKeyEntityId keyEntityId = new VersionedKeyEntityId(vaultFake().baseUri(), keyName);
        input.getKeyType().validate(input.getKeyParameter(), KeyCurveName.class);
        final EcKeyVaultKeyEntity keyEntity = new EcKeyVaultKeyEntity(keyEntityId, vaultFake(),
                input.getKeyParameter(), input.getKeyType().isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public VersionedKeyEntityId createOctKeyVersion(
            @NonNull final String keyName, @NonNull final OctKeyCreationInput input) {
        final VersionedKeyEntityId keyEntityId = new VersionedKeyEntityId(vaultFake().baseUri(), keyName);
        Assert.isTrue(input.getKeyType().isHsm(), "OCT keys are only supported using HSM.");
        final AesKeyVaultKeyEntity keyEntity = new AesKeyVaultKeyEntity(keyEntityId, vaultFake(),
                input.getKeyParameter(), input.getKeyType().isHsm());
        setExpiryBasedOnRotationPolicy(keyEntityId, keyEntity);
        return addVersion(keyEntityId, keyEntity);
    }

    @Override
    public void setKeyOperations(@NonNull final VersionedKeyEntityId keyEntityId,
                                 final List keyOperations) {
        getEntitiesInternal().getEntity(keyEntityId).setOperations(Objects.requireNonNullElse(keyOperations, Collections.emptyList()));
    }

    @Override
    public void timeShift(final int offsetSeconds) {
        super.timeShift(offsetSeconds);
        rotationPolicies.values().forEach(p -> p.timeShift(offsetSeconds));
        performPastRotations();
    }

    private void performPastRotations() {
        rotationPolicies.values().stream()
                .filter(ReadOnlyRotationPolicy::isAutoRotate)
                .forEach(this::performMissedRotationsOfPolicy);
    }

    @Override
    public RotationPolicy rotationPolicy(@NonNull final KeyEntityId keyEntityId) {
        purgeDeletedPolicies();
        return rotationPolicies.get(keyEntityId.id());
    }

    @Override
    public void setRotationPolicy(@NonNull final RotationPolicy rotationPolicy) {
        final ReadOnlyKeyVaultKeyEntity readOnlyEntity = latestReadOnlyKeyVersion(rotationPolicy.getId());
        Assert.state(!readOnlyEntity.isManaged(), "Cannot set rotation policy to managed entity: " + rotationPolicy.getId());
        rotationPolicy.validate(readOnlyEntity.getExpiry().orElse(null));
        final RotationPolicy existingPolicy = rotationPolicy(rotationPolicy.getId());
        if (existingPolicy == null) {
            rotationPolicies.put(rotationPolicy.getId().id(), rotationPolicy);
        } else {
            existingPolicy.setLifetimeActions(rotationPolicy.getLifetimeActions());
            existingPolicy.setExpiryTime(rotationPolicy.getExpiryTime());
        }
    }

    @Override
    public VersionedKeyEntityId rotateKey(@NonNull final KeyEntityId keyEntityId) {
        final ReadOnlyKeyVaultKeyEntity readOnlyEntity = latestReadOnlyKeyVersion(keyEntityId);
        return createKeyVersion(keyEntityId.id(), KeyCreateDetailedInput.builder()
                .key(readOnlyEntity.keyCreationInput())
                .keyOperations(readOnlyEntity.getOperations())
                .enabled(true)
                //never rotate managed entities
                .managed(false)
                .tags(readOnlyEntity.getTags())
                //expiry will be automatically set based on rotation policy when created
                .build());
    }

    private void setExpiryBasedOnRotationPolicy(final VersionedKeyEntityId keyEntityId, final KeyVaultKeyEntity keyEntity) {
        final Optional expiryDays = Optional.ofNullable(rotationPolicies)
                .map(policies -> policies.get(keyEntityId.id()))
                .map(ReadOnlyRotationPolicy::getExpiryTime)
                .map(PeriodUtil::asDays);
        expiryDays.ifPresent(days -> keyEntity.setExpiry(keyEntity.getCreated().plusDays(days)));
    }

    private ReadOnlyKeyVaultKeyEntity latestReadOnlyKeyVersion(final KeyEntityId keyEntityId) {
        final VersionedKeyEntityId latestVersionOfEntity = getEntities().getLatestVersionOfEntity(keyEntityId);
        return getEntities().getReadOnlyEntity(latestVersionOfEntity);
    }

    private void performMissedRotationsOfPolicy(final RotationPolicy rotationPolicy) {
        final KeyEntityId keyEntityId = rotationPolicy.getId();
        final VersionedKeyEntityId latestVersionOfEntity = getEntities().getLatestVersionOfEntity(keyEntityId);
        final ReadOnlyKeyVaultKeyEntity readOnlyEntity = getEntities().getReadOnlyEntity(latestVersionOfEntity);
        rotationPolicy.missedRotations(readOnlyEntity.getCreated())
                .forEach(rotationTime -> simulatePointInTimeRotation(keyEntityId, rotationTime));
    }

    private void simulatePointInTimeRotation(final KeyEntityId keyEntityId, final OffsetDateTime rotationTime) {
        final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
        final int diffSeconds = (int) (now.toEpochSecond() - rotationTime.toEpochSecond());
        final VersionedKeyEntityId versionedKeyEntityId = rotateKey(keyEntityId);
        getEntities().getEntity(versionedKeyEntityId, KeyVaultKeyEntity.class).timeShift(diffSeconds);
    }

    private void purgeDeletedPolicies() {
        keepNamesReadyForRemoval(rotationPolicies.keySet())
                .forEach(rotationPolicies::remove);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy