diff --git a/README.md b/README.md index bac599cc..fc4951dd 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo - ```ES512``` - Backup and restore keys - Get random bytes +- Rotate keys (manually) ### Secrets diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 6edefc58..4bd81864 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -144,7 +144,9 @@ - + + + diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java index 1ac60066..948a1f15 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java @@ -5,6 +5,7 @@ import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupListItem; import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; import com.github.nagyesta.lowkeyvault.service.EntityId; import com.github.nagyesta.lowkeyvault.service.common.BaseVaultEntity; import com.github.nagyesta.lowkeyvault.service.common.BaseVaultFake; @@ -40,8 +41,8 @@ * @param The fake type holding the entities. */ public abstract class BaseBackupRestoreController, M, DM extends M, - P extends BasePropertiesModel, BLI extends BaseBackupListItem

, BL extends List, B extends BaseBackupModel, - BC extends BackupConverter, MC extends RecoveryAwareConverter, + P extends BasePropertiesModel, BLI extends BaseBackupListItem

, BL extends BackupListContainer, + B extends BaseBackupModel, BC extends BackupConverter, MC extends RecoveryAwareConverter, S extends BaseVaultFake> extends BaseEntityReadController { private final MC modelConverter; @@ -62,7 +63,7 @@ protected M restoreEntity(final B backupModel) { final String id = getSingleEntityName(backupModel); final K entityId = entityId(baseUri, id); assertNameDoesNotExistYet(vault, entityId); - backupModel.getValue().forEach(entityVersion -> { + backupModel.getValue().getVersions().forEach(entityVersion -> { final V versionedEntityId = versionedEntityId(baseUri, id, entityVersion.getVersion()); restoreVersion(vault, versionedEntityId, entityVersion); }); @@ -98,11 +99,29 @@ protected B backupEntity(final K entityId) { protected abstract BL getBackupList(); + protected String getSingleEntityName(final B backupModel) { + final List entityNames = backupModel.getValue().getVersions().stream() + .map(BLI::getId) + .distinct() + .collect(Collectors.toUnmodifiableList()); + Assert.isTrue(entityNames.size() == 1, "All backup entities must belong to the same entity."); + return entityNames.get(0); + } + + protected URI getSingleBaseUri(final B backupModel) { + final List uris = backupModel.getValue().getVersions().stream() + .map(BLI::getVaultBaseUri) + .distinct() + .collect(Collectors.toUnmodifiableList()); + Assert.isTrue(uris.size() == 1, "All backup entities must be from the same vault."); + return uris.get(0); + } + private B wrapBackup(final List list) { final BL listModel = Optional.ofNullable(list) .map(l -> { final BL backupList = getBackupList(); - backupList.addAll(l); + backupList.setVersions(l); return backupList; }) .orElse(null); @@ -118,22 +137,4 @@ private void assertNameDoesNotExistYet(final S vault, final K entityId) { "Vault already contains deleted entity with name: " + entityId.id()); } - private String getSingleEntityName(final B backupModel) { - final List entityNames = backupModel.getValue().stream() - .map(BLI::getId) - .distinct() - .collect(Collectors.toUnmodifiableList()); - Assert.isTrue(entityNames.size() == 1, "All backup entities must belong to the same entity."); - return entityNames.get(0); - } - - private URI getSingleBaseUri(final B backupModel) { - final List uris = backupModel.getValue().stream() - .map(BLI::getVaultBaseUri) - .distinct() - .collect(Collectors.toUnmodifiableList()); - Assert.isTrue(uris.size() == 1, "All backup entities must be from the same vault."); - return uris.get(0); - } - } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java index 38bf9aeb..3304a9fa 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java @@ -1,8 +1,14 @@ package com.github.nagyesta.lowkeyvault.controller.common; -import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72BackupConverter; +import com.github.nagyesta.lowkeyvault.mapper.common.BackupConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupListItem; +import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.DeletedKeyVaultKeyModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyPropertiesModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity; import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; @@ -20,19 +26,30 @@ import java.util.Collections; import java.util.Objects; +/** + * Common logic of backup and restore controllers across the different API versions. + * + * @param The type of the list item representing one entity version in the backup model. + * @param The wrapper type of the list in the backup model. + * @param The type of the backup model. + * @param The converter, converting entities to list items of the backup models. + */ @Slf4j -public abstract class CommonKeyBackupRestoreController extends BaseBackupRestoreController { +public abstract class CommonKeyBackupRestoreController, + BL extends BackupListContainer, B extends BaseBackupModel, + BC extends BackupConverter> + extends BaseBackupRestoreController { protected CommonKeyBackupRestoreController( @NonNull final KeyEntityToV72ModelConverter modelConverter, - @NonNull final KeyEntityToV72BackupConverter backupConverter, + @NonNull final BC backupConverter, @NonNull final VaultService vaultService) { super(modelConverter, backupConverter, vaultService, VaultFake::keyVaultFake); } - public ResponseEntity backup( + public ResponseEntity backup( @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, final URI baseUri) { log.info("Received request to {} backup key: {} using API version: {}", @@ -42,32 +59,22 @@ public ResponseEntity backup( public ResponseEntity restore( final URI baseUri, - @Valid final KeyBackupModel keyBackupModel) { + @Valid final B keyBackupModel) { log.info("Received request to {} restore key: {} using API version: {}", - baseUri.toString(), keyBackupModel.getValue().get(0).getId(), apiVersion()); + baseUri.toString(), keyBackupModel.getValue().getVersions().get(0).getId(), apiVersion()); return ResponseEntity.ok(restoreEntity(keyBackupModel)); } @Override protected void restoreVersion(@NonNull final KeyVaultFake vault, @NonNull final VersionedKeyEntityId versionedEntityId, - @NonNull final KeyBackupListItem entityVersion) { - vault.importKeyVersion(versionedEntityId, entityVersion.getKeyMaterial()); + @NonNull final BLI entityVersion) { + vault.importKeyVersion(versionedEntityId, getKeyMaterial(entityVersion)); final KeyVaultKeyEntity entity = vault.getEntities().getEntity(versionedEntityId, KeyVaultKeyEntity.class); - entity.setOperations(Objects.requireNonNullElse(entityVersion.getKeyMaterial().getKeyOps(), Collections.emptyList())); + entity.setOperations(Objects.requireNonNullElse(getKeyMaterial(entityVersion).getKeyOps(), Collections.emptyList())); updateCommonFields(entityVersion, entity); } - @Override - protected KeyBackupList getBackupList() { - return new KeyBackupList(); - } - - @Override - protected KeyBackupModel getBackupModel() { - return new KeyBackupModel(); - } - @Override protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String name, final String version) { return new VersionedKeyEntityId(baseUri, name, version); @@ -77,4 +84,6 @@ protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String protected KeyEntityId entityId(final URI baseUri, final String name) { return new KeyEntityId(baseUri, name); } + + protected abstract JsonWebKeyImportRequest getKeyMaterial(BLI entityVersion); } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java index ddc3298a..4a428566 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java @@ -41,7 +41,7 @@ public ResponseEntity restore( final URI baseUri, @Valid final SecretBackupModel secretBackupModel) { log.info("Received request to {} restore secret: {} using API version: {}", - baseUri.toString(), secretBackupModel.getValue().get(0).getId(), apiVersion()); + baseUri.toString(), secretBackupModel.getValue().getVersions().get(0).getId(), apiVersion()); return ResponseEntity.ok(restoreEntity(secretBackupModel)); } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreController.java index 48318451..7d3f68f3 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreController.java @@ -4,8 +4,11 @@ import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72BackupConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupList; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupListItem; import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupModel; import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; import com.github.nagyesta.lowkeyvault.service.vault.VaultService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -26,7 +29,8 @@ @RestController @Validated @Component("KeyBackupRestoreControllerV72") -public class KeyBackupRestoreController extends CommonKeyBackupRestoreController { +public class KeyBackupRestoreController + extends CommonKeyBackupRestoreController { @Autowired protected KeyBackupRestoreController(final KeyEntityToV72ModelConverter modelConverter, @@ -54,6 +58,21 @@ public ResponseEntity restore(@RequestAttribute(name = ApiCons return super.restore(baseUri, keyBackupModel); } + @Override + protected JsonWebKeyImportRequest getKeyMaterial(final KeyBackupListItem entityVersion) { + return entityVersion.getKeyMaterial(); + } + + @Override + protected KeyBackupList getBackupList() { + return new KeyBackupList(); + } + + @Override + protected KeyBackupModel getBackupModel() { + return new KeyBackupModel(); + } + @Override protected String apiVersion() { return V_7_2; diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java index 87e65933..eee44845 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java @@ -3,13 +3,23 @@ import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyBackupRestoreController; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72BackupConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_3.key.KeyRotationPolicyToV73ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_3.key.KeyRotationPolicyV73ModelToEntityConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupListItem; import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupList; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel; +import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; +import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; import com.github.nagyesta.lowkeyvault.service.vault.VaultService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -17,6 +27,7 @@ import javax.validation.Valid; import javax.validation.constraints.Pattern; import java.net.URI; +import java.util.Optional; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_3; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_3; @@ -26,13 +37,21 @@ @RestController @Validated @Component("KeyBackupRestoreControllerV73") -public class KeyBackupRestoreController extends CommonKeyBackupRestoreController { +public class KeyBackupRestoreController + extends CommonKeyBackupRestoreController { + + private final KeyRotationPolicyToV73ModelConverter keyRotationPolicyToV73ModelConverter; + private final KeyRotationPolicyV73ModelToEntityConverter rotationV73ModelToEntityConverter; @Autowired - protected KeyBackupRestoreController(final KeyEntityToV72ModelConverter modelConverter, - final KeyEntityToV72BackupConverter backupConverter, - final VaultService vaultService) { + protected KeyBackupRestoreController(@NonNull final KeyEntityToV72ModelConverter modelConverter, + @NonNull final KeyEntityToV72BackupConverter backupConverter, + @NonNull final VaultService vaultService, + @NonNull final KeyRotationPolicyToV73ModelConverter keyRotationPolicyToV73ModelConverter, + @NonNull final KeyRotationPolicyV73ModelToEntityConverter rotationV73ModelToEntityConverter) { super(modelConverter, backupConverter, vaultService); + this.keyRotationPolicyToV73ModelConverter = keyRotationPolicyToV73ModelConverter; + this.rotationV73ModelToEntityConverter = rotationV73ModelToEntityConverter; } @Override @@ -41,7 +60,6 @@ protected KeyBackupRestoreController(final KeyEntityToV72ModelConverter modelCon produces = APPLICATION_JSON_VALUE) public ResponseEntity backup(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - //handle differences of rotation policy return super.backup(keyName, baseUri); } @@ -55,8 +73,46 @@ public ResponseEntity restore(@RequestAttribute(name = ApiCons return super.restore(baseUri, keyBackupModel); } + @Override + protected KeyBackupModel backupEntity(final KeyEntityId entityId) { + final KeyBackupModel keyBackupModel = super.backupEntity(entityId); + final KeyBackupList value = keyBackupModel.getValue(); + final ReadOnlyRotationPolicy rotationPolicy = getVaultByUri(entityId.vault()).rotationPolicy(entityId); + value.setKeyRotationPolicy(keyRotationPolicyToV73ModelConverter.convert(rotationPolicy)); + return keyBackupModel; + } + + @Override + protected KeyVaultKeyModel restoreEntity(final KeyBackupModel backupModel) { + final KeyVaultKeyModel keyVaultKeyModel = super.restoreEntity(backupModel); + final URI baseUri = getSingleBaseUri(backupModel); + final String entityName = getSingleEntityName(backupModel); + final KeyEntityId keyEntityId = entityId(baseUri, entityName); + final KeyVaultFake vaultByUri = getVaultByUri(baseUri); + final KeyRotationPolicyModel keyRotationPolicy = backupModel.getValue().getKeyRotationPolicy(); + Optional.ofNullable(keyRotationPolicy) + .map(r -> rotationV73ModelToEntityConverter.convert(keyEntityId, r)) + .ifPresent(vaultByUri::setRotationPolicy); + return keyVaultKeyModel; + } + @Override protected String apiVersion() { return V_7_3; } + + @Override + protected JsonWebKeyImportRequest getKeyMaterial(final KeyBackupListItem entityVersion) { + return entityVersion.getKeyMaterial(); + } + + @Override + protected KeyBackupList getBackupList() { + return new KeyBackupList(); + } + + @Override + protected KeyBackupModel getBackupModel() { + return new KeyBackupModel(); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java index 8b25189e..1b17dcb6 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java @@ -11,6 +11,8 @@ import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.CreateKeyRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.ImportKeyRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.UpdateKeyRequest; +import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; import com.github.nagyesta.lowkeyvault.service.vault.VaultService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -142,6 +144,19 @@ public ResponseEntity updateVersion( return super.updateVersion(keyName, keyVersion, baseUri, request); } + @PostMapping(value = "/keys/{keyName}/rotate", + params = API_VERSION_7_3, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity rotateKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + log.info("Received request to {} rotate key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final VersionedKeyEntityId rotatedKeyId = getVaultByUri(baseUri).rotateKey(entityId(baseUri, keyName)); + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, rotatedKeyId.version()); + return ResponseEntity.ok(convertDetails(keyVaultKeyEntity)); + } + @Override @GetMapping(value = "/deletedkeys/{keyName}", params = API_VERSION_7_3, diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverter.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverter.java new file mode 100644 index 00000000..594cfdd9 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverter.java @@ -0,0 +1,54 @@ +package com.github.nagyesta.lowkeyvault.mapper.v7_3.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +public class KeyRotationPolicyToV73ModelConverter implements Converter { + + @Override + public KeyRotationPolicyModel convert(@Nullable final ReadOnlyRotationPolicy source) { + return Optional.ofNullable(source) + .map(this::convertNonNull) + .orElse(null); + } + + private KeyRotationPolicyModel convertNonNull(final ReadOnlyRotationPolicy readOnlyRotationPolicy) { + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(readOnlyRotationPolicy.getId().asRotationPolicyUri()); + model.setAttributes(convertAttributes(readOnlyRotationPolicy)); + model.setLifetimeActions(convertLifetimeActions(readOnlyRotationPolicy.getLifetimeActions())); + return model; + } + + private KeyRotationPolicyAttributes convertAttributes(final ReadOnlyRotationPolicy readOnlyRotationPolicy) { + final KeyRotationPolicyAttributes attributes = new KeyRotationPolicyAttributes(); + attributes.setExpiryTime(readOnlyRotationPolicy.getExpiryTime()); + attributes.setCreatedOn(readOnlyRotationPolicy.getCreatedOn()); + attributes.setUpdatedOn(readOnlyRotationPolicy.getUpdatedOn()); + return attributes; + } + + private List convertLifetimeActions(final Map lifetimeActions) { + return lifetimeActions.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> this.convertLifetimeAction(e.getValue())) + .collect(Collectors.toList()); + } + + private KeyLifetimeActionModel convertLifetimeAction(final LifetimeAction lifetimeAction) { + return new KeyLifetimeActionModel( + new KeyLifetimeActionTypeModel(lifetimeAction.getActionType()), + new KeyLifetimeActionTriggerModel(lifetimeAction.getTrigger())); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverter.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverter.java new file mode 100644 index 00000000..b77649df --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverter.java @@ -0,0 +1,75 @@ +package com.github.nagyesta.lowkeyvault.mapper.v7_3.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyLifetimeActionTrigger; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyRotationPolicy; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Component +public class KeyRotationPolicyV73ModelToEntityConverter { + + public RotationPolicy convert(final KeyEntityId keyEntityId, @Nullable final KeyRotationPolicyModel source) { + return Optional.ofNullable(source) + .filter(this::isNotEmpty) + .map(s -> convertNonNull(keyEntityId, s)) + .orElse(null); + } + + private boolean isNotEmpty(final KeyRotationPolicyModel rotationPolicyModel) { + return hasLifetimeActions(rotationPolicyModel) || hasAttributes(rotationPolicyModel); + } + + private boolean hasAttributes(final KeyRotationPolicyModel rotationPolicyModel) { + return rotationPolicyModel.getAttributes() != null; + } + + private RotationPolicy convertNonNull(final KeyEntityId keyEntityId, final KeyRotationPolicyModel source) { + Assert.notNull(source.getAttributes(), "Attributes cannot be null."); + Assert.notNull(source.getLifetimeActions(), "LifetimeActions cannot be null."); + Assert.notEmpty(source.getLifetimeActions(), "LifetimeActions cannot be empty."); + final Map actions = convertLifetimeActions(source.getLifetimeActions()); + final Period expiryTime = source.getAttributes().getExpiryTime(); + final RotationPolicy entity = new KeyRotationPolicy(keyEntityId, expiryTime, actions); + return convertAttributes(source.getAttributes(), entity); + } + + private boolean hasLifetimeActions(final KeyRotationPolicyModel source) { + return source.getLifetimeActions() != null && !source.getLifetimeActions().isEmpty(); + } + + private RotationPolicy convertAttributes(final KeyRotationPolicyAttributes source, final RotationPolicy entity) { + entity.setCreatedOn(Optional.ofNullable(source).map(KeyRotationPolicyAttributes::getCreatedOn).orElse(OffsetDateTime.now())); + entity.setUpdatedOn(Optional.ofNullable(source).map(KeyRotationPolicyAttributes::getUpdatedOn).orElse(OffsetDateTime.now())); + return entity; + } + + private Map convertLifetimeActions(final List lifetimeActions) { + return lifetimeActions.stream() + .map(this::convertLifetimeAction) + .collect(Collectors.toMap(LifetimeAction::getActionType, Function.identity())); + } + + private LifetimeAction convertLifetimeAction(final KeyLifetimeActionModel source) { + final KeyLifetimeActionTriggerModel sourceTrigger = Objects.requireNonNull(source.getTrigger()); + final KeyLifetimeActionTypeModel action = Objects.requireNonNull(source.getAction()); + final KeyLifetimeActionTrigger trigger = new KeyLifetimeActionTrigger( + sourceTrigger.getTriggerPeriod(), sourceTrigger.getTriggerType()); + return new KeyLifetimeAction(action.getType(), trigger); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializer.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializer.java similarity index 61% rename from lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializer.java rename to lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializer.java index d250bafc..767c4fc4 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializer.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializer.java @@ -3,13 +3,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupList; -public class Base64ZipKeyDeserializer extends AbstractBase64ZipDeserializer { +public class Base64ZipV72KeyDeserializer extends AbstractBase64ZipDeserializer { - public Base64ZipKeyDeserializer() { + public Base64ZipV72KeyDeserializer() { this(new Base64Deserializer(), new ObjectMapper()); } - protected Base64ZipKeyDeserializer(final Base64Deserializer base64Deserializer, final ObjectMapper objectMapper) { + protected Base64ZipV72KeyDeserializer(final Base64Deserializer base64Deserializer, final ObjectMapper objectMapper) { super(base64Deserializer, objectMapper); } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializer.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializer.java similarity index 54% rename from lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializer.java rename to lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializer.java index 54a07735..62cb2d1b 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializer.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializer.java @@ -3,13 +3,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupList; -public class Base64ZipKeySerializer extends AbstractBase64ZipSerializer { +public class Base64ZipV72KeySerializer extends AbstractBase64ZipSerializer { - public Base64ZipKeySerializer() { + public Base64ZipV72KeySerializer() { this(new Base64Serializer(), new ObjectMapper()); } - protected Base64ZipKeySerializer(final Base64Serializer base64Serializer, final ObjectMapper objectMapper) { + protected Base64ZipV72KeySerializer(final Base64Serializer base64Serializer, final ObjectMapper objectMapper) { super(base64Serializer, objectMapper); } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializer.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializer.java new file mode 100644 index 00000000..8a3b285d --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializer.java @@ -0,0 +1,20 @@ +package com.github.nagyesta.lowkeyvault.model.json.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupList; + +public class Base64ZipV73KeyDeserializer extends AbstractBase64ZipDeserializer { + + public Base64ZipV73KeyDeserializer() { + this(new Base64Deserializer(), new ObjectMapper().findAndRegisterModules()); + } + + protected Base64ZipV73KeyDeserializer(final Base64Deserializer base64Deserializer, final ObjectMapper objectMapper) { + super(base64Deserializer, objectMapper); + } + + @Override + protected Class getType() { + return KeyBackupList.class; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializer.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializer.java new file mode 100644 index 00000000..d4f349cd --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializer.java @@ -0,0 +1,15 @@ +package com.github.nagyesta.lowkeyvault.model.json.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupList; + +public class Base64ZipV73KeySerializer extends AbstractBase64ZipSerializer { + + public Base64ZipV73KeySerializer() { + this(new Base64Serializer(), new ObjectMapper().findAndRegisterModules()); + } + + protected Base64ZipV73KeySerializer(final Base64Serializer base64Serializer, final ObjectMapper objectMapper) { + super(base64Serializer, objectMapper); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupModel.java index eaa01a6f..dd752d73 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupModel.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupModel.java @@ -2,12 +2,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; import lombok.Data; import javax.validation.Valid; import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.util.List; /** * Base class of backup models. @@ -18,10 +17,9 @@ */ @Data @JsonInclude(JsonInclude.Include.NON_NULL) -public class BaseBackupModel

, BL extends List> { +public class BaseBackupModel

, BL extends BackupListContainer> { @Valid @NotNull - @Size(min = 1) private BL value; } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/BackupListContainer.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/BackupListContainer.java new file mode 100644 index 00000000..7f2cff0b --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/BackupListContainer.java @@ -0,0 +1,10 @@ +package com.github.nagyesta.lowkeyvault.model.v7_2.key; + +import java.util.List; + +public interface BackupListContainer { + + List getVersions(); + + void setVersions(List versions); +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupList.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupList.java index 46a5fd5f..c2f02532 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupList.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupList.java @@ -1,6 +1,27 @@ package com.github.nagyesta.lowkeyvault.model.v7_2.key; -import java.util.ArrayList; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; -public class KeyBackupList extends ArrayList { +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +@EqualsAndHashCode +public class KeyBackupList implements BackupListContainer { + + @Valid + @NotNull + @Size(min = 1) + @JsonProperty("versions") + private List versions = List.of(); + + public List getVersions() { + return versions; + } + + public void setVersions(final List versions) { + this.versions = List.copyOf(versions); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupModel.java index 93dfda41..793ab6ed 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupModel.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyBackupModel.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipKeyDeserializer; -import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipKeySerializer; +import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipV72KeyDeserializer; +import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipV72KeySerializer; import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; import lombok.Data; import lombok.EqualsAndHashCode; @@ -14,13 +14,13 @@ @ToString(callSuper = true) public class KeyBackupModel extends BaseBackupModel { - @JsonSerialize(using = Base64ZipKeySerializer.class) + @JsonSerialize(using = Base64ZipV72KeySerializer.class) @Override public KeyBackupList getValue() { return super.getValue(); } - @JsonDeserialize(using = Base64ZipKeyDeserializer.class) + @JsonDeserialize(using = Base64ZipV72KeyDeserializer.class) @Override public void setValue(final KeyBackupList value) { super.setValue(value); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/SecretBackupList.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/SecretBackupList.java index 935e1db8..97dbe69a 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/SecretBackupList.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/SecretBackupList.java @@ -1,6 +1,28 @@ package com.github.nagyesta.lowkeyvault.model.v7_2.secret; -import java.util.ArrayList; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; +import lombok.EqualsAndHashCode; -public class SecretBackupList extends ArrayList { +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; + +@EqualsAndHashCode +public class SecretBackupList implements BackupListContainer { + + @Valid + @NotNull + @Size(min = 1) + @JsonProperty("versions") + private List versions = List.of(); + + public List getVersions() { + return versions; + } + + public void setVersions(final List versions) { + this.versions = List.copyOf(versions); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupList.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupList.java new file mode 100644 index 00000000..697dce22 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupList.java @@ -0,0 +1,22 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; + +import javax.validation.Valid; + +@EqualsAndHashCode(callSuper = true) +public class KeyBackupList extends com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupList { + + @Valid + @JsonProperty("rotationPolicy") + private KeyRotationPolicyModel keyRotationPolicy; + + public KeyRotationPolicyModel getKeyRotationPolicy() { + return keyRotationPolicy; + } + + public void setKeyRotationPolicy(final KeyRotationPolicyModel keyRotationPolicy) { + this.keyRotationPolicy = keyRotationPolicy; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupModel.java new file mode 100644 index 00000000..5ef627e5 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyBackupModel.java @@ -0,0 +1,30 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipV73KeyDeserializer; +import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipV73KeySerializer; +import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupListItem; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyPropertiesModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class KeyBackupModel extends BaseBackupModel { + + @JsonSerialize(using = Base64ZipV73KeySerializer.class) + @Override + public KeyBackupList getValue() { + return super.getValue(); + } + + @JsonDeserialize(using = Base64ZipV73KeyDeserializer.class) + @Override + public void setValue(final KeyBackupList value) { + super.setValue(value); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionModel.java new file mode 100644 index 00000000..b1c14908 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionModel.java @@ -0,0 +1,35 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"trigger", "action"}) +public class KeyLifetimeActionModel { + + @NotNull + @Valid + @JsonProperty("trigger") + private KeyLifetimeActionTriggerModel trigger; + @NotNull + @Valid + @JsonProperty("action") + private KeyLifetimeActionTypeModel action; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public KeyLifetimeActionModel() { + } + + public KeyLifetimeActionModel(final KeyLifetimeActionTypeModel action, final KeyLifetimeActionTriggerModel trigger) { + this(); + this.action = action; + this.trigger = trigger; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModel.java new file mode 100644 index 00000000..c6cc21ea --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModel.java @@ -0,0 +1,60 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + + +import com.fasterxml.jackson.annotation.*; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeActionTrigger; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import lombok.Data; +import lombok.NonNull; +import org.springframework.util.Assert; + +import java.time.Period; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class KeyLifetimeActionTriggerModel { + + @JsonIgnore + private LifetimeActionTriggerType triggerType; + @JsonIgnore + private Period triggerPeriod; + + @JsonCreator + public KeyLifetimeActionTriggerModel(@JsonProperty("timeBeforeExpiry") final Period timeBeforeExpiry, + @JsonProperty("timeAfterCreate") final Period timeAfterCreate) { + Assert.isTrue(timeBeforeExpiry == null || timeAfterCreate == null, + "TimeBeforeExpiry and TimeAfterCreate cannot be populated at the same time."); + Assert.isTrue(timeBeforeExpiry != null || timeAfterCreate != null, + "TimeBeforeExpiry and TimeAfterCreate cannot be null at the same time."); + if (timeAfterCreate != null) { + this.triggerType = LifetimeActionTriggerType.TIME_AFTER_CREATE; + this.triggerPeriod = timeAfterCreate; + } else { + this.triggerType = LifetimeActionTriggerType.TIME_BEFORE_EXPIRY; + this.triggerPeriod = timeBeforeExpiry; + } + } + + public KeyLifetimeActionTriggerModel(@NonNull final LifetimeActionTrigger trigger) { + this.triggerType = trigger.getTriggerType(); + this.triggerPeriod = trigger.getTimePeriod(); + } + + @JsonGetter + public Period getTimeBeforeExpiry() { + Period period = null; + if (triggerType == LifetimeActionTriggerType.TIME_BEFORE_EXPIRY) { + period = triggerPeriod; + } + return period; + } + + @JsonGetter + public Period getTimeAfterCreate() { + Period period = null; + if (triggerType == LifetimeActionTriggerType.TIME_AFTER_CREATE) { + period = triggerPeriod; + } + return period; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModel.java new file mode 100644 index 00000000..3e526220 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModel.java @@ -0,0 +1,27 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import lombok.Data; +import lombok.NonNull; + +import javax.validation.constraints.NotNull; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class KeyLifetimeActionTypeModel { + @NotNull + @JsonProperty("type") + private LifetimeActionType type; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public KeyLifetimeActionTypeModel() { + } + + public KeyLifetimeActionTypeModel(@NonNull final LifetimeActionType type) { + this(); + this.type = type; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyAttributes.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyAttributes.java new file mode 100644 index 00000000..9169c451 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyAttributes.java @@ -0,0 +1,34 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; +import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.ExpiryPeriod; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.time.Period; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"expiryTime", "created", "updated"}) +public class KeyRotationPolicyAttributes { + + @JsonProperty("created") + @JsonSerialize(using = EpochSecondsSerializer.class) + @JsonDeserialize(using = EpochSecondsDeserializer.class) + private OffsetDateTime createdOn; + @JsonProperty("updated") + @JsonSerialize(using = EpochSecondsSerializer.class) + @JsonDeserialize(using = EpochSecondsDeserializer.class) + private OffsetDateTime updatedOn; + @NotNull + @ExpiryPeriod + @JsonProperty("expiryTime") + private Period expiryTime; +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModel.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModel.java new file mode 100644 index 00000000..85128853 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModel.java @@ -0,0 +1,29 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.net.URI; +import java.util.List; + +@Data +@JsonPropertyOrder({"id", "lifetimeActions", "attributes"}) +public class KeyRotationPolicyModel { + + @JsonProperty("id") + private URI id; + @Valid + @NotNull + @JsonProperty("attributes") + private KeyRotationPolicyAttributes attributes; + @Valid + @NotNull + @Size(min = 1, max = 2) + @JsonProperty("lifetimeActions") + private List lifetimeActions; + +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionType.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionType.java new file mode 100644 index 00000000..1b55ec53 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionType.java @@ -0,0 +1,33 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key.constants; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.Arrays; + +public enum LifetimeActionType { + /** + * Notification triggers. + */ + NOTIFY("notify"), + /** + * Automatic rotation trigger. + */ + ROTATE("rotate"); + + private final String value; + + LifetimeActionType(final String value) { + this.value = value; + } + + @JsonCreator + public static LifetimeActionType forValue(final String name) { + return Arrays.stream(values()).filter(actionType -> actionType.getValue().equals(name)).findFirst().orElse(null); + } + + @JsonValue + public String getValue() { + return value; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriod.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriod.java new file mode 100644 index 00000000..b68681eb --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriod.java @@ -0,0 +1,19 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = ExpiryPeriodValidator.class) +public @interface ExpiryPeriod { + + String message() default "Expiry period is invalid."; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidator.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidator.java new file mode 100644 index 00000000..2c2e7ef3 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidator.java @@ -0,0 +1,25 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; + +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.util.PeriodUtil; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.time.Period; +import java.util.Optional; + +public class ExpiryPeriodValidator implements ConstraintValidator { + + @Override + public void initialize(final ExpiryPeriod constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(final Period value, final ConstraintValidatorContext context) { + return Optional.ofNullable(value) + .map(PeriodUtil::asDays) + .map(totalDays -> totalDays >= LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS) + .orElse(true); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultEntity.java index 8e0d9968..eaa2d5a9 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultEntity.java @@ -12,7 +12,7 @@ * * @param The type of the versioned Id identifying this entity. */ -public interface BaseVaultEntity { +public interface BaseVaultEntity extends TimeAware { V getId(); @@ -52,8 +52,6 @@ public interface BaseVaultEntity { boolean canPurge(); - void timeShift(int offsetSeconds); - boolean isManaged(); void setCreatedOn(OffsetDateTime createdOn); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultFake.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultFake.java index 484d7ff8..4ba1c3c8 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultFake.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultFake.java @@ -12,7 +12,7 @@ * @param The versioned key type. * @param The entity type. */ -public interface BaseVaultFake> { +public interface BaseVaultFake> extends TimeAware { ReadOnlyVersionedEntityMultiMap getEntities(); @@ -32,5 +32,4 @@ public interface BaseVaultFake keyCreationInput(); + default byte[] encrypt(final String clear, final EncryptionAlgorithm encryptionAlgorithm, final byte[] iv) { Assert.hasText(clear, "Clear text must not be blank."); return encryptBytes(clear.getBytes(StandardCharsets.UTF_8), encryptionAlgorithm, iv); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyRotationPolicy.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyRotationPolicy.java new file mode 100644 index 00000000..cece7a0c --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyRotationPolicy.java @@ -0,0 +1,22 @@ +package com.github.nagyesta.lowkeyvault.service.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.Map; + +public interface ReadOnlyRotationPolicy { + + KeyEntityId getId(); + + OffsetDateTime getCreatedOn(); + + OffsetDateTime getUpdatedOn(); + + Period getExpiryTime(); + + Map getLifetimeActions(); + +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/RotationPolicy.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/RotationPolicy.java new file mode 100644 index 00000000..f7755bdd --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/RotationPolicy.java @@ -0,0 +1,21 @@ +package com.github.nagyesta.lowkeyvault.service.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.common.TimeAware; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.Map; + +public interface RotationPolicy extends ReadOnlyRotationPolicy, TimeAware { + + void setLifetimeActions(Map lifetimeActions); + + void setCreatedOn(OffsetDateTime createdOn); + + void setUpdatedOn(OffsetDateTime updatedOn); + + void setExpiryTime(Period expiryTime); + + void validate(OffsetDateTime latestKeyVersionExpiry); +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerType.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerType.java new file mode 100644 index 00000000..ba9aa731 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerType.java @@ -0,0 +1,75 @@ +package com.github.nagyesta.lowkeyvault.service.key.constants; + +import com.github.nagyesta.lowkeyvault.service.key.util.PeriodUtil; +import lombok.NonNull; +import org.springframework.util.Assert; + +import java.time.OffsetDateTime; +import java.time.Period; + +public enum LifetimeActionTriggerType { + + /** + * Triggers an action relative to the creation of the key. + */ + TIME_AFTER_CREATE { + @Override + public boolean shouldTrigger(@NonNull final OffsetDateTime createTime, + final OffsetDateTime expiryTime, + @NonNull final Period triggerPeriod) { + return createTime.plusDays(PeriodUtil.asDays(triggerPeriod)).isBefore(OffsetDateTime.now()); + } + + @Override + public void validate(final OffsetDateTime expiryTime, + @NonNull final Period expiryPeriod, + @NonNull final Period triggerPeriod) { + super.validate(expiryTime, expiryPeriod, triggerPeriod); + final Period threshold = expiryPeriod.minusDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY); + Assert.isTrue(PeriodUtil.asDays(threshold) >= PeriodUtil.asDays(triggerPeriod), + "Trigger must be at least " + MINIMUM_THRESHOLD_BEFORE_EXPIRY + " days before expiry."); + } + }, + + /** + * Triggers an action relative to the expiry of the key. + */ + TIME_BEFORE_EXPIRY { + @Override + public boolean shouldTrigger(final OffsetDateTime createTime, + @NonNull final OffsetDateTime expiryTime, + @NonNull final Period triggerPeriod) { + return expiryTime.minusDays(PeriodUtil.asDays(triggerPeriod)).isBefore(OffsetDateTime.now()); + } + + @Override + public void validate(final OffsetDateTime expiryTime, + @NonNull final Period expiryPeriod, + @NonNull final Period triggerPeriod) { + super.validate(expiryTime, expiryPeriod, triggerPeriod); + Assert.notNull(expiryTime, "Expiry time is not set, before expiry triggers are not allowed."); + Assert.isTrue(PeriodUtil.asDays(triggerPeriod) >= MINIMUM_THRESHOLD_BEFORE_EXPIRY, + "Trigger must be at least " + MINIMUM_THRESHOLD_BEFORE_EXPIRY + " days before expiry."); + } + }; + + /** + * Minimum number of days we need to leave for a trigger action after creation and before expiry. + */ + public static final int MINIMUM_THRESHOLD_BEFORE_EXPIRY = 7; + /** + * Minimum number of days needed for expiry periods. + */ + public static final int MINIMUM_EXPIRY_PERIOD_IN_DAYS = 28; + + public abstract boolean shouldTrigger(OffsetDateTime createTime, + OffsetDateTime expiryTime, + Period triggerPeriod); + + public void validate(final OffsetDateTime expiryTime, + final Period expiryPeriod, + final Period triggerPeriod) { + Assert.isTrue(PeriodUtil.asDays(expiryPeriod) >= MINIMUM_EXPIRY_PERIOD_IN_DAYS, + "Expiry period must be at least " + MINIMUM_EXPIRY_PERIOD_IN_DAYS + " days."); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityId.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityId.java index 130c4e29..27280c32 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityId.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityId.java @@ -68,4 +68,8 @@ public URI asRecoveryUri() { public URI asUri(@NonNull final String query) { return URI.create(asUri().toString() + query); } + + public URI asRotationPolicyUri() { + return URI.create(vault + "/keys/" + id() + "/rotationpolicy"); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntity.java index 927d174e..a30db27b 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntity.java @@ -41,6 +41,11 @@ public KeyType getKeyType() { return KeyType.OCT_HSM; } + @Override + public KeyCreationInput keyCreationInput() { + return new OctKeyCreationInput(getKeyType(), getKeySize()); + } + @Override public byte[] getK() { return getKey().getEncoded(); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/DefaultKeyRotationPolicy.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/DefaultKeyRotationPolicy.java new file mode 100644 index 00000000..16a0bc82 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/DefaultKeyRotationPolicy.java @@ -0,0 +1,22 @@ +package com.github.nagyesta.lowkeyvault.service.key.impl; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; + +import java.time.Period; +import java.util.Map; + +public class DefaultKeyRotationPolicy extends KeyRotationPolicy { + + private static final KeyLifetimeActionTrigger TRIGGER_30_DAYS_BEFORE_EXPIRY = + new KeyLifetimeActionTrigger(Period.ofDays(30), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + private static final KeyLifetimeAction NOTIFY_30_DAYS_BEFORE_EXPIRY + = new KeyLifetimeAction(LifetimeActionType.NOTIFY, TRIGGER_30_DAYS_BEFORE_EXPIRY); + private static final Period PERIOD_1_YEAR = Period.ofYears(1); + + public DefaultKeyRotationPolicy(final KeyEntityId keyEntityId) { + super(keyEntityId, PERIOD_1_YEAR, Map.of(LifetimeActionType.NOTIFY, NOTIFY_30_DAYS_BEFORE_EXPIRY)); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java index 0fb9db64..69051159 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntity.java @@ -44,6 +44,11 @@ public KeyType getKeyType() { } } + @Override + public KeyCreationInput keyCreationInput() { + return new EcKeyCreationInput(getKeyType(), getKeyCurveName()); + } + @Override public byte[] getX() { return ((ECPublicKey) getKey().getPublic()).getW().getAffineX().toByteArray(); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTrigger.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTrigger.java new file mode 100644 index 00000000..db555e15 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTrigger.java @@ -0,0 +1,34 @@ +package com.github.nagyesta.lowkeyvault.service.key.impl; + +import com.github.nagyesta.lowkeyvault.service.key.LifetimeActionTrigger; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import lombok.NonNull; + +import java.time.OffsetDateTime; +import java.time.Period; + +public class KeyLifetimeActionTrigger implements LifetimeActionTrigger { + + private final Period timePeriod; + private final LifetimeActionTriggerType triggerType; + + public KeyLifetimeActionTrigger(@NonNull final Period timePeriod, @NonNull final LifetimeActionTriggerType triggerType) { + this.timePeriod = timePeriod; + this.triggerType = triggerType; + } + + @Override + public Period getTimePeriod() { + return timePeriod; + } + + @Override + public LifetimeActionTriggerType getTriggerType() { + return triggerType; + } + + @Override + public boolean shouldTrigger(final OffsetDateTime created, final OffsetDateTime expiry) { + return triggerType.shouldTrigger(created, expiry, timePeriod); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicy.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicy.java new file mode 100644 index 00000000..db89e2f5 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicy.java @@ -0,0 +1,104 @@ +package com.github.nagyesta.lowkeyvault.service.key.impl; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; +import lombok.NonNull; +import org.springframework.util.Assert; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.Map; + +public class KeyRotationPolicy implements RotationPolicy { + + private final KeyEntityId keyEntityId; + private OffsetDateTime createdOn; + private OffsetDateTime updatedOn; + private Period expiryTime; + private Map lifetimeActions; + + public KeyRotationPolicy(@NonNull final KeyEntityId keyEntityId, + @NonNull final Period expiryTime, + @NonNull final Map lifetimeActions) { + this.keyEntityId = keyEntityId; + this.expiryTime = expiryTime; + this.lifetimeActions = Map.copyOf(lifetimeActions); + this.createdOn = OffsetDateTime.now(); + this.updatedOn = OffsetDateTime.now(); + } + + @Override + public KeyEntityId getId() { + return keyEntityId; + } + + @Override + public OffsetDateTime getCreatedOn() { + return createdOn; + } + + @Override + public OffsetDateTime getUpdatedOn() { + return updatedOn; + } + + @Override + public Period getExpiryTime() { + return expiryTime; + } + + @Override + public Map getLifetimeActions() { + return lifetimeActions; + } + + @Override + public void validate(final OffsetDateTime latestKeyVersionExpiry) { + lifetimeActions.values().forEach(action -> { + final Period triggerPeriod = action.getTrigger().getTimePeriod(); + final LifetimeActionTriggerType triggerType = action.getTrigger().getTriggerType(); + triggerType.validate(latestKeyVersionExpiry, expiryTime, triggerPeriod); + Assert.isTrue(action.getActionType() != LifetimeActionType.NOTIFY + || triggerType == LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, + "Notify actions cannot be used with time after creation trigger."); + }); + } + + @Override + public void setCreatedOn(@NonNull final OffsetDateTime createdOn) { + this.createdOn = createdOn; + } + + @Override + public void setUpdatedOn(@NonNull final OffsetDateTime updatedOn) { + this.updatedOn = updatedOn; + } + + @Override + public void setExpiryTime(@NonNull final Period expiryTime) { + this.expiryTime = expiryTime; + this.updatedOn = OffsetDateTime.now(); + } + + @Override + public void setLifetimeActions(@NonNull final Map lifetimeActions) { + Assert.isTrue(notifyIsNotRemoved(lifetimeActions), "Notify action cannot be removed."); + this.lifetimeActions = Map.copyOf(lifetimeActions); + this.updatedOn = OffsetDateTime.now(); + } + + @Override + public void timeShift(final int offsetSeconds) { + Assert.isTrue(offsetSeconds > 0, "Offset must be positive."); + createdOn = createdOn.minusSeconds(offsetSeconds); + updatedOn = updatedOn.minusSeconds(offsetSeconds); + } + + private boolean notifyIsNotRemoved(final Map lifetimeActions) { + return !this.lifetimeActions.containsKey(LifetimeActionType.NOTIFY) + || lifetimeActions.containsKey(LifetimeActionType.NOTIFY); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImpl.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImpl.java index 130d0946..87475867 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImpl.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImpl.java @@ -11,8 +11,11 @@ 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; @@ -20,6 +23,9 @@ 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> @@ -29,6 +35,8 @@ public class KeyVaultFakeImpl 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) { @@ -67,6 +75,7 @@ public VersionedKeyEntityId importRsaKeyVersion( 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); } @@ -77,6 +86,7 @@ public VersionedKeyEntityId importEcKeyVersion( 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); } @@ -88,6 +98,7 @@ public VersionedKeyEntityId importOctKeyVersion( 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); } @@ -97,6 +108,7 @@ public VersionedKeyEntityId createRsaKeyVersion( 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); } @@ -107,6 +119,7 @@ public VersionedKeyEntityId createEcKeyVersion( 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); } @@ -117,6 +130,7 @@ public VersionedKeyEntityId createOctKeyVersion( 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); } @@ -126,4 +140,51 @@ public void setKeyOperations(@NonNull final VersionedKeyEntityId keyEntityId, 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)); + } + + @Override + public RotationPolicy rotationPolicy(@NonNull final KeyEntityId keyEntityId) { + return rotationPolicies.get(keyEntityId.id()); + } + + @Override + public void setRotationPolicy(@NonNull final RotationPolicy rotationPolicy) { + final ReadOnlyKeyVaultKeyEntity readOnlyEntity = latestReadOnlyKeyVersion(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); + final VersionedKeyEntityId rotatedKeyId = createKeyVersion(keyEntityId.id(), readOnlyEntity.keyCreationInput()); + final KeyVaultKeyEntity rotatedEntity = getEntities().getEntity(rotatedKeyId, KeyVaultKeyEntity.class); + rotatedEntity.setOperations(readOnlyEntity.getOperations()); + rotatedEntity.setEnabled(true); + rotatedEntity.setTags(readOnlyEntity.getTags()); + return rotatedKeyId; + } + + 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); + } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java index 1d626c1f..97885f89 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntity.java @@ -14,6 +14,11 @@ import java.util.List; import java.util.concurrent.Callable; +/** + * Common Key entity base class. + * @param The type of the key. + * @param The type of the key parameter. + */ public abstract class KeyVaultKeyEntity extends KeyVaultBaseEntity implements ReadOnlyKeyVaultKeyEntity { private final T key; diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java index 365b6024..d01d6b9a 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntity.java @@ -49,6 +49,11 @@ public KeyType getKeyType() { } } + @Override + public KeyCreationInput keyCreationInput() { + return new RsaKeyCreationInput(getKeyType(), getKeySize(), ((RSAPublicKey) getKey().getPublic()).getPublicExponent()); + } + @Override public byte[] getN() { return ((RSAPublicKey) getKey().getPublic()).getModulus().toByteArray(); diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtil.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtil.java new file mode 100644 index 00000000..123572a3 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtil.java @@ -0,0 +1,22 @@ +package com.github.nagyesta.lowkeyvault.service.key.util; + +import lombok.NonNull; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.temporal.ChronoUnit; + +public final class PeriodUtil { + + private PeriodUtil() { + throw new IllegalCallerException("Utility cannot be instantiated."); + } + + public static long asDays(@NonNull final Period period) { + return asDays(period, OffsetDateTime.now()); + } + + static long asDays(final Period period, final OffsetDateTime relativeTo) { + return ChronoUnit.DAYS.between(relativeTo, relativeTo.plus(period)); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreControllerIntegrationTest.java index 95330412..e7b3a6ce 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreControllerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyBackupRestoreControllerIntegrationTest.java @@ -32,6 +32,7 @@ import java.security.KeyPair; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -110,7 +111,7 @@ void testRestoreEntityShouldRestoreASingleKeyWhenCalledWithValidInput() { } @Test - void testRestoreEntityShouldRestoreAThreeKeysWhenCalledWithValidInput() { + void testRestoreEntityShouldRestoreThreeKeysWhenCalledWithValidInput() { //given final KeyBackupModel backupModel = new KeyBackupModel(); backupModel.setValue(new KeyBackupList()); @@ -270,7 +271,9 @@ private KeyPair addVersionToList(final URI baseUri, final String name, final Str propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); listItem.setAttributes(propertiesModel); listItem.setTags(tags); - backupModel.getValue().add(listItem); + final List list = new ArrayList<>(backupModel.getValue().getVersions()); + list.add(listItem); + backupModel.getValue().setVersions(list); return keyPair; } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreControllerIntegrationTest.java index 2c1e99bf..6acb12a6 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreControllerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreControllerIntegrationTest.java @@ -23,6 +23,8 @@ import org.springframework.util.MimeTypeUtils; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Stream; @@ -244,6 +246,8 @@ private void addVersionToList(final URI baseUri, final String name, final String propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); listItem.setAttributes(propertiesModel); listItem.setTags(tags); - backupModel.getValue().add(listItem); + final List list = new ArrayList<>(backupModel.getValue().getVersions()); + list.add(listItem); + backupModel.getValue().setVersions(list); } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java index 5b3ab517..00a33414 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java @@ -3,14 +3,23 @@ import com.github.nagyesta.lowkeyvault.TestConstantsUri; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72BackupConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_3.key.KeyRotationPolicyToV73ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_3.key.KeyRotationPolicyV73ModelToEntityConverter; import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyBackupListItem; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyPropertiesModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; 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.model.v7_3.key.*; import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +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.impl.EcKeyCreationInput; import com.github.nagyesta.lowkeyvault.service.key.util.KeyGenUtil; @@ -32,23 +41,29 @@ import java.security.KeyPair; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.time.Period; +import java.util.*; import java.util.stream.Stream; import static com.github.nagyesta.lowkeyvault.TestConstants.*; import static com.github.nagyesta.lowkeyvault.TestConstantsKeys.*; +import static com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType.ROTATE; import static org.mockito.Mockito.mock; @SpringBootTest class KeyBackupRestoreControllerIntegrationTest { + private static final Period EXPIRY_TIME = Period.ofDays(LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS); + private static final Period TRIGGER_TIME = Period.ofDays(LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY); @Autowired @Qualifier("KeyBackupRestoreControllerV73") private KeyBackupRestoreController underTest; @Autowired private VaultService vaultService; + @Autowired + private KeyRotationPolicyToV73ModelConverter toModelConverter; + @Autowired + private KeyRotationPolicyV73ModelToEntityConverter toEntityConverter; private URI uri; public static Stream nullProvider() { @@ -86,7 +101,7 @@ void testConstructorShouldThrowExceptionWhenCalledWithNulls( //when Assertions.assertThrows(IllegalArgumentException.class, - () -> new KeyBackupRestoreController(modelConverter, backupConverter, vaultService)); + () -> new KeyBackupRestoreController(modelConverter, backupConverter, vaultService, toModelConverter, toEntityConverter)); //then + exception } @@ -110,7 +125,7 @@ void testRestoreEntityShouldRestoreASingleKeyWhenCalledWithValidInput() { } @Test - void testRestoreEntityShouldRestoreAThreeKeysWhenCalledWithValidInput() { + void testRestoreEntityShouldRestoreThreeKeysWhenCalledWithValidInput() { //given final KeyBackupModel backupModel = new KeyBackupModel(); backupModel.setValue(new KeyBackupList()); @@ -129,6 +144,36 @@ void testRestoreEntityShouldRestoreAThreeKeysWhenCalledWithValidInput() { assertRestoredKeyMatchesExpectations(actualBody, (ECPublicKey) expectedKey.getPublic(), KEY_VERSION_3, TAGS_EMPTY); } + @Test + void testRestoreEntityShouldRestoreRotationPolicyWhenCalledWithValidInput() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + final KeyPair expectedKey = addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, TAGS_EMPTY); + final KeyEntityId keyEntityId = new KeyEntityId(uri, KEY_NAME_1); + backupModel.getValue().setKeyRotationPolicy(keyRotationPolicy(keyEntityId)); + + //when + final ResponseEntity actual = underTest.restore(uri, backupModel); + + //then + Assertions.assertNotNull(actual); + final KeyVaultKeyModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + assertRestoredKeyMatchesExpectations(actualBody, (ECPublicKey) expectedKey.getPublic(), KEY_VERSION_1, TAGS_EMPTY); + final ReadOnlyRotationPolicy rotationPolicy = vaultService.findByUri(uri).keyVaultFake().rotationPolicy(keyEntityId); + Assertions.assertEquals(keyEntityId, rotationPolicy.getId()); + Assertions.assertEquals(TIME_10_MINUTES_AGO, rotationPolicy.getCreatedOn()); + Assertions.assertEquals(NOW, rotationPolicy.getUpdatedOn()); + Assertions.assertEquals(EXPIRY_TIME, rotationPolicy.getExpiryTime()); + Assertions.assertIterableEquals(Collections.singleton(ROTATE), rotationPolicy.getLifetimeActions().keySet()); + final LifetimeAction lifetimeAction = rotationPolicy.getLifetimeActions().get(ROTATE); + Assertions.assertEquals(ROTATE, lifetimeAction.getActionType()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_AFTER_CREATE, lifetimeAction.getTrigger().getTriggerType()); + Assertions.assertEquals(TRIGGER_TIME, lifetimeAction.getTrigger().getTimePeriod()); + } + @Test void testRestoreEntityShouldThrowExceptionWhenCalledWithMoreThanOneUris() { //given @@ -270,7 +315,32 @@ private KeyPair addVersionToList(final URI baseUri, final String name, final Str propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); listItem.setAttributes(propertiesModel); listItem.setTags(tags); - backupModel.getValue().add(listItem); + final List list = new ArrayList<>(backupModel.getValue().getVersions()); + list.add(listItem); + backupModel.getValue().setVersions(list); return keyPair; } + + private KeyRotationPolicyModel keyRotationPolicy(final KeyEntityId keyEntityId) { + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setLifetimeActions(List.of(actionModel())); + model.setAttributes(rotationPolicyAttributes()); + return model; + } + + private KeyRotationPolicyAttributes rotationPolicyAttributes() { + final KeyRotationPolicyAttributes attributes = new KeyRotationPolicyAttributes(); + attributes.setCreatedOn(TIME_10_MINUTES_AGO); + attributes.setUpdatedOn(NOW); + attributes.setExpiryTime(EXPIRY_TIME); + return attributes; + } + + private KeyLifetimeActionModel actionModel() { + final KeyLifetimeActionModel actionModel = new KeyLifetimeActionModel(); + actionModel.setAction(new KeyLifetimeActionTypeModel(ROTATE)); + actionModel.setTrigger(new KeyLifetimeActionTriggerModel(null, TRIGGER_TIME)); + return actionModel; + } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java index c94ff1da..57418360 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java @@ -905,6 +905,39 @@ void testUpdateVersionShouldReturnEntryWhenKeyAndVersionIsFound( verify(keyEntityToV72ModelConverter).convert(same(entity)); } + @Test + void testRotateKeyShouldCallTheKeyVaultFakeForDoingTheRotationWhenCalled() { + //given + final KeyEntityId entityId = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final VersionedKeyEntityId newKeyId = new VersionedKeyEntityId(entityId.vault(), entityId.id()); + final CreateKeyRequest request = createRequest(List.of(), null, null); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(newKeyId, request); + when(keyVaultFake.rotateKey(eq(entityId))) + .thenReturn(newKeyId); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getReadOnlyEntity(eq(newKeyId))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.rotateKey(entityId.id(), entityId.vault()); + + //then + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertEquals(RESPONSE, actual.getBody()); + final InOrder inOrder = inOrder(keyVaultFake, entities, keyEntityToV72ModelConverter); + inOrder.verify(keyVaultFake) + .rotateKey(eq(entityId)); + inOrder.verify(keyVaultFake) + .getEntities(); + inOrder.verify(entities) + .getReadOnlyEntity(eq(newKeyId)); + inOrder.verify(keyEntityToV72ModelConverter) + .convert(same(entity)); + } + @NonNull private CreateKeyRequest createRequest( final List operations, diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java index 619c8668..39de3b36 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java @@ -23,6 +23,8 @@ import org.springframework.util.MimeTypeUtils; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Stream; @@ -244,6 +246,8 @@ private void addVersionToList(final URI baseUri, final String name, final String propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); listItem.setAttributes(propertiesModel); listItem.setTags(tags); - backupModel.getValue().add(listItem); + final List list = new ArrayList<>(backupModel.getValue().getVersions()); + list.add(listItem); + backupModel.getValue().setVersions(list); } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/KeyEntityToV72PropertiesModelConverterTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/KeyEntityToV72PropertiesModelConverterTest.java index 6e234366..6388fa67 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/KeyEntityToV72PropertiesModelConverterTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/KeyEntityToV72PropertiesModelConverterTest.java @@ -6,6 +6,7 @@ import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyCreationInput; import com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultKeyEntity; import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; import org.junit.jupiter.api.AfterEach; @@ -108,6 +109,11 @@ public KeyType getKeyType() { return null; } + @Override + public KeyCreationInput keyCreationInput() { + return null; + } + @Override public byte[] encryptBytes(final byte[] clear, final EncryptionAlgorithm encryptionAlgorithm, final byte[] iv) { diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverterTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverterTest.java new file mode 100644 index 00000000..d08c0912 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyToV73ModelConverterTest.java @@ -0,0 +1,53 @@ +package com.github.nagyesta.lowkeyvault.mapper.v7_3.key; + +import com.github.nagyesta.lowkeyvault.TestConstantsKeys; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyLifetimeActionModel; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyLifetimeActionTrigger; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyRotationPolicy; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Period; +import java.util.Map; + +import static com.github.nagyesta.lowkeyvault.TestConstants.TIME_10_MINUTES_AGO; +import static com.github.nagyesta.lowkeyvault.TestConstants.TIME_IN_10_MINUTES; + +class KeyRotationPolicyToV73ModelConverterTest { + + @Test + void testConvertShouldConvertValuableFieldsWhenCalledWithValidData() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + final Period expiryTime = Period.ofDays(LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS); + final Period triggerPeriod = Period.ofDays(LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY); + + final KeyLifetimeActionTrigger trigger = new KeyLifetimeActionTrigger(triggerPeriod, LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + final KeyRotationPolicy source = new KeyRotationPolicy(keyEntityId, expiryTime, + Map.of(LifetimeActionType.NOTIFY, new KeyLifetimeAction(LifetimeActionType.NOTIFY, trigger))); + source.setCreatedOn(TIME_10_MINUTES_AGO); + source.setUpdatedOn(TIME_IN_10_MINUTES); + + final KeyRotationPolicyToV73ModelConverter underTest = new KeyRotationPolicyToV73ModelConverter(); + + //when + final KeyRotationPolicyModel actual = underTest.convert(source); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(keyEntityId.asRotationPolicyUri(), actual.getId()); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actual.getAttributes().getCreatedOn()); + Assertions.assertEquals(TIME_IN_10_MINUTES, actual.getAttributes().getUpdatedOn()); + Assertions.assertEquals(expiryTime, actual.getAttributes().getExpiryTime()); + Assertions.assertEquals(1, actual.getLifetimeActions().size()); + final KeyLifetimeActionModel actionModel = actual.getLifetimeActions().get(0); + Assertions.assertEquals(triggerPeriod, actionModel.getTrigger().getTriggerPeriod()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, actionModel.getTrigger().getTriggerType()); + Assertions.assertEquals(LifetimeActionType.NOTIFY, actionModel.getAction().getType()); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverterTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverterTest.java new file mode 100644 index 00000000..f879b455 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/KeyRotationPolicyV73ModelToEntityConverterTest.java @@ -0,0 +1,164 @@ +package com.github.nagyesta.lowkeyvault.mapper.v7_3.key; + +import com.github.nagyesta.lowkeyvault.TestConstantsKeys; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Period; +import java.util.List; + +import static com.github.nagyesta.lowkeyvault.TestConstants.NOW; +import static com.github.nagyesta.lowkeyvault.TestConstants.TIME_10_MINUTES_AGO; + +class KeyRotationPolicyV73ModelToEntityConverterTest { + + @Test + void testConvertShouldConvertValuableFieldsWhenCalledWithValidData() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + final Period timeBeforeExpiry = Period.ofDays(LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY); + final Period expiryTime = Period.ofDays(LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS); + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setAttributes(attributes(expiryTime)); + model.setLifetimeActions(List.of(notifyAction(timeBeforeExpiry))); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + final RotationPolicy actual = underTest.convert(keyEntityId, model); + + //then + Assertions.assertEquals(keyEntityId, actual.getId()); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actual.getCreatedOn()); + Assertions.assertEquals(NOW, actual.getUpdatedOn()); + Assertions.assertEquals(expiryTime, actual.getExpiryTime()); + final LifetimeAction actualNotify = actual.getLifetimeActions().get(LifetimeActionType.NOTIFY); + Assertions.assertEquals(timeBeforeExpiry, actualNotify.getTrigger().getTimePeriod()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, actualNotify.getTrigger().getTriggerType()); + Assertions.assertEquals(LifetimeActionType.NOTIFY, actualNotify.getActionType()); + } + + @Test + void testConvertShouldUseDefaultsWhenCalledWithMinimalAttributes() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + final Period expiryTime = Period.ofDays(LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS); + final Period timeBeforeExpiry = Period.ofDays(LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY); + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setLifetimeActions(List.of(notifyAction(timeBeforeExpiry))); + final KeyRotationPolicyAttributes attributes = new KeyRotationPolicyAttributes(); + attributes.setExpiryTime(expiryTime); + model.setAttributes(attributes); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + final RotationPolicy actual = underTest.convert(keyEntityId, model); + + //then + Assertions.assertEquals(keyEntityId, actual.getId()); + Assertions.assertTrue(actual.getCreatedOn().isAfter(NOW)); + Assertions.assertTrue(actual.getUpdatedOn().isAfter(NOW)); + Assertions.assertEquals(expiryTime, actual.getExpiryTime()); + final LifetimeAction actualNotify = actual.getLifetimeActions().get(LifetimeActionType.NOTIFY); + Assertions.assertEquals(timeBeforeExpiry, actualNotify.getTrigger().getTimePeriod()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, actualNotify.getTrigger().getTriggerType()); + Assertions.assertEquals(LifetimeActionType.NOTIFY, actualNotify.getActionType()); + } + + @Test + void testConvertShouldThrowExceptionWhenCalledWithoutAttributes() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + final Period timeBeforeExpiry = Period.ofDays(LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY); + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setLifetimeActions(List.of(notifyAction(timeBeforeExpiry))); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.convert(keyEntityId, model)); + + //then + exception + } + + @Test + void testConvertShouldReturnNullWhenCalledWithEmptyModel() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + final RotationPolicy actual = underTest.convert(keyEntityId, model); + + //then + Assertions.assertNull(actual); + } + + @Test + void testConvertShouldThrowExceptionWhenCalledWithNoList() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + final Period expiryTime = Period.ofDays(LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS); + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setAttributes(attributes(expiryTime)); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.convert(keyEntityId, model)); + + //then + exception + } + + @Test + void testConvertShouldReturnNullWhenCalledWithoutAttributesAndEmptyList() { + //given + final KeyEntityId keyEntityId = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(keyEntityId.asRotationPolicyUri()); + model.setLifetimeActions(List.of()); + + final KeyRotationPolicyV73ModelToEntityConverter underTest = new KeyRotationPolicyV73ModelToEntityConverter(); + + //when + final RotationPolicy actual = underTest.convert(keyEntityId, model); + + //then + Assertions.assertNull(actual); + } + + private KeyLifetimeActionModel notifyAction(final Period timeBeforeExpiry) { + final KeyLifetimeActionModel notify = new KeyLifetimeActionModel(); + notify.setTrigger(new KeyLifetimeActionTriggerModel(timeBeforeExpiry, null)); + notify.setAction(new KeyLifetimeActionTypeModel(LifetimeActionType.NOTIFY)); + return notify; + } + + private KeyRotationPolicyAttributes attributes(final Period expiryTime) { + final KeyRotationPolicyAttributes attributes = new KeyRotationPolicyAttributes(); + attributes.setCreatedOn(TIME_10_MINUTES_AGO); + attributes.setUpdatedOn(NOW); + attributes.setExpiryTime(expiryTime); + return attributes; + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerDeserializerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerDeserializerIntegrationTest.java index 6d9baee5..7ae5637e 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerDeserializerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerDeserializerIntegrationTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.security.KeyPair; +import java.util.List; import java.util.Map; import static com.github.nagyesta.lowkeyvault.TestConstants.*; @@ -74,7 +75,7 @@ void testSerializeShouldConvertContentWhenCalledWithValidValue() throws IOExcept private KeyBackupModel getKeyBackupModel(final KeyBackupListItem item) { final KeyBackupList list = new KeyBackupList(); - list.add(item); + list.setVersions(List.of(item)); final KeyBackupModel input = new KeyBackupModel(); input.setValue(list); return input; diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretSerializerDeserializerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretSerializerDeserializerIntegrationTest.java index c02d0a20..3a4ed1ca 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretSerializerDeserializerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretSerializerDeserializerIntegrationTest.java @@ -15,6 +15,7 @@ import org.springframework.util.MimeTypeUtils; import java.io.IOException; +import java.util.List; import java.util.Map; import static com.github.nagyesta.lowkeyvault.TestConstants.*; @@ -68,7 +69,7 @@ void testSerializeShouldConvertContentWhenCalledWithValidValue() throws IOExcept private SecretBackupModel getSecretBackupModel(final SecretBackupListItem item) { final SecretBackupList list = new SecretBackupList(); - list.add(item); + list.setVersions(List.of(item)); final SecretBackupModel input = new SecretBackupModel(); input.setValue(list); return input; diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializerTest.java similarity index 87% rename from lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializerTest.java rename to lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializerTest.java index 10a494d9..e577752f 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeyDeserializerTest.java @@ -11,14 +11,14 @@ import static org.mockito.Mockito.*; -class Base64ZipKeyDeserializerTest { +class Base64ZipV72KeyDeserializerTest { @Test void testDeserializeShouldThrowExceptionWhenDecodingFails() throws IOException { //given final Base64Deserializer base64Deserializer = mock(Base64Deserializer.class); final ObjectMapper objectMapper = mock(ObjectMapper.class); - final Base64ZipKeyDeserializer underTest = new Base64ZipKeyDeserializer(base64Deserializer, objectMapper); + final Base64ZipV72KeyDeserializer underTest = new Base64ZipV72KeyDeserializer(base64Deserializer, objectMapper); final JsonParser jsonParser = mock(JsonParser.class); final DeserializationContext context = mock(DeserializationContext.class); when(base64Deserializer.deserializeBase64(eq(jsonParser))).thenReturn(new byte[1]); @@ -34,7 +34,7 @@ void testDeserializeShouldThrowExceptionWhenDecodingFails() throws IOException { @Test void testDeserializeShouldWriteNullWhenCalledWithNullInput() throws IOException { //given - final Base64ZipKeyDeserializer underTest = new Base64ZipKeyDeserializer(); + final Base64ZipV72KeyDeserializer underTest = new Base64ZipV72KeyDeserializer(); final JsonParser jsonParser = mock(JsonParser.class); when(jsonParser.readValueAs(eq(String.class))).thenReturn(""); final DeserializationContext context = mock(DeserializationContext.class); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializerTest.java similarity index 87% rename from lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerTest.java rename to lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializerTest.java index ed93b6bb..2b90977a 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV72KeySerializerTest.java @@ -11,14 +11,14 @@ import static org.mockito.Mockito.*; -class Base64ZipKeySerializerTest { +class Base64ZipV72KeySerializerTest { @Test void testSerializeShouldThrowExceptionWhenEncodingFails() { //given final Base64Serializer base64Serializer = mock(Base64Serializer.class); final ObjectMapper objectMapper = new ObjectMapper(); - final Base64ZipKeySerializer underTest = new Base64ZipKeySerializer(base64Serializer, objectMapper); + final Base64ZipV72KeySerializer underTest = new Base64ZipV72KeySerializer(base64Serializer, objectMapper); final JsonGenerator gen = mock(JsonGenerator.class); final SerializerProvider serializers = mock(SerializerProvider.class); when(base64Serializer.base64Encode(any())).thenThrow(new IllegalStateException("Fail")); @@ -34,7 +34,7 @@ void testSerializeShouldThrowExceptionWhenEncodingFails() { @Test void testSerializeShouldWriteNullWhenCalledWithNullInput() throws IOException { //given - final Base64ZipKeySerializer underTest = new Base64ZipKeySerializer(); + final Base64ZipV72KeySerializer underTest = new Base64ZipV72KeySerializer(); final JsonGenerator gen = mock(JsonGenerator.class); final SerializerProvider serializers = mock(SerializerProvider.class); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializerTest.java new file mode 100644 index 00000000..603ec4bf --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeyDeserializerTest.java @@ -0,0 +1,61 @@ +package com.github.nagyesta.lowkeyvault.model.json.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupList; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.mockito.Mockito.*; + +class Base64ZipV73KeyDeserializerTest { + + @Test + void testDeserializeShouldThrowExceptionWhenDecodingFails() throws IOException { + //given + final Base64Deserializer base64Deserializer = mock(Base64Deserializer.class); + final ObjectMapper objectMapper = mock(ObjectMapper.class); + final Base64ZipV73KeyDeserializer underTest = new Base64ZipV73KeyDeserializer(base64Deserializer, objectMapper); + final JsonParser jsonParser = mock(JsonParser.class); + final DeserializationContext context = mock(DeserializationContext.class); + when(base64Deserializer.deserializeBase64(eq(jsonParser))).thenReturn(new byte[1]); + when(objectMapper.reader()).thenThrow(new IllegalStateException("Fail")); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.deserialize(jsonParser, context)); + + //then + exception + verify(base64Deserializer).deserializeBase64(eq(jsonParser)); + } + + @Test + void testDeserializeShouldWriteNullWhenCalledWithNullInput() throws IOException { + //given + final Base64ZipV73KeyDeserializer underTest = new Base64ZipV73KeyDeserializer(); + final JsonParser jsonParser = mock(JsonParser.class); + when(jsonParser.readValueAs(eq(String.class))).thenReturn(""); + final DeserializationContext context = mock(DeserializationContext.class); + + //when + final KeyBackupList actual = underTest.deserialize(jsonParser, context); + + //then + Assertions.assertNull(actual); + verify(jsonParser).readValueAs(eq(String.class)); + } + + @Test + void testGetTypeShouldReturnCorrectTypeWhenCalled() { + //given + final Base64ZipV73KeyDeserializer underTest = new Base64ZipV73KeyDeserializer(); + + //when + final Class actual = underTest.getType(); + + //then + Assertions.assertEquals(KeyBackupList.class, actual); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializerTest.java new file mode 100644 index 00000000..1b3a3132 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipV73KeySerializerTest.java @@ -0,0 +1,48 @@ +package com.github.nagyesta.lowkeyvault.model.json.util; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyBackupList; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.mockito.Mockito.*; + +class Base64ZipV73KeySerializerTest { + + @Test + void testSerializeShouldThrowExceptionWhenEncodingFails() { + //given + final Base64Serializer base64Serializer = mock(Base64Serializer.class); + final ObjectMapper objectMapper = new ObjectMapper(); + final Base64ZipV73KeySerializer underTest = new Base64ZipV73KeySerializer(base64Serializer, objectMapper); + final JsonGenerator gen = mock(JsonGenerator.class); + final SerializerProvider serializers = mock(SerializerProvider.class); + when(base64Serializer.base64Encode(any())).thenThrow(new IllegalStateException("Fail")); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.serialize(new KeyBackupList(), gen, serializers)); + + //then + exception + verify(base64Serializer).base64Encode(any()); + verifyNoInteractions(gen, serializers); + } + + @Test + void testSerializeShouldWriteNullWhenCalledWithNullInput() throws IOException { + //given + final Base64ZipV73KeySerializer underTest = new Base64ZipV73KeySerializer(); + final JsonGenerator gen = mock(JsonGenerator.class); + final SerializerProvider serializers = mock(SerializerProvider.class); + + //when + underTest.serialize(null, gen, serializers); + + //then + verify(gen).writeNull(); + verify(gen, never()).writeString(anyString()); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModelTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModelTest.java new file mode 100644 index 00000000..8c87f5b8 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTriggerModelTest.java @@ -0,0 +1,68 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Period; + +class KeyLifetimeActionTriggerModelTest { + + @Test + void testJsonConstructorShouldThrowExceptionWhenBothInputsAreNull() { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> + new KeyLifetimeActionTriggerModel(null, null)); + + //then + exception + } + + @Test + void testJsonConstructorShouldThrowExceptionWhenBothInputsArePresent() { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> + new KeyLifetimeActionTriggerModel(Period.ZERO, Period.ZERO)); + + //then + exception + } + + @Test + void testJsonConstructorShouldSetBeforeExpiryWhenItIsPopulated() { + //given + + //when + final KeyLifetimeActionTriggerModel actual = new KeyLifetimeActionTriggerModel(Period.ZERO, null); + + //then + Assertions.assertEquals(Period.ZERO, actual.getTriggerPeriod()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, actual.getTriggerType()); + } + + @Test + void testJsonConstructorShouldSetAfterCreateWhenItIsPopulated() { + //given + + //when + final KeyLifetimeActionTriggerModel actual = new KeyLifetimeActionTriggerModel(null, Period.ZERO); + + //then + Assertions.assertEquals(Period.ZERO, actual.getTriggerPeriod()); + Assertions.assertEquals(LifetimeActionTriggerType.TIME_AFTER_CREATE, actual.getTriggerType()); + } + + @SuppressWarnings("ConstantConditions") + @Test + void testConstructorShouldThrowExceptionWhenCalledWithNull() { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> + new KeyLifetimeActionTriggerModel(null)); + + //then + exception + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModelTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModelTest.java new file mode 100644 index 00000000..db0a9ffb --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModelTest.java @@ -0,0 +1,31 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class KeyLifetimeActionTypeModelTest { + + @SuppressWarnings("ConstantConditions") + @Test + void testConstructorShouldThrowExceptionWhenCalledWithNull() { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> new KeyLifetimeActionTypeModel(null)); + + //then + exception + } + + @Test + void testConstructorShouldSetActionTypeWhenCalledWithValidValue() { + //given + final LifetimeActionType expected = LifetimeActionType.NOTIFY; + + //when + final KeyLifetimeActionTypeModel actual = new KeyLifetimeActionTypeModel(expected); + + //then + Assertions.assertEquals(expected, actual.getType()); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModelIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModelIntegrationTest.java new file mode 100644 index 00000000..a3e64c5e --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModelIntegrationTest.java @@ -0,0 +1,165 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.nagyesta.lowkeyvault.ResourceUtils; +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyLifetimeActionTrigger; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import java.io.IOException; +import java.net.URI; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +@SpringBootTest +@TestPropertySource(locations = "classpath:test-application.properties") +class KeyRotationPolicyModelIntegrationTest { + + private static final KeyLifetimeActionTrigger TRIGGER_90_DAYS_AFTER_CREATION = + new KeyLifetimeActionTrigger(Period.ofDays(90), LifetimeActionTriggerType.TIME_AFTER_CREATE); + private static final KeyLifetimeActionModel ROTATE_ACTION = + new KeyLifetimeActionModel( + new KeyLifetimeActionTypeModel(LifetimeActionType.ROTATE), + new KeyLifetimeActionTriggerModel(TRIGGER_90_DAYS_AFTER_CREATION)); + private static final KeyLifetimeActionTrigger TRIGGER_30_DAYS_BEFORE_EXPIRY = + new KeyLifetimeActionTrigger(Period.ofDays(30), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + private static final KeyLifetimeActionModel NOTIFY_ACTION = + new KeyLifetimeActionModel( + new KeyLifetimeActionTypeModel(LifetimeActionType.NOTIFY), + new KeyLifetimeActionTriggerModel(TRIGGER_30_DAYS_BEFORE_EXPIRY)); + private static final String POLICY_URI_STRING = "https:/localhost:8443/keys/key-name/rotationpolicy"; + private static final String MINIMUM_JSON = "/key/rotation/valid-rotation-policy-minimum.json"; + private static final String FULL_JSON = "/key/rotation/valid-rotation-policy-full.json"; + private static final Period EXPIRY_PERIOD_4M = Period.ofMonths(4); + private static final OffsetDateTime CREATED_ON = OffsetDateTime.ofInstant(Instant.ofEpochSecond(1482188947), ZoneOffset.UTC); + private static final OffsetDateTime UPDATED_ON = OffsetDateTime.ofInstant(Instant.ofEpochSecond(1482188948), ZoneOffset.UTC); + @Autowired + private Validator validator; + @Autowired + private ObjectMapper objectMapper; + + public static Stream invalidProvider() { + return Stream.builder() + .add(Arguments.of("/key/rotation/invalid-rotation-policy-empty-actions.json", "lifetimeActions")) + .add(Arguments.of("/key/rotation/invalid-rotation-policy-missing-attributes.json", "attributes")) + .add(Arguments.of("/key/rotation/invalid-rotation-policy-missing-expiry.json", "attributes.expiryTime")) + .add(Arguments.of("/key/rotation/invalid-rotation-policy-missing-actions.json", "lifetimeActions")) + .add(Arguments.of("/key/rotation/invalid-rotation-policy-null-action-type.json", "lifetimeActions[0].action.type")) + .add(Arguments.of("/key/rotation/invalid-rotation-policy-null-trigger.json", "lifetimeActions[0].trigger")) + .build(); + } + + @Test + void testJsonSerializationShouldContainAllValuableFieldsWhenCalledOnFullyPopulatedObject() throws JsonProcessingException { + //given + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(URI.create(POLICY_URI_STRING)); + model.setLifetimeActions(List.of(ROTATE_ACTION, NOTIFY_ACTION)); + model.setAttributes(policyAttributes(EXPIRY_PERIOD_4M)); + final String expected = readResourceAsStringRemoveWhitespace(FULL_JSON); + + //when + final String actual = objectMapper.writer().writeValueAsString(model); + + //then + Assertions.assertEquals(expected, actual); + } + + @Test + void testJsonSerializationShouldContainAllValuableFieldsWhenCalledOnMinimalObject() throws JsonProcessingException { + //given + final KeyRotationPolicyModel model = new KeyRotationPolicyModel(); + model.setId(URI.create(POLICY_URI_STRING)); + model.setLifetimeActions(List.of(ROTATE_ACTION)); + model.setAttributes(policyAttributes(null)); + final String expected = readResourceAsStringRemoveWhitespace(MINIMUM_JSON); + + //when + final String actual = objectMapper.writer().writeValueAsString(model); + + //then + Assertions.assertEquals(expected, actual); + } + + @Test + void testJsonDeserializationShouldRestoreAllValuableFieldsWhenCalledWithFullyPopulatedJson() throws IOException { + //given + + //when + final KeyRotationPolicyModel actual = loadResourceAsObject(FULL_JSON); + + //then + Assertions.assertEquals(POLICY_URI_STRING, actual.getId().toString()); + Assertions.assertEquals(EXPIRY_PERIOD_4M, actual.getAttributes().getExpiryTime()); + Assertions.assertEquals(CREATED_ON, actual.getAttributes().getCreatedOn()); + Assertions.assertEquals(UPDATED_ON, actual.getAttributes().getUpdatedOn()); + Assertions.assertIterableEquals(List.of(ROTATE_ACTION, NOTIFY_ACTION), actual.getLifetimeActions()); + } + + + @Test + void testJsonDeserializationShouldRestoreAllValuableFieldsWhenCalledWithMinimalJson() throws IOException { + //given + + //when + final KeyRotationPolicyModel actual = loadResourceAsObject(MINIMUM_JSON); + + //then + Assertions.assertEquals(POLICY_URI_STRING, actual.getId().toString()); + Assertions.assertNull(actual.getAttributes().getExpiryTime()); + Assertions.assertEquals(CREATED_ON, actual.getAttributes().getCreatedOn()); + Assertions.assertEquals(UPDATED_ON, actual.getAttributes().getUpdatedOn()); + Assertions.assertIterableEquals(List.of(ROTATE_ACTION), actual.getLifetimeActions()); + } + + @ParameterizedTest + @MethodSource("invalidProvider") + void testValidateShouldReportViolationsWhenCalledWithInvalidData( + final String resource, final String expectedPath) throws IOException { + //given + final KeyRotationPolicyModel underTest = loadResourceAsObject(resource); + + //when + final Set> violations = validator.validate(underTest); + + //then + Assertions.assertEquals(1, violations.size()); + Assertions.assertEquals(expectedPath, violations.iterator().next().getPropertyPath().toString()); + } + + private KeyRotationPolicyAttributes policyAttributes( + final Period expiryTime) { + final KeyRotationPolicyAttributes attributes = new KeyRotationPolicyAttributes(); + attributes.setExpiryTime(expiryTime); + attributes.setCreatedOn(CREATED_ON); + attributes.setUpdatedOn(UPDATED_ON); + return attributes; + } + + private KeyRotationPolicyModel loadResourceAsObject(final String resource) throws IOException { + final String json = ResourceUtils.loadResourceAsString(resource); + return objectMapper.reader().readValue(json, KeyRotationPolicyModel.class); + } + + private String readResourceAsStringRemoveWhitespace(final String resource) { + final String json = ResourceUtils.loadResourceAsString(resource); + return Objects.requireNonNull(json).replaceAll("[ \\n]+", ""); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionTypeTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionTypeTest.java new file mode 100644 index 00000000..f2378e80 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionTypeTest.java @@ -0,0 +1,37 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key.constants; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +class LifetimeActionTypeTest { + + public static Stream valueProvider() { + final List list = new ArrayList<>(); + list.add(null); + list.addAll(Arrays.asList(LifetimeActionType.values())); + return list.stream() + .map(value -> Arguments.of( + Optional.ofNullable(value).map(LifetimeActionType::getValue).orElse("unknown"), + value)); + } + + @ParameterizedTest + @MethodSource("valueProvider") + void testForValueShouldReturnEnumWhenValueStringMatches(final String input, final LifetimeActionType expected) { + //given + + //when + final LifetimeActionType actual = LifetimeActionType.forValue(input); + + //then + Assertions.assertEquals(expected, actual); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidatorTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidatorTest.java new file mode 100644 index 00000000..a66b5bc0 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidatorTest.java @@ -0,0 +1,51 @@ +package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import javax.validation.ConstraintValidatorContext; +import java.time.Period; +import java.util.stream.Stream; + +import static org.mockito.Mockito.mock; + +class ExpiryPeriodValidatorTest { + + @ExpiryPeriod + private Period dummy; + + public static Stream isValidProvider() { + return Stream.builder() + .add(Arguments.of(Period.parse("-P1D"), false)) + .add(Arguments.of(Period.parse("P1D"), false)) + .add(Arguments.of(Period.parse("P5D"), false)) + .add(Arguments.of(Period.parse("P10D"), false)) + .add(Arguments.of(Period.parse("P26D"), false)) + .add(Arguments.of(Period.parse("P27D"), false)) + .add(Arguments.of(Period.parse("P28D"), true)) + .add(Arguments.of(Period.parse("P29D"), true)) + .add(Arguments.of(Period.parse("P1M"), true)) + .add(Arguments.of(Period.parse("P2M"), true)) + .add(Arguments.of(Period.parse("P1Y2M"), true)) + .add(Arguments.of(null, true)) + .build(); + } + + @ParameterizedTest + @MethodSource("isValidProvider") + void testIsValidShouldReturnTrueOnlyWhenCalledWithValidData(final Period input, final boolean expected) throws NoSuchFieldException { + //given + final ExpiryPeriodValidator underTest = new ExpiryPeriodValidator(); + final ExpiryPeriod annotation = this.getClass().getDeclaredField("dummy").getAnnotation(ExpiryPeriod.class); + + //when + underTest.initialize(annotation); + final boolean actual = underTest.isValid(input, mock(ConstraintValidatorContext.class)); + + //then + Assertions.assertEquals(expected, actual); + } + +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/KeyLifetimeActionTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/KeyLifetimeActionTest.java new file mode 100644 index 00000000..6f6d1e0a --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/KeyLifetimeActionTest.java @@ -0,0 +1,49 @@ +package com.github.nagyesta.lowkeyvault.service.key; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyLifetimeActionTrigger; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.Period; +import java.util.stream.Stream; + +class KeyLifetimeActionTest { + + public static Stream invalidProvider() { + return Stream.builder() + .add(Arguments.of(null, null)) + .add(Arguments.of(null, new KeyLifetimeActionTrigger(Period.ZERO, LifetimeActionTriggerType.TIME_BEFORE_EXPIRY))) + .add(Arguments.of(LifetimeActionType.NOTIFY, null)) + .build(); + } + + @ParameterizedTest + @MethodSource("invalidProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNulls(final LifetimeActionType type, final LifetimeActionTrigger trigger) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> new KeyLifetimeAction(type, trigger)); + + //then + exception + } + + @Test + void testConstructorShouldSetValuesWhenCalledWithValidInput() { + //given + final LifetimeActionType type = LifetimeActionType.NOTIFY; + final LifetimeActionTrigger trigger = new KeyLifetimeActionTrigger(Period.ZERO, LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + + //when + final KeyLifetimeAction actual = new KeyLifetimeAction(type, trigger); + + //then + Assertions.assertEquals(trigger, actual.getTrigger()); + Assertions.assertEquals(type, actual.getActionType()); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerTypeTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerTypeTest.java new file mode 100644 index 00000000..028d74ae --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/constants/LifetimeActionTriggerTypeTest.java @@ -0,0 +1,132 @@ +package com.github.nagyesta.lowkeyvault.service.key.constants; + +import com.github.nagyesta.lowkeyvault.TestConstants; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType.TIME_AFTER_CREATE; +import static com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType.TIME_BEFORE_EXPIRY; + +class LifetimeActionTriggerTypeTest { + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream validationDataProvider() { + return Stream.builder() + .add(Arguments.of(TIME_BEFORE_EXPIRY, null, 30, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.TIME_10_MINUTES_AGO, 30, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 30, 5, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 30, 6, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 30, 8, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 1, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 27, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 28, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, null, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.NOW, 28, null, false)) + .add(Arguments.of(TIME_AFTER_CREATE, null, 30, 7, true)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.TIME_10_MINUTES_AGO, 30, 23, true)) + .add(Arguments.of(TIME_AFTER_CREATE, null, 30, 25, false)) + .add(Arguments.of(TIME_AFTER_CREATE, null, 30, 24, false)) + .add(Arguments.of(TIME_AFTER_CREATE, null, 30, 22, true)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 1, 20, false)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 27, 20, false)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 28, 20, true)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 28, 21, true)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 28, 22, false)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, null, 21, false)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.NOW, 28, null, false)) + .build(); + } + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream shouldTriggerValidDataProvider() { + return Stream.builder() + .add(Arguments.of(TIME_BEFORE_EXPIRY, 50, 44, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 50, 43, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 50, 42, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 50, 41, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 28, 22, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 28, 21, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 28, 20, 7, false)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 10, 3, 7, true)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, 10, 3, 6, false)) + .add(Arguments.of(TIME_AFTER_CREATE, 50, 44, 43, true)) + .add(Arguments.of(TIME_AFTER_CREATE, 50, 43, 43, true)) + .add(Arguments.of(TIME_AFTER_CREATE, 50, 42, 43, false)) + .add(Arguments.of(TIME_AFTER_CREATE, 50, 41, 43, false)) + .add(Arguments.of(TIME_AFTER_CREATE, 28, 22, 21, true)) + .add(Arguments.of(TIME_AFTER_CREATE, 28, 21, 21, true)) + .add(Arguments.of(TIME_AFTER_CREATE, 28, 20, 21, false)) + .add(Arguments.of(TIME_AFTER_CREATE, 10, 3, 3, true)) + .add(Arguments.of(TIME_AFTER_CREATE, 10, 3, 4, false)) + .build(); + } + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream shouldTriggerInvalidDataProvider() { + return Stream.builder() + .add(Arguments.of(TIME_BEFORE_EXPIRY, null, null, null)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.TIME_10_MINUTES_AGO, null, 0)) + .add(Arguments.of(TIME_BEFORE_EXPIRY, TestConstants.TIME_10_MINUTES_AGO, TestConstants.NOW, null)) + .add(Arguments.of(TIME_AFTER_CREATE, null, null, null)) + .add(Arguments.of(TIME_AFTER_CREATE, TestConstants.TIME_10_MINUTES_AGO, null, null)) + .add(Arguments.of(TIME_AFTER_CREATE, null, TestConstants.NOW, 0)) + .build(); + } + + @ParameterizedTest + @MethodSource("validationDataProvider") + void testValidateShouldThrowExceptionWhenInputIsInvalid( + final LifetimeActionTriggerType underTest, final OffsetDateTime expires, + final Integer expiryDays, final Integer triggerDays, final boolean valid) { + //given + final Period expiryPeriod = Optional.ofNullable(expiryDays).map(Period::ofDays).orElse(null); + final Period triggerPeriod = Optional.ofNullable(triggerDays).map(Period::ofDays).orElse(null); + + //when + if (valid) { + Assertions.assertDoesNotThrow(() -> underTest.validate(expires, expiryPeriod, triggerPeriod)); + } else { + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.validate(expires, expiryPeriod, triggerPeriod)); + } + + //then + exception if not valid + } + + @ParameterizedTest + @MethodSource("shouldTriggerValidDataProvider") + void testShouldTriggerShouldReturnTrueWhenCalledInsideTheTriggerPeriod( + final LifetimeActionTriggerType underTest, final int expiryAfterCreateDays, + final int createOffsetDays, final int triggerDays, final boolean expected) { + //given + final OffsetDateTime createTime = TestConstants.NOW.minusDays(createOffsetDays); + final OffsetDateTime expiryTime = createTime.plusDays(expiryAfterCreateDays); + final Period triggerPeriod = Period.ofDays(triggerDays); + + //when + final boolean actual = underTest.shouldTrigger(createTime, expiryTime, triggerPeriod); + + //then + Assertions.assertEquals(expected, actual); + } + + @ParameterizedTest + @MethodSource("shouldTriggerInvalidDataProvider") + void testShouldTriggerShouldThrowExceptionWhenCalledWithInvalidData( + final LifetimeActionTriggerType underTest, final OffsetDateTime createTime, + final OffsetDateTime expiryTime, final Integer triggerDays) { + //given + final Period triggerPeriod = Optional.ofNullable(triggerDays).map(Period::ofDays).orElse(null); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.shouldTrigger(createTime, expiryTime, triggerPeriod)); + + //then + exception + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityIdTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityIdTest.java new file mode 100644 index 00000000..bd35e711 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityIdTest.java @@ -0,0 +1,22 @@ +package com.github.nagyesta.lowkeyvault.service.key.id; + +import com.github.nagyesta.lowkeyvault.TestConstantsKeys; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.net.URI; + +class KeyEntityIdTest { + + @Test + void testAsRotationPolicyUriShouldReturnRotationPolicyUriWhenCalled() { + //given + final KeyEntityId underTest = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + + //when + final URI actual = underTest.asRotationPolicyUri(); + + //then + Assertions.assertEquals(underTest.asUri("rotationpolicy"), actual); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntityTest.java index 664e1166..303b9274 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/AesKeyVaultKeyEntityTest.java @@ -244,4 +244,21 @@ void testVerifyShouldThrowExceptionWhenCalled() { //then + exception } + + @Test + void testKeyCreationInputShouldReturnOriginalParameters() { + //given + final VaultFake vaultFake = new VaultFakeImpl(HTTPS_LOWKEY_VAULT); + final int keySize = KeyType.OCT.getValidKeyParameters(Integer.class).first(); + final AesKeyVaultKeyEntity underTest = new AesKeyVaultKeyEntity( + VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keySize, false); + + //when + final KeyCreationInput actual = underTest.keyCreationInput(); + + //then + Assertions.assertInstanceOf(OctKeyCreationInput.class, actual); + final OctKeyCreationInput value = (OctKeyCreationInput) actual; + Assertions.assertEquals(keySize, value.getKeyParameter()); + } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java index 9a8e073d..7f7ddc72 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyVaultKeyEntityTest.java @@ -265,6 +265,23 @@ void testVerifyShouldThrowExceptionWhenWhenDigestSizeIsNotCompatible(final byte[ //then + exception } + @Test + void testKeyCreationInputShouldReturnOriginalParameters() { + //given + final VaultFake vaultFake = new VaultFakeImpl(HTTPS_LOWKEY_VAULT); + final KeyCurveName keyCurveName = KeyCurveName.P_384; + final EcKeyVaultKeyEntity underTest = new EcKeyVaultKeyEntity( + VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keyCurveName, false); + + //when + final KeyCreationInput actual = underTest.keyCreationInput(); + + //then + Assertions.assertInstanceOf(EcKeyCreationInput.class, actual); + final EcKeyCreationInput value = (EcKeyCreationInput) actual; + Assertions.assertEquals(keyCurveName, value.getKeyParameter()); + } + private byte[] hash(final byte[] text, final SignatureAlgorithm algorithm) { try { final MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHMS.get(algorithm)); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTriggerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTriggerTest.java new file mode 100644 index 00000000..d7dbe9cf --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTriggerTest.java @@ -0,0 +1,71 @@ +package com.github.nagyesta.lowkeyvault.service.key.impl; + +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.TestConstants.NOW; +import static com.github.nagyesta.lowkeyvault.TestConstants.TIME_IN_10_MINUTES; +import static com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType.*; + +class KeyLifetimeActionTriggerTest { + + public static final OffsetDateTime A_WEEK_AGO = NOW.minusDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY); + public static final OffsetDateTime IN_A_WEEK = NOW.plusDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY); + public static final OffsetDateTime IN_A_WEEK_AND_10_MINUTES = TIME_IN_10_MINUTES.plusDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY); + public static final Period PERIOD_A_WEEK = Period.ofDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY); + public static final Period PERIOD_28_DAYS = Period.ofDays(MINIMUM_EXPIRY_PERIOD_IN_DAYS); + + public static Stream invalidProvider() { + return Stream.builder() + .add(Arguments.of(null, null)) + .add(Arguments.of(null, LifetimeActionTriggerType.TIME_BEFORE_EXPIRY)) + .add(Arguments.of(Period.ZERO, null)) + .build(); + } + + + public static Stream validProvider() { + return Stream.builder() + .add(Arguments.of(PERIOD_A_WEEK, TIME_AFTER_CREATE, A_WEEK_AGO, null, true)) + .add(Arguments.of(PERIOD_28_DAYS, TIME_AFTER_CREATE, A_WEEK_AGO, null, false)) + .add(Arguments.of(PERIOD_28_DAYS, TIME_AFTER_CREATE, A_WEEK_AGO, NOW, false)) + .add(Arguments.of(PERIOD_A_WEEK, TIME_BEFORE_EXPIRY, NOW, IN_A_WEEK, true)) + .add(Arguments.of(PERIOD_A_WEEK, TIME_BEFORE_EXPIRY, NOW, IN_A_WEEK_AND_10_MINUTES, false)) + .add(Arguments.of(PERIOD_28_DAYS, TIME_BEFORE_EXPIRY, NOW.minusYears(1), NOW.plusYears(1), false)) + .add(Arguments.of(PERIOD_28_DAYS, TIME_BEFORE_EXPIRY, NOW.minusYears(1), NOW.plusDays(1), true)) + .build(); + } + + @ParameterizedTest + @MethodSource("invalidProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNulls(final Period period, final LifetimeActionTriggerType type) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> new KeyLifetimeActionTrigger(period, type)); + + //then + exception + } + + @ParameterizedTest + @MethodSource("validProvider") + void shouldTrigger(final Period period, final LifetimeActionTriggerType type, + final OffsetDateTime created, final OffsetDateTime expiry, + final boolean expected) { + //given + final KeyLifetimeActionTrigger underTest = new KeyLifetimeActionTrigger(period, type); + + //when + final boolean actual = underTest.shouldTrigger(created, expiry); + + //then + Assertions.assertEquals(expected, actual); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicyTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicyTest.java new file mode 100644 index 00000000..a8b38a42 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyRotationPolicyTest.java @@ -0,0 +1,305 @@ +package com.github.nagyesta.lowkeyvault.service.key.impl; + +import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; +import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.LifetimeAction; +import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; +import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.time.OffsetDateTime; +import java.time.Period; +import java.util.Map; +import java.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.TestConstants.*; +import static com.github.nagyesta.lowkeyvault.TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; + +class KeyRotationPolicyTest { + + private static final String DAYS_42 = "P42D"; + private static final String DAYS_100 = "P100D"; + private static final String DAYS_7 = "P7D"; + private static final KeyLifetimeActionTrigger TRIGGER_7_DAYS_BEFORE_EXPIRY = + new KeyLifetimeActionTrigger(Period.parse(DAYS_7), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + private static final KeyLifetimeActionTrigger TRIGGER_42_DAYS_BEFORE_EXPIRY = + new KeyLifetimeActionTrigger(Period.parse(DAYS_42), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); + private static final KeyLifetimeAction NOTIFY_7_DAYS_BEFORE_EXPIRY = + new KeyLifetimeAction(LifetimeActionType.NOTIFY, TRIGGER_7_DAYS_BEFORE_EXPIRY); + private static final KeyLifetimeAction NOTIFY_42_DAYS_BEFORE_EXPIRY = + new KeyLifetimeAction(LifetimeActionType.NOTIFY, TRIGGER_42_DAYS_BEFORE_EXPIRY); + + private static final KeyLifetimeActionTrigger TRIGGER_42_DAYS_AFTER_CREATE = + new KeyLifetimeActionTrigger(Period.parse(DAYS_42), LifetimeActionTriggerType.TIME_AFTER_CREATE); + private static final KeyLifetimeAction ROTATE_42_DAYS_AFTER_CREATE = + new KeyLifetimeAction(LifetimeActionType.ROTATE, TRIGGER_42_DAYS_AFTER_CREATE); + private static final int OFFSET_SECONDS_10_MINUTES = 600; + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream invalidDataProvider() { + return Stream.builder() + .add(Arguments.of(OffsetDateTime.now().plusDays(10), Period.ofDays(120), + Period.ofDays(30), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, + Period.ofDays(115), LifetimeActionTriggerType.TIME_AFTER_CREATE, false)) + .add(Arguments.of(OffsetDateTime.now().plusDays(10), Period.ofDays(20), + Period.ofDays(7), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, + Period.ofDays(13), LifetimeActionTriggerType.TIME_AFTER_CREATE, false)) + .add(Arguments.of(OffsetDateTime.now().plusDays(10), Period.ofDays(120), + Period.ofDays(7), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, + Period.ofDays(13), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY, true)) + .add(Arguments.of(OffsetDateTime.now().plusDays(10), Period.ofDays(120), + Period.ofDays(30), LifetimeActionTriggerType.TIME_AFTER_CREATE, + Period.ofDays(100), LifetimeActionTriggerType.TIME_AFTER_CREATE, false)) + .build(); + } + + public static Stream invalidProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null)) + .add(Arguments.of(UNVERSIONED_KEY_ENTITY_ID_1, null, null)) + .add(Arguments.of(null, Period.ZERO, null)) + .add(Arguments.of(null, null, Map.of())) + .add(Arguments.of(null, Period.ZERO, Map.of())) + .add(Arguments.of(UNVERSIONED_KEY_ENTITY_ID_1, null, Map.of())) + .add(Arguments.of(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, null)) + .build(); + } + + @ParameterizedTest + @MethodSource("invalidProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNull(final KeyEntityId keyEntityId, + final Period period, + final Map actions) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> new KeyRotationPolicy(keyEntityId, period, actions)); + + //then + exception + } + + @Test + void testSetCreatedOnShouldUpdateValueWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + final OffsetDateTime original = underTest.getCreatedOn(); + + //when + underTest.setCreatedOn(TIME_10_MINUTES_AGO); + final OffsetDateTime actual = underTest.getCreatedOn(); + + //then + Assertions.assertNotEquals(original, actual); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actual); + } + + @SuppressWarnings("ConstantConditions") + @Test + void testSetCreatedOnShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setCreatedOn(null)); + + //then + exception + } + + @Test + void testSetUpdatedOnShouldUpdateValueWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + final OffsetDateTime original = underTest.getUpdatedOn(); + + //when + underTest.setUpdatedOn(TIME_10_MINUTES_AGO); + final OffsetDateTime actual = underTest.getUpdatedOn(); + + //then + Assertions.assertNotEquals(original, actual); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actual); + } + + @SuppressWarnings("ConstantConditions") + @Test + void testSetUpdatedOnShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setUpdatedOn(null)); + + //then + exception + } + + @Test + void testSetExpiryTimeShouldUpdateValueWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + final Period original = underTest.getExpiryTime(); + + //when + underTest.setExpiryTime(Period.parse(DAYS_42)); + final Period actual = underTest.getExpiryTime(); + + //then + Assertions.assertEquals(Period.ZERO, original); + Assertions.assertEquals(Period.parse(DAYS_42), actual); + } + + @SuppressWarnings("ConstantConditions") + @Test + void testSetExpiryTimeShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setExpiryTime(null)); + + //then + exception + } + + @Test + void testSetExpiryTimeShouldUpdateUpdatedOnWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + underTest.setUpdatedOn(TIME_10_MINUTES_AGO); + + //when + underTest.setExpiryTime(Period.parse(DAYS_42)); + final OffsetDateTime actual = underTest.getUpdatedOn(); + + //then + Assertions.assertTrue(actual.isAfter(TIME_10_MINUTES_AGO)); + } + + @Test + void testSetLifetimeActionsShouldUpdateValueWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + final Map original = underTest.getLifetimeActions(); + final Map map = Map.of(LifetimeActionType.NOTIFY, NOTIFY_42_DAYS_BEFORE_EXPIRY); + + //when + underTest.setLifetimeActions(map); + final Map actual = underTest.getLifetimeActions(); + + //then + Assertions.assertTrue(original.isEmpty()); + Assertions.assertIterableEquals(map.entrySet(), actual.entrySet()); + } + + @Test + void testSetLifetimeActionsShouldUpdateValueWhenNotifyIsReplacedWithAnotherNotify() { + //given + final Map map = Map.of(LifetimeActionType.NOTIFY, NOTIFY_42_DAYS_BEFORE_EXPIRY); + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.parse(DAYS_100), map); + final Map original = underTest.getLifetimeActions(); + final Map newValue = Map.of( + LifetimeActionType.NOTIFY, NOTIFY_7_DAYS_BEFORE_EXPIRY, + LifetimeActionType.ROTATE, ROTATE_42_DAYS_AFTER_CREATE); + + //when + underTest.setLifetimeActions(newValue); + final Map actual = underTest.getLifetimeActions(); + + //then + Assertions.assertIterableEquals(map.entrySet(), original.entrySet()); + Assertions.assertIterableEquals(newValue.entrySet(), actual.entrySet()); + } + + @Test + void testSetLifetimeActionsShouldThrowExceptionWhenNotifyIsBeingRemoved() { + //given + final Map map = Map.of(LifetimeActionType.NOTIFY, NOTIFY_42_DAYS_BEFORE_EXPIRY); + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, map); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setLifetimeActions(Map.of())); + + //then + exception + } + + @SuppressWarnings("ConstantConditions") + @Test + void testSetLifetimeActionsShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setLifetimeActions(null)); + + //then + exception + } + + @Test + void testSetLifetimeActionsShouldUpdateUpdatedOnWhenCalledWithValidInput() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + underTest.setUpdatedOn(TIME_10_MINUTES_AGO); + + //when + underTest.setLifetimeActions(Map.of()); + final OffsetDateTime actual = underTest.getUpdatedOn(); + + //then + Assertions.assertTrue(actual.isAfter(TIME_10_MINUTES_AGO)); + } + + @Test + void testTimeShiftShouldAdjustCreatedOnAndUpdatedOnWhenCalledWithValidData() { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + underTest.setCreatedOn(NOW); + underTest.setUpdatedOn(TIME_IN_10_MINUTES); + + //when + underTest.timeShift(OFFSET_SECONDS_10_MINUTES); + + //then + Assertions.assertEquals(TIME_10_MINUTES_AGO, underTest.getCreatedOn()); + Assertions.assertEquals(NOW, underTest.getUpdatedOn()); + } + + @ParameterizedTest + @ValueSource(ints = {-42, -2, -1, 0}) + void testTimeShiftShouldThrowExceptionWhenCalledWithInvalidValue(final int value) { + //given + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, Period.ZERO, Map.of()); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.timeShift(value)); + + //then + exception + } + + @ParameterizedTest + @MethodSource("invalidDataProvider") + void testValidateShouldThrowExceptionWhenDataIsInvalid( + final OffsetDateTime expiryTime, final Period expiryPeriod, + final Period notifyPeriod, final LifetimeActionTriggerType notifyTriggerType, + final Period rotatePeriod, final LifetimeActionTriggerType rotateTriggerType, + final boolean valid) { + //given + final KeyLifetimeActionTrigger notifyTrigger = new KeyLifetimeActionTrigger(notifyPeriod, notifyTriggerType); + final KeyLifetimeAction notify = new KeyLifetimeAction(LifetimeActionType.NOTIFY, notifyTrigger); + final KeyLifetimeActionTrigger rotateTrigger = new KeyLifetimeActionTrigger(rotatePeriod, rotateTriggerType); + final KeyLifetimeAction rotate = new KeyLifetimeAction(LifetimeActionType.ROTATE, rotateTrigger); + final KeyRotationPolicy underTest = new KeyRotationPolicy(UNVERSIONED_KEY_ENTITY_ID_1, expiryPeriod, + Map.of(notify.getActionType(), notify, rotate.getActionType(), rotate)); + + //when + if (valid) { + Assertions.assertDoesNotThrow(() -> underTest.validate(expiryTime)); + } else { + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.validate(expiryTime)); + } + + //then + exception + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImplTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImplTest.java index fd191fce..4cfe9a78 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImplTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultFakeImplTest.java @@ -4,10 +4,14 @@ 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_3.key.constants.LifetimeActionType; import com.github.nagyesta.lowkeyvault.service.exception.AlreadyExistsException; import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; +import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; 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.constants.LifetimeActionTriggerType; import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; @@ -22,10 +26,8 @@ import org.junit.jupiter.params.provider.ValueSource; import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.Deque; -import java.util.List; +import java.time.Period; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -33,6 +35,8 @@ import static com.github.nagyesta.lowkeyvault.TestConstants.*; import static com.github.nagyesta.lowkeyvault.TestConstantsKeys.*; import static com.github.nagyesta.lowkeyvault.TestConstantsUri.HTTPS_LOCALHOST; +import static com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS; +import static com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType.MINIMUM_THRESHOLD_BEFORE_EXPIRY; import static org.mockito.Mockito.mock; class KeyVaultFakeImplTest { @@ -605,7 +609,6 @@ void testRecoverShouldThrowExceptionWhenCalledWithNullKey() { } - @Test void testPurgeShouldThrowExceptionWhenCalledWithMissingDeletedKey() { //given @@ -648,7 +651,6 @@ void testPurgeShouldThrowExceptionWhenCalledWithNullKey() { //then + exception } - @SuppressWarnings("checkstyle:MagicNumber") @ParameterizedTest @ValueSource(ints = {-42, -10, -5, -3, -2, -1, 0}) void testTimeShiftShouldThrowExceptionWhenCalledWithNegativeOrZero(final int value) { @@ -670,6 +672,15 @@ void testTimeShiftShouldReduceTimeStampsWhenCalledOnActiveEntityWithPositiveValu final ReadOnlyKeyVaultKeyEntity before = underTest.getEntities().getReadOnlyEntity(keyEntityId); final OffsetDateTime createdOriginal = before.getCreated(); final OffsetDateTime updatedOriginal = before.getUpdated(); + final KeyLifetimeActionTrigger trigger = new KeyLifetimeActionTrigger( + Period.ofDays(MINIMUM_EXPIRY_PERIOD_IN_DAYS), + LifetimeActionTriggerType.TIME_AFTER_CREATE); + final Period expiryTime = Period.ofDays(MINIMUM_EXPIRY_PERIOD_IN_DAYS + MINIMUM_THRESHOLD_BEFORE_EXPIRY); + underTest.setRotationPolicy(new KeyRotationPolicy(keyEntityId, expiryTime, + Map.of(LifetimeActionType.ROTATE, new KeyLifetimeAction(LifetimeActionType.ROTATE, trigger)))); + final ReadOnlyRotationPolicy beforePolicy = underTest.rotationPolicy(keyEntityId); + final OffsetDateTime createdPolicyOriginal = beforePolicy.getCreatedOn(); + final OffsetDateTime updatedPolicyOriginal = beforePolicy.getUpdatedOn(); //when underTest.timeShift(NUMBER_OF_SECONDS_IN_10_MINUTES); @@ -680,6 +691,9 @@ void testTimeShiftShouldReduceTimeStampsWhenCalledOnActiveEntityWithPositiveValu Assertions.assertEquals(updatedOriginal.minusSeconds(NUMBER_OF_SECONDS_IN_10_MINUTES), after.getUpdated()); Assertions.assertEquals(TIME_10_MINUTES_AGO, after.getNotBefore().orElse(null)); Assertions.assertEquals(NOW, after.getExpiry().orElse(null)); + final ReadOnlyRotationPolicy afterPolicy = underTest.rotationPolicy(keyEntityId); + Assertions.assertEquals(createdPolicyOriginal.minusSeconds(NUMBER_OF_SECONDS_IN_10_MINUTES), afterPolicy.getCreatedOn()); + Assertions.assertEquals(updatedPolicyOriginal.minusSeconds(NUMBER_OF_SECONDS_IN_10_MINUTES), afterPolicy.getUpdatedOn()); } @SuppressWarnings("OptionalGetWithoutIsPresent") @@ -709,6 +723,119 @@ void testTimeShiftShouldReduceTimeStampsWhenCalledOnDeletedEntityWithPositiveVal Assertions.assertEquals(NOW, after.getExpiry().orElse(null)); } + @Test + void testSetRotationPolicyShouldKeepCreatedWhenCalledASecondTime() { + //given + final KeyVaultFake underTest = createUnderTest(); + final VersionedKeyEntityId keyEntityId = underTest.createEcKeyVersion(KEY_NAME_1, EC_KEY_CREATION_INPUT); + underTest.setExpiry(keyEntityId, NOW, TIME_IN_10_MINUTES); + final KeyLifetimeActionTrigger rotateOriginal = new KeyLifetimeActionTrigger( + Period.ofDays(MINIMUM_THRESHOLD_BEFORE_EXPIRY), + LifetimeActionTriggerType.TIME_AFTER_CREATE); + final KeyLifetimeActionTrigger rotateSecond = new KeyLifetimeActionTrigger( + Period.ofDays(MINIMUM_EXPIRY_PERIOD_IN_DAYS), + LifetimeActionTriggerType.TIME_AFTER_CREATE); + final Period expiryTime = Period.ofDays(MINIMUM_EXPIRY_PERIOD_IN_DAYS + MINIMUM_THRESHOLD_BEFORE_EXPIRY); + final KeyRotationPolicy rotationPolicyOriginal = new KeyRotationPolicy(keyEntityId, expiryTime, + Map.of(LifetimeActionType.ROTATE, new KeyLifetimeAction(LifetimeActionType.ROTATE, rotateOriginal))); + final KeyRotationPolicy rotationPolicySecond = new KeyRotationPolicy(keyEntityId, expiryTime, + Map.of(LifetimeActionType.ROTATE, new KeyLifetimeAction(LifetimeActionType.ROTATE, rotateSecond))); + underTest.setRotationPolicy(rotationPolicyOriginal); + final ReadOnlyRotationPolicy beforePolicy = underTest.rotationPolicy(keyEntityId); + final OffsetDateTime createdPolicyOriginal = beforePolicy.getCreatedOn(); + final OffsetDateTime updatedPolicyOriginal = beforePolicy.getUpdatedOn(); + + //when + underTest.setRotationPolicy(rotationPolicySecond); + + //then + final ReadOnlyRotationPolicy afterPolicy = underTest.rotationPolicy(keyEntityId); + Assertions.assertEquals(createdPolicyOriginal, afterPolicy.getCreatedOn()); + Assertions.assertTrue(updatedPolicyOriginal.isBefore(afterPolicy.getUpdatedOn())); + } + + @Test + void testRotationPolicyShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyVaultFake underTest = createUnderTest(); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.rotationPolicy(null)); + + //then + exception + } + + @Test + void testSetRotationPolicyShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyVaultFake underTest = createUnderTest(); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.setRotationPolicy(null)); + + //then + exception + } + + @Test + void testRotateKeyShouldCreateNewKeyVersionKeepingTagsAndOperationsWhenCalledWithExistingKey() { + //given + final KeyCurveName keyParameter = KeyCurveName.P_384; + final Map tags = Map.of(KEY_1, VALUE_1); + final List operations = List.of(KeyOperation.ENCRYPT); + + final KeyVaultFake underTest = createUnderTest(); + final VersionedKeyEntityId keyEntityId = underTest + .createKeyVersion(KEY_NAME_1, new EcKeyCreationInput(KeyType.EC, keyParameter)); + underTest.setKeyOperations(keyEntityId, operations); + underTest.addTags(keyEntityId, tags); + final VersionedKeyEntityId latestBeforeRotate = underTest.getEntities().getLatestVersionOfEntity(keyEntityId); + underTest.setExpiry(keyEntityId, null, TIME_IN_10_MINUTES); + underTest.setRotationPolicy(new DefaultKeyRotationPolicy(keyEntityId)); + + //when + underTest.rotateKey(keyEntityId); + + //then + final VersionedKeyEntityId latestAfterRotate = underTest.getEntities().getLatestVersionOfEntity(keyEntityId); + Assertions.assertNotEquals(latestBeforeRotate, latestAfterRotate); + final ReadOnlyKeyVaultKeyEntity actual = underTest.getEntities().getReadOnlyEntity(latestAfterRotate); + Assertions.assertEquals(keyParameter, actual.keyCreationInput().getKeyParameter()); + Assertions.assertEquals(actual.getCreated().plusYears(1), actual.getExpiry().orElse(null)); + Assertions.assertIterableEquals(operations, actual.getOperations()); + Assertions.assertIterableEquals(tags.entrySet(), actual.getTags().entrySet()); + } + + @Test + void testRotateKeyShouldCreateNewKeyVersionWhenNoRotationPolicyIsDefined() { + //given + final KeyCurveName keyParameter = KeyCurveName.P_384; + + final KeyVaultFake underTest = createUnderTest(); + final VersionedKeyEntityId keyEntityId = underTest + .createKeyVersion(KEY_NAME_1, new EcKeyCreationInput(KeyType.EC, keyParameter)); + final VersionedKeyEntityId latestBeforeRotate = underTest.getEntities().getLatestVersionOfEntity(keyEntityId); + + //when + underTest.rotateKey(keyEntityId); + + //then + final VersionedKeyEntityId latestAfterRotate = underTest.getEntities().getLatestVersionOfEntity(keyEntityId); + Assertions.assertNotEquals(latestBeforeRotate, latestAfterRotate); + final ReadOnlyKeyVaultKeyEntity actual = underTest.getEntities().getReadOnlyEntity(latestAfterRotate); + Assertions.assertEquals(keyParameter, actual.keyCreationInput().getKeyParameter()); + } + + @Test + void testRotateKeyShouldThrowExceptionWhenCalledWithNull() { + //given + final KeyVaultFake underTest = createUnderTest(); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.rotateKey(null)); + + //then + exception + } + private KeyVaultFake createUnderTest() { final KeyVaultFake underTest = new VaultFakeImpl(HTTPS_LOCALHOST, RecoveryLevel.RECOVERABLE_AND_PURGEABLE, diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntityTest.java index f20e974b..1e93340a 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyVaultKeyEntityTest.java @@ -116,7 +116,6 @@ void testCanPurgeShouldReturnFalseWhenRecoveryLevelIsNotPurgeableAndItemIsNotDel Assertions.assertFalse(actual); } - @SuppressWarnings("checkstyle:MagicNumber") @ParameterizedTest @ValueSource(ints = {-42, -10, -5, -3, -2, -1, 0}) void testTimeShiftShouldThrowExceptionWhenCalledWithNegativeOrZero(final int value) { diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java index df0b6e79..ecbff34d 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyVaultKeyEntityTest.java @@ -245,4 +245,23 @@ void testVerifyShouldThrowExceptionWhenKeyIsNotEnabled() { //then + exception } + + @Test + void testKeyCreationInputShouldReturnOriginalParameters() { + //given + final VaultFake vaultFake = new VaultFakeImpl(HTTPS_LOWKEY_VAULT); + final int keySize = EncryptionAlgorithm.RSA_OAEP_256.getMinKeySize(); + final BigInteger publicExponent = new BigInteger("3"); + final RsaKeyVaultKeyEntity underTest = new RsaKeyVaultKeyEntity( + VERSIONED_KEY_ENTITY_ID_1_VERSION_1, vaultFake, keySize, publicExponent, false); + + //when + final KeyCreationInput actual = underTest.keyCreationInput(); + + //then + Assertions.assertInstanceOf(RsaKeyCreationInput.class, actual); + final RsaKeyCreationInput value = (RsaKeyCreationInput) actual; + Assertions.assertEquals(keySize, value.getKeyParameter()); + Assertions.assertEquals(publicExponent, value.getPublicExponent()); + } } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtilTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtilTest.java new file mode 100644 index 00000000..b044102a --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtilTest.java @@ -0,0 +1,67 @@ +package com.github.nagyesta.lowkeyvault.service.key.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.ZoneOffset; +import java.util.stream.Stream; + +class PeriodUtilTest { + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream validProvider() { + return Stream.builder() + .add(Arguments.of("-P1D", -1)) + .add(Arguments.of("P0D", 0)) + .add(Arguments.of("P1D", 1)) + .add(Arguments.of("P5D", 5)) + .add(Arguments.of("P7D", 7)) + .add(Arguments.of("P2M10D", 71)) + .add(Arguments.of("P1Y1M1D", 397)) + .build(); + } + + @Test + void testConstructorShouldThrowExceptionWhenCalled() throws NoSuchMethodException { + //given + final Constructor constructor = PeriodUtil.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + //when + Assertions.assertThrows(InvocationTargetException.class, constructor::newInstance); + + //then + exception + } + + @SuppressWarnings("ConstantConditions") + @Test + void testAsDaysShouldThrowExceptionWhenCalledWithNull() { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> PeriodUtil.asDays(null)); + + //then + exception + } + + @ParameterizedTest + @MethodSource("validProvider") + void testAsDaysShouldReturnNumberOfDaysWhenCalledWithValidPeriods(final String period, final int expected) { + //given + final OffsetDateTime time = OffsetDateTime.of(2022, 5, 10, 0, 0, 0, 0, ZoneOffset.UTC); + final Period parsed = Period.parse(period); + + //when + final long actual = PeriodUtil.asDays(parsed, time); + + //then + Assertions.assertEquals(expected, actual); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultFakeImplTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultFakeImplTest.java index 7a72a7f7..acf53489 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultFakeImplTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultFakeImplTest.java @@ -216,7 +216,6 @@ void testGetCreatedOnShouldBeInThePastWhenCalled() { Assertions.assertTrue(actual.isBefore(OffsetDateTime.now())); } - @SuppressWarnings("checkstyle:MagicNumber") @ParameterizedTest @ValueSource(ints = {-42, -10, -5, -3, -2, -1, 0}) void testTimeShiftShouldThrowExceptionWhenCalledWithNegativeOrZero(final int value) { diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultServiceImplTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultServiceImplTest.java index 1cfe0ca6..9a869753 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultServiceImplTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/vault/impl/VaultServiceImplTest.java @@ -268,7 +268,6 @@ void testListDeletedShouldNotReturnPurgedItemsWhenCalled(final Map }); } - @SuppressWarnings("checkstyle:MagicNumber") @ParameterizedTest @ValueSource(ints = {-42, -10, -5, -3, -2, -1, 0}) void testTimeShiftShouldThrowExceptionWhenCalledWithNegativeOrZero(final int value) { diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-empty-actions.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-empty-actions.json new file mode 100644 index 00000000..0fb3a68a --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-empty-actions.json @@ -0,0 +1,9 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [], + "attributes": { + "expiryTime": "P4M", + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-actions.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-actions.json new file mode 100644 index 00000000..064d9a9d --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-actions.json @@ -0,0 +1,8 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "attributes": { + "expiryTime": "P4M", + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-attributes.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-attributes.json new file mode 100644 index 00000000..68328efb --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-attributes.json @@ -0,0 +1,21 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeAfterCreate": "P90D" + }, + "action": { + "type": "rotate" + } + }, + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + } + ] +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-expiry.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-expiry.json new file mode 100644 index 00000000..8de7f79c --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-expiry.json @@ -0,0 +1,25 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeAfterCreate": "P90D" + }, + "action": { + "type": "rotate" + } + }, + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + } + ], + "attributes": { + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-action-type.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-action-type.json new file mode 100644 index 00000000..3c73b163 --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-action-type.json @@ -0,0 +1,26 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeAfterCreate": "P90D" + }, + "action": { + "type": null + } + }, + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + } + ], + "attributes": { + "expiryTime": "P4M", + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-trigger.json b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-trigger.json new file mode 100644 index 00000000..e2971725 --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-trigger.json @@ -0,0 +1,16 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": null, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P4M", + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-full.json b/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-full.json new file mode 100644 index 00000000..b8daf9d4 --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-full.json @@ -0,0 +1,26 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeAfterCreate": "P90D" + }, + "action": { + "type": "rotate" + } + }, + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + } + ], + "attributes": { + "expiryTime": "P4M", + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-minimum.json b/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-minimum.json new file mode 100644 index 00000000..262047aa --- /dev/null +++ b/lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-minimum.json @@ -0,0 +1,17 @@ +{ + "id": "https:/localhost:8443/keys/key-name/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeAfterCreate": "P90D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "created": 1482188947, + "updated": 1482188948 + } +} diff --git a/lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/management/TimeShiftContextTest.java b/lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/management/TimeShiftContextTest.java index 28a81c45..0daf68f3 100644 --- a/lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/management/TimeShiftContextTest.java +++ b/lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/management/TimeShiftContextTest.java @@ -7,7 +7,6 @@ class TimeShiftContextTest { - @SuppressWarnings("checkstyle:MagicNumber") @ParameterizedTest @ValueSource(ints = {-42, -10, -5, -3, -2, -1, 0}) void testBuilderShouldThrowExceptionWhenCalledWithNegativeValue(final int value) { diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/hook/MissionOutlineDefinition.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/hook/MissionOutlineDefinition.java index e55d67cc..24894c5d 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/hook/MissionOutlineDefinition.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/hook/MissionOutlineDefinition.java @@ -48,7 +48,7 @@ protected Map> defineOutline() { }); }); - Stream.of("CreateVault", "KeyImport", "KeyEncrypt", "KeySign", "RSA", "EC", "OCT").forEach(tag -> { + Stream.of("CreateVault", "KeyRotate", "KeyImport", "KeyEncrypt", "KeySign", "RSA", "EC", "OCT").forEach(tag -> { final MissionHealthCheckMatcher matcher = matcher().dependencyWith(tag).extractor(extractor).build(); final MissionHealthCheckEvaluator tagPercentage = percentageBasedEvaluator(matcher) .abortThreshold(ABORT_THRESHOLD) diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/CommonAssertions.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/CommonAssertions.java index f3b094dd..557731a7 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/CommonAssertions.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/CommonAssertions.java @@ -61,7 +61,8 @@ protected String readResourceContent(final String resource) throws IOException { try (InputStream stream = getClass().getResourceAsStream(resource); InputStreamReader reader = new InputStreamReader(Objects.requireNonNull(stream)); BufferedReader bufferedReader = new BufferedReader(reader)) { - return bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())); + return bufferedReader.lines().collect(Collectors.joining("")) + .replaceAll(" +", ""); } } } diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java index 303a4386..51ab51ed 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefs.java @@ -443,6 +443,14 @@ public void theVaultIsCalledForBytesOfRandomData(final int count) { context.setBackupBytes("random", bytes); } + @When("the key named {name} is rotated") + public void theKeyIsRotated(final String name) { + final String oldId = context.getLastResult().getId(); + final KeyVaultKey keyVaultKey = context.getClient(context.getKeyServiceVersion()).rotateKey(name); + assertTrue(!oldId.equals(keyVaultKey.getId())); + context.addFetchedKey(name, keyVaultKey); + } + private byte[] hash(final byte[] text, final String algorithm) { try { final MessageDigest md = MessageDigest.getInstance("SHA-" + algorithm.substring(2, 5)); diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefsAssertions.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefsAssertions.java index 8a6a5551..fbb3f028 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefsAssertions.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/KeysStepDefsAssertions.java @@ -173,7 +173,8 @@ public void theKeyNamedNameMatchesThePreviousBackup(final String name) { @And("the unpacked backup of {name} matches the content of the classpath resource") public void theKeyNamedNameMatchesTheResourceContent(final String name) throws IOException { final byte[] bytes = context.getClient(context.getKeyServiceVersion()).backupKey(name); - final String backup = context.getLowkeyVaultManagementClient().unpackBackup(bytes); + final String backup = context.getLowkeyVaultManagementClient().unpackBackup(bytes) + .replaceAll("[ \\n]+", ""); final String expected = readResourceContent("/json/backups/" + name + ".json"); assertEquals(expected, backup); } diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/RotateKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/RotateKeys.feature new file mode 100644 index 00000000..3ab3e5ea --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/RotateKeys.feature @@ -0,0 +1,92 @@ +Feature: Key rotation + + @Key @KeyCreate @KeyRotate @RSA + Scenario Outline: RSA_ROTATE_01 Single versions of RSA keys can be created with the key client, then rotated and result observed + Given key API version is used + And a key client is created with the vault named keys-generic + And an RSA key named is prepared with bits size HSM + And the key is set to expire seconds after creation + And the key is set to be not usable until seconds after creation + And the key is set to use as tags + And the key has operations granted + And the key is set to be + And the RSA key is created + When the key named is rotated + Then the created key is using RSA algorithm with bytes length + And the key name is + And the key URL contains the vault url and + And the key enabled status is enabled + And the key has as operations + And the key has as tags + And the key was created HSM + And the EC specific fields are not populated + And the OCT specific fields are not populated + And the key recovery settings are default + + Examples: + | api | hsm | keyName | keySize | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.3 | without | 73-rotateRsaKey | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | without | 73-rotateRsaKeyAllOps | 4096 | 513 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | without | 73-rotateRsaKeyNotEnabled | 2048 | 257 | not enabled | null | 4321 | 1234 | null | + | 7.3 | without | 73-rotateRsaKeyMap | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | + + @Key @KeyCreate @KeyRotate @EC + Scenario Outline: EC_ROTATE_01 Single versions of EC keys can be created with the key client, then rotated and result observed + Given key API version is used + And a key client is created with the vault named keys-generic + And an EC key named is prepared with and HSM + And the key is set to expire seconds after creation + And the key is set to be not usable until seconds after creation + And the key is set to use as tags + And the key has operations granted + And the key is set to be + And the EC key is created + When the key named is rotated + Then the created key is using EC algorithm with curve name and bytes length + And the key name is + And the key URL contains the vault url and + And the key enabled status is enabled + And the key has as operations + And the key has as tags + And the key was created HSM + And the RSA specific fields are not populated + And the OCT specific fields are not populated + And the key recovery settings are default + + Examples: + | api | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.3 | without | 73-rotateEcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | without | 73-rotateEcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | without | 73-rotateEcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | without | 73-rotateEcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + + @Key @KeyCreate @KeyRotate @OCT + Scenario Outline: OCT_ROTATE_01 Single versions of OCT keys can be created with the key client, then rotated and result observed + Given key API version is used + And a key client is created with the vault named keys-generic + And an OCT key named is prepared with bits size + And the key is set to expire seconds after creation + And the key is set to be not usable until seconds after creation + And the key is set to use as tags + And the key has operations granted + And the key is set to be + And the OCT key is created + When the key named is rotated + Then the created key is using OCT algorithm + And the key name is + And the key URL contains the vault url and + And the key enabled status is enabled + And the key has as operations + And the key has as tags + And the key was created HSM + And the RSA specific fields are not populated + And the EC specific fields are not populated + And the OCT specific fields are not populated + And the key recovery settings are default + + Examples: + | api | hsm | keyName | keySize | enabledStatus | operations | expires | notBefore | tagMap | + | 7.3 | with | 73-rotateOctKey | 128 | enabled | null | null | null | null | + | 7.3 | with | 73-rotateOctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | with | 73-rotateOctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | with | 73-rotateOctKeyNotEnabled | 128 | not enabled | null | null | null | null | diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json index b466454f..c6987b61 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json @@ -1,23 +1,31 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256-72.localhost:8443", - "entityId" : "jsonBackupEc-256-72", - "entityVersion" : "f16a91376c13478996195a0c2cee39cb", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-256", - "d" : "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256-72.localhost:8443/keys/jsonBackupEc-256-72/f16a91376c13478996195a0c2cee39cb", - "kty" : "EC", - "x" : "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", - "y" : "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-256-72.localhost:8443", + "entityId": "jsonBackupEc-256-72", + "entityVersion": "f16a91376c13478996195a0c2cee39cb", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-256", + "d": "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-256-72.localhost:8443/keys/jsonBackupEc-256-72/f16a91376c13478996195a0c2cee39cb", + "kty": "EC", + "x": "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", + "y": "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json index 4e285295..84eba7a6 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json @@ -1,23 +1,58 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256-73.localhost:8443", - "entityId" : "jsonBackupEc-256-73", - "entityVersion" : "f16a91376c13478996195a0c2cee39cb", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-256", - "d" : "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256-73.localhost:8443/keys/jsonBackupEc-256-73/f16a91376c13478996195a0c2cee39cb", - "kty" : "EC", - "x" : "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", - "y" : "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-256-73.localhost:8443", + "entityId": "jsonBackupEc-256-73", + "entityVersion": "f16a91376c13478996195a0c2cee39cb", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-256", + "d": "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-256-73.localhost:8443/keys/jsonBackupEc-256-73/f16a91376c13478996195a0c2cee39cb", + "kty": "EC", + "x": "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", + "y": "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupEc-256-73.localhost:8443/keys/jsonBackupEc-256-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503879, + "updated": 1649503879 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json index 8984af85..bdce4661 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json @@ -1,23 +1,31 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256k-72.localhost:8443", - "entityId" : "jsonBackupEc-256k-72", - "entityVersion" : "5425a5872c1f4a9fa96db8f40e6c5d07", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-256K", - "d" : "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256k-72.localhost:8443/keys/jsonBackupEc-256k-72/5425a5872c1f4a9fa96db8f40e6c5d07", - "kty" : "EC", - "x" : "AOSHhd4mwdHNED-SdhTnMasUYuEwLPWpLb8rNx-NV9OH", - "y" : "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-256k-72.localhost:8443", + "entityId": "jsonBackupEc-256k-72", + "entityVersion": "5425a5872c1f4a9fa96db8f40e6c5d07", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-256K", + "d": "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-256k-72.localhost:8443/keys/jsonBackupEc-256k-72/5425a5872c1f4a9fa96db8f40e6c5d07", + "kty": "EC", + "x": "AOSHhd4mwdHNED-SdhTnMasUYuEwLPWpLb8rNx-NV9OH", + "y": "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json index a5fd4441..566c35d4 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json @@ -1,23 +1,58 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256k-73.localhost:8443", - "entityId" : "jsonBackupEc-256k-73", - "entityVersion" : "5425a5872c1f4a9fa96db8f40e6c5d07", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-256K", - "d" : "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256k-73.localhost:8443/keys/jsonBackupEc-256k-73/5425a5872c1f4a9fa96db8f40e6c5d07", - "kty" : "EC", - "x" : "AOSHhd4mwdHNED-SdhTnMasUYuEwLPWpLb8rNx-NV9OH", - "y" : "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-256k-73.localhost:8443", + "entityId": "jsonBackupEc-256k-73", + "entityVersion": "5425a5872c1f4a9fa96db8f40e6c5d07", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-256K", + "d": "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-256k-73.localhost:8443/keys/jsonBackupEc-256k-73/5425a5872c1f4a9fa96db8f40e6c5d07", + "kty": "EC", + "x": "AOSHhd4mwdHNED-SdhTnMasUYuEwLPWpLb8rNx-NV9OH", + "y": "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupEc-256k-73.localhost:8443/keys/jsonBackupEc-256k-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503879, + "updated": 1649503879 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json index c4d1a241..f2629934 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json @@ -1,23 +1,31 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-384-72.localhost:8443", - "entityId" : "jsonBackupEc-384-72", - "entityVersion" : "7f4c0a2ef5454e07a533e597434984a8", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-384", - "d" : "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-384-72.localhost:8443/keys/jsonBackupEc-384-72/7f4c0a2ef5454e07a533e597434984a8", - "kty" : "EC", - "x" : "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", - "y" : "AKYy8hXwjc0O8mVBXOolUvHklqEV2POLIN6c3EpZIJ-Sz2H_Vce0EoAF320bZhxrfw" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-384-72.localhost:8443", + "entityId": "jsonBackupEc-384-72", + "entityVersion": "7f4c0a2ef5454e07a533e597434984a8", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-384", + "d": "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-384-72.localhost:8443/keys/jsonBackupEc-384-72/7f4c0a2ef5454e07a533e597434984a8", + "kty": "EC", + "x": "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", + "y": "AKYy8hXwjc0O8mVBXOolUvHklqEV2POLIN6c3EpZIJ-Sz2H_Vce0EoAF320bZhxrfw" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json index e27b2a10..321b9912 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json @@ -1,23 +1,58 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-384-73.localhost:8443", - "entityId" : "jsonBackupEc-384-73", - "entityVersion" : "7f4c0a2ef5454e07a533e597434984a8", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-384", - "d" : "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-384-73.localhost:8443/keys/jsonBackupEc-384-73/7f4c0a2ef5454e07a533e597434984a8", - "kty" : "EC", - "x" : "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", - "y" : "AKYy8hXwjc0O8mVBXOolUvHklqEV2POLIN6c3EpZIJ-Sz2H_Vce0EoAF320bZhxrfw" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-384-73.localhost:8443", + "entityId": "jsonBackupEc-384-73", + "entityVersion": "7f4c0a2ef5454e07a533e597434984a8", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-384", + "d": "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-384-73.localhost:8443/keys/jsonBackupEc-384-73/7f4c0a2ef5454e07a533e597434984a8", + "kty": "EC", + "x": "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", + "y": "AKYy8hXwjc0O8mVBXOolUvHklqEV2POLIN6c3EpZIJ-Sz2H_Vce0EoAF320bZhxrfw" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupEc-384-73.localhost:8443/keys/jsonBackupEc-384-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503879, + "updated": 1649503879 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json index 55e6b332..e5cc79da 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json @@ -1,23 +1,31 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-521-72.localhost:8443", - "entityId" : "jsonBackupEc-521-72", - "entityVersion" : "9301e31d18a540e08c68ad5230e60e21", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-521", - "d" : "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-521-72.localhost:8443/keys/jsonBackupEc-521-72/9301e31d18a540e08c68ad5230e60e21", - "kty" : "EC", - "x" : "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", - "y" : "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-521-72.localhost:8443", + "entityId": "jsonBackupEc-521-72", + "entityVersion": "9301e31d18a540e08c68ad5230e60e21", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-521", + "d": "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-521-72.localhost:8443/keys/jsonBackupEc-521-72/9301e31d18a540e08c68ad5230e60e21", + "kty": "EC", + "x": "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", + "y": "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json index 0c3b2281..84fa4d0f 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json @@ -1,23 +1,58 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-521-73.localhost:8443", - "entityId" : "jsonBackupEc-521-73", - "entityVersion" : "9301e31d18a540e08c68ad5230e60e21", - "attributes" : { - "enabled" : true, - "created" : 1649503879, - "updated" : 1649503879, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "crv" : "P-521", - "d" : "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-521-73.localhost:8443/keys/jsonBackupEc-521-73/9301e31d18a540e08c68ad5230e60e21", - "kty" : "EC", - "x" : "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", - "y" : "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupEc-521-73.localhost:8443", + "entityId": "jsonBackupEc-521-73", + "entityVersion": "9301e31d18a540e08c68ad5230e60e21", + "attributes": { + "enabled": true, + "created": 1649503879, + "updated": 1649503879, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "crv": "P-521", + "d": "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupEc-521-73.localhost:8443/keys/jsonBackupEc-521-73/9301e31d18a540e08c68ad5230e60e21", + "kty": "EC", + "x": "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", + "y": "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupEc-521-73.localhost:8443/keys/jsonBackupEc-521-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503879, + "updated": 1649503879 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json index d915b260..299b6ba5 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json @@ -1,20 +1,28 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-128-72.localhost:8443", - "entityId" : "jsonBackupOct-128-72", - "entityVersion" : "730490a0df9b4ac78ed675902077a18f", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "sx32Vta2Zx1BsdQBY2l5iQ", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-128-72.localhost:8443/keys/jsonBackupOct-128-72/730490a0df9b4ac78ed675902077a18f", - "kty" : "oct-HSM" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-128-72.localhost:8443", + "entityId": "jsonBackupOct-128-72", + "entityVersion": "730490a0df9b4ac78ed675902077a18f", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "sx32Vta2Zx1BsdQBY2l5iQ", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-128-72.localhost:8443/keys/jsonBackupOct-128-72/730490a0df9b4ac78ed675902077a18f", + "kty": "oct-HSM" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json index c070ccdc..35bccb64 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json @@ -1,20 +1,55 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-128-73.localhost:8443", - "entityId" : "jsonBackupOct-128-73", - "entityVersion" : "730490a0df9b4ac78ed675902077a18f", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "sx32Vta2Zx1BsdQBY2l5iQ", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-128-73.localhost:8443/keys/jsonBackupOct-128-73/730490a0df9b4ac78ed675902077a18f", - "kty" : "oct-HSM" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-128-73.localhost:8443", + "entityId": "jsonBackupOct-128-73", + "entityVersion": "730490a0df9b4ac78ed675902077a18f", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "sx32Vta2Zx1BsdQBY2l5iQ", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-128-73.localhost:8443/keys/jsonBackupOct-128-73/730490a0df9b4ac78ed675902077a18f", + "kty": "oct-HSM" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupOct-128-73.localhost:8443/keys/jsonBackupOct-128-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503106, + "updated": 1649503106 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json index bca9e439..24a6d808 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json @@ -1,20 +1,28 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-192-72.localhost:8443", - "entityId" : "jsonBackupOct-192-72", - "entityVersion" : "55ccab8f94b244ab97d82899340c22dd", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-192-72.localhost:8443/keys/jsonBackupOct-192-72/55ccab8f94b244ab97d82899340c22dd", - "kty" : "oct-HSM" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-192-72.localhost:8443", + "entityId": "jsonBackupOct-192-72", + "entityVersion": "55ccab8f94b244ab97d82899340c22dd", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-192-72.localhost:8443/keys/jsonBackupOct-192-72/55ccab8f94b244ab97d82899340c22dd", + "kty": "oct-HSM" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json index 2fd02eae..95d7be06 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json @@ -1,20 +1,56 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-192-73.localhost:8443", - "entityId" : "jsonBackupOct-192-73", - "entityVersion" : "55ccab8f94b244ab97d82899340c22dd", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-192-73.localhost:8443/keys/jsonBackupOct-192-73/55ccab8f94b244ab97d82899340c22dd", - "kty" : "oct-HSM" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-192-73.localhost:8443", + "entityId": "jsonBackupOct-192-73", + "entityVersion": "55ccab8f94b244ab97d82899340c22dd", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-192-73.localhost:8443/keys/jsonBackupOct-192-73/55ccab8f94b244ab97d82899340c22dd", + "kty": "oct-HSM" + } + } + ], + + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupOct-192-73.localhost:8443/keys/jsonBackupOct-192-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503106, + "updated": 1649503106 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json index 96e403a7..475c2cba 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json @@ -1,20 +1,28 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-256-72.localhost:8443", - "entityId" : "jsonBackupOct-256-72", - "entityVersion" : "19c6d13acb844fda8ccb14751252433a", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-256-72.localhost:8443/keys/jsonBackupOct-256-72/19c6d13acb844fda8ccb14751252433a", - "kty" : "oct-HSM" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-256-72.localhost:8443", + "entityId": "jsonBackupOct-256-72", + "entityVersion": "19c6d13acb844fda8ccb14751252433a", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-256-72.localhost:8443/keys/jsonBackupOct-256-72/19c6d13acb844fda8ccb14751252433a", + "kty": "oct-HSM" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json index 90c4ca01..b5151034 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json @@ -1,20 +1,56 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-256-73.localhost:8443", - "entityId" : "jsonBackupOct-256-73", - "entityVersion" : "19c6d13acb844fda8ccb14751252433a", - "attributes" : { - "enabled" : true, - "created" : 1649503106, - "updated" : 1649503106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "k" : "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-256-73.localhost:8443/keys/jsonBackupOct-256-73/19c6d13acb844fda8ccb14751252433a", - "kty" : "oct-HSM" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupOct-256-73.localhost:8443", + "entityId": "jsonBackupOct-256-73", + "entityVersion": "19c6d13acb844fda8ccb14751252433a", + "attributes": { + "enabled": true, + "created": 1649503106, + "updated": 1649503106, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "k": "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupOct-256-73.localhost:8443/keys/jsonBackupOct-256-73/19c6d13acb844fda8ccb14751252433a", + "kty": "oct-HSM" + } + } + ], + + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupOct-256-73.localhost:8443/keys/jsonBackupOct-256-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649503106, + "updated": 1649503106 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json index 56d1396c..254feb0a 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json @@ -1,27 +1,35 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-2048-72.localhost:8443", - "entityId" : "jsonBackupRsa-2048-72", - "entityVersion" : "2d93f37afada4679b00b528f7238ad5c", - "attributes" : { - "enabled" : true, - "created" : 1649504957, - "updated" : 1649504957, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "BOSCOBcA_oaZAMPp3APYWbQvyPuw0uFwblDxFSN8YGsSaMqNZMc4b01hC7oMRJzTC7k5dFkCcvikKJU3xWiqNY-FP9HEPJkHezEOQ9rk4nKuJuex157gf1SUxqzDttjQxty027P0ZVL-EmB6OdET2CKVpEyTo7RVZ9UBvQnb1S7-ZBU7UTBotWA7MRsfItvTX_EHX9atPJegpSWmJaJ05-_-dXkRnffrk43bLJ5cJEF4G6X7c4rUFjxSTJ3OwTjmpZPAJT1q85H3-NXrhXiabkFicR3cIj4OpA4egUpRwVze0VAbNOlIsZYIUocLuBelKYLLmr5hTnP_UKSKwznayQ", - "dp" : "AN9pfqyKw-uT9FWr8GqsyhqJYR9KLgTtX6uqxJk_Y-4NZEMooCeq2olaVpFic0tfaOMJnDufYsUBL_i1TxoD9w3s0YjVwBiURFKs-0Ghs2RGxk22WGbKrnS4Zh8k2RKkNvFPnRciXva1nfMjeWEahb6O8ReA7AZNECKAqJtE5UTB", - "dq" : "B7sIKKG4bo4Gvm4xj7KBnlP4X9UQH8FLnisGVe9HqDJH4Zu7d-dj5yHT-HaX07MU5p0r2H26owT829SxZGZB2qR_njnuyt8KnmAdEJPttNkW6L25csAoEzqdexi08wmWH0fAEMVDgeobAg1YtByts-VmajO0TWxYyO1oiJFc6ok", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-2048-72.localhost:8443/keys/jsonBackupRsa-2048-72/2d93f37afada4679b00b528f7238ad5c", - "kty" : "RSA-HSM", - "n" : "AMfXpr8tkLe86czuJnDBbzcNFNk5paAm0hcI6zD-P1OYvinL2zJG3-8O0GQNuHeExZykhpfOqo6nOv1tbcqesUwkgcT385z2Cmf0G0TfK7EcvDRO8APxqMIvuQpglzFOXAJ4aqVD56IWFMzzkkaR73DKmDF-6cmWtrZu0dEv7LN5cZAk2wd421k5JTbcHoo5kAZBv_CJNt_M0n5VMK5jACV_d53mmaumTHffu-BLOyfBlr0cQMr63aBYh8q5KlcL7U5QJC01umOl4Wwl7yrbD4jZo5f3kWwYTDfeRQlz40h68XUssPc9lG7Gsj9hYxalyCwP9ix5dcs4x-OsYXLX-nM", - "p" : "AOqsdAb_YNN9OdgFRuVNUmMlG2DpgQfCs8kHUi0_s4IGLjgu4yX1LUhVNnBMLCxTEGO7U-N9e3jHRDexQLHbXzt2IkrN-pwh3MZVEry6yxQILxbMp4aPqZpWTs9ufSiX0iI68lb7uZhsiaMcpEYQX_N67Vlj1Z6-rCGM1K_-86op", - "q" : "ANoA3nnRaVah2bdmqdjK1Hh20wj4gTEQrKnt_XZDlxMo_HZ77saX_hMjJpYX008w-ZPV-LFNc_clcHK7ZedJZB6pcmuumPvLuvqSqifD4wEwcQAR3a8aTs2gBeqmjyxXXtQk8KK8LNpIzclm98n0vmk1kc0fHobmr_KVFyFBYAs7", - "qi" : "AOfgFCIEztUVLY7nA5-vMDPRYsf8oKSVeeXDqueXogic75BzYL4mfWevgfkyatP0aa8y8E38annoMN-E1J-cu2gBneLQCd9rf0cIqRIooTwZ-RMLPEKtpBNcT3sBirfeviITpDqOirbke92XbMNGkAEPoC5JPiCYU1FASGDaJFaF" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-2048-72.localhost:8443", + "entityId": "jsonBackupRsa-2048-72", + "entityVersion": "2d93f37afada4679b00b528f7238ad5c", + "attributes": { + "enabled": true, + "created": 1649504957, + "updated": 1649504957, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "BOSCOBcA_oaZAMPp3APYWbQvyPuw0uFwblDxFSN8YGsSaMqNZMc4b01hC7oMRJzTC7k5dFkCcvikKJU3xWiqNY-FP9HEPJkHezEOQ9rk4nKuJuex157gf1SUxqzDttjQxty027P0ZVL-EmB6OdET2CKVpEyTo7RVZ9UBvQnb1S7-ZBU7UTBotWA7MRsfItvTX_EHX9atPJegpSWmJaJ05-_-dXkRnffrk43bLJ5cJEF4G6X7c4rUFjxSTJ3OwTjmpZPAJT1q85H3-NXrhXiabkFicR3cIj4OpA4egUpRwVze0VAbNOlIsZYIUocLuBelKYLLmr5hTnP_UKSKwznayQ", + "dp": "AN9pfqyKw-uT9FWr8GqsyhqJYR9KLgTtX6uqxJk_Y-4NZEMooCeq2olaVpFic0tfaOMJnDufYsUBL_i1TxoD9w3s0YjVwBiURFKs-0Ghs2RGxk22WGbKrnS4Zh8k2RKkNvFPnRciXva1nfMjeWEahb6O8ReA7AZNECKAqJtE5UTB", + "dq": "B7sIKKG4bo4Gvm4xj7KBnlP4X9UQH8FLnisGVe9HqDJH4Zu7d-dj5yHT-HaX07MU5p0r2H26owT829SxZGZB2qR_njnuyt8KnmAdEJPttNkW6L25csAoEzqdexi08wmWH0fAEMVDgeobAg1YtByts-VmajO0TWxYyO1oiJFc6ok", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-2048-72.localhost:8443/keys/jsonBackupRsa-2048-72/2d93f37afada4679b00b528f7238ad5c", + "kty": "RSA-HSM", + "n": "AMfXpr8tkLe86czuJnDBbzcNFNk5paAm0hcI6zD-P1OYvinL2zJG3-8O0GQNuHeExZykhpfOqo6nOv1tbcqesUwkgcT385z2Cmf0G0TfK7EcvDRO8APxqMIvuQpglzFOXAJ4aqVD56IWFMzzkkaR73DKmDF-6cmWtrZu0dEv7LN5cZAk2wd421k5JTbcHoo5kAZBv_CJNt_M0n5VMK5jACV_d53mmaumTHffu-BLOyfBlr0cQMr63aBYh8q5KlcL7U5QJC01umOl4Wwl7yrbD4jZo5f3kWwYTDfeRQlz40h68XUssPc9lG7Gsj9hYxalyCwP9ix5dcs4x-OsYXLX-nM", + "p": "AOqsdAb_YNN9OdgFRuVNUmMlG2DpgQfCs8kHUi0_s4IGLjgu4yX1LUhVNnBMLCxTEGO7U-N9e3jHRDexQLHbXzt2IkrN-pwh3MZVEry6yxQILxbMp4aPqZpWTs9ufSiX0iI68lb7uZhsiaMcpEYQX_N67Vlj1Z6-rCGM1K_-86op", + "q": "ANoA3nnRaVah2bdmqdjK1Hh20wj4gTEQrKnt_XZDlxMo_HZ77saX_hMjJpYX008w-ZPV-LFNc_clcHK7ZedJZB6pcmuumPvLuvqSqifD4wEwcQAR3a8aTs2gBeqmjyxXXtQk8KK8LNpIzclm98n0vmk1kc0fHobmr_KVFyFBYAs7", + "qi": "AOfgFCIEztUVLY7nA5-vMDPRYsf8oKSVeeXDqueXogic75BzYL4mfWevgfkyatP0aa8y8E38annoMN-E1J-cu2gBneLQCd9rf0cIqRIooTwZ-RMLPEKtpBNcT3sBirfeviITpDqOirbke92XbMNGkAEPoC5JPiCYU1FASGDaJFaF" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json index 268cc230..7ef16d8c 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json @@ -1,27 +1,62 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-2048-73.localhost:8443", - "entityId" : "jsonBackupRsa-2048-73", - "entityVersion" : "2d93f37afada4679b00b528f7238ad5c", - "attributes" : { - "enabled" : true, - "created" : 1649504957, - "updated" : 1649504957, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "BOSCOBcA_oaZAMPp3APYWbQvyPuw0uFwblDxFSN8YGsSaMqNZMc4b01hC7oMRJzTC7k5dFkCcvikKJU3xWiqNY-FP9HEPJkHezEOQ9rk4nKuJuex157gf1SUxqzDttjQxty027P0ZVL-EmB6OdET2CKVpEyTo7RVZ9UBvQnb1S7-ZBU7UTBotWA7MRsfItvTX_EHX9atPJegpSWmJaJ05-_-dXkRnffrk43bLJ5cJEF4G6X7c4rUFjxSTJ3OwTjmpZPAJT1q85H3-NXrhXiabkFicR3cIj4OpA4egUpRwVze0VAbNOlIsZYIUocLuBelKYLLmr5hTnP_UKSKwznayQ", - "dp" : "AN9pfqyKw-uT9FWr8GqsyhqJYR9KLgTtX6uqxJk_Y-4NZEMooCeq2olaVpFic0tfaOMJnDufYsUBL_i1TxoD9w3s0YjVwBiURFKs-0Ghs2RGxk22WGbKrnS4Zh8k2RKkNvFPnRciXva1nfMjeWEahb6O8ReA7AZNECKAqJtE5UTB", - "dq" : "B7sIKKG4bo4Gvm4xj7KBnlP4X9UQH8FLnisGVe9HqDJH4Zu7d-dj5yHT-HaX07MU5p0r2H26owT829SxZGZB2qR_njnuyt8KnmAdEJPttNkW6L25csAoEzqdexi08wmWH0fAEMVDgeobAg1YtByts-VmajO0TWxYyO1oiJFc6ok", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-2048-73.localhost:8443/keys/jsonBackupRsa-2048-73/2d93f37afada4679b00b528f7238ad5c", - "kty" : "RSA-HSM", - "n" : "AMfXpr8tkLe86czuJnDBbzcNFNk5paAm0hcI6zD-P1OYvinL2zJG3-8O0GQNuHeExZykhpfOqo6nOv1tbcqesUwkgcT385z2Cmf0G0TfK7EcvDRO8APxqMIvuQpglzFOXAJ4aqVD56IWFMzzkkaR73DKmDF-6cmWtrZu0dEv7LN5cZAk2wd421k5JTbcHoo5kAZBv_CJNt_M0n5VMK5jACV_d53mmaumTHffu-BLOyfBlr0cQMr63aBYh8q5KlcL7U5QJC01umOl4Wwl7yrbD4jZo5f3kWwYTDfeRQlz40h68XUssPc9lG7Gsj9hYxalyCwP9ix5dcs4x-OsYXLX-nM", - "p" : "AOqsdAb_YNN9OdgFRuVNUmMlG2DpgQfCs8kHUi0_s4IGLjgu4yX1LUhVNnBMLCxTEGO7U-N9e3jHRDexQLHbXzt2IkrN-pwh3MZVEry6yxQILxbMp4aPqZpWTs9ufSiX0iI68lb7uZhsiaMcpEYQX_N67Vlj1Z6-rCGM1K_-86op", - "q" : "ANoA3nnRaVah2bdmqdjK1Hh20wj4gTEQrKnt_XZDlxMo_HZ77saX_hMjJpYX008w-ZPV-LFNc_clcHK7ZedJZB6pcmuumPvLuvqSqifD4wEwcQAR3a8aTs2gBeqmjyxXXtQk8KK8LNpIzclm98n0vmk1kc0fHobmr_KVFyFBYAs7", - "qi" : "AOfgFCIEztUVLY7nA5-vMDPRYsf8oKSVeeXDqueXogic75BzYL4mfWevgfkyatP0aa8y8E38annoMN-E1J-cu2gBneLQCd9rf0cIqRIooTwZ-RMLPEKtpBNcT3sBirfeviITpDqOirbke92XbMNGkAEPoC5JPiCYU1FASGDaJFaF" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-2048-73.localhost:8443", + "entityId": "jsonBackupRsa-2048-73", + "entityVersion": "2d93f37afada4679b00b528f7238ad5c", + "attributes": { + "enabled": true, + "created": 1649504957, + "updated": 1649504957, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "BOSCOBcA_oaZAMPp3APYWbQvyPuw0uFwblDxFSN8YGsSaMqNZMc4b01hC7oMRJzTC7k5dFkCcvikKJU3xWiqNY-FP9HEPJkHezEOQ9rk4nKuJuex157gf1SUxqzDttjQxty027P0ZVL-EmB6OdET2CKVpEyTo7RVZ9UBvQnb1S7-ZBU7UTBotWA7MRsfItvTX_EHX9atPJegpSWmJaJ05-_-dXkRnffrk43bLJ5cJEF4G6X7c4rUFjxSTJ3OwTjmpZPAJT1q85H3-NXrhXiabkFicR3cIj4OpA4egUpRwVze0VAbNOlIsZYIUocLuBelKYLLmr5hTnP_UKSKwznayQ", + "dp": "AN9pfqyKw-uT9FWr8GqsyhqJYR9KLgTtX6uqxJk_Y-4NZEMooCeq2olaVpFic0tfaOMJnDufYsUBL_i1TxoD9w3s0YjVwBiURFKs-0Ghs2RGxk22WGbKrnS4Zh8k2RKkNvFPnRciXva1nfMjeWEahb6O8ReA7AZNECKAqJtE5UTB", + "dq": "B7sIKKG4bo4Gvm4xj7KBnlP4X9UQH8FLnisGVe9HqDJH4Zu7d-dj5yHT-HaX07MU5p0r2H26owT829SxZGZB2qR_njnuyt8KnmAdEJPttNkW6L25csAoEzqdexi08wmWH0fAEMVDgeobAg1YtByts-VmajO0TWxYyO1oiJFc6ok", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-2048-73.localhost:8443/keys/jsonBackupRsa-2048-73/2d93f37afada4679b00b528f7238ad5c", + "kty": "RSA-HSM", + "n": "AMfXpr8tkLe86czuJnDBbzcNFNk5paAm0hcI6zD-P1OYvinL2zJG3-8O0GQNuHeExZykhpfOqo6nOv1tbcqesUwkgcT385z2Cmf0G0TfK7EcvDRO8APxqMIvuQpglzFOXAJ4aqVD56IWFMzzkkaR73DKmDF-6cmWtrZu0dEv7LN5cZAk2wd421k5JTbcHoo5kAZBv_CJNt_M0n5VMK5jACV_d53mmaumTHffu-BLOyfBlr0cQMr63aBYh8q5KlcL7U5QJC01umOl4Wwl7yrbD4jZo5f3kWwYTDfeRQlz40h68XUssPc9lG7Gsj9hYxalyCwP9ix5dcs4x-OsYXLX-nM", + "p": "AOqsdAb_YNN9OdgFRuVNUmMlG2DpgQfCs8kHUi0_s4IGLjgu4yX1LUhVNnBMLCxTEGO7U-N9e3jHRDexQLHbXzt2IkrN-pwh3MZVEry6yxQILxbMp4aPqZpWTs9ufSiX0iI68lb7uZhsiaMcpEYQX_N67Vlj1Z6-rCGM1K_-86op", + "q": "ANoA3nnRaVah2bdmqdjK1Hh20wj4gTEQrKnt_XZDlxMo_HZ77saX_hMjJpYX008w-ZPV-LFNc_clcHK7ZedJZB6pcmuumPvLuvqSqifD4wEwcQAR3a8aTs2gBeqmjyxXXtQk8KK8LNpIzclm98n0vmk1kc0fHobmr_KVFyFBYAs7", + "qi": "AOfgFCIEztUVLY7nA5-vMDPRYsf8oKSVeeXDqueXogic75BzYL4mfWevgfkyatP0aa8y8E38annoMN-E1J-cu2gBneLQCd9rf0cIqRIooTwZ-RMLPEKtpBNcT3sBirfeviITpDqOirbke92XbMNGkAEPoC5JPiCYU1FASGDaJFaF" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupRsa-2048-73.localhost:8443/keys/jsonBackupRsa-2048-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649504966, + "updated": 1649504966 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json index cb651411..851bcd4f 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json @@ -1,27 +1,35 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-3072-72.localhost:8443", - "entityId" : "jsonBackupRsa-3072-72", - "entityVersion" : "5ae349c717644397a14df7ece5eda28f", - "attributes" : { - "enabled" : true, - "created" : 1649506106, - "updated" : 1649506106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "FwpaGDisPKDVoOzuCws9yGvvK2nu2LGFp1PefmoZ5mabkS2T-IpqdL-UUfpdZllIxWaFWmuN_c21nJXZhA1UL9g1qywUi5D3lKePDB00jVTIfdZSDrrXr_KTZwLQsYZ_iIeyiG1wPX4hWzx-rKFJTEXLEKUc3yWdlIJyFf9olfBy5PG_83Y6DC56RNFSIvqQUg9AhN-6KauP-0SrdBU2csSBkj97viPvhua2mrW7knwC1TajMQVhwn8DidcV1G13nib1ChBjia70Z3b4n_lisK3330BWfuKZAJyEfcUpIPZjFCF3YfAFcKJGA1hBqG5Up4IA02yekMHGtga_TnPPn9Z7RZ-eA6WlaFKgFvY0IKZYVNwkJ88coQxUmmMvgETy-Xz-yQBPLAaQmVW4dhBi9VUGCDHbgKC9n9XK6DNKz6ejhklctehpePrjweTEG4v0sJsSd1TiQBPL60dme3A8jwlLFdSxSjamn8ysDMdVe6Ck5Qtztm2GSRSdC6cb0d3B", - "dp" : "NgJjxYCpARiZJCwWrjgBLfajs55DykK1Vy6GtSuVhDtit-7vWNstxSnJIErnfv3-MNjQ0hVOOGck-c9cDuvXNNh0Nx8OTBqCOG4EPX6GiZdUpB27gpSmxEUNsa308kEXV-qmIRptQOco0rGY8HrJ7S1ktWQID89V0jVUHBvw9T6Ktc833rtlffQUEdLex3ijerDnqWorAAkKinXH9TwjgKH4FldpQrOe3b4KpslHn7oUeEsYvepOSFaKKyifym5B", - "dq" : "b6-Ny82SZFzMFz4dADqRLapsic8r6YRgjh0txKWgL-3tnXzF-YTutxRIhSUf_ypcKN3KmRLNK9ZIjB1YtC56CgbTseBABUbw9EuSN-g9T6zMM0XA916N7JmQhDeWxEaGPzLvLB50ZWoFnyyws4mC_lB8E4hWo615zBJ_06A87be5wRK-e5fIuVe9Zknlnuu3DuuP6ZSPP7lYf0A8ms_Eg8RHHUx_1WTNsCnwdPDfY_O1Fsg4MVpXdeqho7s6UouZ", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-3072-72.localhost:8443/keys/jsonBackupRsa-3072-72/5ae349c717644397a14df7ece5eda28f", - "kty" : "RSA-HSM", - "n" : "AI2OC6QgoA3x6CzwXr8GPdMUZSOJ3FdKxS598-xoSCbwJGB6a761d0ugcFnqX87RsQAxaEp3OS3xviSGa0ilc-NIeGkbtKjd96bAkgAKxHRB99fkyHAxA-y8QQzvPF7xgx-eKKs0uR_NGFyJDeqOZPzmfdLZydpq8Y_Rmc_uNPxGaHzbTCiumkkNd9OXs1r6U1OPJecnv6tHLYDh8OMLJ3WZNbJ5OqlpOGMShWgoCV1c5olqcBoHQRyMCH31x9fnxKqWJMFnF5B3yimD-8AaTkTo4EEmi5-mrqJ4IDGoUSh9p8hgafv7XNmkms0ZkEUrsXOLuLvvPeyza_Ka9tgJBUDmkrjUu9mTUyRcKaiBHyR5VLxUBmeNtvS_Df_Vqoi4G-6vMQvVgut1pp6bijBjYtKp5b7m8mxtJ6my1nShu-NxAw4JFMQMmh477cJxqzfrBzlfefvFnhtFS8K78t1CJNRuEmDNB4TNlgrzLlvJq9BUzflBD-XvT_9Vj1_oIcGw3Q", - "p" : "AMVKqqCIRq9jh5SoW5UxLTfNrsjZkK5J4RAxgwcf2AtQXL0dxOUU-FepkASfo3MoYkStP9aJPlZKMJ0iixkgUjL-Fr0cX2tFgkZoFmyDKTmLguGvCEt07NJD633cnzAZ728OMsP6cK_bYcE48TuRtNvnbVq_QPbm7JLJii_qf2HHPL-EdFlBoFTWWWErxYfRd9XwxAgwHvd0FiNrlHRvtUxuc8h1OnYwORhtXPlrx5D0WFSUlWHbgLOEAnp_EI2FQQ", - "q" : "ALetcnXW5mFbud_4hh_Vw6QHuRrw1l0MYtgeFvVld6pDzAMzoiVbAVLM0rgmk9vVrHta5b5uj2cT08QqU8EXDRHgnC_fOT-ay966AaQmu3RUACeVv0CcRJQB_FzQ4RLUtHoDj7No23jGjLH849NNc8YaTYAWD7ntQEoVorxhZIyxGI7Zaf_pajkgQPKe2rT88SbONK9UTV-iAQ6wr-_W4amWlhpCWvObCHkhWFL01kmJhVDcrHvslCq4hdDuCeD4nQ", - "qi" : "MLMFbdT1qRanqk8krWVwbhStSNOdznS75gS3PgmrCLmJ63OCkk6O0EczBHrByPQF5P5sha_ec7ZCIFHaYGakTFrk-4vQJTFtFhx5IripRFUQ2NFhLzhDh_P2Tlg_TBjlaA1AF99iuKPoon2s114a5vq_C14Yzx0aTZgn4UzPUTAi5GATnyGknlR0A9bErtxSdLbHkgOTzYfQBMJvtzgbN8JSsT23cuE-bmitsGjokTFhMvyvmH0ZcaFY4RzTKXeQ" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-3072-72.localhost:8443", + "entityId": "jsonBackupRsa-3072-72", + "entityVersion": "5ae349c717644397a14df7ece5eda28f", + "attributes": { + "enabled": true, + "created": 1649506106, + "updated": 1649506106, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "FwpaGDisPKDVoOzuCws9yGvvK2nu2LGFp1PefmoZ5mabkS2T-IpqdL-UUfpdZllIxWaFWmuN_c21nJXZhA1UL9g1qywUi5D3lKePDB00jVTIfdZSDrrXr_KTZwLQsYZ_iIeyiG1wPX4hWzx-rKFJTEXLEKUc3yWdlIJyFf9olfBy5PG_83Y6DC56RNFSIvqQUg9AhN-6KauP-0SrdBU2csSBkj97viPvhua2mrW7knwC1TajMQVhwn8DidcV1G13nib1ChBjia70Z3b4n_lisK3330BWfuKZAJyEfcUpIPZjFCF3YfAFcKJGA1hBqG5Up4IA02yekMHGtga_TnPPn9Z7RZ-eA6WlaFKgFvY0IKZYVNwkJ88coQxUmmMvgETy-Xz-yQBPLAaQmVW4dhBi9VUGCDHbgKC9n9XK6DNKz6ejhklctehpePrjweTEG4v0sJsSd1TiQBPL60dme3A8jwlLFdSxSjamn8ysDMdVe6Ck5Qtztm2GSRSdC6cb0d3B", + "dp": "NgJjxYCpARiZJCwWrjgBLfajs55DykK1Vy6GtSuVhDtit-7vWNstxSnJIErnfv3-MNjQ0hVOOGck-c9cDuvXNNh0Nx8OTBqCOG4EPX6GiZdUpB27gpSmxEUNsa308kEXV-qmIRptQOco0rGY8HrJ7S1ktWQID89V0jVUHBvw9T6Ktc833rtlffQUEdLex3ijerDnqWorAAkKinXH9TwjgKH4FldpQrOe3b4KpslHn7oUeEsYvepOSFaKKyifym5B", + "dq": "b6-Ny82SZFzMFz4dADqRLapsic8r6YRgjh0txKWgL-3tnXzF-YTutxRIhSUf_ypcKN3KmRLNK9ZIjB1YtC56CgbTseBABUbw9EuSN-g9T6zMM0XA916N7JmQhDeWxEaGPzLvLB50ZWoFnyyws4mC_lB8E4hWo615zBJ_06A87be5wRK-e5fIuVe9Zknlnuu3DuuP6ZSPP7lYf0A8ms_Eg8RHHUx_1WTNsCnwdPDfY_O1Fsg4MVpXdeqho7s6UouZ", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-3072-72.localhost:8443/keys/jsonBackupRsa-3072-72/5ae349c717644397a14df7ece5eda28f", + "kty": "RSA-HSM", + "n": "AI2OC6QgoA3x6CzwXr8GPdMUZSOJ3FdKxS598-xoSCbwJGB6a761d0ugcFnqX87RsQAxaEp3OS3xviSGa0ilc-NIeGkbtKjd96bAkgAKxHRB99fkyHAxA-y8QQzvPF7xgx-eKKs0uR_NGFyJDeqOZPzmfdLZydpq8Y_Rmc_uNPxGaHzbTCiumkkNd9OXs1r6U1OPJecnv6tHLYDh8OMLJ3WZNbJ5OqlpOGMShWgoCV1c5olqcBoHQRyMCH31x9fnxKqWJMFnF5B3yimD-8AaTkTo4EEmi5-mrqJ4IDGoUSh9p8hgafv7XNmkms0ZkEUrsXOLuLvvPeyza_Ka9tgJBUDmkrjUu9mTUyRcKaiBHyR5VLxUBmeNtvS_Df_Vqoi4G-6vMQvVgut1pp6bijBjYtKp5b7m8mxtJ6my1nShu-NxAw4JFMQMmh477cJxqzfrBzlfefvFnhtFS8K78t1CJNRuEmDNB4TNlgrzLlvJq9BUzflBD-XvT_9Vj1_oIcGw3Q", + "p": "AMVKqqCIRq9jh5SoW5UxLTfNrsjZkK5J4RAxgwcf2AtQXL0dxOUU-FepkASfo3MoYkStP9aJPlZKMJ0iixkgUjL-Fr0cX2tFgkZoFmyDKTmLguGvCEt07NJD633cnzAZ728OMsP6cK_bYcE48TuRtNvnbVq_QPbm7JLJii_qf2HHPL-EdFlBoFTWWWErxYfRd9XwxAgwHvd0FiNrlHRvtUxuc8h1OnYwORhtXPlrx5D0WFSUlWHbgLOEAnp_EI2FQQ", + "q": "ALetcnXW5mFbud_4hh_Vw6QHuRrw1l0MYtgeFvVld6pDzAMzoiVbAVLM0rgmk9vVrHta5b5uj2cT08QqU8EXDRHgnC_fOT-ay966AaQmu3RUACeVv0CcRJQB_FzQ4RLUtHoDj7No23jGjLH849NNc8YaTYAWD7ntQEoVorxhZIyxGI7Zaf_pajkgQPKe2rT88SbONK9UTV-iAQ6wr-_W4amWlhpCWvObCHkhWFL01kmJhVDcrHvslCq4hdDuCeD4nQ", + "qi": "MLMFbdT1qRanqk8krWVwbhStSNOdznS75gS3PgmrCLmJ63OCkk6O0EczBHrByPQF5P5sha_ec7ZCIFHaYGakTFrk-4vQJTFtFhx5IripRFUQ2NFhLzhDh_P2Tlg_TBjlaA1AF99iuKPoon2s114a5vq_C14Yzx0aTZgn4UzPUTAi5GATnyGknlR0A9bErtxSdLbHkgOTzYfQBMJvtzgbN8JSsT23cuE-bmitsGjokTFhMvyvmH0ZcaFY4RzTKXeQ" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json index 16c6ac2d..59302e34 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json @@ -1,27 +1,62 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-3072-73.localhost:8443", - "entityId" : "jsonBackupRsa-3072-73", - "entityVersion" : "5ae349c717644397a14df7ece5eda28f", - "attributes" : { - "enabled" : true, - "created" : 1649506106, - "updated" : 1649506106, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "FwpaGDisPKDVoOzuCws9yGvvK2nu2LGFp1PefmoZ5mabkS2T-IpqdL-UUfpdZllIxWaFWmuN_c21nJXZhA1UL9g1qywUi5D3lKePDB00jVTIfdZSDrrXr_KTZwLQsYZ_iIeyiG1wPX4hWzx-rKFJTEXLEKUc3yWdlIJyFf9olfBy5PG_83Y6DC56RNFSIvqQUg9AhN-6KauP-0SrdBU2csSBkj97viPvhua2mrW7knwC1TajMQVhwn8DidcV1G13nib1ChBjia70Z3b4n_lisK3330BWfuKZAJyEfcUpIPZjFCF3YfAFcKJGA1hBqG5Up4IA02yekMHGtga_TnPPn9Z7RZ-eA6WlaFKgFvY0IKZYVNwkJ88coQxUmmMvgETy-Xz-yQBPLAaQmVW4dhBi9VUGCDHbgKC9n9XK6DNKz6ejhklctehpePrjweTEG4v0sJsSd1TiQBPL60dme3A8jwlLFdSxSjamn8ysDMdVe6Ck5Qtztm2GSRSdC6cb0d3B", - "dp" : "NgJjxYCpARiZJCwWrjgBLfajs55DykK1Vy6GtSuVhDtit-7vWNstxSnJIErnfv3-MNjQ0hVOOGck-c9cDuvXNNh0Nx8OTBqCOG4EPX6GiZdUpB27gpSmxEUNsa308kEXV-qmIRptQOco0rGY8HrJ7S1ktWQID89V0jVUHBvw9T6Ktc833rtlffQUEdLex3ijerDnqWorAAkKinXH9TwjgKH4FldpQrOe3b4KpslHn7oUeEsYvepOSFaKKyifym5B", - "dq" : "b6-Ny82SZFzMFz4dADqRLapsic8r6YRgjh0txKWgL-3tnXzF-YTutxRIhSUf_ypcKN3KmRLNK9ZIjB1YtC56CgbTseBABUbw9EuSN-g9T6zMM0XA916N7JmQhDeWxEaGPzLvLB50ZWoFnyyws4mC_lB8E4hWo615zBJ_06A87be5wRK-e5fIuVe9Zknlnuu3DuuP6ZSPP7lYf0A8ms_Eg8RHHUx_1WTNsCnwdPDfY_O1Fsg4MVpXdeqho7s6UouZ", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-3072-73.localhost:8443/keys/jsonBackupRsa-3072-73/5ae349c717644397a14df7ece5eda28f", - "kty" : "RSA-HSM", - "n" : "AI2OC6QgoA3x6CzwXr8GPdMUZSOJ3FdKxS598-xoSCbwJGB6a761d0ugcFnqX87RsQAxaEp3OS3xviSGa0ilc-NIeGkbtKjd96bAkgAKxHRB99fkyHAxA-y8QQzvPF7xgx-eKKs0uR_NGFyJDeqOZPzmfdLZydpq8Y_Rmc_uNPxGaHzbTCiumkkNd9OXs1r6U1OPJecnv6tHLYDh8OMLJ3WZNbJ5OqlpOGMShWgoCV1c5olqcBoHQRyMCH31x9fnxKqWJMFnF5B3yimD-8AaTkTo4EEmi5-mrqJ4IDGoUSh9p8hgafv7XNmkms0ZkEUrsXOLuLvvPeyza_Ka9tgJBUDmkrjUu9mTUyRcKaiBHyR5VLxUBmeNtvS_Df_Vqoi4G-6vMQvVgut1pp6bijBjYtKp5b7m8mxtJ6my1nShu-NxAw4JFMQMmh477cJxqzfrBzlfefvFnhtFS8K78t1CJNRuEmDNB4TNlgrzLlvJq9BUzflBD-XvT_9Vj1_oIcGw3Q", - "p" : "AMVKqqCIRq9jh5SoW5UxLTfNrsjZkK5J4RAxgwcf2AtQXL0dxOUU-FepkASfo3MoYkStP9aJPlZKMJ0iixkgUjL-Fr0cX2tFgkZoFmyDKTmLguGvCEt07NJD633cnzAZ728OMsP6cK_bYcE48TuRtNvnbVq_QPbm7JLJii_qf2HHPL-EdFlBoFTWWWErxYfRd9XwxAgwHvd0FiNrlHRvtUxuc8h1OnYwORhtXPlrx5D0WFSUlWHbgLOEAnp_EI2FQQ", - "q" : "ALetcnXW5mFbud_4hh_Vw6QHuRrw1l0MYtgeFvVld6pDzAMzoiVbAVLM0rgmk9vVrHta5b5uj2cT08QqU8EXDRHgnC_fOT-ay966AaQmu3RUACeVv0CcRJQB_FzQ4RLUtHoDj7No23jGjLH849NNc8YaTYAWD7ntQEoVorxhZIyxGI7Zaf_pajkgQPKe2rT88SbONK9UTV-iAQ6wr-_W4amWlhpCWvObCHkhWFL01kmJhVDcrHvslCq4hdDuCeD4nQ", - "qi" : "MLMFbdT1qRanqk8krWVwbhStSNOdznS75gS3PgmrCLmJ63OCkk6O0EczBHrByPQF5P5sha_ec7ZCIFHaYGakTFrk-4vQJTFtFhx5IripRFUQ2NFhLzhDh_P2Tlg_TBjlaA1AF99iuKPoon2s114a5vq_C14Yzx0aTZgn4UzPUTAi5GATnyGknlR0A9bErtxSdLbHkgOTzYfQBMJvtzgbN8JSsT23cuE-bmitsGjokTFhMvyvmH0ZcaFY4RzTKXeQ" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-3072-73.localhost:8443", + "entityId": "jsonBackupRsa-3072-73", + "entityVersion": "5ae349c717644397a14df7ece5eda28f", + "attributes": { + "enabled": true, + "created": 1649506106, + "updated": 1649506106, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "FwpaGDisPKDVoOzuCws9yGvvK2nu2LGFp1PefmoZ5mabkS2T-IpqdL-UUfpdZllIxWaFWmuN_c21nJXZhA1UL9g1qywUi5D3lKePDB00jVTIfdZSDrrXr_KTZwLQsYZ_iIeyiG1wPX4hWzx-rKFJTEXLEKUc3yWdlIJyFf9olfBy5PG_83Y6DC56RNFSIvqQUg9AhN-6KauP-0SrdBU2csSBkj97viPvhua2mrW7knwC1TajMQVhwn8DidcV1G13nib1ChBjia70Z3b4n_lisK3330BWfuKZAJyEfcUpIPZjFCF3YfAFcKJGA1hBqG5Up4IA02yekMHGtga_TnPPn9Z7RZ-eA6WlaFKgFvY0IKZYVNwkJ88coQxUmmMvgETy-Xz-yQBPLAaQmVW4dhBi9VUGCDHbgKC9n9XK6DNKz6ejhklctehpePrjweTEG4v0sJsSd1TiQBPL60dme3A8jwlLFdSxSjamn8ysDMdVe6Ck5Qtztm2GSRSdC6cb0d3B", + "dp": "NgJjxYCpARiZJCwWrjgBLfajs55DykK1Vy6GtSuVhDtit-7vWNstxSnJIErnfv3-MNjQ0hVOOGck-c9cDuvXNNh0Nx8OTBqCOG4EPX6GiZdUpB27gpSmxEUNsa308kEXV-qmIRptQOco0rGY8HrJ7S1ktWQID89V0jVUHBvw9T6Ktc833rtlffQUEdLex3ijerDnqWorAAkKinXH9TwjgKH4FldpQrOe3b4KpslHn7oUeEsYvepOSFaKKyifym5B", + "dq": "b6-Ny82SZFzMFz4dADqRLapsic8r6YRgjh0txKWgL-3tnXzF-YTutxRIhSUf_ypcKN3KmRLNK9ZIjB1YtC56CgbTseBABUbw9EuSN-g9T6zMM0XA916N7JmQhDeWxEaGPzLvLB50ZWoFnyyws4mC_lB8E4hWo615zBJ_06A87be5wRK-e5fIuVe9Zknlnuu3DuuP6ZSPP7lYf0A8ms_Eg8RHHUx_1WTNsCnwdPDfY_O1Fsg4MVpXdeqho7s6UouZ", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-3072-73.localhost:8443/keys/jsonBackupRsa-3072-73/5ae349c717644397a14df7ece5eda28f", + "kty": "RSA-HSM", + "n": "AI2OC6QgoA3x6CzwXr8GPdMUZSOJ3FdKxS598-xoSCbwJGB6a761d0ugcFnqX87RsQAxaEp3OS3xviSGa0ilc-NIeGkbtKjd96bAkgAKxHRB99fkyHAxA-y8QQzvPF7xgx-eKKs0uR_NGFyJDeqOZPzmfdLZydpq8Y_Rmc_uNPxGaHzbTCiumkkNd9OXs1r6U1OPJecnv6tHLYDh8OMLJ3WZNbJ5OqlpOGMShWgoCV1c5olqcBoHQRyMCH31x9fnxKqWJMFnF5B3yimD-8AaTkTo4EEmi5-mrqJ4IDGoUSh9p8hgafv7XNmkms0ZkEUrsXOLuLvvPeyza_Ka9tgJBUDmkrjUu9mTUyRcKaiBHyR5VLxUBmeNtvS_Df_Vqoi4G-6vMQvVgut1pp6bijBjYtKp5b7m8mxtJ6my1nShu-NxAw4JFMQMmh477cJxqzfrBzlfefvFnhtFS8K78t1CJNRuEmDNB4TNlgrzLlvJq9BUzflBD-XvT_9Vj1_oIcGw3Q", + "p": "AMVKqqCIRq9jh5SoW5UxLTfNrsjZkK5J4RAxgwcf2AtQXL0dxOUU-FepkASfo3MoYkStP9aJPlZKMJ0iixkgUjL-Fr0cX2tFgkZoFmyDKTmLguGvCEt07NJD633cnzAZ728OMsP6cK_bYcE48TuRtNvnbVq_QPbm7JLJii_qf2HHPL-EdFlBoFTWWWErxYfRd9XwxAgwHvd0FiNrlHRvtUxuc8h1OnYwORhtXPlrx5D0WFSUlWHbgLOEAnp_EI2FQQ", + "q": "ALetcnXW5mFbud_4hh_Vw6QHuRrw1l0MYtgeFvVld6pDzAMzoiVbAVLM0rgmk9vVrHta5b5uj2cT08QqU8EXDRHgnC_fOT-ay966AaQmu3RUACeVv0CcRJQB_FzQ4RLUtHoDj7No23jGjLH849NNc8YaTYAWD7ntQEoVorxhZIyxGI7Zaf_pajkgQPKe2rT88SbONK9UTV-iAQ6wr-_W4amWlhpCWvObCHkhWFL01kmJhVDcrHvslCq4hdDuCeD4nQ", + "qi": "MLMFbdT1qRanqk8krWVwbhStSNOdznS75gS3PgmrCLmJ63OCkk6O0EczBHrByPQF5P5sha_ec7ZCIFHaYGakTFrk-4vQJTFtFhx5IripRFUQ2NFhLzhDh_P2Tlg_TBjlaA1AF99iuKPoon2s114a5vq_C14Yzx0aTZgn4UzPUTAi5GATnyGknlR0A9bErtxSdLbHkgOTzYfQBMJvtzgbN8JSsT23cuE-bmitsGjokTFhMvyvmH0ZcaFY4RzTKXeQ" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupRsa-3072-73.localhost:8443/keys/jsonBackupRsa-3072-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649504966, + "updated": 1649504966 + } } -} ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json index b0ad62c6..29fb02aa 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json @@ -1,27 +1,35 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-4096-72.localhost:8443", - "entityId" : "jsonBackupRsa-4096-72", - "entityVersion" : "6f009cc18cf146cf960a96254e8d3f39", - "attributes" : { - "enabled" : true, - "created" : 1649504966, - "updated" : 1649504966, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "Hrwbq60uhw31jEz1xm3xuBKcrsun5kzstkxTwkH4QgFvIDszuC1uWN9K46AT10Qr725AyTPTWMwdAXjw01wnuU9gmqcBSjM0kVus2z-WUW_b3qaoTNTmfZ4FkaAzcdslDkeoAu8uRlhbmcuela6pTYSF2vsrIpq-OPCB8PQ4kRU33mmCHk_Ikwx-sjrHqBZqrQubMGCat4n4mgLniHikqv91GK8k0XUDuvwngB6aIMFQ14LoudI12ED7AcP9ia1XwSjSWMRFUzrvJgt2R-jk5iSmZ0y-7WjwR8UBCyHEKEa_ZNP0gtyykSgY2jCwXuC0uRaeSv4AjZOTQuywaV3-aSDjzMx76xxCd2APs3dxkmD07-bRImC1Gcrz21Im7wzskYZTx8TIi6_2PmK6oLZJcewmFLdaveEPSX5KxfSYFNINI5Y9ybsYHA3EBr_vhXYywnvpVOUkRBHcckgxplf7Ehw-P1PFmk1UVsCvhbm2jASbbbG80-nwXocT0X6AimQfKhxgbOJ7I-v71pJD3v3R8UAnkhl8Y3SFwGjbgl_ow6E4OFuuxRZ2LwxFgYJCOnwEE5Xh8Sul9mJB41WN7XmusPqA83kdtJscBne8BmnwmdGaFWoxgxV6k5LNIdIZQox3DBaxgB8BTl5C_NX46TzpnRtpXUQvAK4v564Vc4discU", - "dp" : "XsWQH0UXj7rUjaaeFYWNP6fclxC5opmLrD4YWYBev3PBvS-cO5EhPL1WQtRicTtTPvabOxlXw9L24jZ6lwmpOkQuWMqnpds_Lpj79KAQTLRrQZ3lRMYsQ20z-ILmzCHaiFxkRK-mxYtVXuQ0-k6C4LDJTMxEWlan3iavG0FNoL44VEZGfw-d7vrUMCCZzOTOgIwi4pT_GLuhy2c7XE-Fs_Fia-MAbMbyTNQb1FcNYytcl3-H5e7pdR40rXV4ISr3ZfgU6lE--kQuK83Sgwkn9QCrwchY7ssK_fxGjv5YWBS9FhNE_u2fxGjo1qzoWRY-PepekuP7IBe-PvUnTMvcNQ", - "dq" : "Xh3PxcEoe17LVHtyf2qsfbE-PsDLz_petl-94WCVRHD9yOdM8uPIl20cX0paBKNFq9gCpQEag7iO8cJcuuHhdb1i9uBw2wPaSYvUYMWfdWvEmLCMUYNJn428mW6iAiGbvb6uzXTtcJBVC2OxoBEnEeNFlxtcSAuhAtD3sFi15td6gh8g_-Sh7EzBaxwIDS0pVKLs72V60b9OOggl-jcNL_cmaaNq7iAV2FgzOkZM-lkEfrwb_ovu0XdMT4aDaxLgrHHXpYlR6rKg3BBIZdxihR3Ned_6Mv_tJvaEBiu6aTiBMPsSrLZXN5VkO7-nWfP1unvktpuwnwqxyYcqcHdI4Q", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-4096-72.localhost:8443/keys/jsonBackupRsa-4096-72/6f009cc18cf146cf960a96254e8d3f39", - "kty" : "RSA-HSM", - "n" : "AKNbPrSp6qQr5wIuHOh7U5i9Mdi0mSLYNfa9vaM2CuY32eqaB1uCcfdgYWP_SHRZDOUgzB2zoTc-30gX4ccHrFHoBMaYvckN9RIEAu0AhToqxm2xr77_9G0CNwgPoohlC2WB69pSYtX17fYfQQVfOPE0K8vo6mSWSgIcsKC9Nenib0DbQLMWPl04f0h_rleYKqyt8ecPpVEOkS9bb0d5p7GbaZCekPLdZ6GS_cYZJbiBqwpNjJoR6QbRCE9JyEo4UHDJ3_TX1rc50X5mAhV4lt_x-f-2YmRhExA7JU0IHQaHDABxeAIZ61bZT6MFNmA14316uAr33MeanjvlXrRyZDQ0QYfeXpDrg7qXJHCwP1kuZmQDWgzX5gUPi_9VqUD1FbxfBDWxjeHvmMVfJqIxRAd5Pk2PtJerIyER3YnmPdFK_Vo_31z84X21NFWTm4M93hudv01rr_cFwRAi3SZZ5ymPMjWbTjVjIyXmLMkgn51g8EwKdxOkv-GtE7NacdorbmcrGlL6CXg6MzbMtIXriqOrjGMTZwKRJXCyE56GblZCxbQpbd5Vm2exG_O_pDmobd6D5dJQBLBAPHhcgbqiVmOy9fcpLeVVEAyVoRnOSGLNKXREDHOxWTZPj-IG9AFl1VAaoW1XwpkwaMOP0Xy-kfKDoJsGaqE0BIY8kiXYHmHz", - "p" : "ANxECyf5q69UokIVyU-5uFNsvxPuNlIQSiJn8Bcs3nY6E_H-q1Mw56sCxNz2VW07eDQ8vOyYVF6WjHnGfCMeV-F44PpctxpfXySWqAGPy9KmSMsCXlVVlbi6SeEonJR1yuRM4j_8U8npCg-qelSbw6NJIziu5tbmp32LcQK1i3wj43q2bv1ZnYnNg936-EKpOH_skQds3kwtMsb6KQgIlqogMjwTy1d-kPdJx3ZSMhkZp3RfEPQ9zU4XcHH5jOgUH5orRuPI9XI4U_WT4ETQLsGr8EMAXqDNDHJ1ArwwzQSj025hrYym9nxiPg7VDJCXkjjpG6o_fwwjEJomcP4V-r8", - "q" : "AL3brFARAay4YLokvlYWqWNEnfmQACHWYliew86QygP1LMW6tB5j8ZblfZuRwXUq4m2K_QN_JSr0G1BidFj_6HvMgMIvl5J51Rz7okw6wPw-PCyLu66tUWfBr_9VANNVhtWoORUluE4mf24yiJJFhlv96mV_7qDPmDnilKfP40A3O4VnF8IDq6lf3SN4feXRJofhwPrJbQ0AsXbgy8Jza0i3vrK6oWQhEERx0BwwuRYsfwVc8MP8kzzhaKKJMgCNzbCXUzTFXqcWmIsSgWdbqUVIMx8vHzpDtGhrwbES2meolZo55fadgGS1hLSSx0ciOjxHGe240sKAGeEONwekKc0", - "qi" : "Izh5IJH37JMCWCMkws4wkx_I2p5YFawshvDQ791kVGKudOALGW9ljggOs6HNlPh09YieCUIfJ9-FRQs4XVaAWcaiCNwCY4tyis0-Z1UNLMrsLlYUFfMSmoGQ_aFj3xkAcFvoj2NKpoYbJA83XvAgCi7bm3r5cPq5DdU3F6yngIx1OjQ5OAikidYK1Sj5_Ue4zZLvSRt6-1BdpePwTBkp9RV7uqIn4jg4LQgC1SkkSZSu5mmSdL3msefrLSkRCxsv3qk92P4OsZpxvFm4Oi_A7uKOQ9d23c6iD5wjyCkl1OTuiPEcdjXJ5y0djNccebk0iZDZrsyLuO9qGAIZMAGCvw" - } -} ] +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-4096-72.localhost:8443", + "entityId": "jsonBackupRsa-4096-72", + "entityVersion": "6f009cc18cf146cf960a96254e8d3f39", + "attributes": { + "enabled": true, + "created": 1649504966, + "updated": 1649504966, + "recoveryLevel": "Recoverable+Purgeable", + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "Hrwbq60uhw31jEz1xm3xuBKcrsun5kzstkxTwkH4QgFvIDszuC1uWN9K46AT10Qr725AyTPTWMwdAXjw01wnuU9gmqcBSjM0kVus2z-WUW_b3qaoTNTmfZ4FkaAzcdslDkeoAu8uRlhbmcuela6pTYSF2vsrIpq-OPCB8PQ4kRU33mmCHk_Ikwx-sjrHqBZqrQubMGCat4n4mgLniHikqv91GK8k0XUDuvwngB6aIMFQ14LoudI12ED7AcP9ia1XwSjSWMRFUzrvJgt2R-jk5iSmZ0y-7WjwR8UBCyHEKEa_ZNP0gtyykSgY2jCwXuC0uRaeSv4AjZOTQuywaV3-aSDjzMx76xxCd2APs3dxkmD07-bRImC1Gcrz21Im7wzskYZTx8TIi6_2PmK6oLZJcewmFLdaveEPSX5KxfSYFNINI5Y9ybsYHA3EBr_vhXYywnvpVOUkRBHcckgxplf7Ehw-P1PFmk1UVsCvhbm2jASbbbG80-nwXocT0X6AimQfKhxgbOJ7I-v71pJD3v3R8UAnkhl8Y3SFwGjbgl_ow6E4OFuuxRZ2LwxFgYJCOnwEE5Xh8Sul9mJB41WN7XmusPqA83kdtJscBne8BmnwmdGaFWoxgxV6k5LNIdIZQox3DBaxgB8BTl5C_NX46TzpnRtpXUQvAK4v564Vc4discU", + "dp": "XsWQH0UXj7rUjaaeFYWNP6fclxC5opmLrD4YWYBev3PBvS-cO5EhPL1WQtRicTtTPvabOxlXw9L24jZ6lwmpOkQuWMqnpds_Lpj79KAQTLRrQZ3lRMYsQ20z-ILmzCHaiFxkRK-mxYtVXuQ0-k6C4LDJTMxEWlan3iavG0FNoL44VEZGfw-d7vrUMCCZzOTOgIwi4pT_GLuhy2c7XE-Fs_Fia-MAbMbyTNQb1FcNYytcl3-H5e7pdR40rXV4ISr3ZfgU6lE--kQuK83Sgwkn9QCrwchY7ssK_fxGjv5YWBS9FhNE_u2fxGjo1qzoWRY-PepekuP7IBe-PvUnTMvcNQ", + "dq": "Xh3PxcEoe17LVHtyf2qsfbE-PsDLz_petl-94WCVRHD9yOdM8uPIl20cX0paBKNFq9gCpQEag7iO8cJcuuHhdb1i9uBw2wPaSYvUYMWfdWvEmLCMUYNJn428mW6iAiGbvb6uzXTtcJBVC2OxoBEnEeNFlxtcSAuhAtD3sFi15td6gh8g_-Sh7EzBaxwIDS0pVKLs72V60b9OOggl-jcNL_cmaaNq7iAV2FgzOkZM-lkEfrwb_ovu0XdMT4aDaxLgrHHXpYlR6rKg3BBIZdxihR3Ned_6Mv_tJvaEBiu6aTiBMPsSrLZXN5VkO7-nWfP1unvktpuwnwqxyYcqcHdI4Q", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-4096-72.localhost:8443/keys/jsonBackupRsa-4096-72/6f009cc18cf146cf960a96254e8d3f39", + "kty": "RSA-HSM", + "n": "AKNbPrSp6qQr5wIuHOh7U5i9Mdi0mSLYNfa9vaM2CuY32eqaB1uCcfdgYWP_SHRZDOUgzB2zoTc-30gX4ccHrFHoBMaYvckN9RIEAu0AhToqxm2xr77_9G0CNwgPoohlC2WB69pSYtX17fYfQQVfOPE0K8vo6mSWSgIcsKC9Nenib0DbQLMWPl04f0h_rleYKqyt8ecPpVEOkS9bb0d5p7GbaZCekPLdZ6GS_cYZJbiBqwpNjJoR6QbRCE9JyEo4UHDJ3_TX1rc50X5mAhV4lt_x-f-2YmRhExA7JU0IHQaHDABxeAIZ61bZT6MFNmA14316uAr33MeanjvlXrRyZDQ0QYfeXpDrg7qXJHCwP1kuZmQDWgzX5gUPi_9VqUD1FbxfBDWxjeHvmMVfJqIxRAd5Pk2PtJerIyER3YnmPdFK_Vo_31z84X21NFWTm4M93hudv01rr_cFwRAi3SZZ5ymPMjWbTjVjIyXmLMkgn51g8EwKdxOkv-GtE7NacdorbmcrGlL6CXg6MzbMtIXriqOrjGMTZwKRJXCyE56GblZCxbQpbd5Vm2exG_O_pDmobd6D5dJQBLBAPHhcgbqiVmOy9fcpLeVVEAyVoRnOSGLNKXREDHOxWTZPj-IG9AFl1VAaoW1XwpkwaMOP0Xy-kfKDoJsGaqE0BIY8kiXYHmHz", + "p": "ANxECyf5q69UokIVyU-5uFNsvxPuNlIQSiJn8Bcs3nY6E_H-q1Mw56sCxNz2VW07eDQ8vOyYVF6WjHnGfCMeV-F44PpctxpfXySWqAGPy9KmSMsCXlVVlbi6SeEonJR1yuRM4j_8U8npCg-qelSbw6NJIziu5tbmp32LcQK1i3wj43q2bv1ZnYnNg936-EKpOH_skQds3kwtMsb6KQgIlqogMjwTy1d-kPdJx3ZSMhkZp3RfEPQ9zU4XcHH5jOgUH5orRuPI9XI4U_WT4ETQLsGr8EMAXqDNDHJ1ArwwzQSj025hrYym9nxiPg7VDJCXkjjpG6o_fwwjEJomcP4V-r8", + "q": "AL3brFARAay4YLokvlYWqWNEnfmQACHWYliew86QygP1LMW6tB5j8ZblfZuRwXUq4m2K_QN_JSr0G1BidFj_6HvMgMIvl5J51Rz7okw6wPw-PCyLu66tUWfBr_9VANNVhtWoORUluE4mf24yiJJFhlv96mV_7qDPmDnilKfP40A3O4VnF8IDq6lf3SN4feXRJofhwPrJbQ0AsXbgy8Jza0i3vrK6oWQhEERx0BwwuRYsfwVc8MP8kzzhaKKJMgCNzbCXUzTFXqcWmIsSgWdbqUVIMx8vHzpDtGhrwbES2meolZo55fadgGS1hLSSx0ciOjxHGe240sKAGeEONwekKc0", + "qi": "Izh5IJH37JMCWCMkws4wkx_I2p5YFawshvDQ791kVGKudOALGW9ljggOs6HNlPh09YieCUIfJ9-FRQs4XVaAWcaiCNwCY4tyis0-Z1UNLMrsLlYUFfMSmoGQ_aFj3xkAcFvoj2NKpoYbJA83XvAgCi7bm3r5cPq5DdU3F6yngIx1OjQ5OAikidYK1Sj5_Ue4zZLvSRt6-1BdpePwTBkp9RV7uqIn4jg4LQgC1SkkSZSu5mmSdL3msefrLSkRCxsv3qk92P4OsZpxvFm4Oi_A7uKOQ9d23c6iD5wjyCkl1OTuiPEcdjXJ5y0djNccebk0iZDZrsyLuO9qGAIZMAGCvw" + } + } + ] +} diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json index 3671f817..2cdd9752 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json @@ -1,27 +1,62 @@ -[ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-4096-73.localhost:8443", - "entityId" : "jsonBackupRsa-4096-73", - "entityVersion" : "6f009cc18cf146cf960a96254e8d3f39", - "attributes" : { - "enabled" : true, - "created" : 1649504966, - "updated" : 1649504966, - "recoveryLevel" : "Recoverable+Purgeable", - "recoverableDays" : 90 - }, - "tags" : { }, - "managed" : false, - "keyMaterial" : { - "d" : "Hrwbq60uhw31jEz1xm3xuBKcrsun5kzstkxTwkH4QgFvIDszuC1uWN9K46AT10Qr725AyTPTWMwdAXjw01wnuU9gmqcBSjM0kVus2z-WUW_b3qaoTNTmfZ4FkaAzcdslDkeoAu8uRlhbmcuela6pTYSF2vsrIpq-OPCB8PQ4kRU33mmCHk_Ikwx-sjrHqBZqrQubMGCat4n4mgLniHikqv91GK8k0XUDuvwngB6aIMFQ14LoudI12ED7AcP9ia1XwSjSWMRFUzrvJgt2R-jk5iSmZ0y-7WjwR8UBCyHEKEa_ZNP0gtyykSgY2jCwXuC0uRaeSv4AjZOTQuywaV3-aSDjzMx76xxCd2APs3dxkmD07-bRImC1Gcrz21Im7wzskYZTx8TIi6_2PmK6oLZJcewmFLdaveEPSX5KxfSYFNINI5Y9ybsYHA3EBr_vhXYywnvpVOUkRBHcckgxplf7Ehw-P1PFmk1UVsCvhbm2jASbbbG80-nwXocT0X6AimQfKhxgbOJ7I-v71pJD3v3R8UAnkhl8Y3SFwGjbgl_ow6E4OFuuxRZ2LwxFgYJCOnwEE5Xh8Sul9mJB41WN7XmusPqA83kdtJscBne8BmnwmdGaFWoxgxV6k5LNIdIZQox3DBaxgB8BTl5C_NX46TzpnRtpXUQvAK4v564Vc4discU", - "dp" : "XsWQH0UXj7rUjaaeFYWNP6fclxC5opmLrD4YWYBev3PBvS-cO5EhPL1WQtRicTtTPvabOxlXw9L24jZ6lwmpOkQuWMqnpds_Lpj79KAQTLRrQZ3lRMYsQ20z-ILmzCHaiFxkRK-mxYtVXuQ0-k6C4LDJTMxEWlan3iavG0FNoL44VEZGfw-d7vrUMCCZzOTOgIwi4pT_GLuhy2c7XE-Fs_Fia-MAbMbyTNQb1FcNYytcl3-H5e7pdR40rXV4ISr3ZfgU6lE--kQuK83Sgwkn9QCrwchY7ssK_fxGjv5YWBS9FhNE_u2fxGjo1qzoWRY-PepekuP7IBe-PvUnTMvcNQ", - "dq" : "Xh3PxcEoe17LVHtyf2qsfbE-PsDLz_petl-94WCVRHD9yOdM8uPIl20cX0paBKNFq9gCpQEag7iO8cJcuuHhdb1i9uBw2wPaSYvUYMWfdWvEmLCMUYNJn428mW6iAiGbvb6uzXTtcJBVC2OxoBEnEeNFlxtcSAuhAtD3sFi15td6gh8g_-Sh7EzBaxwIDS0pVKLs72V60b9OOggl-jcNL_cmaaNq7iAV2FgzOkZM-lkEfrwb_ovu0XdMT4aDaxLgrHHXpYlR6rKg3BBIZdxihR3Ned_6Mv_tJvaEBiu6aTiBMPsSrLZXN5VkO7-nWfP1unvktpuwnwqxyYcqcHdI4Q", - "e" : "AQAB", - "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-4096-73.localhost:8443/keys/jsonBackupRsa-4096-73/6f009cc18cf146cf960a96254e8d3f39", - "kty" : "RSA-HSM", - "n" : "AKNbPrSp6qQr5wIuHOh7U5i9Mdi0mSLYNfa9vaM2CuY32eqaB1uCcfdgYWP_SHRZDOUgzB2zoTc-30gX4ccHrFHoBMaYvckN9RIEAu0AhToqxm2xr77_9G0CNwgPoohlC2WB69pSYtX17fYfQQVfOPE0K8vo6mSWSgIcsKC9Nenib0DbQLMWPl04f0h_rleYKqyt8ecPpVEOkS9bb0d5p7GbaZCekPLdZ6GS_cYZJbiBqwpNjJoR6QbRCE9JyEo4UHDJ3_TX1rc50X5mAhV4lt_x-f-2YmRhExA7JU0IHQaHDABxeAIZ61bZT6MFNmA14316uAr33MeanjvlXrRyZDQ0QYfeXpDrg7qXJHCwP1kuZmQDWgzX5gUPi_9VqUD1FbxfBDWxjeHvmMVfJqIxRAd5Pk2PtJerIyER3YnmPdFK_Vo_31z84X21NFWTm4M93hudv01rr_cFwRAi3SZZ5ymPMjWbTjVjIyXmLMkgn51g8EwKdxOkv-GtE7NacdorbmcrGlL6CXg6MzbMtIXriqOrjGMTZwKRJXCyE56GblZCxbQpbd5Vm2exG_O_pDmobd6D5dJQBLBAPHhcgbqiVmOy9fcpLeVVEAyVoRnOSGLNKXREDHOxWTZPj-IG9AFl1VAaoW1XwpkwaMOP0Xy-kfKDoJsGaqE0BIY8kiXYHmHz", - "p" : "ANxECyf5q69UokIVyU-5uFNsvxPuNlIQSiJn8Bcs3nY6E_H-q1Mw56sCxNz2VW07eDQ8vOyYVF6WjHnGfCMeV-F44PpctxpfXySWqAGPy9KmSMsCXlVVlbi6SeEonJR1yuRM4j_8U8npCg-qelSbw6NJIziu5tbmp32LcQK1i3wj43q2bv1ZnYnNg936-EKpOH_skQds3kwtMsb6KQgIlqogMjwTy1d-kPdJx3ZSMhkZp3RfEPQ9zU4XcHH5jOgUH5orRuPI9XI4U_WT4ETQLsGr8EMAXqDNDHJ1ArwwzQSj025hrYym9nxiPg7VDJCXkjjpG6o_fwwjEJomcP4V-r8", - "q" : "AL3brFARAay4YLokvlYWqWNEnfmQACHWYliew86QygP1LMW6tB5j8ZblfZuRwXUq4m2K_QN_JSr0G1BidFj_6HvMgMIvl5J51Rz7okw6wPw-PCyLu66tUWfBr_9VANNVhtWoORUluE4mf24yiJJFhlv96mV_7qDPmDnilKfP40A3O4VnF8IDq6lf3SN4feXRJofhwPrJbQ0AsXbgy8Jza0i3vrK6oWQhEERx0BwwuRYsfwVc8MP8kzzhaKKJMgCNzbCXUzTFXqcWmIsSgWdbqUVIMx8vHzpDtGhrwbES2meolZo55fadgGS1hLSSx0ciOjxHGe240sKAGeEONwekKc0", - "qi" : "Izh5IJH37JMCWCMkws4wkx_I2p5YFawshvDQ791kVGKudOALGW9ljggOs6HNlPh09YieCUIfJ9-FRQs4XVaAWcaiCNwCY4tyis0-Z1UNLMrsLlYUFfMSmoGQ_aFj3xkAcFvoj2NKpoYbJA83XvAgCi7bm3r5cPq5DdU3F6yngIx1OjQ5OAikidYK1Sj5_Ue4zZLvSRt6-1BdpePwTBkp9RV7uqIn4jg4LQgC1SkkSZSu5mmSdL3msefrLSkRCxsv3qk92P4OsZpxvFm4Oi_A7uKOQ9d23c6iD5wjyCkl1OTuiPEcdjXJ5y0djNccebk0iZDZrsyLuO9qGAIZMAGCvw" +{ + "versions": [ + { + "vaultBaseUri": "https://keys-backup-jsonBackupRsa-4096-73.localhost:8443", + "entityId": "jsonBackupRsa-4096-73", + "entityVersion": "6f009cc18cf146cf960a96254e8d3f39", + "attributes": { + "enabled": true, + "created": 1649504966, + "updated": 1649504966, + "recoveryLevel": "Recoverable+Purgeable", + "exp": 1659871879, + "recoverableDays": 90 + }, + "tags": {}, + "managed": false, + "keyMaterial": { + "d": "Hrwbq60uhw31jEz1xm3xuBKcrsun5kzstkxTwkH4QgFvIDszuC1uWN9K46AT10Qr725AyTPTWMwdAXjw01wnuU9gmqcBSjM0kVus2z-WUW_b3qaoTNTmfZ4FkaAzcdslDkeoAu8uRlhbmcuela6pTYSF2vsrIpq-OPCB8PQ4kRU33mmCHk_Ikwx-sjrHqBZqrQubMGCat4n4mgLniHikqv91GK8k0XUDuvwngB6aIMFQ14LoudI12ED7AcP9ia1XwSjSWMRFUzrvJgt2R-jk5iSmZ0y-7WjwR8UBCyHEKEa_ZNP0gtyykSgY2jCwXuC0uRaeSv4AjZOTQuywaV3-aSDjzMx76xxCd2APs3dxkmD07-bRImC1Gcrz21Im7wzskYZTx8TIi6_2PmK6oLZJcewmFLdaveEPSX5KxfSYFNINI5Y9ybsYHA3EBr_vhXYywnvpVOUkRBHcckgxplf7Ehw-P1PFmk1UVsCvhbm2jASbbbG80-nwXocT0X6AimQfKhxgbOJ7I-v71pJD3v3R8UAnkhl8Y3SFwGjbgl_ow6E4OFuuxRZ2LwxFgYJCOnwEE5Xh8Sul9mJB41WN7XmusPqA83kdtJscBne8BmnwmdGaFWoxgxV6k5LNIdIZQox3DBaxgB8BTl5C_NX46TzpnRtpXUQvAK4v564Vc4discU", + "dp": "XsWQH0UXj7rUjaaeFYWNP6fclxC5opmLrD4YWYBev3PBvS-cO5EhPL1WQtRicTtTPvabOxlXw9L24jZ6lwmpOkQuWMqnpds_Lpj79KAQTLRrQZ3lRMYsQ20z-ILmzCHaiFxkRK-mxYtVXuQ0-k6C4LDJTMxEWlan3iavG0FNoL44VEZGfw-d7vrUMCCZzOTOgIwi4pT_GLuhy2c7XE-Fs_Fia-MAbMbyTNQb1FcNYytcl3-H5e7pdR40rXV4ISr3ZfgU6lE--kQuK83Sgwkn9QCrwchY7ssK_fxGjv5YWBS9FhNE_u2fxGjo1qzoWRY-PepekuP7IBe-PvUnTMvcNQ", + "dq": "Xh3PxcEoe17LVHtyf2qsfbE-PsDLz_petl-94WCVRHD9yOdM8uPIl20cX0paBKNFq9gCpQEag7iO8cJcuuHhdb1i9uBw2wPaSYvUYMWfdWvEmLCMUYNJn428mW6iAiGbvb6uzXTtcJBVC2OxoBEnEeNFlxtcSAuhAtD3sFi15td6gh8g_-Sh7EzBaxwIDS0pVKLs72V60b9OOggl-jcNL_cmaaNq7iAV2FgzOkZM-lkEfrwb_ovu0XdMT4aDaxLgrHHXpYlR6rKg3BBIZdxihR3Ned_6Mv_tJvaEBiu6aTiBMPsSrLZXN5VkO7-nWfP1unvktpuwnwqxyYcqcHdI4Q", + "e": "AQAB", + "key_ops": [ + "sign", + "encrypt", + "wrapKey" + ], + "kid": "https://keys-backup-jsonBackupRsa-4096-73.localhost:8443/keys/jsonBackupRsa-4096-73/6f009cc18cf146cf960a96254e8d3f39", + "kty": "RSA-HSM", + "n": "AKNbPrSp6qQr5wIuHOh7U5i9Mdi0mSLYNfa9vaM2CuY32eqaB1uCcfdgYWP_SHRZDOUgzB2zoTc-30gX4ccHrFHoBMaYvckN9RIEAu0AhToqxm2xr77_9G0CNwgPoohlC2WB69pSYtX17fYfQQVfOPE0K8vo6mSWSgIcsKC9Nenib0DbQLMWPl04f0h_rleYKqyt8ecPpVEOkS9bb0d5p7GbaZCekPLdZ6GS_cYZJbiBqwpNjJoR6QbRCE9JyEo4UHDJ3_TX1rc50X5mAhV4lt_x-f-2YmRhExA7JU0IHQaHDABxeAIZ61bZT6MFNmA14316uAr33MeanjvlXrRyZDQ0QYfeXpDrg7qXJHCwP1kuZmQDWgzX5gUPi_9VqUD1FbxfBDWxjeHvmMVfJqIxRAd5Pk2PtJerIyER3YnmPdFK_Vo_31z84X21NFWTm4M93hudv01rr_cFwRAi3SZZ5ymPMjWbTjVjIyXmLMkgn51g8EwKdxOkv-GtE7NacdorbmcrGlL6CXg6MzbMtIXriqOrjGMTZwKRJXCyE56GblZCxbQpbd5Vm2exG_O_pDmobd6D5dJQBLBAPHhcgbqiVmOy9fcpLeVVEAyVoRnOSGLNKXREDHOxWTZPj-IG9AFl1VAaoW1XwpkwaMOP0Xy-kfKDoJsGaqE0BIY8kiXYHmHz", + "p": "ANxECyf5q69UokIVyU-5uFNsvxPuNlIQSiJn8Bcs3nY6E_H-q1Mw56sCxNz2VW07eDQ8vOyYVF6WjHnGfCMeV-F44PpctxpfXySWqAGPy9KmSMsCXlVVlbi6SeEonJR1yuRM4j_8U8npCg-qelSbw6NJIziu5tbmp32LcQK1i3wj43q2bv1ZnYnNg936-EKpOH_skQds3kwtMsb6KQgIlqogMjwTy1d-kPdJx3ZSMhkZp3RfEPQ9zU4XcHH5jOgUH5orRuPI9XI4U_WT4ETQLsGr8EMAXqDNDHJ1ArwwzQSj025hrYym9nxiPg7VDJCXkjjpG6o_fwwjEJomcP4V-r8", + "q": "AL3brFARAay4YLokvlYWqWNEnfmQACHWYliew86QygP1LMW6tB5j8ZblfZuRwXUq4m2K_QN_JSr0G1BidFj_6HvMgMIvl5J51Rz7okw6wPw-PCyLu66tUWfBr_9VANNVhtWoORUluE4mf24yiJJFhlv96mV_7qDPmDnilKfP40A3O4VnF8IDq6lf3SN4feXRJofhwPrJbQ0AsXbgy8Jza0i3vrK6oWQhEERx0BwwuRYsfwVc8MP8kzzhaKKJMgCNzbCXUzTFXqcWmIsSgWdbqUVIMx8vHzpDtGhrwbES2meolZo55fadgGS1hLSSx0ciOjxHGe240sKAGeEONwekKc0", + "qi": "Izh5IJH37JMCWCMkws4wkx_I2p5YFawshvDQ791kVGKudOALGW9ljggOs6HNlPh09YieCUIfJ9-FRQs4XVaAWcaiCNwCY4tyis0-Z1UNLMrsLlYUFfMSmoGQ_aFj3xkAcFvoj2NKpoYbJA83XvAgCi7bm3r5cPq5DdU3F6yngIx1OjQ5OAikidYK1Sj5_Ue4zZLvSRt6-1BdpePwTBkp9RV7uqIn4jg4LQgC1SkkSZSu5mmSdL3msefrLSkRCxsv3qk92P4OsZpxvFm4Oi_A7uKOQ9d23c6iD5wjyCkl1OTuiPEcdjXJ5y0djNccebk0iZDZrsyLuO9qGAIZMAGCvw" + } + } + ], + "rotationPolicy": { + "id": "https://keys-backup-jsonBackupRsa-4096-73.localhost:8443/keys/jsonBackupRsa-4096-73/rotationpolicy", + "lifetimeActions": [ + { + "trigger": { + "timeBeforeExpiry": "P30D" + }, + "action": { + "type": "notify" + } + }, + { + "trigger": { + "timeAfterCreate": "P100D" + }, + "action": { + "type": "rotate" + } + } + ], + "attributes": { + "expiryTime": "P120D", + "created": 1649504966, + "updated": 1649504966 + } } -} ] +}