From 885f473a0e96ac0a2597c861a790e011810b439d Mon Sep 17 00:00:00 2001 From: Esta Nagy Date: Sun, 8 May 2022 00:07:29 +0200 Subject: [PATCH] Add basic support for v7.3 API (#153) - Adds endpoint aliases for 7.3 Secret API - Adds endpoint aliases for 7.3 Key API - Adds new test cases covering 7.3 endpoints too - Updates readme Resolves #150 {minor} Signed-off-by: Esta Nagy --- README.md | 4 +- .../BaseBackupRestoreController.java | 2 +- .../BaseEntityReadController.java | 11 +- .../CommonKeyBackupRestoreController.java | 80 ++ .../common/CommonKeyController.java | 222 ++++ .../common/CommonKeyCryptoController.java | 104 ++ .../CommonSecretBackupRestoreController.java | 76 ++ .../common/CommonSecretController.java | 190 ++++ .../GenericEntityController.java | 6 +- .../v7_2/KeyBackupRestoreController.java | 68 +- .../controller/v7_2/KeyController.java | 157 +-- .../controller/v7_2/KeyCryptoController.java | 65 +- .../v7_2/SecretBackupRestoreController.java | 58 +- .../controller/v7_2/SecretController.java | 129 +-- .../v7_3/KeyBackupRestoreController.java | 63 ++ .../controller/v7_3/KeyController.java | 185 ++++ .../controller/v7_3/KeyCryptoController.java | 102 ++ .../v7_3/SecretBackupRestoreController.java | 64 ++ .../controller/v7_3/SecretController.java | 177 ++++ .../model/common/ApiConstants.java | 16 + .../lowkeyvault/MissionOutlineDefinition.java | 8 +- .../{v7_2 => common}/PingControllerTest.java | 2 +- ...ackupRestoreControllerIntegrationTest.java | 2 + .../v7_2/KeyControllerIntegrationTest.java | 2 + ...ackupRestoreControllerIntegrationTest.java | 2 + ...ackupRestoreControllerIntegrationTest.java | 276 +++++ .../v7_3/KeyControllerIntegrationTest.java | 175 ++++ .../controller/v7_3/KeyControllerTest.java | 947 ++++++++++++++++++ .../v7_3/KeyCryptoControllerTest.java | 248 +++++ ...ackupRestoreControllerIntegrationTest.java | 249 +++++ .../controller/v7_3/SecretControllerTest.java | 857 ++++++++++++++++ .../http/ApacheHttpClientProvider.java | 43 +- lowkey-vault-docker/build.gradle | 1 + .../context/CommonTestContext.java | 9 +- .../lowkeyvault/context/KeyTestContext.java | 26 +- .../context/SecretTestContext.java | 24 +- .../lowkeyvault/steps/KeysStepDefs.java | 63 +- .../steps/KeysStepDefsAssertions.java | 4 +- .../lowkeyvault/steps/ParameterTypeDefs.java | 5 + .../lowkeyvault/steps/SecretsStepDefs.java | 36 +- .../keys/BackupAndRestoreKeys.feature | 120 ++- .../lowkeyvault/keys/CreateKeys.feature | 129 ++- .../lowkeyvault/keys/DeleteKeys.feature | 99 +- .../lowkeyvault/keys/EncryptWithKeys.feature | 46 +- .../nagyesta/lowkeyvault/keys/GetKeys.feature | 209 ++-- .../lowkeyvault/keys/ImportKeys.feature | 75 +- .../lowkeyvault/keys/ListDeletedKeys.feature | 63 +- .../lowkeyvault/keys/ListKeys.feature | 63 +- .../lowkeyvault/keys/SignWithKeys.feature | 52 +- .../secrets/BackupAndRestoreSecrets.feature | 31 +- .../lowkeyvault/secrets/CreateSecrets.feature | 41 +- .../lowkeyvault/secrets/DeleteSecrets.feature | 42 +- .../lowkeyvault/secrets/GetSecrets.feature | 96 +- .../secrets/ListDeletedSecrets.feature | 25 +- .../lowkeyvault/secrets/ListSecrets.feature | 25 +- ...upEc-256.json => jsonBackupEc-256-72.json} | 8 +- .../json/backups/jsonBackupEc-256-73.json | 23 + ...Ec-256k.json => jsonBackupEc-256k-72.json} | 8 +- .../json/backups/jsonBackupEc-256k-73.json | 23 + ...upEc-384.json => jsonBackupEc-384-72.json} | 8 +- .../json/backups/jsonBackupEc-384-73.json | 23 + ...upEc-521.json => jsonBackupEc-521-72.json} | 8 +- .../json/backups/jsonBackupEc-521-73.json | 23 + ...Oct-128.json => jsonBackupOct-128-72.json} | 6 +- .../json/backups/jsonBackupOct-128-73.json | 20 + ...Oct-192.json => jsonBackupOct-192-72.json} | 6 +- .../json/backups/jsonBackupOct-192-73.json | 20 + ...Oct-256.json => jsonBackupOct-256-72.json} | 6 +- .../json/backups/jsonBackupOct-256-73.json | 20 + ...a-2048.json => jsonBackupRsa-2048-72.json} | 8 +- .../json/backups/jsonBackupRsa-2048-73.json | 27 + ...a-3072.json => jsonBackupRsa-3072-72.json} | 8 +- .../json/backups/jsonBackupRsa-3072-73.json | 27 + ...a-4096.json => jsonBackupRsa-4096-72.json} | 8 +- .../json/backups/jsonBackupRsa-4096-73.json | 27 + lowkey-vault-testcontainers/build.gradle | 1 + 76 files changed, 5310 insertions(+), 872 deletions(-) rename lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/{v7_2 => common}/BaseBackupRestoreController.java (99%) rename lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/{v7_2 => common}/BaseEntityReadController.java (90%) create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyCryptoController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretController.java rename lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/{v7_2 => common}/GenericEntityController.java (96%) create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreController.java create mode 100644 lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretController.java rename lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/{v7_2 => common}/PingControllerTest.java (91%) create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerIntegrationTest.java create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java create mode 100644 lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretControllerTest.java rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupEc-256.json => jsonBackupEc-256-72.json} (69%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupEc-256k.json => jsonBackupEc-256k-72.json} (69%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupEc-384.json => jsonBackupEc-384-72.json} (71%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupEc-521.json => jsonBackupEc-521-72.json} (73%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupOct-128.json => jsonBackupOct-128-72.json} (62%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupOct-192.json => jsonBackupOct-192-72.json} (62%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupOct-256.json => jsonBackupOct-256-72.json} (63%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupRsa-2048.json => jsonBackupRsa-2048-72.json} (88%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupRsa-3072.json => jsonBackupRsa-3072-72.json} (91%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json rename lowkey-vault-docker/src/test/resources/json/backups/{jsonBackupRsa-4096.json => jsonBackupRsa-4096-72.json} (93%) create mode 100644 lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json diff --git a/README.md b/README.md index 2563c8ed..425136f4 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo ### Keys -- API version supported: ```7.2``` +- API version supported: ```7.2```, partially ```7.3``` - Create key (```RSA```, ```EC```, ```OCT```) - Including metadata - Import key (```RSA```, ```EC```, ```OCT```) @@ -119,7 +119,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo ### Secrets -- API version supported: ```7.2``` +- API version supported: ```7.2```, ```7.3``` - Set secret - Including metadata - Get available secret versions diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java similarity index 99% rename from lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseBackupRestoreController.java rename to lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java index 33bd41b4..1ac60066 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseBackupRestoreController.java @@ -1,4 +1,4 @@ -package com.github.nagyesta.lowkeyvault.controller.v7_2; +package com.github.nagyesta.lowkeyvault.controller.common; import com.github.nagyesta.lowkeyvault.mapper.common.BackupConverter; import com.github.nagyesta.lowkeyvault.mapper.common.RecoveryAwareConverter; diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseEntityReadController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseEntityReadController.java similarity index 90% rename from lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseEntityReadController.java rename to lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseEntityReadController.java index f375e8a4..dba4c595 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/BaseEntityReadController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/BaseEntityReadController.java @@ -1,4 +1,4 @@ -package com.github.nagyesta.lowkeyvault.controller.v7_2; +package com.github.nagyesta.lowkeyvault.controller.common; import com.github.nagyesta.lowkeyvault.controller.ErrorHandlingAwareController; import com.github.nagyesta.lowkeyvault.service.EntityId; @@ -13,8 +13,6 @@ import java.util.Optional; import java.util.function.Function; -import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; - /** * The base implementation of the backup/restore controllers. * @@ -25,10 +23,7 @@ */ public abstract class BaseEntityReadController, S extends BaseVaultFake> extends ErrorHandlingAwareController { - /** - * API version. - */ - protected static final String API_VERSION_7_2 = "api-version=" + V_7_2; + /** * RegExp of entity names (key name, secret name, certificate name). */ @@ -62,4 +57,6 @@ protected S getVaultByUri(final URI baseUri) { protected abstract K entityId(URI baseUri, String name); + protected abstract String apiVersion(); + } 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 new file mode 100644 index 00000000..38bf9aeb --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyBackupRestoreController.java @@ -0,0 +1,80 @@ +package com.github.nagyesta.lowkeyvault.controller.common; + +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.v7_2.key.*; +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; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; +import java.util.Collections; +import java.util.Objects; + +@Slf4j +public abstract class CommonKeyBackupRestoreController extends BaseBackupRestoreController { + + protected CommonKeyBackupRestoreController( + @NonNull final KeyEntityToV72ModelConverter modelConverter, + @NonNull final KeyEntityToV72BackupConverter backupConverter, + @NonNull final VaultService vaultService) { + super(modelConverter, backupConverter, vaultService, VaultFake::keyVaultFake); + } + + public ResponseEntity backup( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} backup key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + return ResponseEntity.ok(backupEntity(entityId(baseUri, keyName))); + } + + public ResponseEntity restore( + final URI baseUri, + @Valid final KeyBackupModel keyBackupModel) { + log.info("Received request to {} restore key: {} using API version: {}", + baseUri.toString(), keyBackupModel.getValue().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()); + final KeyVaultKeyEntity entity = vault.getEntities().getEntity(versionedEntityId, KeyVaultKeyEntity.class); + entity.setOperations(Objects.requireNonNullElse(entityVersion.getKeyMaterial().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); + } + + @Override + protected KeyEntityId entityId(final URI baseUri, final String name) { + return new KeyEntityId(baseUri, name); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyController.java new file mode 100644 index 00000000..f4c2a966 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyController.java @@ -0,0 +1,222 @@ +package com.github.nagyesta.lowkeyvault.controller.common; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; +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.JsonWebKeyImportRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.UpdateKeyRequest; +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; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; +import java.util.Objects; +import java.util.Optional; + +@Slf4j +public abstract class CommonKeyController extends GenericEntityController { + + protected CommonKeyController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + @NonNull final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService, VaultFake::keyVaultFake); + } + + public ResponseEntity create( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri, + @Valid final CreateKeyRequest request) { + log.info("Received request to {} create key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final VersionedKeyEntityId keyEntityId = createKeyWithAttributes(keyVaultFake, keyName, request); + return ResponseEntity.ok(getModelById(keyVaultFake, keyEntityId)); + } + + public ResponseEntity importKey( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri, + @Valid final ImportKeyRequest request) { + log.info("Received request to {} import key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final VersionedKeyEntityId keyEntityId = importKeyWithAttributes(keyVaultFake, keyName, request); + return ResponseEntity.ok(getModelById(keyVaultFake, keyEntityId)); + } + + public ResponseEntity delete( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} delete key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); + keyVaultFake.delete(entityId); + final VersionedKeyEntityId latestVersion = keyVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getDeletedModelById(keyVaultFake, latestVersion)); + } + + public ResponseEntity> versions( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list key versions: {} , (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), keyName, maxResults, skipToken, apiVersion()); + + return ResponseEntity.ok(getPageOfItemVersions(baseUri, keyName, maxResults, skipToken, "/keys/" + keyName + "/versions")); + } + + public ResponseEntity> listKeys( + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list keys, (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), maxResults, skipToken, apiVersion()); + + return ResponseEntity.ok(getPageOfItems(baseUri, maxResults, skipToken, "/keys")); + } + + public ResponseEntity> listDeletedKeys( + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list deleted keys, (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), maxResults, skipToken, apiVersion()); + + return ResponseEntity.ok(getPageOfDeletedItems(baseUri, maxResults, skipToken, "/deletedkeys")); + } + + public ResponseEntity get( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} get key: {} with version: -LATEST- using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + return ResponseEntity.ok(getLatestEntityModel(baseUri, keyName)); + } + + public ResponseEntity getWithVersion( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri) { + log.info("Received request to {} get key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); + return ResponseEntity.ok(convertDetails(keyVaultKeyEntity)); + } + + public ResponseEntity updateVersion( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri, + @Valid final UpdateKeyRequest request) { + log.info("Received request to {} update key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final VersionedKeyEntityId entityId = versionedEntityId(baseUri, keyName, keyVersion); + Optional.ofNullable(request.getKeyOperations()) + .ifPresent(operations -> keyVaultFake.setKeyOperations(entityId, operations)); + updateAttributes(keyVaultFake, entityId, request.getProperties()); + updateTags(keyVaultFake, entityId, request.getTags()); + return ResponseEntity.ok(getModelById(keyVaultFake, entityId)); + } + + public ResponseEntity getDeletedKey( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} get deleted key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); + final VersionedKeyEntityId latestVersion = keyVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getDeletedModelById(keyVaultFake, latestVersion)); + } + + public ResponseEntity recoverDeletedKey( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} recover deleted key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); + keyVaultFake.recover(entityId); + final VersionedKeyEntityId latestVersion = keyVaultFake.getEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getModelById(keyVaultFake, latestVersion)); + } + + public ResponseEntity purgeDeleted( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + final URI baseUri) { + log.info("Received request to {} purge deleted key: {} using API version: {}", + baseUri.toString(), keyName, apiVersion()); + + final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); + final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); + keyVaultFake.purge(entityId); + return ResponseEntity.noContent().build(); + } + + @Override + protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String name, final String version) { + return new VersionedKeyEntityId(baseUri, name, version); + } + + @Override + protected KeyEntityId entityId(final URI baseUri, final String name) { + return new KeyEntityId(baseUri, name); + } + + private VersionedKeyEntityId createKeyWithAttributes( + final KeyVaultFake keyVaultFake, final String keyName, final CreateKeyRequest request) { + final KeyPropertiesModel properties = Objects.requireNonNullElse(request.getProperties(), new KeyPropertiesModel()); + final VersionedKeyEntityId keyEntityId = keyVaultFake.createKeyVersion(keyName, request.toKeyCreationInput()); + keyVaultFake.setKeyOperations(keyEntityId, request.getKeyOperations()); + keyVaultFake.addTags(keyEntityId, request.getTags()); + keyVaultFake.setExpiry(keyEntityId, properties.getNotBefore(), properties.getExpiresOn()); + keyVaultFake.setEnabled(keyEntityId, properties.isEnabled()); + //no need to set managed property as this endpoint cannot create managed entities by definition + return keyEntityId; + } + + private VersionedKeyEntityId importKeyWithAttributes( + final KeyVaultFake keyVaultFake, final String keyName, final ImportKeyRequest request) { + final BasePropertiesUpdateModel properties = Objects.requireNonNullElse(request.getProperties(), new BasePropertiesUpdateModel()); + Assert.isTrue(request.getHsm() == null || request.getHsm() == request.getKey().getKeyType().isHsm(), + "When HSM property is set in request, key type must match it."); + final VersionedKeyEntityId keyEntityId = keyVaultFake.importKeyVersion(keyName, request.getKey()); + final JsonWebKeyImportRequest keyImport = request.getKey(); + keyVaultFake.setKeyOperations(keyEntityId, keyImport.getKeyOps()); + keyVaultFake.addTags(keyEntityId, request.getTags()); + keyVaultFake.setExpiry(keyEntityId, properties.getNotBefore(), properties.getExpiresOn()); + keyVaultFake.setEnabled(keyEntityId, Objects.requireNonNullElse(properties.getEnabled(), true)); + //no need to set managed property as this endpoint cannot create managed entities by definition + return keyEntityId; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyCryptoController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyCryptoController.java new file mode 100644 index 00000000..e2c61ba9 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonKeyCryptoController.java @@ -0,0 +1,104 @@ +package com.github.nagyesta.lowkeyvault.controller.common; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +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.key.request.KeyOperationsParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyVerifyParameters; +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; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +@Slf4j +public abstract class CommonKeyCryptoController extends GenericEntityController { + + protected CommonKeyCryptoController( + @NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + @NonNull final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService, VaultFake::keyVaultFake); + } + + public ResponseEntity encrypt( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri, + @Valid final KeyOperationsParameters request) { + log.info("Received request to {} encrypt using key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); + final byte[] encrypted = keyVaultKeyEntity.encryptBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm(), + request.getInitializationVector()); + return ResponseEntity.ok(KeyOperationsResult.forBytes(keyVaultKeyEntity.getId(), encrypted, request)); + } + + public ResponseEntity decrypt( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri, + @Valid final KeyOperationsParameters request) { + log.info("Received request to {} decrypt using key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); + final byte[] decrypted = keyVaultKeyEntity.decryptToBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm(), + request.getInitializationVector()); + return ResponseEntity.ok(KeyOperationsResult.forBytes(keyVaultKeyEntity.getId(), decrypted, request)); + } + + public ResponseEntity sign( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri, + @Valid final KeySignParameters request) { + log.info("Received request to {} sign using key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); + final byte[] signature = keyVaultKeyEntity.signBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm()); + return ResponseEntity.ok(KeySignResult.forBytes(keyVaultKeyEntity.getId(), signature)); + } + + public ResponseEntity verify( + @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + final URI baseUri, + @Valid final KeyVerifyParameters request) { + log.info("Received request to {} verify using key: {} with version: {} using API version: {}", + baseUri.toString(), keyName, keyVersion, apiVersion()); + + final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); + final boolean result = keyVaultKeyEntity.verifySignedBytes(request.getDigestAsBase64DecodedBytes(), request.getAlgorithm(), + request.getValueAsBase64DecodedBytes()); + return ResponseEntity.ok(new KeyVerifyResult(result)); + } + + @Override + protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String name, final String version) { + return new VersionedKeyEntityId(baseUri, name, version); + } + + @Override + protected KeyEntityId entityId(final URI baseUri, final String name) { + return new KeyEntityId(baseUri, name); + } + +} 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 new file mode 100644 index 00000000..ddc3298a --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretBackupRestoreController.java @@ -0,0 +1,76 @@ +package com.github.nagyesta.lowkeyvault.controller.common; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; +import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; +import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; +import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; +import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; +import com.github.nagyesta.lowkeyvault.service.secret.impl.KeyVaultSecretEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +@Slf4j +public abstract class CommonSecretBackupRestoreController extends BaseBackupRestoreController { + + protected CommonSecretBackupRestoreController( + @NonNull final SecretEntityToV72ModelConverter modelConverter, + @NonNull final SecretEntityToV72BackupConverter backupConverter, + @NonNull final VaultService vaultService) { + super(modelConverter, backupConverter, vaultService, VaultFake::secretVaultFake); + } + + public ResponseEntity backup( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} backup secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + return ResponseEntity.ok(backupEntity(entityId(baseUri, secretName))); + } + + 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()); + return ResponseEntity.ok(restoreEntity(secretBackupModel)); + } + + @Override + protected void restoreVersion(@NonNull final SecretVaultFake vault, + @NonNull final VersionedSecretEntityId versionedEntityId, + @NonNull final SecretBackupListItem entityVersion) { + vault.createSecretVersion(versionedEntityId, entityVersion.getValue(), entityVersion.getContentType()); + final KeyVaultSecretEntity entity = vault.getEntities().getEntity(versionedEntityId, KeyVaultSecretEntity.class); + updateCommonFields(entityVersion, entity); + } + + @Override + protected SecretBackupList getBackupList() { + return new SecretBackupList(); + } + + @Override + protected SecretBackupModel getBackupModel() { + return new SecretBackupModel(); + } + + @Override + protected VersionedSecretEntityId versionedEntityId(final URI baseUri, final String name, final String version) { + return new VersionedSecretEntityId(baseUri, name, version); + } + + @Override + protected SecretEntityId entityId(final URI baseUri, final String name) { + return new SecretEntityId(baseUri, name); + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretController.java new file mode 100644 index 00000000..3f47820d --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/CommonSecretController.java @@ -0,0 +1,190 @@ +package com.github.nagyesta.lowkeyvault.controller.common; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.CreateSecretRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.UpdateSecretRequest; +import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; +import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; +import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; +import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; +import java.util.Objects; + +@Slf4j +public abstract class CommonSecretController extends GenericEntityController { + + protected CommonSecretController( + @NonNull final SecretEntityToV72ModelConverter secretEntityToV72ModelConverter, + @NonNull final SecretEntityToV72SecretItemModelConverter secretEntityToV72SecretItemModelConverter, + @NonNull final SecretEntityToV72SecretVersionItemModelConverter secretEntityToV72SecretVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(secretEntityToV72ModelConverter, secretEntityToV72SecretItemModelConverter, + secretEntityToV72SecretVersionItemModelConverter, vaultService, VaultFake::secretVaultFake); + } + + public ResponseEntity create( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri, + @Valid final CreateSecretRequest request) { + log.info("Received request to {} create secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final VersionedSecretEntityId secretEntityId = createSecretWithAttributes(secretVaultFake, secretName, request); + return ResponseEntity.ok(getModelById(secretVaultFake, secretEntityId)); + } + + public ResponseEntity delete( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} delete secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); + secretVaultFake.delete(entityId); + final VersionedSecretEntityId latestVersion = secretVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getDeletedModelById(secretVaultFake, latestVersion)); + } + + public ResponseEntity> versions( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list secret versions: {} , (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), secretName, maxResults, skipToken, apiVersion()); + + return ResponseEntity + .ok(getPageOfItemVersions(baseUri, secretName, maxResults, skipToken, "/secrets/" + secretName + "/versions")); + } + + public ResponseEntity> listSecrets( + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list secrets, (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), maxResults, skipToken, apiVersion()); + + return ResponseEntity.ok(getPageOfItems(baseUri, maxResults, skipToken, "/secrets")); + } + + public ResponseEntity> listDeletedSecrets( + final URI baseUri, + final int maxResults, + final int skipToken) { + log.info("Received request to {} list deleted secrets, (max results: {}, skip: {}) using API version: {}", + baseUri.toString(), maxResults, skipToken, apiVersion()); + + return ResponseEntity.ok(getPageOfDeletedItems(baseUri, maxResults, skipToken, "/deletedsecrets")); + } + + public ResponseEntity get( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} get secret: {} with version: -LATEST- using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + return ResponseEntity.ok(getLatestEntityModel(baseUri, secretName)); + } + + public ResponseEntity getWithVersion( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, + final URI baseUri) { + log.info("Received request to {} get secret: {} with version: {} using API version: {}", + baseUri.toString(), secretName, secretVersion, apiVersion()); + + final ReadOnlyKeyVaultSecretEntity keyVaultSecretEntity = getEntityByNameAndVersion(baseUri, secretName, secretVersion); + return ResponseEntity.ok(convertDetails(keyVaultSecretEntity)); + } + + public ResponseEntity updateVersion( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, + final URI baseUri, + @Valid final UpdateSecretRequest request) { + log.info("Received request to {} update secret: {} with version: {} using API version: {}", + baseUri.toString(), secretName, secretVersion, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final VersionedSecretEntityId entityId = versionedEntityId(baseUri, secretName, secretVersion); + updateAttributes(secretVaultFake, entityId, request.getProperties()); + updateTags(secretVaultFake, entityId, request.getTags()); + return ResponseEntity.ok(getModelById(secretVaultFake, entityId)); + } + + public ResponseEntity getDeletedSecret( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} get deleted secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); + final VersionedSecretEntityId latestVersion = secretVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getDeletedModelById(secretVaultFake, latestVersion)); + } + + public ResponseEntity purgeDeleted( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} purge deleted secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); + secretVaultFake.purge(entityId); + return ResponseEntity.noContent().build(); + } + + public ResponseEntity recoverDeletedSecret( + @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + final URI baseUri) { + log.info("Received request to {} recover deleted secret: {} using API version: {}", + baseUri.toString(), secretName, apiVersion()); + + final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); + final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); + secretVaultFake.recover(entityId); + final VersionedSecretEntityId latestVersion = secretVaultFake.getEntities().getLatestVersionOfEntity(entityId); + return ResponseEntity.ok(getModelById(secretVaultFake, latestVersion)); + } + + @Override + protected VersionedSecretEntityId versionedEntityId(final URI baseUri, final String name, final String version) { + return new VersionedSecretEntityId(baseUri, name, version); + } + + @Override + protected SecretEntityId entityId(final URI baseUri, final String name) { + return new SecretEntityId(baseUri, name); + } + + private VersionedSecretEntityId createSecretWithAttributes( + final SecretVaultFake secretVaultFake, final String secretName, final CreateSecretRequest request) { + final SecretPropertiesModel properties = Objects.requireNonNullElse(request.getProperties(), new SecretPropertiesModel()); + final VersionedSecretEntityId secretEntityId = secretVaultFake + .createSecretVersion(secretName, request.getValue(), request.getContentType()); + secretVaultFake.addTags(secretEntityId, request.getTags()); + secretVaultFake.setExpiry(secretEntityId, properties.getNotBefore(), properties.getExpiresOn()); + secretVaultFake.setEnabled(secretEntityId, properties.isEnabled()); + //no need to set managed property as this endpoint cannot create managed entities by definition + return secretEntityId; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/GenericEntityController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/GenericEntityController.java similarity index 96% rename from lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/GenericEntityController.java rename to lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/GenericEntityController.java index fe439bb5..b43f0df3 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/GenericEntityController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/common/GenericEntityController.java @@ -1,4 +1,4 @@ -package com.github.nagyesta.lowkeyvault.controller.v7_2; +package com.github.nagyesta.lowkeyvault.controller.common; import com.github.nagyesta.lowkeyvault.mapper.common.RecoveryAwareConverter; import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; @@ -15,6 +15,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_PREFIX; + /** * The base implementation of the entity controllers. * @@ -162,7 +164,7 @@ private boolean hasMorePages(final int limit, final int offset, final Collection } private String pageSuffix(final int maxResults, final int skip) { - return "?" + API_VERSION_7_2 + "&" + SKIP_TOKEN_PARAM + "=" + skip + "&" + MAX_RESULTS_PARAM + "=" + maxResults; + return "?" + API_VERSION_PREFIX + apiVersion() + "&" + SKIP_TOKEN_PARAM + "=" + skip + "&" + MAX_RESULTS_PARAM + "=" + maxResults; } } 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 bdaf35ef..50153f8d 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 @@ -1,96 +1,62 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +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.model.common.ApiConstants; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; -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; -import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; -import com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultKeyEntity; -import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +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.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.*; import javax.validation.Valid; import javax.validation.constraints.Pattern; import java.net.URI; -import java.util.Collections; -import java.util.Objects; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_2; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Slf4j @RestController @Validated -public class KeyBackupRestoreController extends BaseBackupRestoreController { +@Component("KeyBackupRestoreControllerV72") +public class KeyBackupRestoreController extends CommonKeyBackupRestoreController { @Autowired - protected KeyBackupRestoreController( - @NonNull final KeyEntityToV72ModelConverter modelConverter, - @NonNull final KeyEntityToV72BackupConverter backupConverter, - @NonNull final VaultService vaultService) { - super(modelConverter, backupConverter, vaultService, VaultFake::keyVaultFake); + protected KeyBackupRestoreController(final KeyEntityToV72ModelConverter modelConverter, + final KeyEntityToV72BackupConverter backupConverter, + final VaultService vaultService) { + super(modelConverter, backupConverter, vaultService); } + @Override @PostMapping(value = "/keys/{keyName}/backup", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, 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) { - log.info("Received request to {} backup key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - return ResponseEntity.ok(backupEntity(entityId(baseUri, keyName))); + return super.backup(keyName, baseUri); } + @Override @PostMapping(value = "/keys/restore", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity restore(@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final KeyBackupModel keyBackupModel) { - log.info("Received request to {} restore key: {} using API version: {}", - baseUri.toString(), keyBackupModel.getValue().get(0).getId(), V_7_2); - 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()); - final KeyVaultKeyEntity entity = vault.getEntities().getEntity(versionedEntityId, KeyVaultKeyEntity.class); - entity.setOperations(Objects.requireNonNullElse(entityVersion.getKeyMaterial().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); + return super.restore(baseUri, keyBackupModel); } @Override - protected KeyEntityId entityId(final URI baseUri, final String name) { - return new KeyEntityId(baseUri, name); + protected String apiVersion() { + return V_7_2; } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyController.java index 714051da..aef997a4 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyController.java @@ -1,46 +1,38 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyController; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; -import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; -import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyItemModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; 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.JsonWebKeyImportRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.UpdateKeyRequest; -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; -import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; -import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; 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.util.Assert; +import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.Pattern; import java.net.URI; -import java.util.Objects; -import java.util.Optional; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_2; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Slf4j @RestController @Validated -public class KeyController extends GenericEntityController { +@Component("KeyControllerV72") +public class KeyController extends CommonKeyController { @Autowired public KeyController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, @@ -48,9 +40,10 @@ public KeyController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72M @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, @NonNull final VaultService vaultService) { super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, - keyEntityToV72KeyVersionItemModelConverter, vaultService, VaultFake::keyVaultFake); + keyEntityToV72KeyVersionItemModelConverter, vaultService); } + @Override @PostMapping(value = "/keys/{keyName}/create", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -58,14 +51,10 @@ public KeyController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72M public ResponseEntity create(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final CreateKeyRequest request) { - log.info("Received request to {} create key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final VersionedKeyEntityId keyEntityId = createKeyWithAttributes(keyVaultFake, keyName, request); - return ResponseEntity.ok(getModelById(keyVaultFake, keyEntityId)); + return super.create(keyName, baseUri, request); } + @Override @PutMapping(value = "/keys/{keyName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -73,30 +62,20 @@ public ResponseEntity create(@PathVariable @Valid @Pattern(reg public ResponseEntity importKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final ImportKeyRequest request) { - log.info("Received request to {} import key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final VersionedKeyEntityId keyEntityId = importKeyWithAttributes(keyVaultFake, keyName, request); - return ResponseEntity.ok(getModelById(keyVaultFake, keyEntityId)); + return super.importKey(keyName, baseUri, request); } + @Override @DeleteMapping(value = "/keys/{keyName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity delete(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} delete key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); - keyVaultFake.delete(entityId); - final VersionedKeyEntityId latestVersion = keyVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getDeletedModelById(keyVaultFake, latestVersion)); + return super.delete(keyName, baseUri); } + @Override @GetMapping(value = "/keys/{keyName}/versions", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -106,12 +85,10 @@ public ResponseEntity> versions( @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list key versions: {} , (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), keyName, maxResults, skipToken, V_7_2); - - return ResponseEntity.ok(getPageOfItemVersions(baseUri, keyName, maxResults, skipToken, "/keys/" + keyName + "/versions")); + return super.versions(keyName, baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/keys", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -120,12 +97,10 @@ public ResponseEntity> listKeys( @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list keys, (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), maxResults, skipToken, V_7_2); - - return ResponseEntity.ok(getPageOfItems(baseUri, maxResults, skipToken, "/keys")); + return super.listKeys(baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/deletedkeys", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -134,12 +109,10 @@ public ResponseEntity> listDeletedKe @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list deleted keys, (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), maxResults, skipToken, V_7_2); - - return ResponseEntity.ok(getPageOfDeletedItems(baseUri, maxResults, skipToken, "/deletedkeys")); + return super.listDeletedKeys(baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/keys/{keyName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -147,12 +120,10 @@ public ResponseEntity> listDeletedKe public ResponseEntity get( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get key: {} with version: -LATEST- using API version: {}", - baseUri.toString(), keyName, V_7_2); - - return ResponseEntity.ok(getLatestEntityModel(baseUri, keyName)); + return super.get(keyName, baseUri); } + @Override @GetMapping(value = "/keys/{keyName}/{keyVersion}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -161,13 +132,10 @@ public ResponseEntity getWithVersion( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); - return ResponseEntity.ok(convertDetails(keyVaultKeyEntity)); + return super.getWithVersion(keyName, keyVersion, baseUri); } + @Override @PatchMapping(value = "/keys/{keyName}/{keyVersion}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -177,98 +145,41 @@ public ResponseEntity updateVersion( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @NonNull @Valid @RequestBody final UpdateKeyRequest request) { - log.info("Received request to {} update key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final VersionedKeyEntityId entityId = versionedEntityId(baseUri, keyName, keyVersion); - Optional.ofNullable(request.getKeyOperations()) - .ifPresent(operations -> keyVaultFake.setKeyOperations(entityId, operations)); - updateAttributes(keyVaultFake, entityId, request.getProperties()); - updateTags(keyVaultFake, entityId, request.getTags()); - return ResponseEntity.ok(getModelById(keyVaultFake, entityId)); + return super.updateVersion(keyName, keyVersion, baseUri, request); } + @Override @GetMapping(value = "/deletedkeys/{keyName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity getDeletedKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get deleted key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); - final VersionedKeyEntityId latestVersion = keyVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getDeletedModelById(keyVaultFake, latestVersion)); + return super.getDeletedKey(keyName, baseUri); } + @Override @PostMapping(value = "/deletedkeys/{keyName}/recover", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity recoverDeletedKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} recover deleted key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); - keyVaultFake.recover(entityId); - final VersionedKeyEntityId latestVersion = keyVaultFake.getEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getModelById(keyVaultFake, latestVersion)); + return super.recoverDeletedKey(keyName, baseUri); } + @Override @DeleteMapping(value = "/deletedkeys/{keyName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity purgeDeleted(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} purge deleted key: {} using API version: {}", - baseUri.toString(), keyName, V_7_2); - - final KeyVaultFake keyVaultFake = getVaultByUri(baseUri); - final KeyEntityId entityId = new KeyEntityId(baseUri, keyName); - keyVaultFake.purge(entityId); - return ResponseEntity.noContent().build(); + return super.purgeDeleted(keyName, baseUri); } @Override - protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String name, final String version) { - return new VersionedKeyEntityId(baseUri, name, version); - } - - @Override - protected KeyEntityId entityId(final URI baseUri, final String name) { - return new KeyEntityId(baseUri, name); - } - - private VersionedKeyEntityId createKeyWithAttributes( - final KeyVaultFake keyVaultFake, final String keyName, final CreateKeyRequest request) { - final KeyPropertiesModel properties = Objects.requireNonNullElse(request.getProperties(), new KeyPropertiesModel()); - final VersionedKeyEntityId keyEntityId = keyVaultFake.createKeyVersion(keyName, request.toKeyCreationInput()); - keyVaultFake.setKeyOperations(keyEntityId, request.getKeyOperations()); - keyVaultFake.addTags(keyEntityId, request.getTags()); - keyVaultFake.setExpiry(keyEntityId, properties.getNotBefore(), properties.getExpiresOn()); - keyVaultFake.setEnabled(keyEntityId, properties.isEnabled()); - //no need to set managed property as this endpoint cannot create managed entities by definition - return keyEntityId; - } - - private VersionedKeyEntityId importKeyWithAttributes( - final KeyVaultFake keyVaultFake, final String keyName, final ImportKeyRequest request) { - final BasePropertiesUpdateModel properties = Objects.requireNonNullElse(request.getProperties(), new BasePropertiesUpdateModel()); - Assert.isTrue(request.getHsm() == null || request.getHsm() == request.getKey().getKeyType().isHsm(), - "When HSM property is set in request, key type must match it."); - final VersionedKeyEntityId keyEntityId = keyVaultFake.importKeyVersion(keyName, request.getKey()); - final JsonWebKeyImportRequest keyImport = request.getKey(); - keyVaultFake.setKeyOperations(keyEntityId, keyImport.getKeyOps()); - keyVaultFake.addTags(keyEntityId, request.getTags()); - keyVaultFake.setExpiry(keyEntityId, properties.getNotBefore(), properties.getExpiresOn()); - keyVaultFake.setEnabled(keyEntityId, Objects.requireNonNullElse(properties.getEnabled(), true)); - //no need to set managed property as this endpoint cannot create managed entities by definition - return keyEntityId; + protected String apiVersion() { + return V_7_2; } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoController.java index 3f4f365e..1508dd32 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyCryptoController.java @@ -1,23 +1,22 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyCryptoController; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; 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.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyOperationsResult; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeySignResult; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVerifyResult; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyOperationsParameters; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyVerifyParameters; -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; -import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; -import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; 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.*; @@ -25,16 +24,15 @@ import javax.validation.constraints.Pattern; import java.net.URI; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_2; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Slf4j @RestController @Validated -public class KeyCryptoController extends GenericEntityController { +@Component("KeyCryptoControllerV72") +public class KeyCryptoController extends CommonKeyCryptoController { @Autowired public KeyCryptoController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, @@ -42,9 +40,10 @@ public KeyCryptoController(@NonNull final KeyEntityToV72ModelConverter keyEntity @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, @NonNull final VaultService vaultService) { super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, - keyEntityToV72KeyVersionItemModelConverter, vaultService, VaultFake::keyVaultFake); + keyEntityToV72KeyVersionItemModelConverter, vaultService); } + @Override @PostMapping(value = {"/keys/{keyName}/{keyVersion}/encrypt", "/keys/{keyName}/{keyVersion}/wrap"}, params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -54,15 +53,10 @@ public ResponseEntity encrypt( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final KeyOperationsParameters request) { - log.info("Received request to {} encrypt using key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); - final byte[] encrypted = keyVaultKeyEntity.encryptBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm(), - request.getInitializationVector()); - return ResponseEntity.ok(KeyOperationsResult.forBytes(keyVaultKeyEntity.getId(), encrypted, request)); + return super.encrypt(keyName, keyVersion, baseUri, request); } + @Override @PostMapping(value = {"/keys/{keyName}/{keyVersion}/decrypt", "/keys/{keyName}/{keyVersion}/unwrap"}, params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -72,15 +66,10 @@ public ResponseEntity decrypt( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final KeyOperationsParameters request) { - log.info("Received request to {} decrypt using key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); - final byte[] decrypted = keyVaultKeyEntity.decryptToBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm(), - request.getInitializationVector()); - return ResponseEntity.ok(KeyOperationsResult.forBytes(keyVaultKeyEntity.getId(), decrypted, request)); + return super.decrypt(keyName, keyVersion, baseUri, request); } + @Override @PostMapping(value = "/keys/{keyName}/{keyVersion}/sign", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -90,14 +79,10 @@ public ResponseEntity sign( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final KeySignParameters request) { - log.info("Received request to {} sign using key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); - final byte[] signature = keyVaultKeyEntity.signBytes(request.getValueAsBase64DecodedBytes(), request.getAlgorithm()); - return ResponseEntity.ok(KeySignResult.forBytes(keyVaultKeyEntity.getId(), signature)); + return super.sign(keyName, keyVersion, baseUri, request); } + @Override @PostMapping(value = "/keys/{keyName}/{keyVersion}/verify", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -107,23 +92,11 @@ public ResponseEntity verify( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final KeyVerifyParameters request) { - log.info("Received request to {} verify using key: {} with version: {} using API version: {}", - baseUri.toString(), keyName, keyVersion, V_7_2); - - final ReadOnlyKeyVaultKeyEntity keyVaultKeyEntity = getEntityByNameAndVersion(baseUri, keyName, keyVersion); - final boolean result = keyVaultKeyEntity.verifySignedBytes(request.getDigestAsBase64DecodedBytes(), request.getAlgorithm(), - request.getValueAsBase64DecodedBytes()); - return ResponseEntity.ok(new KeyVerifyResult(result)); - } - - @Override - protected VersionedKeyEntityId versionedEntityId(final URI baseUri, final String name, final String version) { - return new VersionedKeyEntityId(baseUri, name, version); + return super.verify(keyName, keyVersion, baseUri, request); } @Override - protected KeyEntityId entityId(final URI baseUri, final String name) { - return new KeyEntityId(baseUri, name); + protected String apiVersion() { + return V_7_2; } - } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreController.java index 47fa0cd9..83ecd660 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretBackupRestoreController.java @@ -1,19 +1,17 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +import com.github.nagyesta.lowkeyvault.controller.common.CommonSecretBackupRestoreController; import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; -import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; -import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; -import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; -import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; -import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; -import com.github.nagyesta.lowkeyvault.service.secret.impl.KeyVaultSecretEntity; -import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretEntityToV72BackupConverter; 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.*; @@ -21,72 +19,46 @@ import javax.validation.constraints.Pattern; import java.net.URI; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_2; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Slf4j @RestController @Validated -public class SecretBackupRestoreController extends BaseBackupRestoreController { +@Component("SecretBackupRestoreControllerV72") +public class SecretBackupRestoreController extends CommonSecretBackupRestoreController { @Autowired protected SecretBackupRestoreController( @NonNull final SecretEntityToV72ModelConverter modelConverter, @NonNull final SecretEntityToV72BackupConverter backupConverter, @NonNull final VaultService vaultService) { - super(modelConverter, backupConverter, vaultService, VaultFake::secretVaultFake); + super(modelConverter, backupConverter, vaultService); } + @Override @PostMapping(value = "/secrets/{secretName}/backup", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity backup(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} backup secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - return ResponseEntity.ok(backupEntity(entityId(baseUri, secretName))); + return super.backup(secretName, baseUri); } + @Override @PostMapping(value = "/secrets/restore", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity restore(@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final SecretBackupModel secretBackupModel) { - log.info("Received request to {} restore secret: {} using API version: {}", - baseUri.toString(), secretBackupModel.getValue().get(0).getId(), V_7_2); - return ResponseEntity.ok(restoreEntity(secretBackupModel)); - } - - @Override - protected void restoreVersion(@NonNull final SecretVaultFake vault, - @NonNull final VersionedSecretEntityId versionedEntityId, - @NonNull final SecretBackupListItem entityVersion) { - vault.createSecretVersion(versionedEntityId, entityVersion.getValue(), entityVersion.getContentType()); - final KeyVaultSecretEntity entity = vault.getEntities().getEntity(versionedEntityId, KeyVaultSecretEntity.class); - updateCommonFields(entityVersion, entity); - } - - @Override - protected SecretBackupList getBackupList() { - return new SecretBackupList(); - } - - @Override - protected SecretBackupModel getBackupModel() { - return new SecretBackupModel(); - } - - @Override - protected VersionedSecretEntityId versionedEntityId(final URI baseUri, final String name, final String version) { - return new VersionedSecretEntityId(baseUri, name, version); + return super.restore(baseUri, secretBackupModel); } @Override - protected SecretEntityId entityId(final URI baseUri, final String name) { - return new SecretEntityId(baseUri, name); + protected String apiVersion() { + return V_7_2; } } diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretController.java index 56951c97..ae7b6fd1 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretController.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_2/SecretController.java @@ -1,41 +1,37 @@ package com.github.nagyesta.lowkeyvault.controller.v7_2; +import com.github.nagyesta.lowkeyvault.controller.common.CommonSecretController; import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretItemModelConverter; import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretVersionItemModelConverter; import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; -import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretItemModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretModel; import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.CreateSecretRequest; import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.UpdateSecretRequest; -import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; -import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; -import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; -import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; -import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; 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.*; import javax.validation.Valid; import javax.validation.constraints.Pattern; import java.net.URI; -import java.util.Objects; +import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_2; import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_2; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @Slf4j @RestController @Validated -public class SecretController extends GenericEntityController { +@Component("SecretControllerV72") +public class SecretController extends CommonSecretController { @Autowired public SecretController( @@ -44,9 +40,10 @@ public SecretController( @NonNull final SecretEntityToV72SecretVersionItemModelConverter secretEntityToV72SecretVersionItemModelConverter, @NonNull final VaultService vaultService) { super(secretEntityToV72ModelConverter, secretEntityToV72SecretItemModelConverter, - secretEntityToV72SecretVersionItemModelConverter, vaultService, VaultFake::secretVaultFake); + secretEntityToV72SecretVersionItemModelConverter, vaultService); } + @Override @PutMapping(value = "/secrets/{secretName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -54,30 +51,20 @@ public SecretController( public ResponseEntity create(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @Valid @RequestBody final CreateSecretRequest request) { - log.info("Received request to {} create secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final VersionedSecretEntityId secretEntityId = createSecretWithAttributes(secretVaultFake, secretName, request); - return ResponseEntity.ok(getModelById(secretVaultFake, secretEntityId)); + return super.create(secretName, baseUri, request); } + @Override @DeleteMapping(value = "/secrets/{secretName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) public ResponseEntity delete(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} delete secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); - secretVaultFake.delete(entityId); - final VersionedSecretEntityId latestVersion = secretVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getDeletedModelById(secretVaultFake, latestVersion)); + return super.delete(secretName, baseUri); } + @Override @GetMapping(value = "/secrets/{secretName}/versions", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -87,13 +74,10 @@ public ResponseEntity> versions( @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list secret versions: {} , (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), secretName, maxResults, skipToken, V_7_2); - - return ResponseEntity - .ok(getPageOfItemVersions(baseUri, secretName, maxResults, skipToken, "/secrets/" + secretName + "/versions")); + return super.versions(secretName, baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/secrets", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -102,12 +86,10 @@ public ResponseEntity> listSecret @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list secrets, (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), maxResults, skipToken, V_7_2); - - return ResponseEntity.ok(getPageOfItems(baseUri, maxResults, skipToken, "/secrets")); + return super.listSecrets(baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/deletedsecrets", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -116,12 +98,10 @@ public ResponseEntity> listDelete @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { - log.info("Received request to {} list deleted secrets, (max results: {}, skip: {}) using API version: {}", - baseUri.toString(), maxResults, skipToken, V_7_2); - - return ResponseEntity.ok(getPageOfDeletedItems(baseUri, maxResults, skipToken, "/deletedsecrets")); + return super.listDeletedSecrets(baseUri, maxResults, skipToken); } + @Override @GetMapping(value = "/secrets/{secretName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -129,12 +109,10 @@ public ResponseEntity> listDelete public ResponseEntity get( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get secret: {} with version: -LATEST- using API version: {}", - baseUri.toString(), secretName, V_7_2); - - return ResponseEntity.ok(getLatestEntityModel(baseUri, secretName)); + return super.get(secretName, baseUri); } + @Override @GetMapping(value = "/secrets/{secretName}/{secretVersion}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -143,13 +121,10 @@ public ResponseEntity getWithVersion( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get secret: {} with version: {} using API version: {}", - baseUri.toString(), secretName, secretVersion, V_7_2); - - final ReadOnlyKeyVaultSecretEntity keyVaultSecretEntity = getEntityByNameAndVersion(baseUri, secretName, secretVersion); - return ResponseEntity.ok(convertDetails(keyVaultSecretEntity)); + return super.getWithVersion(secretName, secretVersion, baseUri); } + @Override @PatchMapping(value = "/secrets/{secretName}/{secretVersion}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -159,16 +134,10 @@ public ResponseEntity updateVersion( @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, @NonNull @Valid @RequestBody final UpdateSecretRequest request) { - log.info("Received request to {} update secret: {} with version: {} using API version: {}", - baseUri.toString(), secretName, secretVersion, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final VersionedSecretEntityId entityId = versionedEntityId(baseUri, secretName, secretVersion); - updateAttributes(secretVaultFake, entityId, request.getProperties()); - updateTags(secretVaultFake, entityId, request.getTags()); - return ResponseEntity.ok(getModelById(secretVaultFake, entityId)); + return super.updateVersion(secretName, secretVersion, baseUri, request); } + @Override @GetMapping(value = "/deletedsecrets/{secretName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -176,15 +145,10 @@ public ResponseEntity updateVersion( public ResponseEntity getDeletedSecret( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} get deleted secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); - final VersionedSecretEntityId latestVersion = secretVaultFake.getDeletedEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getDeletedModelById(secretVaultFake, latestVersion)); + return super.getDeletedSecret(secretName, baseUri); } + @Override @DeleteMapping(value = "/deletedsecrets/{secretName}", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -192,15 +156,10 @@ public ResponseEntity getDeletedSecret( public ResponseEntity purgeDeleted( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} purge deleted secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); - secretVaultFake.purge(entityId); - return ResponseEntity.noContent().build(); + return super.purgeDeleted(secretName, baseUri); } + @Override @PostMapping(value = "/deletedsecrets/{secretName}/recover", params = API_VERSION_7_2, consumes = APPLICATION_JSON_VALUE, @@ -208,35 +167,11 @@ public ResponseEntity purgeDeleted( public ResponseEntity recoverDeletedSecret( @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { - log.info("Received request to {} recover deleted secret: {} using API version: {}", - baseUri.toString(), secretName, V_7_2); - - final SecretVaultFake secretVaultFake = getVaultByUri(baseUri); - final SecretEntityId entityId = new SecretEntityId(baseUri, secretName); - secretVaultFake.recover(entityId); - final VersionedSecretEntityId latestVersion = secretVaultFake.getEntities().getLatestVersionOfEntity(entityId); - return ResponseEntity.ok(getModelById(secretVaultFake, latestVersion)); - } - - @Override - protected VersionedSecretEntityId versionedEntityId(final URI baseUri, final String name, final String version) { - return new VersionedSecretEntityId(baseUri, name, version); + return super.recoverDeletedSecret(secretName, baseUri); } @Override - protected SecretEntityId entityId(final URI baseUri, final String name) { - return new SecretEntityId(baseUri, name); - } - - private VersionedSecretEntityId createSecretWithAttributes( - final SecretVaultFake secretVaultFake, final String secretName, final CreateSecretRequest request) { - final SecretPropertiesModel properties = Objects.requireNonNullElse(request.getProperties(), new SecretPropertiesModel()); - final VersionedSecretEntityId secretEntityId = secretVaultFake - .createSecretVersion(secretName, request.getValue(), request.getContentType()); - secretVaultFake.addTags(secretEntityId, request.getTags()); - secretVaultFake.setExpiry(secretEntityId, properties.getNotBefore(), properties.getExpiresOn()); - secretVaultFake.setEnabled(secretEntityId, properties.isEnabled()); - //no need to set managed property as this endpoint cannot create managed entities by definition - return secretEntityId; + 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 new file mode 100644 index 00000000..d933a2be --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreController.java @@ -0,0 +1,63 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +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.model.common.ApiConstants; +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.service.vault.VaultService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +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; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Slf4j +@RestController +@Validated +@Component("KeyBackupRestoreControllerV73") +public class KeyBackupRestoreController extends CommonKeyBackupRestoreController { + + @Autowired + protected KeyBackupRestoreController(final KeyEntityToV72ModelConverter modelConverter, + final KeyEntityToV72BackupConverter backupConverter, + final VaultService vaultService) { + super(modelConverter, backupConverter, vaultService); + } + + @Override + @PostMapping(value = "/keys/{keyName}/backup", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + 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); + } + + @Override + @PostMapping(value = "/keys/restore", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity restore(@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final KeyBackupModel keyBackupModel) { + return super.restore(baseUri, keyBackupModel); + } + + @Override + protected String apiVersion() { + return V_7_3; + } +} 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 new file mode 100644 index 00000000..40739d05 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyController.java @@ -0,0 +1,185 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyController; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyItemModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; +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.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.*; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +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; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Slf4j +@RestController +@Validated +@Component("KeyControllerV73") +public class KeyController extends CommonKeyController { + + @Autowired + public KeyController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + @NonNull final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService); + } + + @Override + @PostMapping(value = "/keys/{keyName}/create", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity create(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final CreateKeyRequest request) { + return super.create(keyName, baseUri, request); + } + + @Override + @PutMapping(value = "/keys/{keyName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity importKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final ImportKeyRequest request) { + return super.importKey(keyName, baseUri, request); + } + + @Override + @DeleteMapping(value = "/keys/{keyName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity delete(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.delete(keyName, baseUri); + } + + @Override + @GetMapping(value = "/keys/{keyName}/versions", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> versions( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.versions(keyName, baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/keys", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> listKeys( + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.listKeys(baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/deletedkeys", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> listDeletedKeys( + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.listDeletedKeys(baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/keys/{keyName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity get( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.get(keyName, baseUri); + } + + @Override + @GetMapping(value = "/keys/{keyName}/{keyVersion}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity getWithVersion( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.getWithVersion(keyName, keyVersion, baseUri); + } + + @Override + @PatchMapping(value = "/keys/{keyName}/{keyVersion}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity updateVersion( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @NonNull @Valid @RequestBody final UpdateKeyRequest request) { + return super.updateVersion(keyName, keyVersion, baseUri, request); + } + + @Override + @GetMapping(value = "/deletedkeys/{keyName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity getDeletedKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.getDeletedKey(keyName, baseUri); + } + + @Override + @PostMapping(value = "/deletedkeys/{keyName}/recover", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity recoverDeletedKey(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.recoverDeletedKey(keyName, baseUri); + } + + @Override + @DeleteMapping(value = "/deletedkeys/{keyName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity purgeDeleted(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.purgeDeleted(keyName, baseUri); + } + + @Override + protected String apiVersion() { + return V_7_3; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoController.java new file mode 100644 index 00000000..5d24961b --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoController.java @@ -0,0 +1,102 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyCryptoController; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +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.KeyOperationsResult; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeySignResult; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVerifyResult; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyOperationsParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyVerifyParameters; +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.*; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +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; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Slf4j +@RestController +@Validated +@Component("KeyCryptoControllerV73") +public class KeyCryptoController extends CommonKeyCryptoController { + + @Autowired + public KeyCryptoController(@NonNull final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + @NonNull final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + @NonNull final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService); + } + + @Override + @PostMapping(value = {"/keys/{keyName}/{keyVersion}/encrypt", "/keys/{keyName}/{keyVersion}/wrap"}, + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity encrypt( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final KeyOperationsParameters request) { + return super.encrypt(keyName, keyVersion, baseUri, request); + } + + @Override + @PostMapping(value = {"/keys/{keyName}/{keyVersion}/decrypt", "/keys/{keyName}/{keyVersion}/unwrap"}, + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity decrypt( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final KeyOperationsParameters request) { + return super.decrypt(keyName, keyVersion, baseUri, request); + } + + @Override + @PostMapping(value = "/keys/{keyName}/{keyVersion}/sign", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity sign( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final KeySignParameters request) { + return super.sign(keyName, keyVersion, baseUri, request); + } + + @Override + @PostMapping(value = "/keys/{keyName}/{keyVersion}/verify", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity verify( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String keyVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final KeyVerifyParameters request) { + return super.verify(keyName, keyVersion, baseUri, request); + } + + @Override + protected String apiVersion() { + return V_7_3; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreController.java new file mode 100644 index 00000000..a7d84543 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreController.java @@ -0,0 +1,64 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.controller.common.CommonSecretBackupRestoreController; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretBackupModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretEntityToV72BackupConverter; +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.*; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +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; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Slf4j +@RestController +@Validated +@Component("SecretBackupRestoreControllerV73") +public class SecretBackupRestoreController extends CommonSecretBackupRestoreController { + + @Autowired + protected SecretBackupRestoreController( + @NonNull final SecretEntityToV72ModelConverter modelConverter, + @NonNull final SecretEntityToV72BackupConverter backupConverter, + @NonNull final VaultService vaultService) { + super(modelConverter, backupConverter, vaultService); + } + + @Override + @PostMapping(value = "/secrets/{secretName}/backup", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity backup(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.backup(secretName, baseUri); + } + + @Override + @PostMapping(value = "/secrets/restore", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity restore(@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final SecretBackupModel secretBackupModel) { + return super.restore(baseUri, secretBackupModel); + } + + @Override + protected String apiVersion() { + return V_7_3; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretController.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretController.java new file mode 100644 index 00000000..90cf02b2 --- /dev/null +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretController.java @@ -0,0 +1,177 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.controller.common.CommonSecretController; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretItemModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.KeyVaultSecretModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.CreateSecretRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.UpdateSecretRequest; +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.*; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import java.net.URI; + +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; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Slf4j +@RestController +@Validated +@Component("SecretControllerV73") +public class SecretController extends CommonSecretController { + + @Autowired + public SecretController( + @NonNull final SecretEntityToV72ModelConverter secretEntityToV72ModelConverter, + @NonNull final SecretEntityToV72SecretItemModelConverter secretEntityToV72SecretItemModelConverter, + @NonNull final SecretEntityToV72SecretVersionItemModelConverter secretEntityToV72SecretVersionItemModelConverter, + @NonNull final VaultService vaultService) { + super(secretEntityToV72ModelConverter, secretEntityToV72SecretItemModelConverter, + secretEntityToV72SecretVersionItemModelConverter, vaultService); + } + + @Override + @PutMapping(value = "/secrets/{secretName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity create(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @Valid @RequestBody final CreateSecretRequest request) { + return super.create(secretName, baseUri, request); + } + + @Override + @DeleteMapping(value = "/secrets/{secretName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity delete(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.delete(secretName, baseUri); + } + + @Override + @GetMapping(value = "/secrets/{secretName}/versions", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> versions( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.versions(secretName, baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/secrets", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> listSecrets( + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.listSecrets(baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/deletedsecrets", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity> listDeletedSecrets( + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @RequestParam(name = MAX_RESULTS_PARAM, required = false, defaultValue = DEFAULT_MAX) final int maxResults, + @RequestParam(name = SKIP_TOKEN_PARAM, required = false, defaultValue = SKIP_ZERO) final int skipToken) { + return super.listDeletedSecrets(baseUri, maxResults, skipToken); + } + + @Override + @GetMapping(value = "/secrets/{secretName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity get( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.get(secretName, baseUri); + } + + @Override + @GetMapping(value = "/secrets/{secretName}/{secretVersion}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity getWithVersion( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.getWithVersion(secretName, secretVersion, baseUri); + } + + @Override + @PatchMapping(value = "/secrets/{secretName}/{secretVersion}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity updateVersion( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @PathVariable @Valid @Pattern(regexp = VERSION_NAME_PATTERN) final String secretVersion, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri, + @NonNull @Valid @RequestBody final UpdateSecretRequest request) { + return super.updateVersion(secretName, secretVersion, baseUri, request); + } + + @Override + @GetMapping(value = "/deletedsecrets/{secretName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity getDeletedSecret( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.getDeletedSecret(secretName, baseUri); + } + + @Override + @DeleteMapping(value = "/deletedsecrets/{secretName}", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity purgeDeleted( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.purgeDeleted(secretName, baseUri); + } + + @Override + @PostMapping(value = "/deletedsecrets/{secretName}/recover", + params = API_VERSION_7_3, + consumes = APPLICATION_JSON_VALUE, + produces = APPLICATION_JSON_VALUE) + public ResponseEntity recoverDeletedSecret( + @PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String secretName, + @RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) { + return super.recoverDeletedSecret(secretName, baseUri); + } + + @Override + protected String apiVersion() { + return V_7_3; + } +} diff --git a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/ApiConstants.java b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/ApiConstants.java index 9c62974b..b380e3a4 100644 --- a/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/ApiConstants.java +++ b/lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/ApiConstants.java @@ -10,6 +10,22 @@ public final class ApiConstants { * The version of the v7.2 API. */ public static final String V_7_2 = "7.2"; + /** + * The version of the v7.2 API. + */ + public static final String V_7_3 = "7.3"; + /** + * API version prefix. + */ + public static final String API_VERSION_PREFIX = "api-version="; + /** + * API version param for 7.2. + */ + public static final String API_VERSION_7_2 = API_VERSION_PREFIX + V_7_2; + /** + * API version param for 7.2. + */ + public static final String API_VERSION_7_3 = API_VERSION_PREFIX + V_7_3; private ApiConstants() { throw new IllegalCallerException("Utility cannot be instantiated."); diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/MissionOutlineDefinition.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/MissionOutlineDefinition.java index bf826624..e2d8007c 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/MissionOutlineDefinition.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/MissionOutlineDefinition.java @@ -2,21 +2,25 @@ import com.github.nagyesta.abortmission.core.AbortMissionCommandOps; import com.github.nagyesta.abortmission.core.healthcheck.MissionHealthCheckEvaluator; +import com.github.nagyesta.abortmission.core.matcher.MissionHealthCheckMatcher; import com.github.nagyesta.abortmission.core.outline.MissionOutline; import java.util.Map; import java.util.function.Consumer; -import static com.github.nagyesta.abortmission.core.MissionControl.matcher; -import static com.github.nagyesta.abortmission.core.MissionControl.reportOnlyEvaluator; +import static com.github.nagyesta.abortmission.core.MissionControl.*; public class MissionOutlineDefinition extends MissionOutline { @Override protected Map> defineOutline() { return Map.of(SHARED_CONTEXT, ops -> { + final MissionHealthCheckMatcher integrationTestMatcher = matcher().classNamePattern(".*IntegrationTest").build(); + final MissionHealthCheckEvaluator integrationTests = percentageBasedEvaluator(integrationTestMatcher) + .build(); final MissionHealthCheckEvaluator reportOnly = reportOnlyEvaluator(matcher().anyClass().build()) .build(); + ops.registerHealthCheck(integrationTests); ops.registerHealthCheck(reportOnly); }); } diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/PingControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/common/PingControllerTest.java similarity index 91% rename from lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/PingControllerTest.java rename to lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/common/PingControllerTest.java index ddd96461..f1e5614d 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/PingControllerTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/common/PingControllerTest.java @@ -1,4 +1,4 @@ -package com.github.nagyesta.lowkeyvault.controller.v7_2; +package com.github.nagyesta.lowkeyvault.controller.common; import com.github.nagyesta.lowkeyvault.controller.PingController; import org.junit.jupiter.api.Assertions; 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 a3e0ef3e..95330412 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 @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -44,6 +45,7 @@ class KeyBackupRestoreControllerIntegrationTest { @Autowired + @Qualifier("KeyBackupRestoreControllerV72") private KeyBackupRestoreController underTest; @Autowired private VaultService vaultService; diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyControllerIntegrationTest.java index 212c4c48..bfc69380 100644 --- a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyControllerIntegrationTest.java +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_2/KeyControllerIntegrationTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.ResponseEntity; @@ -44,6 +45,7 @@ class KeyControllerIntegrationTest { @Autowired private VaultService vaultService; @Autowired + @Qualifier("KeyControllerV72") private KeyController underTest; @Autowired private ObjectMapper objectMapper; 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 d5104f9f..2c1e99bf 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 @@ -16,6 +16,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -34,6 +35,7 @@ class SecretBackupRestoreControllerIntegrationTest { @Autowired + @Qualifier("SecretBackupRestoreControllerV72") private SecretBackupRestoreController underTest; @Autowired private VaultService vaultService; 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 new file mode 100644 index 00000000..5b3ab517 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyBackupRestoreControllerIntegrationTest.java @@ -0,0 +1,276 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +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.model.v7_2.common.constants.RecoveryLevel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; +import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; +import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; +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; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +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.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.net.URI; +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.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.TestConstants.*; +import static com.github.nagyesta.lowkeyvault.TestConstantsKeys.*; +import static org.mockito.Mockito.mock; + +@SpringBootTest +class KeyBackupRestoreControllerIntegrationTest { + + @Autowired + @Qualifier("KeyBackupRestoreControllerV73") + private KeyBackupRestoreController underTest; + @Autowired + private VaultService vaultService; + private URI uri; + + public static Stream nullProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null)) + .add(Arguments.of(mock(KeyEntityToV72ModelConverter.class), null, null)) + .add(Arguments.of(null, mock(KeyEntityToV72BackupConverter.class), null)) + .add(Arguments.of(null, null, mock(VaultService.class))) + .add(Arguments.of(null, mock(KeyEntityToV72BackupConverter.class), null)) + .add(Arguments.of(mock(KeyEntityToV72ModelConverter.class), null, mock(VaultService.class))) + .add(Arguments.of(mock(KeyEntityToV72ModelConverter.class), mock(KeyEntityToV72BackupConverter.class), null)) + .build(); + } + + @BeforeEach + void setUp() { + final String name = UUID.randomUUID().toString(); + uri = URI.create("https://" + name + ".localhost"); + vaultService.create(uri, RecoveryLevel.RECOVERABLE_AND_PURGEABLE, RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); + } + + @AfterEach + void tearDown() { + vaultService.delete(uri); + vaultService.purge(uri); + } + + @ParameterizedTest + @MethodSource("nullProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNulls( + final KeyEntityToV72ModelConverter modelConverter, + final KeyEntityToV72BackupConverter backupConverter, + final VaultService vaultService) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, + () -> new KeyBackupRestoreController(modelConverter, backupConverter, vaultService)); + + //then + exception + } + + @Test + void testRestoreEntityShouldRestoreASingleKeyWhenCalledWithValidInput() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + final KeyPair expectedKey = addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, TAGS_THREE_KEYS); + + //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_THREE_KEYS); + } + + @Test + void testRestoreEntityShouldRestoreAThreeKeysWhenCalledWithValidInput() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, null); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_2, backupModel, TAGS_THREE_KEYS); + final KeyPair expectedKey = addVersionToList(uri, KEY_NAME_1, KEY_VERSION_3, backupModel, TAGS_EMPTY); + + //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_3, TAGS_EMPTY); + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithMoreThanOneUris() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, null); + addVersionToList(TestConstantsUri.HTTPS_DEFAULT_LOWKEY_VAULT, KEY_NAME_1, KEY_VERSION_2, backupModel, TAGS_THREE_KEYS); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithMoreThanOneNames() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, null); + addVersionToList(uri, KEY_NAME_2, KEY_VERSION_2, backupModel, TAGS_THREE_KEYS); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithUnknownUri() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(URI.create("https://uknknown.uri"), KEY_NAME_1, KEY_VERSION_1, backupModel, null); + + //when + Assertions.assertThrows(NotFoundException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenNameMatchesActiveKey() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_2, backupModel, TAGS_ONE_KEY); + vaultService.findByUri(uri).keyVaultFake() + .createKeyVersion(KEY_NAME_1, new EcKeyCreationInput(KeyType.EC, KeyCurveName.P_256)); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenNameMatchesDeletedKey() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_2, backupModel, TAGS_ONE_KEY); + final KeyVaultFake vaultFake = vaultService.findByUri(uri).keyVaultFake(); + final VersionedKeyEntityId keyVersion = vaultFake + .createKeyVersion(KEY_NAME_1, new EcKeyCreationInput(KeyType.EC, KeyCurveName.P_256)); + vaultFake.delete(keyVersion); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testBackupEntityShouldReturnTheOriginalBackupModelWhenCalledAfterRestoreEntity() { + //given + final KeyBackupModel backupModel = new KeyBackupModel(); + backupModel.setValue(new KeyBackupList()); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, KEY_NAME_1, KEY_VERSION_2, backupModel, TAGS_ONE_KEY); + underTest.restore(uri, backupModel); + + //when + final ResponseEntity actual = underTest.backup(KEY_NAME_1, uri); + + //then + Assertions.assertNotNull(actual); + final KeyBackupModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(backupModel, actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + } + + private void assertRestoredKeyMatchesExpectations( + final KeyVaultKeyModel actualBody, final ECPublicKey publicKey, + final String version, final Map expectedTags) { + Assertions.assertEquals(new VersionedKeyEntityId(uri, KEY_NAME_1, version).asUri().toString(), actualBody.getKey().getId()); + Assertions.assertEquals(KeyCurveName.P_256, actualBody.getKey().getCurveName()); + Assertions.assertEquals(KeyType.EC, actualBody.getKey().getKeyType()); + Assertions.assertIterableEquals(List.of(KeyOperation.SIGN, KeyOperation.VERIFY), actualBody.getKey().getKeyOps()); + Assertions.assertArrayEquals(publicKey.getW().getAffineX().toByteArray(), actualBody.getKey().getX()); + Assertions.assertArrayEquals(publicKey.getW().getAffineY().toByteArray(), actualBody.getKey().getY()); + //do not return private key material in response + Assertions.assertNull(actualBody.getKey().getD()); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actualBody.getAttributes().getCreatedOn()); + Assertions.assertEquals(NOW, actualBody.getAttributes().getUpdatedOn()); + Assertions.assertEquals(TIME_IN_10_MINUTES, actualBody.getAttributes().getNotBefore()); + Assertions.assertEquals(TIME_IN_10_MINUTES.plusDays(1), actualBody.getAttributes().getExpiresOn()); + Assertions.assertEquals(RecoveryLevel.RECOVERABLE_AND_PURGEABLE, actualBody.getAttributes().getRecoveryLevel()); + Assertions.assertEquals(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE, actualBody.getAttributes().getRecoverableDays()); + Assertions.assertTrue(actualBody.getAttributes().isEnabled()); + Assertions.assertEquals(expectedTags, actualBody.getTags()); + } + + private KeyPair addVersionToList(final URI baseUri, final String name, final String version, + final KeyBackupModel backupModel, final Map tags) { + final KeyPair keyPair = KeyGenUtil.generateEc(KeyCurveName.P_256); + final JsonWebKeyImportRequest keyMaterial = new JsonWebKeyImportRequest(); + keyMaterial.setKeyType(KeyType.EC); + keyMaterial.setCurveName(KeyCurveName.P_256); + keyMaterial.setKeyOps(List.of(KeyOperation.SIGN, KeyOperation.VERIFY)); + keyMaterial.setD(((ECPrivateKey) keyPair.getPrivate()).getS().toByteArray()); + keyMaterial.setX(((ECPublicKey) keyPair.getPublic()).getW().getAffineX().toByteArray()); + keyMaterial.setY(((ECPublicKey) keyPair.getPublic()).getW().getAffineY().toByteArray()); + keyMaterial.setId(new VersionedKeyEntityId(baseUri, name, version).asUri().toString()); + final KeyBackupListItem listItem = new KeyBackupListItem(); + listItem.setKeyMaterial(keyMaterial); + listItem.setVaultBaseUri(baseUri); + listItem.setId(name); + listItem.setVersion(version); + final KeyPropertiesModel propertiesModel = new KeyPropertiesModel(); + propertiesModel.setCreatedOn(TIME_10_MINUTES_AGO); + propertiesModel.setUpdatedOn(NOW); + propertiesModel.setNotBefore(TIME_IN_10_MINUTES); + propertiesModel.setExpiresOn(TIME_IN_10_MINUTES.plusDays(1)); + propertiesModel.setRecoveryLevel(RecoveryLevel.RECOVERABLE_AND_PURGEABLE); + propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); + listItem.setAttributes(propertiesModel); + listItem.setTags(tags); + backupModel.getValue().add(listItem); + return keyPair; + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerIntegrationTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerIntegrationTest.java new file mode 100644 index 00000000..0431dcf5 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerIntegrationTest.java @@ -0,0 +1,175 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.nagyesta.lowkeyvault.ResourceUtils; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.EncryptionAlgorithm; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; +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.model.v7_2.key.request.ImportKeyRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; +import com.github.nagyesta.lowkeyvault.service.common.ReadOnlyVersionedEntityMultiMap; +import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity; +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.AesKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.key.impl.EcKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.key.impl.RsaKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +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.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.ResponseEntity; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.stream.Stream; + +@SpringBootTest +class KeyControllerIntegrationTest { + + private static final byte[] IV = "_iv-param-value_".getBytes(StandardCharsets.UTF_8); + private static final int AES_256 = 256; + private static final int RSA_2048 = 2048; + private static final String SHA_256 = "SHA-256"; + + @Autowired + private VaultService vaultService; + @Autowired + @Qualifier("KeyControllerV73") + private KeyController underTest; + @Autowired + private ObjectMapper objectMapper; + + public static Stream invalidHsmProvider() { + return Stream.builder() + .add(Arguments.of(false, KeyType.EC_HSM)) + .add(Arguments.of(true, KeyType.EC)) + .build(); + } + + @ParameterizedTest + @MethodSource("invalidHsmProvider") + void testImportShouldThrowExceptionWhenCalledWithMisalignedHsmConfiguration(final Boolean hsm, final KeyType keyType) { + //given + final ImportKeyRequest input = new ImportKeyRequest(); + input.setHsm(hsm); + final JsonWebKeyImportRequest key = new JsonWebKeyImportRequest(); + key.setKeyType(keyType); + key.setCurveName(KeyCurveName.P_256); + key.setD(new byte[2]); + key.setX(new byte[2]); + key.setY(new byte[2]); + input.setKey(key); + final URI baseUri = URI.create("https://primary.localhost:8443"); + final String name = "invalid-ec-name"; + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.importKey(name, baseUri, input)); + + //then + exception + } + + @Test + void testImportRsaShouldUseImportKeyWhenCalledWithValidPayload() throws IOException { + //given + final String resource = "/key/import/rsa-import-valid.json"; + final ImportKeyRequest input = loadResourceAsObject(resource); + final URI baseUri = URI.create("https://primary.localhost:8443"); + final String name = "rsa-name"; + final ReadOnlyVersionedEntityMultiMap entities = vaultService + .findByUri(baseUri) + .keyVaultFake() + .getEntities(); + + //when + final ResponseEntity response = underTest.importKey(name, baseUri, input); + final VersionedKeyEntityId id = entities.getLatestVersionOfEntity(new KeyEntityId(baseUri, name)); + final RsaKeyVaultKeyEntity actual = entities.getEntity(id, RsaKeyVaultKeyEntity.class); + + //then + Assertions.assertNotNull(response); + Assertions.assertNotNull(actual); + Assertions.assertEquals(RSA_2048, actual.getKeySize()); + final byte[] encrypted = actual.encryptBytes(name.getBytes(StandardCharsets.UTF_8), EncryptionAlgorithm.RSA_OAEP_256, null); + final byte[] decrypted = actual.decryptToBytes(encrypted, EncryptionAlgorithm.RSA_OAEP_256, null); + Assertions.assertEquals(name, new String(decrypted)); + } + + @Test + void testImportAesShouldUseImportKeyWhenCalledWithValidPayload() throws IOException { + //given + final String resource = "/key/import/aes-import-valid.json"; + final ImportKeyRequest input = loadResourceAsObject(resource); + final URI baseUri = URI.create("https://primary.localhost:8443"); + final String name = "aes-name"; + final ReadOnlyVersionedEntityMultiMap entities = vaultService + .findByUri(baseUri) + .keyVaultFake() + .getEntities(); + + //when + final ResponseEntity response = underTest.importKey(name, baseUri, input); + final VersionedKeyEntityId id = entities.getLatestVersionOfEntity(new KeyEntityId(baseUri, name)); + final AesKeyVaultKeyEntity actual = entities.getEntity(id, AesKeyVaultKeyEntity.class); + + //then + Assertions.assertNotNull(response); + Assertions.assertNotNull(actual); + Assertions.assertEquals(AES_256, actual.getKeySize()); + final byte[] encrypted = actual.encryptBytes(name.getBytes(StandardCharsets.UTF_8), EncryptionAlgorithm.A256CBCPAD, IV); + final byte[] decrypted = actual.decryptToBytes(encrypted, EncryptionAlgorithm.A256CBCPAD, IV); + Assertions.assertEquals(name, new String(decrypted)); + } + + @Test + void testImportEcShouldUseImportKeyWhenCalledWithValidPayload() throws IOException { + //given + final String resource = "/key/import/ec-import-valid.json"; + final ImportKeyRequest input = loadResourceAsObject(resource); + final URI baseUri = URI.create("https://primary.localhost:8443"); + final String name = "ec-name"; + final ReadOnlyVersionedEntityMultiMap entities = vaultService + .findByUri(baseUri) + .keyVaultFake() + .getEntities(); + final byte[] digest = hash(name.getBytes(StandardCharsets.UTF_8)); + + //when + final ResponseEntity response = underTest.importKey(name, baseUri, input); + final VersionedKeyEntityId id = entities.getLatestVersionOfEntity(new KeyEntityId(baseUri, name)); + final EcKeyVaultKeyEntity actual = entities.getEntity(id, EcKeyVaultKeyEntity.class); + + //then + Assertions.assertNotNull(response); + Assertions.assertNotNull(actual); + Assertions.assertEquals(KeyCurveName.P_256, actual.getKeyCurveName()); + final byte[] signature = actual.signBytes(digest, SignatureAlgorithm.ES256); + final boolean valid = actual.verifySignedBytes(digest, SignatureAlgorithm.ES256, signature); + Assertions.assertTrue(valid); + } + + private ImportKeyRequest loadResourceAsObject(final String resource) throws IOException { + final String json = ResourceUtils.loadResourceAsString(resource); + return objectMapper.reader().readValue(json, ImportKeyRequest.class); + } + + private byte[] hash(final byte[] text) { + try { + final MessageDigest md = MessageDigest.getInstance(SHA_256); + md.update(text); + return md.digest(); + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } +} 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 new file mode 100644 index 00000000..c94ff1da --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyControllerTest.java @@ -0,0 +1,947 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.ErrorMessage; +import com.github.nagyesta.lowkeyvault.model.common.ErrorModel; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; +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.constants.KeyOperation; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.CreateKeyRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.UpdateKeyRequest; +import com.github.nagyesta.lowkeyvault.service.common.ReadOnlyVersionedEntityMultiMap; +import com.github.nagyesta.lowkeyvault.service.exception.AlreadyExistsException; +import com.github.nagyesta.lowkeyvault.service.exception.CryptoException; +import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; +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; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.key.impl.RsaKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import org.assertj.core.util.Arrays; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +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.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +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.TestConstantsUri.HTTPS_LOCALHOST_8443; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.*; + +class KeyControllerTest { + + private static final KeyVaultKeyModel RESPONSE = createResponse(); + private static final DeletedKeyVaultKeyModel DELETED_RESPONSE = createDeletedResponse(); + @Mock + private KeyEntityToV72ModelConverter keyEntityToV72ModelConverter; + @Mock + private KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter; + @Mock + private KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter; + @Mock + private VaultService vaultService; + @Mock + private VaultFake vaultFake; + @Mock + private KeyVaultFake keyVaultFake; + @Mock + private ReadOnlyVersionedEntityMultiMap entities; + @Mock + private ReadOnlyVersionedEntityMultiMap deletedEntities; + private KeyController underTest; + private AutoCloseable openMocks; + + private static KeyVaultKeyModel createResponse() { + final KeyVaultKeyModel model = new KeyVaultKeyModel(); + model.setKey(new JsonWebKeyModel()); + model.setAttributes(new KeyPropertiesModel()); + model.setTags(Map.of()); + return model; + } + + private static DeletedKeyVaultKeyModel createDeletedResponse() { + final DeletedKeyVaultKeyModel model = new DeletedKeyVaultKeyModel(); + model.setKey(new JsonWebKeyModel()); + model.setAttributes(new KeyPropertiesModel()); + model.setTags(Map.of()); + model.setDeletedDate(TIME_10_MINUTES_AGO); + model.setScheduledPurgeDate(TIME_IN_10_MINUTES); + model.setRecoveryId(VERSIONED_KEY_ENTITY_ID_1_VERSION_1.asRecoveryUri().toString()); + return model; + } + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream keyAttributeProvider() { + return Stream.builder() + .add(Arguments.of(null, + null, null, null, null)) + .add(Arguments.of(List.of(), + null, null, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(List.of(), + RecoveryLevel.RECOVERABLE, 90, null, null)) + .add(Arguments.of(List.of(), + RecoveryLevel.RECOVERABLE, 90, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(List.of(), + RecoveryLevel.RECOVERABLE_AND_PURGEABLE, 90, null, null)) + .add(Arguments.of(List.of(), + RecoveryLevel.RECOVERABLE_AND_PURGEABLE, 90, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(List.of(KeyOperation.ENCRYPT), + RecoveryLevel.CUSTOMIZED_RECOVERABLE, 42, null, null)) + .add(Arguments.of(List.of(KeyOperation.ENCRYPT), + RecoveryLevel.CUSTOMIZED_RECOVERABLE, 42, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(Arrays.asList(KeyOperation.values()), + RecoveryLevel.PURGEABLE, null, null, null)) + .add(Arguments.of(Arrays.asList(KeyOperation.values()), + RecoveryLevel.PURGEABLE, null, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .build(); + } + + public static Stream nullProvider() { + final KeyEntityToV72ModelConverter ec = mock(KeyEntityToV72ModelConverter.class); + final KeyEntityToV72KeyItemModelConverter ic = mock(KeyEntityToV72KeyItemModelConverter.class); + final KeyEntityToV72KeyVersionItemModelConverter vic = mock(KeyEntityToV72KeyVersionItemModelConverter.class); + return Stream.builder() + .add(Arguments.of(null, null, null, null)) + .add(Arguments.of(ec, null, null, null)) + .add(Arguments.of(null, ic, null, null)) + .add(Arguments.of(null, null, vic, null)) + .add(Arguments.of(null, null, null, mock(VaultService.class))) + .add(Arguments.of(null, ic, vic, mock(VaultService.class))) + .add(Arguments.of(ec, null, vic, mock(VaultService.class))) + .add(Arguments.of(ec, ic, null, mock(VaultService.class))) + .add(Arguments.of(ec, ic, vic, null)) + .build(); + } + + public static Stream updateAttributeProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null, null, null)) + .add(Arguments.of(List.of(), TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES, null, TAGS_EMPTY)) + .add(Arguments.of(List.of(), null, TIME_IN_10_MINUTES, null, null)) + .add(Arguments.of(List.of(), null, null, true, TAGS_THREE_KEYS)) + .add(Arguments.of(List.of(KeyOperation.ENCRYPT), TIME_10_MINUTES_AGO, null, false, TAGS_TWO_KEYS)) + .add(Arguments.of(List.of(KeyOperation.ENCRYPT), TIME_IN_10_MINUTES, null, null, TAGS_TWO_KEYS)) + .add(Arguments.of(List.of(KeyOperation.ENCRYPT, KeyOperation.DECRYPT), + TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES, false, TAGS_TWO_KEYS)) + .build(); + } + + public static Stream exceptionProvider() { + final String message = "Message"; + final String failed = "failed"; + return Stream.builder() + .add(Arguments.of(new IllegalStateException(message), + HttpStatus.INTERNAL_SERVER_ERROR, message, null)) + .add(Arguments.of(new NotFoundException(message), + HttpStatus.NOT_FOUND, message, null)) + .add(Arguments.of(new AlreadyExistsException(message), + HttpStatus.CONFLICT, message, null)) + .add(Arguments.of(new CryptoException(message, new RuntimeException(failed)), + HttpStatus.INTERNAL_SERVER_ERROR, message, failed)) + .build(); + } + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + underTest = new KeyController(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService); + when(vaultService.findByUri(eq(HTTPS_LOCALHOST_8443))).thenReturn(vaultFake); + when(vaultFake.baseUri()).thenReturn(HTTPS_LOCALHOST_8443); + when(vaultFake.keyVaultFake()).thenReturn(keyVaultFake); + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + } + + @ParameterizedTest + @MethodSource("exceptionProvider") + void testErrorHandlerConvertsExceptionWhenCaught(final Exception exception, final HttpStatus status, + final String message, final String innerMessage) { + //given + + //when + final ResponseEntity actual = underTest.handleException(exception); + + //then + Assertions.assertEquals(status, actual.getStatusCode()); + final ErrorModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertNotNull(actualBody.getError()); + Assertions.assertEquals(message, actualBody.getError().getMessage()); + Assertions.assertEquals(exception.getClass().getName(), actualBody.getError().getCode()); + final ErrorMessage actualInnerError = actualBody.getError().getInnerError(); + if (innerMessage != null) { + Assertions.assertNotNull(actualInnerError); + Assertions.assertEquals(exception.getCause().getClass().getName(), actualInnerError.getCode()); + Assertions.assertEquals(innerMessage, actualInnerError.getMessage()); + } else { + Assertions.assertNull(actualInnerError); + } + } + + @Test + void testErrorHandlerConvertsIllegalArgumentExceptionWhenCaught() { + //given + final HttpStatus status = HttpStatus.BAD_REQUEST; + final String message = "Message"; + final Exception exception = new IllegalArgumentException(message); + + //when + final ResponseEntity actual = underTest.handleArgumentException(exception); + + //then + Assertions.assertEquals(status, actual.getStatusCode()); + final ErrorModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertNotNull(actualBody.getError()); + Assertions.assertEquals(message, actualBody.getError().getMessage()); + Assertions.assertEquals(exception.getClass().getName(), actualBody.getError().getCode()); + final ErrorMessage actualInnerError = actualBody.getError().getInnerError(); + Assertions.assertNull(actualInnerError); + } + + @ParameterizedTest + @MethodSource("nullProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNull( + final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + final VaultService vaultService) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, + () -> new KeyController(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService)); + + //then + exception + } + + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testCreateShouldUseInputParametersWhenCalled( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + when(vaultFake.getRecoveryLevel()).thenReturn(RecoveryLevel.PURGEABLE); + when(vaultFake.getRecoverableDays()).thenReturn(null); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + when(keyVaultFake.createKeyVersion(eq(KEY_NAME_1), eq(request.toKeyCreationInput()))) + .thenReturn(VERSIONED_KEY_ENTITY_ID_1_VERSION_1); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_1))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.create(KEY_NAME_1, HTTPS_LOCALHOST_8443, request); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(keyVaultFake).createKeyVersion(eq(KEY_NAME_1), eq(request.toKeyCreationInput())); + verify(keyVaultFake) + .setKeyOperations(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_1), eq(operations)); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake).setExpiry(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_1), eq(notBefore), eq(expiry)); + verify(keyVaultFake).setEnabled(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_1), eq(true)); + verify(keyVaultFake).addTags(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_1), same(TAGS_TWO_KEYS)); + verify(keyEntityToV72ModelConverter).convert(same(entity)); + } + + @Test + void testVersionsShouldThrowExceptionWhenKeyIsNotFound() { + //given + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null)))) + .thenThrow(new NotFoundException("not found")); + + //when + Assertions.assertThrows(NotFoundException.class, + () -> underTest.versions(KEY_NAME_1, HTTPS_LOCALHOST_8443, 0, 0)); + + //then + exception + } + + @SuppressWarnings("checkstyle:MagicNumber") + @Test + void testVersionsShouldFilterTheListReturnedWhenKeyIsFoundAndHasMoreVersionsThanNeeded() { + //given + final int index = 30; + final LinkedList fullList = IntStream.range(0, 42) + .mapToObj(i -> UUID.randomUUID().toString().replaceAll("-", "")) + .collect(Collectors.toCollection(LinkedList::new)); + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final String expectedNextUri = baseUri.asUri("versions?api-version=7.3&$skiptoken=31&maxresults=1").toString(); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(baseUri))).thenReturn(fullList); + when(entities.getReadOnlyEntity(any())).thenAnswer(invocation -> { + final VersionedKeyEntityId keyEntityId = invocation.getArgument(0, VersionedKeyEntityId.class); + return createEntity(keyEntityId, createRequest(null, null, null)); + }); + when(keyEntityToV72KeyVersionItemModelConverter.convert(any())).thenAnswer(invocation -> { + final KeyVaultKeyEntity entity = invocation.getArgument(0, KeyVaultKeyEntity.class); + return keyVaultKeyItemModel(entity.getId().asUri(), Map.of()); + }); + final URI expected = new VersionedKeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, fullList.get(index)).asUri(); + + //when + final ResponseEntity> actual = + underTest.versions(KEY_NAME_1, HTTPS_LOCALHOST_8443, 1, index); + + //then + Assertions.assertNotNull(actual); + final KeyVaultItemListModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertEquals(expected.toString(), actualBody.getValue().get(0).getKeyId()); + Assertions.assertNotNull(actualBody.getNextLink()); + Assertions.assertEquals(expectedNextUri, actualBody.getNextLink()); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @Test + void testVersionsShouldNotContainNextUriWhenLastPageIsReturnedFully() { + //given + final LinkedList fullList = IntStream.range(0, 25) + .mapToObj(i -> UUID.randomUUID().toString().replaceAll("-", "")) + .collect(Collectors.toCollection(LinkedList::new)); + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(baseUri))).thenReturn(fullList); + when(entities.getReadOnlyEntity(any())).thenAnswer(invocation -> { + final VersionedKeyEntityId keyEntityId = invocation.getArgument(0, VersionedKeyEntityId.class); + return createEntity(keyEntityId, createRequest(null, null, null)); + }); + when(keyEntityToV72KeyVersionItemModelConverter.convert(any())).thenAnswer(invocation -> { + final KeyVaultKeyEntity entity = invocation.getArgument(0, KeyVaultKeyEntity.class); + return keyVaultKeyItemModel(entity.getId().asUri(), Map.of()); + }); + final List expected = fullList.stream() + .map(e -> new VersionedKeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, e).asUri()) + .collect(Collectors.toList()); + + //when + final ResponseEntity> actual = + underTest.versions(KEY_NAME_1, HTTPS_LOCALHOST_8443, 25, 0); + + //then + Assertions.assertNotNull(actual); + final KeyVaultItemListModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + final List actualList = actualBody.getValue().stream() + .map(KeyVaultKeyItemModel::getKeyId) + .map(URI::create) + .collect(Collectors.toList()); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertIterableEquals(expected, actualList); + Assertions.assertNull(actualBody.getNextLink()); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testDeleteKeyShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(keyVaultFake.getDeletedEntities()) + .thenReturn(deletedEntities); + doNothing().when(keyVaultFake).delete(eq(baseUri)); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(deletedEntities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_KEY_ENTITY_ID_1_VERSION_3); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(deletedEntities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convertDeleted(same(entity))) + .thenReturn(DELETED_RESPONSE); + + //when + final ResponseEntity actual = underTest.delete(KEY_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(DELETED_RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + final InOrder inOrder = inOrder(keyVaultFake); + inOrder.verify(keyVaultFake).delete(eq(baseUri)); + inOrder.verify(keyVaultFake, atLeastOnce()).getDeletedEntities(); + verify(keyVaultFake, never()).getEntities(); + verify(deletedEntities).getLatestVersionOfEntity(eq(baseUri)); + verify(deletedEntities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testRecoverDeletedKeyShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(keyVaultFake.getDeletedEntities()) + .thenReturn(deletedEntities); + doNothing().when(keyVaultFake).delete(eq(baseUri)); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_KEY_ENTITY_ID_1_VERSION_3); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.recoverDeletedKey(KEY_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + final InOrder inOrder = inOrder(keyVaultFake); + inOrder.verify(keyVaultFake).recover(eq(baseUri)); + inOrder.verify(keyVaultFake, atLeastOnce()).getEntities(); + verify(keyVaultFake, never()).getDeletedEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetDeletedKeyShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_KEY_ENTITY_ID_1_VERSION_3); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convertDeleted(same(entity))) + .thenReturn(DELETED_RESPONSE); + + //when + final ResponseEntity actual = underTest.getDeletedKey(KEY_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(DELETED_RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, never()).getEntities(); + verify(keyVaultFake, atLeastOnce()).getDeletedEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_KEY_ENTITY_ID_1_VERSION_3); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.get(KEY_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, atLeastOnce()).getEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetKeysShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity)); + final KeyVaultKeyItemModel keyItemModel = keyVaultKeyItemModel(baseUri.asUri(), Map.of()); + when(keyEntityToV72KeyItemModelConverter.convert(same(entity))) + .thenReturn(keyItemModel); + + //when + final ResponseEntity> actual = + underTest.listKeys(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(keyItemModel, actual.getBody().getValue().get(0)); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, atLeastOnce()).getEntities(); + verify(keyVaultFake, never()).getDeletedEntities(); + verify(entities).listLatestEntities(); + verify(keyEntityToV72KeyItemModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetKeysShouldReturnNextLinkWhenNotOnLastPage( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity, entity, entity)); + final KeyVaultKeyItemModel keyItemModel = keyVaultKeyItemModel(baseUri.asUri(), Map.of()); + when(keyEntityToV72KeyItemModelConverter.convert(same(entity))) + .thenReturn(keyItemModel); + + //when + final ResponseEntity> actual = + underTest.listKeys(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(keyItemModel, actual.getBody().getValue().get(0)); + final String expectedNextLink = HTTPS_LOCALHOST_8443 + "/keys?api-version=7.3&$skiptoken=1&maxresults=1"; + Assertions.assertEquals(expectedNextLink, actual.getBody().getNextLink()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, atLeastOnce()).getEntities(); + verify(keyVaultFake, never()).getDeletedEntities(); + verify(entities).listLatestEntities(); + verify(keyEntityToV72KeyItemModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetDeletedKeysShouldReturnEntryWhenKeyIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity)); + final DeletedKeyVaultKeyItemModel keyItemModel = deletedKeyVaultKeyItemModel(baseUri, Map.of()); + when(keyEntityToV72KeyItemModelConverter.convertDeleted(same(entity))) + .thenReturn(keyItemModel); + + //when + final ResponseEntity> actual = + underTest.listDeletedKeys(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(keyItemModel, actual.getBody().getValue().get(0)); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, atLeastOnce()).getDeletedEntities(); + verify(keyVaultFake, never()).getEntities(); + verify(entities).listLatestEntities(); + verify(keyEntityToV72KeyItemModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testPurgeDeletedShouldRemoveEntryWhenDeletedKeyIsPurgeable( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + when(keyVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + final RecoveryLevel nonNullRecoveryLevel = Optional.ofNullable(recoveryLevel).orElse(RecoveryLevel.PURGEABLE); + if (!nonNullRecoveryLevel.isPurgeable()) { + doThrow(IllegalStateException.class).when(keyVaultFake).purge(eq(UNVERSIONED_KEY_ENTITY_ID_1)); + } + + //when + if (nonNullRecoveryLevel.isPurgeable()) { + final ResponseEntity response = underTest.purgeDeleted(KEY_NAME_1, HTTPS_LOCALHOST_8443); + Assertions.assertNotNull(response); + Assertions.assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + } else { + Assertions.assertThrows(IllegalStateException.class, () -> underTest.purgeDeleted(KEY_NAME_1, HTTPS_LOCALHOST_8443)); + } + + //then + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, never()).getDeletedEntities(); + verify(keyVaultFake, atLeastOnce()).purge(eq(UNVERSIONED_KEY_ENTITY_ID_1)); + verify(keyVaultFake, never()).getEntities(); + verify(entities, never()).listLatestEntities(); + verify(keyEntityToV72KeyItemModelConverter, never()).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetDeletedKeysShouldReturnNextLinkWhenNotOnLastPage( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + when(keyVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity, entity, entity)); + final DeletedKeyVaultKeyItemModel keyItemModel = deletedKeyVaultKeyItemModel(baseUri, Map.of()); + when(keyEntityToV72KeyItemModelConverter.convertDeleted(same(entity))) + .thenReturn(keyItemModel); + + //when + final ResponseEntity> actual = + underTest.listDeletedKeys(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(keyItemModel, actual.getBody().getValue().get(0)); + final String expectedNextLink = HTTPS_LOCALHOST_8443 + "/deletedkeys?api-version=7.3&$skiptoken=1&maxresults=1"; + Assertions.assertEquals(expectedNextLink, actual.getBody().getNextLink()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake, atLeastOnce()).getDeletedEntities(); + verify(keyVaultFake, never()).getEntities(); + verify(entities).listLatestEntities(); + verify(keyEntityToV72KeyItemModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("keyAttributeProvider") + void testGetWithVersionShouldReturnEntryWhenKeyAndVersionIsFound( + final List operations, final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final CreateKeyRequest request = createRequest(operations, expiry, notBefore); + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, request); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.getWithVersion(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("updateAttributeProvider") + void testUpdateVersionShouldReturnEntryWhenKeyAndVersionIsFound( + final List operations, final OffsetDateTime expiry, final OffsetDateTime notBefore, + final Boolean enabled, final Map tags) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final CreateKeyRequest createKeyRequest = createRequest(null, null, null); + final UpdateKeyRequest updateKeyRequest = new UpdateKeyRequest(); + if (operations != null) { + updateKeyRequest.setKeyOperations(operations); + } + if (tags != null) { + updateKeyRequest.setTags(tags); + } + if (enabled != null || expiry != null || notBefore != null) { + final BasePropertiesUpdateModel properties = new BasePropertiesUpdateModel(); + properties.setEnabled(enabled); + properties.setExpiresOn(expiry); + properties.setNotBefore(notBefore); + updateKeyRequest.setProperties(properties); + } + final ReadOnlyKeyVaultKeyEntity entity = createEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, createKeyRequest); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(RecoveryLevel.PURGEABLE); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest + .updateVersion(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, updateKeyRequest); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).keyVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(keyVaultFake).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + final InOrder inOrder = inOrder(keyVaultFake, entities); + if (operations != null) { + inOrder.verify(keyVaultFake) + .setKeyOperations(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), same(updateKeyRequest.getKeyOperations())); + } else { + inOrder.verify(keyVaultFake, never()) + .setKeyOperations(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), anyList()); + } + if (enabled != null) { + inOrder.verify(keyVaultFake) + .setEnabled(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), eq(enabled)); + } else { + inOrder.verify(keyVaultFake, never()) + .setEnabled(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), anyBoolean()); + } + if (expiry != null || notBefore != null) { + inOrder.verify(keyVaultFake) + .setExpiry(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), eq(notBefore), eq(expiry)); + } else { + inOrder.verify(keyVaultFake, never()) + .setExpiry(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), any(), any()); + } + if (tags != null) { + inOrder.verify(keyVaultFake) + .clearTags(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + inOrder.verify(keyVaultFake) + .addTags(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), same(updateKeyRequest.getTags())); + } else { + inOrder.verify(keyVaultFake, never()) + .clearTags(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + inOrder.verify(keyVaultFake, never()) + .addTags(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3), anyMap()); + } + inOrder.verify(entities).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + verify(keyEntityToV72ModelConverter).convert(same(entity)); + } + + @NonNull + private CreateKeyRequest createRequest( + final List operations, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + final CreateKeyRequest keyRequest = new CreateKeyRequest(); + keyRequest.setKeyType(KeyType.RSA); + keyRequest.setKeyOperations(operations); + final KeyPropertiesModel properties = new KeyPropertiesModel(); + properties.setExpiresOn(expiry); + properties.setNotBefore(notBefore); + properties.setEnabled(true); + keyRequest.setProperties(properties); + keyRequest.setTags(TAGS_TWO_KEYS); + return keyRequest; + } + + @NonNull + private KeyVaultKeyEntity createEntity(final VersionedKeyEntityId keyEntityId, final CreateKeyRequest createKeyRequest) { + return new RsaKeyVaultKeyEntity(keyEntityId, vaultFake, createKeyRequest.getKeySize(), null, false); + } + + private KeyVaultKeyItemModel keyVaultKeyItemModel(final URI asUriNoVersion, final Map tags) { + final KeyVaultKeyItemModel model = new KeyVaultKeyItemModel(); + model.setAttributes(new KeyPropertiesModel()); + model.setKeyId(asUriNoVersion.toString()); + model.setTags(tags); + return model; + } + + private DeletedKeyVaultKeyItemModel deletedKeyVaultKeyItemModel(final KeyEntityId id, final Map tags) { + final DeletedKeyVaultKeyItemModel model = new DeletedKeyVaultKeyItemModel(); + model.setAttributes(new KeyPropertiesModel()); + model.setKeyId(id.asUriNoVersion().toString()); + model.setTags(tags); + model.setDeletedDate(TIME_10_MINUTES_AGO); + model.setScheduledPurgeDate(TIME_IN_10_MINUTES); + model.setRecoveryId(id.asRecoveryUri().toString()); + return model; + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java new file mode 100644 index 00000000..da340875 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/KeyCryptoControllerTest.java @@ -0,0 +1,248 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.key.KeyEntityToV72KeyVersionItemModelConverter; +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.key.constants.EncryptionAlgorithm; +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.constants.SignatureAlgorithm; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.CreateKeyRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyOperationsParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeySignParameters; +import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.KeyVerifyParameters; +import com.github.nagyesta.lowkeyvault.service.common.ReadOnlyVersionedEntityMultiMap; +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; +import com.github.nagyesta.lowkeyvault.service.key.id.VersionedKeyEntityId; +import com.github.nagyesta.lowkeyvault.service.key.impl.KeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.key.impl.RsaKeyVaultKeyEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +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 org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Map; +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.TestConstantsUri.HTTPS_LOCALHOST_8443; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.*; + +class KeyCryptoControllerTest { + + private static final Base64.Encoder ENCODER = Base64.getUrlEncoder().withoutPadding(); + private static final Base64.Decoder DECODER = Base64.getUrlDecoder(); + private static final KeyVaultKeyModel RESPONSE = createResponse(); + @Mock + private KeyEntityToV72ModelConverter keyEntityToV72ModelConverter; + @Mock + private KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter; + @Mock + private KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter; + @Mock + private VaultService vaultService; + @Mock + private VaultFake vaultFake; + @Mock + private KeyVaultFake keyVaultFake; + @Mock + private ReadOnlyVersionedEntityMultiMap entities; + private KeyCryptoController underTest; + private AutoCloseable openMocks; + + private static KeyVaultKeyModel createResponse() { + final KeyVaultKeyModel model = new KeyVaultKeyModel(); + model.setKey(new JsonWebKeyModel()); + model.setAttributes(new KeyPropertiesModel()); + model.setTags(Map.of()); + return model; + } + + public static Stream nullProvider() { + final KeyEntityToV72ModelConverter ec = mock(KeyEntityToV72ModelConverter.class); + final KeyEntityToV72KeyItemModelConverter ic = mock(KeyEntityToV72KeyItemModelConverter.class); + final KeyEntityToV72KeyVersionItemModelConverter vic = mock(KeyEntityToV72KeyVersionItemModelConverter.class); + return Stream.builder() + .add(Arguments.of(null, null, null, null)) + .add(Arguments.of(ec, null, null, null)) + .add(Arguments.of(null, ic, null, null)) + .add(Arguments.of(null, null, vic, null)) + .add(Arguments.of(null, null, null, mock(VaultService.class))) + .add(Arguments.of(null, ic, vic, mock(VaultService.class))) + .add(Arguments.of(ec, null, vic, mock(VaultService.class))) + .add(Arguments.of(ec, ic, null, mock(VaultService.class))) + .add(Arguments.of(ec, ic, vic, null)) + .build(); + } + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + underTest = new KeyCryptoController(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService); + when(vaultService.findByUri(eq(HTTPS_LOCALHOST_8443))).thenReturn(vaultFake); + when(vaultFake.baseUri()).thenReturn(HTTPS_LOCALHOST_8443); + when(vaultFake.keyVaultFake()).thenReturn(keyVaultFake); + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + } + + @ParameterizedTest + @MethodSource("nullProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNull( + final KeyEntityToV72ModelConverter keyEntityToV72ModelConverter, + final KeyEntityToV72KeyItemModelConverter keyEntityToV72KeyItemModelConverter, + final KeyEntityToV72KeyVersionItemModelConverter keyEntityToV72KeyVersionItemModelConverter, + final VaultService vaultService) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, + () -> new KeyCryptoController(keyEntityToV72ModelConverter, keyEntityToV72KeyItemModelConverter, + keyEntityToV72KeyVersionItemModelConverter, vaultService)); + + //then + exception + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @ValueSource(strings = {BLANK, DEFAULT_VAULT, LOCALHOST, LOWKEY_VAULT}) + void testEncryptAndDecryptShouldGetBackOriginalInputWhenKeyAndVersionIsFound(final String clearText) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final List operations = List.of( + KeyOperation.ENCRYPT, KeyOperation.DECRYPT, KeyOperation.WRAP_KEY, KeyOperation.UNWRAP_KEY); + final CreateKeyRequest request = createRequest(operations); + final RsaKeyVaultKeyEntity entity = (RsaKeyVaultKeyEntity) createEntity(request); + entity.setEnabled(true); + entity.setOperations(operations); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + final KeyOperationsParameters encryptParameters = new KeyOperationsParameters(); + encryptParameters.setAlgorithm(EncryptionAlgorithm.RSA_OAEP_256); + encryptParameters.setValue(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + + //when + final ResponseEntity encrypted = underTest + .encrypt(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, encryptParameters); + Assertions.assertNotNull(encrypted); + Assertions.assertEquals(HttpStatus.OK, encrypted.getStatusCode()); + Assertions.assertNotNull(encrypted.getBody()); + Assertions.assertNotEquals(clearText, encrypted.getBody().getValue()); + + final KeyOperationsParameters decryptParameters = new KeyOperationsParameters(); + decryptParameters.setAlgorithm(EncryptionAlgorithm.RSA_OAEP_256); + decryptParameters.setValue(encrypted.getBody().getValue()); + final ResponseEntity actual = underTest + .decrypt(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, decryptParameters); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + final String decoded = new String(DECODER.decode(actual.getBody().getValue()), StandardCharsets.UTF_8); + Assertions.assertEquals(clearText, decoded); + + verify(vaultService, times(2)).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake, times(2)).keyVaultFake(); + verify(keyVaultFake, times(2)).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + verify(entities, times(2)).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @ValueSource(strings = {BLANK, DEFAULT_VAULT, LOCALHOST, LOWKEY_VAULT}) + void testSignAndVerifyShouldReturnTrueWhenKeyAndVersionIsFoundAndCalledInSequence(final String clearText) { + //given + final KeyEntityId baseUri = new KeyEntityId(HTTPS_LOCALHOST_8443, KEY_NAME_1, null); + final List operations = List.of(KeyOperation.SIGN, KeyOperation.VERIFY); + final CreateKeyRequest request = createRequest(operations); + final RsaKeyVaultKeyEntity entity = (RsaKeyVaultKeyEntity) createEntity(request); + entity.setEnabled(true); + entity.setOperations(operations); + when(keyVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(keyEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + final KeySignParameters keySignParameters = new KeySignParameters(); + keySignParameters.setAlgorithm(SignatureAlgorithm.PS256); + keySignParameters.setValue(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + + //when + final ResponseEntity signature = underTest + .sign(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, keySignParameters); + Assertions.assertNotNull(signature); + Assertions.assertEquals(HttpStatus.OK, signature.getStatusCode()); + Assertions.assertNotNull(signature.getBody()); + Assertions.assertNotEquals(clearText, signature.getBody().getValue()); + + final KeyVerifyParameters verifyParameters = new KeyVerifyParameters(); + verifyParameters.setAlgorithm(SignatureAlgorithm.PS256); + verifyParameters.setDigest(ENCODER.encodeToString(clearText.getBytes(StandardCharsets.UTF_8))); + verifyParameters.setValue(signature.getBody().getValue()); + final ResponseEntity actual = underTest + .verify(KEY_NAME_1, KEY_VERSION_3, HTTPS_LOCALHOST_8443, verifyParameters); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertTrue(actual.getBody().isValue()); + + verify(vaultService, times(2)).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake, times(2)).keyVaultFake(); + verify(keyVaultFake, times(2)).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + verify(entities, times(2)).getReadOnlyEntity(eq(VERSIONED_KEY_ENTITY_ID_1_VERSION_3)); + } + + @NonNull + private CreateKeyRequest createRequest( + final List operations) { + final CreateKeyRequest keyRequest = new CreateKeyRequest(); + keyRequest.setKeyType(KeyType.RSA); + keyRequest.setKeyOperations(operations); + final KeyPropertiesModel properties = new KeyPropertiesModel(); + properties.setExpiresOn(null); + properties.setNotBefore(null); + properties.setEnabled(true); + keyRequest.setProperties(properties); + keyRequest.setTags(TAGS_TWO_KEYS); + return keyRequest; + } + + @NonNull + private KeyVaultKeyEntity createEntity(final CreateKeyRequest createKeyRequest) { + return new RsaKeyVaultKeyEntity(VERSIONED_KEY_ENTITY_ID_1_VERSION_1, + vaultFake, createKeyRequest.getKeySize(), null, false); + } +} 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 new file mode 100644 index 00000000..619c8668 --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretBackupRestoreControllerIntegrationTest.java @@ -0,0 +1,249 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.TestConstantsUri; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; +import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; +import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; +import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +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.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MimeTypeUtils; + +import java.net.URI; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.TestConstants.*; +import static com.github.nagyesta.lowkeyvault.TestConstantsSecrets.*; +import static org.mockito.Mockito.mock; + +@SpringBootTest +class SecretBackupRestoreControllerIntegrationTest { + + @Autowired + @Qualifier("SecretBackupRestoreControllerV73") + private SecretBackupRestoreController underTest; + @Autowired + private VaultService vaultService; + private URI uri; + + public static Stream nullProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null)) + .add(Arguments.of(mock(SecretEntityToV72ModelConverter.class), null, null)) + .add(Arguments.of(null, mock(SecretEntityToV72BackupConverter.class), null)) + .add(Arguments.of(null, null, mock(VaultService.class))) + .add(Arguments.of(null, mock(SecretEntityToV72BackupConverter.class), null)) + .add(Arguments.of(mock(SecretEntityToV72ModelConverter.class), null, mock(VaultService.class))) + .add(Arguments.of(mock(SecretEntityToV72ModelConverter.class), mock(SecretEntityToV72BackupConverter.class), null)) + .build(); + } + + @BeforeEach + void setUp() { + final String name = UUID.randomUUID().toString(); + uri = URI.create("https://" + name + ".localhost"); + vaultService.create(uri, RecoveryLevel.RECOVERABLE_AND_PURGEABLE, RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); + } + + @AfterEach + void tearDown() { + vaultService.delete(uri); + vaultService.purge(uri); + } + + @ParameterizedTest + @MethodSource("nullProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNulls( + final SecretEntityToV72ModelConverter modelConverter, + final SecretEntityToV72BackupConverter backupConverter, + final VaultService vaultService) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, + () -> new SecretBackupRestoreController(modelConverter, backupConverter, vaultService)); + + //then + exception + } + + @Test + void testRestoreEntityShouldRestoreASingleSecretWhenCalledWithValidInput() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, TAGS_THREE_KEYS); + + //when + final ResponseEntity actual = underTest.restore(uri, backupModel); + + //then + Assertions.assertNotNull(actual); + final KeyVaultSecretModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + assertRestoredSecretMatchesExpectations(actualBody, SECRET_VERSION_1, TAGS_THREE_KEYS); + } + + @Test + void testRestoreEntityShouldRestoreAThreeSecretsWhenCalledWithValidInput() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, null); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_2, backupModel, TAGS_THREE_KEYS); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_3, backupModel, TAGS_EMPTY); + + //when + final ResponseEntity actual = underTest.restore(uri, backupModel); + + //then + Assertions.assertNotNull(actual); + final KeyVaultSecretModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + assertRestoredSecretMatchesExpectations(actualBody, SECRET_VERSION_3, TAGS_EMPTY); + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithMoreThanOneUris() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, null); + addVersionToList(TestConstantsUri.HTTPS_DEFAULT_LOWKEY_VAULT, SECRET_NAME_1, SECRET_VERSION_2, backupModel, TAGS_THREE_KEYS); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithMoreThanOneNames() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, null); + addVersionToList(uri, SECRET_NAME_2, SECRET_VERSION_2, backupModel, TAGS_THREE_KEYS); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenCalledWithUnknownUri() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(URI.create("https://uknknown.uri"), SECRET_NAME_1, SECRET_VERSION_1, backupModel, null); + + //when + Assertions.assertThrows(NotFoundException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenNameMatchesActiveSecret() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_2, backupModel, TAGS_ONE_KEY); + vaultService.findByUri(uri).secretVaultFake().createSecretVersion(SECRET_NAME_1, LOWKEY_VAULT, null); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testRestoreEntityShouldThrowExceptionWhenNameMatchesDeletedSecret() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_2, backupModel, TAGS_ONE_KEY); + final SecretVaultFake vaultFake = vaultService.findByUri(uri).secretVaultFake(); + final VersionedSecretEntityId secretVersion = vaultFake.createSecretVersion(SECRET_NAME_1, LOWKEY_VAULT, null); + vaultFake.delete(secretVersion); + + //when + Assertions.assertThrows(IllegalArgumentException.class, () -> underTest.restore(uri, backupModel)); + + //then + exception + } + + @Test + void testBackupEntityShouldReturnTheOriginalBackupModelWhenCalledAfterRestoreEntity() { + //given + final SecretBackupModel backupModel = new SecretBackupModel(); + backupModel.setValue(new SecretBackupList()); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_1, backupModel, TAGS_EMPTY); + addVersionToList(uri, SECRET_NAME_1, SECRET_VERSION_2, backupModel, TAGS_ONE_KEY); + underTest.restore(uri, backupModel); + + //when + final ResponseEntity actual = underTest.backup(SECRET_NAME_1, uri); + + //then + Assertions.assertNotNull(actual); + final SecretBackupModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(backupModel, actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + } + + private void assertRestoredSecretMatchesExpectations( + final KeyVaultSecretModel actualBody, final String version, final Map expectedTags) { + Assertions.assertEquals(LOWKEY_VAULT, actualBody.getValue()); + Assertions.assertEquals(MimeTypeUtils.TEXT_PLAIN_VALUE, actualBody.getContentType()); + Assertions.assertEquals(new VersionedSecretEntityId(uri, SECRET_NAME_1, version).asUri().toString(), actualBody.getId()); + Assertions.assertEquals(TIME_10_MINUTES_AGO, actualBody.getAttributes().getCreatedOn()); + Assertions.assertEquals(NOW, actualBody.getAttributes().getUpdatedOn()); + Assertions.assertEquals(TIME_IN_10_MINUTES, actualBody.getAttributes().getNotBefore()); + Assertions.assertEquals(TIME_IN_10_MINUTES.plusDays(1), actualBody.getAttributes().getExpiresOn()); + Assertions.assertEquals(RecoveryLevel.RECOVERABLE_AND_PURGEABLE, actualBody.getAttributes().getRecoveryLevel()); + Assertions.assertEquals(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE, actualBody.getAttributes().getRecoverableDays()); + Assertions.assertTrue(actualBody.getAttributes().isEnabled()); + Assertions.assertEquals(expectedTags, actualBody.getTags()); + } + + private void addVersionToList(final URI baseUri, final String name, final String version, + final SecretBackupModel backupModel, final Map tags) { + final SecretBackupListItem listItem = new SecretBackupListItem(); + listItem.setValue(LOWKEY_VAULT); + listItem.setContentType(MimeTypeUtils.TEXT_PLAIN_VALUE); + listItem.setVaultBaseUri(baseUri); + listItem.setId(name); + listItem.setVersion(version); + final SecretPropertiesModel propertiesModel = new SecretPropertiesModel(); + propertiesModel.setCreatedOn(TIME_10_MINUTES_AGO); + propertiesModel.setUpdatedOn(NOW); + propertiesModel.setNotBefore(TIME_IN_10_MINUTES); + propertiesModel.setExpiresOn(TIME_IN_10_MINUTES.plusDays(1)); + propertiesModel.setRecoveryLevel(RecoveryLevel.RECOVERABLE_AND_PURGEABLE); + propertiesModel.setRecoverableDays(RecoveryLevel.MAX_RECOVERABLE_DAYS_INCLUSIVE); + listItem.setAttributes(propertiesModel); + listItem.setTags(tags); + backupModel.getValue().add(listItem); + } +} diff --git a/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretControllerTest.java b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretControllerTest.java new file mode 100644 index 00000000..7faf1c0c --- /dev/null +++ b/lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/v7_3/SecretControllerTest.java @@ -0,0 +1,857 @@ +package com.github.nagyesta.lowkeyvault.controller.v7_3; + +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72ModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretItemModelConverter; +import com.github.nagyesta.lowkeyvault.mapper.v7_2.secret.SecretEntityToV72SecretVersionItemModelConverter; +import com.github.nagyesta.lowkeyvault.model.common.KeyVaultItemListModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; +import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.*; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.CreateSecretRequest; +import com.github.nagyesta.lowkeyvault.model.v7_2.secret.request.UpdateSecretRequest; +import com.github.nagyesta.lowkeyvault.service.common.ReadOnlyVersionedEntityMultiMap; +import com.github.nagyesta.lowkeyvault.service.exception.NotFoundException; +import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; +import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; +import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; +import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; +import com.github.nagyesta.lowkeyvault.service.secret.impl.KeyVaultSecretEntity; +import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; +import com.github.nagyesta.lowkeyvault.service.vault.VaultService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +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.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.NonNull; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.github.nagyesta.lowkeyvault.TestConstants.*; +import static com.github.nagyesta.lowkeyvault.TestConstantsSecrets.*; +import static com.github.nagyesta.lowkeyvault.TestConstantsUri.HTTPS_LOCALHOST_8443; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.*; + +class SecretControllerTest { + + private static final KeyVaultSecretModel RESPONSE = createResponse(); + private static final DeletedKeyVaultSecretModel DELETED_RESPONSE = createDeletedResponse(); + @Mock + private SecretEntityToV72ModelConverter secretEntityToV72ModelConverter; + @Mock + private SecretEntityToV72SecretItemModelConverter secretEntityToV72SecretItemModelConverter; + @Mock + private SecretEntityToV72SecretVersionItemModelConverter secretEntityToV72SecretVersionItemModelConverter; + @Mock + private VaultService vaultService; + @Mock + private VaultFake vaultFake; + @Mock + private SecretVaultFake secretVaultFake; + @Mock + private ReadOnlyVersionedEntityMultiMap entities; + @Mock + private ReadOnlyVersionedEntityMultiMap deletedEntities; + private SecretController underTest; + private AutoCloseable openMocks; + + private static KeyVaultSecretModel createResponse() { + final KeyVaultSecretModel model = new KeyVaultSecretModel(); + model.setValue(LOWKEY_VAULT); + model.setAttributes(new SecretPropertiesModel()); + model.setTags(Map.of()); + return model; + } + + private static DeletedKeyVaultSecretModel createDeletedResponse() { + final DeletedKeyVaultSecretModel model = new DeletedKeyVaultSecretModel(); + model.setValue(LOWKEY_VAULT); + model.setAttributes(new SecretPropertiesModel()); + model.setTags(Map.of()); + model.setDeletedDate(TIME_10_MINUTES_AGO); + model.setScheduledPurgeDate(TIME_IN_10_MINUTES); + model.setRecoveryId(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1.asRecoveryUri().toString()); + return model; + } + + @SuppressWarnings("checkstyle:MagicNumber") + public static Stream secretAttributeProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null, null)) + .add(Arguments.of(null, null, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(RecoveryLevel.RECOVERABLE, 90, null, null)) + .add(Arguments.of(RecoveryLevel.RECOVERABLE, 90, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(RecoveryLevel.RECOVERABLE_AND_PURGEABLE, 90, null, null)) + .add(Arguments.of(RecoveryLevel.RECOVERABLE_AND_PURGEABLE, 90, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(RecoveryLevel.CUSTOMIZED_RECOVERABLE, 42, null, null)) + .add(Arguments.of(RecoveryLevel.CUSTOMIZED_RECOVERABLE, 42, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .add(Arguments.of(RecoveryLevel.PURGEABLE, null, null, null)) + .add(Arguments.of(RecoveryLevel.PURGEABLE, null, TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES)) + .build(); + } + + public static Stream nullProvider() { + final SecretEntityToV72ModelConverter ec = mock(SecretEntityToV72ModelConverter.class); + final SecretEntityToV72SecretItemModelConverter ic = mock(SecretEntityToV72SecretItemModelConverter.class); + final SecretEntityToV72SecretVersionItemModelConverter vic = mock(SecretEntityToV72SecretVersionItemModelConverter.class); + return Stream.builder() + .add(Arguments.of(null, null, null, null)) + .add(Arguments.of(ec, null, null, null)) + .add(Arguments.of(null, ic, null, null)) + .add(Arguments.of(null, null, vic, null)) + .add(Arguments.of(null, null, null, mock(VaultService.class))) + .add(Arguments.of(null, ic, vic, mock(VaultService.class))) + .add(Arguments.of(ec, null, vic, mock(VaultService.class))) + .add(Arguments.of(ec, ic, null, mock(VaultService.class))) + .add(Arguments.of(ec, ic, vic, null)) + .build(); + } + + public static Stream updateAttributeProvider() { + return Stream.builder() + .add(Arguments.of(null, null, null, null)) + .add(Arguments.of(TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES, null, TAGS_EMPTY)) + .add(Arguments.of(null, TIME_IN_10_MINUTES, null, null)) + .add(Arguments.of(null, null, true, TAGS_THREE_KEYS)) + .add(Arguments.of(TIME_10_MINUTES_AGO, null, false, TAGS_TWO_KEYS)) + .add(Arguments.of(TIME_IN_10_MINUTES, null, null, TAGS_TWO_KEYS)) + .add(Arguments.of(TIME_10_MINUTES_AGO, TIME_IN_10_MINUTES, false, TAGS_TWO_KEYS)) + .build(); + } + + @BeforeEach + void setUp() { + openMocks = MockitoAnnotations.openMocks(this); + underTest = new SecretController(secretEntityToV72ModelConverter, secretEntityToV72SecretItemModelConverter, + secretEntityToV72SecretVersionItemModelConverter, vaultService); + when(vaultService.findByUri(eq(HTTPS_LOCALHOST_8443))).thenReturn(vaultFake); + when(vaultFake.baseUri()).thenReturn(HTTPS_LOCALHOST_8443); + when(vaultFake.secretVaultFake()).thenReturn(secretVaultFake); + } + + @AfterEach + void tearDown() throws Exception { + openMocks.close(); + } + + @ParameterizedTest + @MethodSource("nullProvider") + void testConstructorShouldThrowExceptionWhenCalledWithNull( + final SecretEntityToV72ModelConverter secretEntityToV72ModelConverter, + final SecretEntityToV72SecretItemModelConverter secretEntityToV72SecretItemModelConverter, + final SecretEntityToV72SecretVersionItemModelConverter secretEntityToV72SecretVersionItemModelConverter, + final VaultService vaultService) { + //given + + //when + Assertions.assertThrows(IllegalArgumentException.class, + () -> new SecretController(secretEntityToV72ModelConverter, secretEntityToV72SecretItemModelConverter, + secretEntityToV72SecretVersionItemModelConverter, vaultService)); + + //then + exception + } + + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testCreateShouldUseInputParametersWhenCalled( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + when(vaultFake.getRecoveryLevel()).thenReturn(RecoveryLevel.PURGEABLE); + when(vaultFake.getRecoverableDays()).thenReturn(null); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + when(secretVaultFake.createSecretVersion(eq(SECRET_NAME_1), eq(request.getValue()), eq(request.getContentType()))) + .thenReturn(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.create(SECRET_NAME_1, HTTPS_LOCALHOST_8443, request); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(secretVaultFake).createSecretVersion(eq(SECRET_NAME_1), eq(request.getValue()), eq(request.getContentType())); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake).setExpiry(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1), eq(notBefore), eq(expiry)); + verify(secretVaultFake).setEnabled(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1), eq(true)); + verify(secretVaultFake).addTags(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1), same(TAGS_TWO_KEYS)); + verify(secretEntityToV72ModelConverter).convert(same(entity)); + } + + @Test + void testVersionsShouldThrowExceptionWhenSecretIsNotFound() { + //given + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null)))) + .thenThrow(new NotFoundException("not found")); + + //when + Assertions.assertThrows(NotFoundException.class, + () -> underTest.versions(SECRET_NAME_1, HTTPS_LOCALHOST_8443, 0, 0)); + + //then + exception + } + + @SuppressWarnings("checkstyle:MagicNumber") + @Test + void testVersionsShouldFilterTheListReturnedWhenSecretIsFoundAndHasMoreVersionsThanNeeded() { + //given + final int index = 30; + final LinkedList fullList = IntStream.range(0, 42) + .mapToObj(i -> UUID.randomUUID().toString().replaceAll("-", "")) + .collect(Collectors.toCollection(LinkedList::new)); + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + final String expectedNextUri = baseUri.asUri("versions?api-version=7.3&$skiptoken=31&maxresults=1").toString(); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(baseUri))).thenReturn(fullList); + when(entities.getReadOnlyEntity(any())).thenAnswer(invocation -> { + final VersionedSecretEntityId secretEntityId = invocation.getArgument(0, VersionedSecretEntityId.class); + return createEntity(secretEntityId, createRequest(null, null)); + }); + when(secretEntityToV72SecretVersionItemModelConverter.convert(any())).thenAnswer(invocation -> { + final KeyVaultSecretEntity entity = invocation.getArgument(0, KeyVaultSecretEntity.class); + return keyVaultSecretItemModel(entity.getId().asUri(), Map.of()); + }); + final URI expected = new VersionedSecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, fullList.get(index)).asUri(); + + //when + final ResponseEntity> actual = + underTest.versions(SECRET_NAME_1, HTTPS_LOCALHOST_8443, 1, index); + + //then + Assertions.assertNotNull(actual); + final KeyVaultItemListModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertEquals(expected.toString(), actualBody.getValue().get(0).getId()); + Assertions.assertNotNull(actualBody.getNextLink()); + Assertions.assertEquals(expectedNextUri, actualBody.getNextLink()); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @Test + void testVersionsShouldNotContainNextUriWhenLastPageIsReturnedFully() { + //given + final LinkedList fullList = IntStream.range(0, 25) + .mapToObj(i -> UUID.randomUUID().toString().replaceAll("-", "")) + .collect(Collectors.toCollection(LinkedList::new)); + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(entities.getVersions(eq(baseUri))).thenReturn(fullList); + when(entities.getReadOnlyEntity(any())).thenAnswer(invocation -> { + final VersionedSecretEntityId secretEntityId = invocation.getArgument(0, VersionedSecretEntityId.class); + return createEntity(secretEntityId, createRequest(null, null)); + }); + when(secretEntityToV72SecretVersionItemModelConverter.convert(any())).thenAnswer(invocation -> { + final KeyVaultSecretEntity entity = invocation.getArgument(0, KeyVaultSecretEntity.class); + return keyVaultSecretItemModel(entity.getId().asUri(), Map.of()); + }); + final List expected = fullList.stream() + .map(e -> new VersionedSecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, e).asUri()) + .collect(Collectors.toList()); + + //when + final ResponseEntity> actual = + underTest.versions(SECRET_NAME_1, HTTPS_LOCALHOST_8443, 25, 0); + + //then + Assertions.assertNotNull(actual); + final KeyVaultItemListModel actualBody = actual.getBody(); + Assertions.assertNotNull(actualBody); + final List actualList = actualBody.getValue().stream() + .map(KeyVaultSecretItemModel::getId) + .map(URI::create) + .collect(Collectors.toList()); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertIterableEquals(expected, actualList); + Assertions.assertNull(actualBody.getNextLink()); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testDeleteSecretShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(deletedEntities); + doNothing().when(secretVaultFake).delete(eq(baseUri)); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(deletedEntities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(deletedEntities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convertDeleted(same(entity))) + .thenReturn(DELETED_RESPONSE); + + //when + final ResponseEntity actual = underTest.delete(SECRET_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(DELETED_RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + final InOrder inOrder = inOrder(secretVaultFake); + inOrder.verify(secretVaultFake).delete(eq(baseUri)); + inOrder.verify(secretVaultFake, atLeastOnce()).getDeletedEntities(); + verify(secretVaultFake, never()).getEntities(); + verify(deletedEntities).getLatestVersionOfEntity(eq(baseUri)); + verify(deletedEntities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testRecoverDeletedSecretShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(deletedEntities); + doNothing().when(secretVaultFake).delete(eq(baseUri)); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.recoverDeletedSecret(SECRET_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + final InOrder inOrder = inOrder(secretVaultFake); + inOrder.verify(secretVaultFake).recover(eq(baseUri)); + inOrder.verify(secretVaultFake, atLeastOnce()).getEntities(); + verify(secretVaultFake, never()).getDeletedEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetDeletedSecretShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convertDeleted(same(entity))) + .thenReturn(DELETED_RESPONSE); + + //when + final ResponseEntity actual = underTest.getDeletedSecret(SECRET_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(DELETED_RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, never()).getEntities(); + verify(secretVaultFake, atLeastOnce()).getDeletedEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testPurgeDeletedShouldSucceedWhenDeletedSecretIsPurgeable( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + final RecoveryLevel nonNullRecoveryLevel = Optional.ofNullable(recoveryLevel).orElse(RecoveryLevel.PURGEABLE); + if (!nonNullRecoveryLevel.isPurgeable()) { + doThrow(IllegalStateException.class).when(secretVaultFake).purge(eq(UNVERSIONED_SECRET_ENTITY_ID_1)); + } + + //when + if (nonNullRecoveryLevel.isPurgeable()) { + final ResponseEntity response = underTest.purgeDeleted(SECRET_NAME_1, HTTPS_LOCALHOST_8443); + Assertions.assertNotNull(response); + Assertions.assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode()); + } else { + Assertions.assertThrows(IllegalStateException.class, () -> underTest.purgeDeleted(SECRET_NAME_1, HTTPS_LOCALHOST_8443)); + } + + //then + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, never()).getEntities(); + verify(secretVaultFake, never()).getDeletedEntities(); + verify(secretVaultFake, atLeastOnce()).purge(eq(UNVERSIONED_SECRET_ENTITY_ID_1)); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + verify(entities, never()).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter, never()).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getLatestVersionOfEntity((eq(baseUri)))) + .thenReturn(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.get(SECRET_NAME_1, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, atLeastOnce()).getEntities(); + verify(entities).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetSecretsShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity)); + final KeyVaultSecretItemModel secretItemModel = keyVaultSecretItemModel(baseUri.asUri(), Map.of()); + when(secretEntityToV72SecretItemModelConverter.convert(same(entity))) + .thenReturn(secretItemModel); + + //when + final ResponseEntity> actual = + underTest.listSecrets(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(secretItemModel, actual.getBody().getValue().get(0)); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, atLeastOnce()).getEntities(); + verify(secretVaultFake, never()).getDeletedEntities(); + verify(entities).listLatestEntities(); + verify(secretEntityToV72SecretItemModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetSecretsShouldReturnNextLinkWhenNotOnLastPage( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity, entity, entity)); + final KeyVaultSecretItemModel secretItemModel = keyVaultSecretItemModel(baseUri.asUri(), Map.of()); + when(secretEntityToV72SecretItemModelConverter.convert(same(entity))) + .thenReturn(secretItemModel); + + //when + final ResponseEntity> actual = + underTest.listSecrets(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(secretItemModel, actual.getBody().getValue().get(0)); + final String expectedNextLink = HTTPS_LOCALHOST_8443 + "/secrets?api-version=7.3&$skiptoken=1&maxresults=1"; + Assertions.assertEquals(expectedNextLink, actual.getBody().getNextLink()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, atLeastOnce()).getEntities(); + verify(secretVaultFake, never()).getDeletedEntities(); + verify(entities).listLatestEntities(); + verify(secretEntityToV72SecretItemModelConverter).convert(same(entity)); + } + + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetDeletedSecretsShouldReturnEntryWhenSecretIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity)); + final DeletedKeyVaultSecretItemModel secretItemModel = deletedKeyVaultSecretItemModel(baseUri, Map.of()); + when(secretEntityToV72SecretItemModelConverter.convertDeleted(same(entity))) + .thenReturn(secretItemModel); + + //when + final ResponseEntity> actual = + underTest.listDeletedSecrets(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(secretItemModel, actual.getBody().getValue().get(0)); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, atLeastOnce()).getDeletedEntities(); + verify(secretVaultFake, never()).getEntities(); + verify(entities).listLatestEntities(); + verify(secretEntityToV72SecretItemModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetDeletedSecretsShouldReturnNextLinkWhenNotOnLastPage( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + when(secretVaultFake.getDeletedEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + entity.setDeletedDate(TIME_10_MINUTES_AGO); + entity.setScheduledPurgeDate(TIME_IN_10_MINUTES); + when(entities.listLatestEntities()) + .thenReturn(List.of(entity, entity, entity)); + final DeletedKeyVaultSecretItemModel secretItemModel = deletedKeyVaultSecretItemModel(baseUri, Map.of()); + when(secretEntityToV72SecretItemModelConverter.convertDeleted(same(entity))) + .thenReturn(secretItemModel); + + //when + final ResponseEntity> actual = + underTest.listDeletedSecrets(HTTPS_LOCALHOST_8443, 1, 0); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertNotNull(actual.getBody()); + Assertions.assertNotNull(actual.getBody().getValue()); + Assertions.assertEquals(1, actual.getBody().getValue().size()); + Assertions.assertSame(secretItemModel, actual.getBody().getValue().get(0)); + final String expectedNextLink = HTTPS_LOCALHOST_8443 + "/deletedsecrets?api-version=7.3&$skiptoken=1&maxresults=1"; + Assertions.assertEquals(expectedNextLink, actual.getBody().getNextLink()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake, atLeastOnce()).getDeletedEntities(); + verify(secretVaultFake, never()).getEntities(); + verify(entities).listLatestEntities(); + verify(secretEntityToV72SecretItemModelConverter).convertDeleted(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("secretAttributeProvider") + void testGetWithVersionShouldReturnEntryWhenSecretAndVersionIsFound( + final RecoveryLevel recoveryLevel, final Integer recoverableDays, + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + final CreateSecretRequest request = createRequest(expiry, notBefore); + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, request); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(recoveryLevel); + when(vaultFake.getRecoverableDays()) + .thenReturn(recoverableDays); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest.getWithVersion(SECRET_NAME_1, SECRET_VERSION_3, HTTPS_LOCALHOST_8443); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + verify(entities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convert(same(entity)); + } + + @SuppressWarnings("checkstyle:MagicNumber") + @ParameterizedTest + @MethodSource("updateAttributeProvider") + void testUpdateVersionShouldReturnEntryWhenSecretAndVersionIsFound( + final OffsetDateTime expiry, final OffsetDateTime notBefore, + final Boolean enabled, final Map tags) { + //given + final SecretEntityId baseUri = new SecretEntityId(HTTPS_LOCALHOST_8443, SECRET_NAME_1, null); + final CreateSecretRequest createSecretRequest = createRequest(null, null); + final UpdateSecretRequest updateSecretRequest = new UpdateSecretRequest(); + if (tags != null) { + updateSecretRequest.setTags(tags); + } + if (enabled != null || expiry != null || notBefore != null) { + final BasePropertiesUpdateModel properties = new BasePropertiesUpdateModel(); + properties.setEnabled(enabled); + properties.setExpiresOn(expiry); + properties.setNotBefore(notBefore); + updateSecretRequest.setProperties(properties); + } + final ReadOnlyKeyVaultSecretEntity entity = createEntity(VERSIONED_SECRET_ENTITY_ID_1_VERSION_1, createSecretRequest); + when(secretVaultFake.getEntities()) + .thenReturn(entities); + when(vaultFake.getRecoveryLevel()) + .thenReturn(RecoveryLevel.PURGEABLE); + when(entities.getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3))) + .thenReturn(entity); + when(secretEntityToV72ModelConverter.convert(same(entity))) + .thenReturn(RESPONSE); + + //when + final ResponseEntity actual = underTest + .updateVersion(SECRET_NAME_1, SECRET_VERSION_3, HTTPS_LOCALHOST_8443, updateSecretRequest); + + //then + Assertions.assertNotNull(actual); + Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); + Assertions.assertSame(RESPONSE, actual.getBody()); + verify(vaultService).findByUri(eq(HTTPS_LOCALHOST_8443)); + verify(vaultFake).secretVaultFake(); + verify(vaultFake).getRecoveryLevel(); + verify(vaultFake).getRecoverableDays(); + verify(secretVaultFake).getEntities(); + verify(entities, never()).getLatestVersionOfEntity(eq(baseUri)); + final InOrder inOrder = inOrder(secretVaultFake, entities); + if (enabled != null) { + inOrder.verify(secretVaultFake) + .setEnabled(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), eq(enabled)); + } else { + inOrder.verify(secretVaultFake, never()) + .setEnabled(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), anyBoolean()); + } + if (expiry != null || notBefore != null) { + inOrder.verify(secretVaultFake) + .setExpiry(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), eq(notBefore), eq(expiry)); + } else { + inOrder.verify(secretVaultFake, never()) + .setExpiry(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), any(), any()); + } + if (tags != null) { + inOrder.verify(secretVaultFake) + .clearTags(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + inOrder.verify(secretVaultFake) + .addTags(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), same(updateSecretRequest.getTags())); + } else { + inOrder.verify(secretVaultFake, never()) + .clearTags(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + inOrder.verify(secretVaultFake, never()) + .addTags(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3), anyMap()); + } + inOrder.verify(entities).getReadOnlyEntity(eq(VERSIONED_SECRET_ENTITY_ID_1_VERSION_3)); + verify(secretEntityToV72ModelConverter).convert(same(entity)); + } + + @NonNull + private CreateSecretRequest createRequest( + final OffsetDateTime expiry, final OffsetDateTime notBefore) { + final CreateSecretRequest secretRequest = new CreateSecretRequest(); + secretRequest.setValue(LOWKEY_VAULT); + final SecretPropertiesModel properties = new SecretPropertiesModel(); + properties.setExpiresOn(expiry); + properties.setNotBefore(notBefore); + properties.setEnabled(true); + secretRequest.setProperties(properties); + secretRequest.setTags(TAGS_TWO_KEYS); + return secretRequest; + } + + @NonNull + private KeyVaultSecretEntity createEntity(final VersionedSecretEntityId secretEntityId, final CreateSecretRequest createSecretRequest) { + return new KeyVaultSecretEntity(secretEntityId, vaultFake, createSecretRequest.getValue(), createSecretRequest.getContentType()); + } + + private KeyVaultSecretItemModel keyVaultSecretItemModel(final URI asUriNoVersion, final Map tags) { + final KeyVaultSecretItemModel model = new KeyVaultSecretItemModel(); + model.setAttributes(new SecretPropertiesModel()); + model.setId(asUriNoVersion.toString()); + model.setTags(tags); + return model; + } + + private DeletedKeyVaultSecretItemModel deletedKeyVaultSecretItemModel(final SecretEntityId id, final Map tags) { + final DeletedKeyVaultSecretItemModel model = new DeletedKeyVaultSecretItemModel(); + model.setAttributes(new SecretPropertiesModel()); + model.setId(id.asUriNoVersion().toString()); + model.setTags(tags); + model.setDeletedDate(TIME_10_MINUTES_AGO); + model.setScheduledPurgeDate(TIME_IN_10_MINUTES); + model.setRecoveryId(id.asRecoveryUri().toString()); + return model; + } +} diff --git a/lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/ApacheHttpClientProvider.java b/lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/ApacheHttpClientProvider.java index a62882ce..d1b83126 100644 --- a/lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/ApacheHttpClientProvider.java +++ b/lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/ApacheHttpClientProvider.java @@ -59,32 +59,56 @@ public HttpClient createInstance() { return new ApacheHttpClient(hostOverrideFunction); } + public LowkeyVaultManagementClient getLowkeyVaultManagementClient(final ObjectMapper objectMapper) { + return new LowkeyVaultManagementClientImpl(vaultUrl, createInstance(), objectMapper); + } + public KeyAsyncClient getKeyAsyncClient() { - return getKeyBuilder().buildAsyncClient(); + return getKeyAsyncClient(KeyServiceVersion.V7_3); } - public LowkeyVaultManagementClient getLowkeyVaultManagementClient(final ObjectMapper objectMapper) { - return new LowkeyVaultManagementClientImpl(vaultUrl, createInstance(), objectMapper); + public KeyAsyncClient getKeyAsyncClient(final KeyServiceVersion version) { + return getKeyBuilder().serviceVersion(version).buildAsyncClient(); } public KeyClient getKeyClient() { - return getKeyBuilder().buildClient(); + return getKeyClient(KeyServiceVersion.V7_3); + } + + public KeyClient getKeyClient(final KeyServiceVersion version) { + return getKeyBuilder().serviceVersion(version).buildClient(); } public SecretAsyncClient getSecretAsyncClient() { - return getSecretBuilder().buildAsyncClient(); + return getSecretAsyncClient(SecretServiceVersion.V7_3); + } + + public SecretAsyncClient getSecretAsyncClient(final SecretServiceVersion version) { + return getSecretBuilder().serviceVersion(version).buildAsyncClient(); } public SecretClient getSecretClient() { - return getSecretBuilder().buildClient(); + return getSecretClient(SecretServiceVersion.V7_3); + } + + public SecretClient getSecretClient(final SecretServiceVersion version) { + return getSecretBuilder().serviceVersion(version).buildClient(); } public CryptographyAsyncClient getCryptoAsyncClient(final String webKeyId) { - return getCryptoBuilder(webKeyId).buildAsyncClient(); + return getCryptoAsyncClient(webKeyId, CryptographyServiceVersion.V7_3); + } + + public CryptographyAsyncClient getCryptoAsyncClient(final String webKeyId, final CryptographyServiceVersion version) { + return getCryptoBuilder(webKeyId).serviceVersion(version).buildAsyncClient(); } public CryptographyClient getCryptoClient(final String webKeyId) { - return getCryptoBuilder(webKeyId).buildClient(); + return getCryptoClient(webKeyId, CryptographyServiceVersion.V7_3); + } + + public CryptographyClient getCryptoClient(final String webKeyId, final CryptographyServiceVersion version) { + return getCryptoBuilder(webKeyId).serviceVersion(version).buildClient(); } private KeyClientBuilder getKeyBuilder() { @@ -92,7 +116,6 @@ private KeyClientBuilder getKeyBuilder() { .vaultUrl(getVaultUrl()) .credential(new BasicAuthenticationCredential(DUMMY, DUMMY)) .httpClient(createInstance()) - .serviceVersion(KeyServiceVersion.V7_2) .retryPolicy(new RetryPolicy(new FixedDelay(0, Duration.ZERO))); } @@ -101,7 +124,6 @@ private SecretClientBuilder getSecretBuilder() { .vaultUrl(getVaultUrl()) .credential(new BasicAuthenticationCredential(DUMMY, DUMMY)) .httpClient(createInstance()) - .serviceVersion(SecretServiceVersion.V7_2) .retryPolicy(new RetryPolicy(new FixedDelay(0, Duration.ZERO))); } @@ -110,7 +132,6 @@ private CryptographyClientBuilder getCryptoBuilder(final String webKeyId) { .keyIdentifier(Objects.requireNonNull(webKeyId)) .credential(new BasicAuthenticationCredential(DUMMY, DUMMY)) .httpClient(createInstance()) - .serviceVersion(CryptographyServiceVersion.V7_2) .retryPolicy(new RetryPolicy(new FixedDelay(0, Duration.ZERO))); } diff --git a/lowkey-vault-docker/build.gradle b/lowkey-vault-docker/build.gradle index 1a696997..96224a4d 100644 --- a/lowkey-vault-docker/build.gradle +++ b/lowkey-vault-docker/build.gradle @@ -41,6 +41,7 @@ task copyAppJar(type: Copy) { 'lowkey-vault.jar' } dependsOn(":lowkey-vault-app:bootJar") + dependsOn(":lowkey-vault-app:test") } docker { diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/CommonTestContext.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/CommonTestContext.java index 4de518dc..766adbc2 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/CommonTestContext.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/CommonTestContext.java @@ -1,5 +1,6 @@ package com.github.nagyesta.lowkeyvault.context; +import com.azure.core.util.ServiceVersion; import com.github.nagyesta.lowkeyvault.http.ApacheHttpClientProvider; import java.time.OffsetDateTime; @@ -11,7 +12,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Function; -public abstract class CommonTestContext { +public abstract class CommonTestContext { public static final OffsetDateTime NOW = OffsetDateTime.now(ZoneOffset.UTC); @@ -38,9 +39,9 @@ public void setProvider(final ApacheHttpClientProvider provider) { this.provider = provider; } - public synchronized C getClient() { + public synchronized C getClient(final V version) { if (client == null) { - client = providerToClient(getProvider()); + client = providerToClient(getProvider(), version); } return client; } @@ -49,7 +50,7 @@ public void setClient(final C client) { this.client = client; } - protected abstract C providerToClient(ApacheHttpClientProvider provider); + protected abstract C providerToClient(ApacheHttpClientProvider provider, V version); public Map> getCreatedEntities() { return createdEntities; diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/KeyTestContext.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/KeyTestContext.java index 1d2e0305..e89e6d86 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/KeyTestContext.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/KeyTestContext.java @@ -1,7 +1,9 @@ package com.github.nagyesta.lowkeyvault.context; import com.azure.security.keyvault.keys.KeyClient; +import com.azure.security.keyvault.keys.KeyServiceVersion; import com.azure.security.keyvault.keys.cryptography.CryptographyClient; +import com.azure.security.keyvault.keys.cryptography.CryptographyServiceVersion; import com.azure.security.keyvault.keys.cryptography.models.DecryptResult; import com.azure.security.keyvault.keys.cryptography.models.EncryptResult; import com.azure.security.keyvault.keys.models.*; @@ -11,9 +13,10 @@ import javax.crypto.SecretKey; import java.security.KeyPair; +import java.util.Arrays; import java.util.Optional; -public class KeyTestContext extends CommonTestContext { +public class KeyTestContext extends CommonTestContext { private final ObjectMapper objectMapper = new ObjectMapper(); private LowkeyVaultManagementClient lowkeyVaultManagementClient; @@ -28,14 +31,31 @@ public class KeyTestContext extends CommonTestContext v.getVersion().equalsIgnoreCase(version)).findFirst().orElseThrow(); + cryptoServiceVersion = Arrays.stream(CryptographyServiceVersion.values()) + .filter(v -> v.getVersion().equalsIgnoreCase(version)).findFirst().orElseThrow(); + } + + public KeyServiceVersion getKeyServiceVersion() { + return keyServiceVersion; + } + + public CryptographyServiceVersion getCryptoServiceVersion() { + return cryptoServiceVersion; + } + @Override - protected KeyClient providerToClient(final ApacheHttpClientProvider provider) { - return provider.getKeyClient(); + protected KeyClient providerToClient(final ApacheHttpClientProvider provider, final KeyServiceVersion version) { + return provider.getKeyClient(version); } public synchronized LowkeyVaultManagementClient getLowkeyVaultManagementClient() { diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/SecretTestContext.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/SecretTestContext.java index 08da5ea1..5190e3eb 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/SecretTestContext.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/context/SecretTestContext.java @@ -1,27 +1,37 @@ package com.github.nagyesta.lowkeyvault.context; import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretServiceVersion; import com.azure.security.keyvault.secrets.models.DeletedSecret; import com.azure.security.keyvault.secrets.models.KeyVaultSecret; import com.azure.security.keyvault.secrets.models.SecretProperties; import com.github.nagyesta.lowkeyvault.http.ApacheHttpClientProvider; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; +import java.util.Arrays; -public class SecretTestContext extends CommonTestContext { - - public static final OffsetDateTime NOW = OffsetDateTime.now(ZoneOffset.UTC); +public class SecretTestContext + extends CommonTestContext { private KeyVaultSecret createSecretOptions; + private SecretServiceVersion secretServiceVersion = SecretServiceVersion.getLatest(); + + public SecretServiceVersion getSecretServiceVersion() { + return secretServiceVersion; + } + + public void setApiVersion(final String version) { + secretServiceVersion = Arrays.stream(SecretServiceVersion.values()) + .filter(v -> v.getVersion().equalsIgnoreCase(version)).findFirst().orElseThrow(); + } + public SecretTestContext(final ApacheHttpClientProvider provider) { super(provider); } @Override - protected SecretClient providerToClient(final ApacheHttpClientProvider provider) { - return provider.getSecretClient(); + protected SecretClient providerToClient(final ApacheHttpClientProvider provider, final SecretServiceVersion version) { + return provider.getSecretClient(version); } public KeyVaultSecret getCreateSecretOptions() { 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 b9d1a8c2..951942c3 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 @@ -40,6 +40,11 @@ public class KeysStepDefs extends CommonAssertions { @Autowired private KeyTestContext context; + @Given("key API version {api} is used") + public void apiVersionApiIsUsed(final String version) { + context.setApiVersion(version); + } + @Given("a key client is created with the vault named {name}") public void theKeyClientIsCreatedWithVaultNameSelected(final String vaultName) { final String vaultAuthority = vaultName + ".localhost:8443"; @@ -70,7 +75,7 @@ public void anRsaKeyIsCreatedWithNameKeySizeAndHsmSet(final String keyName, fina public void ecKeyCreationRequestIsSentVersionsCountTimes(final int versionsCount) { final CreateEcKeyOptions createKeyOptions = context.getCreateEcKeyOptions(); IntStream.range(0, versionsCount).forEach(i -> { - final KeyVaultKey ecKey = context.getClient().createEcKey(createKeyOptions); + final KeyVaultKey ecKey = context.getClient(context.getKeyServiceVersion()).createEcKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), ecKey); }); } @@ -79,7 +84,7 @@ public void ecKeyCreationRequestIsSentVersionsCountTimes(final int versionsCount public void octKeyCreationRequestIsSentVersionsCountTimes(final int versionsCount) { final CreateOctKeyOptions createKeyOptions = context.getCreateOctKeyOptions(); IntStream.range(0, versionsCount).forEach(i -> { - final KeyVaultKey octKey = context.getClient().createOctKey(createKeyOptions); + final KeyVaultKey octKey = context.getClient(context.getKeyServiceVersion()).createOctKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), octKey); }); } @@ -88,7 +93,7 @@ public void octKeyCreationRequestIsSentVersionsCountTimes(final int versionsCoun public void rsaKeyCreationRequestIsSentVersionsCountTimes(final int versionsCount) { final CreateRsaKeyOptions createKeyOptions = context.getCreateRsaKeyOptions(); IntStream.range(0, versionsCount).forEach(i -> { - final KeyVaultKey rsaKey = context.getClient().createRsaKey(createKeyOptions); + final KeyVaultKey rsaKey = context.getClient(context.getKeyServiceVersion()).createRsaKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), rsaKey); }); } @@ -96,21 +101,21 @@ public void rsaKeyCreationRequestIsSentVersionsCountTimes(final int versionsCoun @And("the EC key is created") public void ecKeyCreationRequestIsSent() { final CreateEcKeyOptions createKeyOptions = context.getCreateEcKeyOptions(); - final KeyVaultKey ecKey = context.getClient().createEcKey(createKeyOptions); + final KeyVaultKey ecKey = context.getClient(context.getKeyServiceVersion()).createEcKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), ecKey); } @And("the OCT key is created") public void octKeyCreationRequestIsSent() { final CreateOctKeyOptions createKeyOptions = context.getCreateOctKeyOptions(); - final KeyVaultKey octKey = context.getClient().createOctKey(createKeyOptions); + final KeyVaultKey octKey = context.getClient(context.getKeyServiceVersion()).createOctKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), octKey); } @And("the RSA key is created") public void rsaKeyCreationRequestIsSent() { final CreateRsaKeyOptions createKeyOptions = context.getCreateRsaKeyOptions(); - final KeyVaultKey rsaKey = context.getClient().createRsaKey(createKeyOptions); + final KeyVaultKey rsaKey = context.getClient(context.getKeyServiceVersion()).createRsaKey(createKeyOptions); context.addCreatedEntity(createKeyOptions.getName(), rsaKey); } @@ -125,7 +130,7 @@ public void ecKeyImportedWithNameAndParameters(final String name, final KeyCurve } final ImportKeyOptions options = new ImportKeyOptions(name, key) .setHardwareProtected(hsm); - final KeyVaultKey ecKey = context.getClient().importKey(options); + final KeyVaultKey ecKey = context.getClient(context.getKeyServiceVersion()).importKey(options); context.addCreatedEntity(name, ecKey); } @@ -140,7 +145,7 @@ public void rsaKeyImportedWithNameAndParameters(final String name, final int siz } final ImportKeyOptions options = new ImportKeyOptions(name, key) .setHardwareProtected(hsm); - final KeyVaultKey rsaKey = context.getClient().importKey(options); + final KeyVaultKey rsaKey = context.getClient(context.getKeyServiceVersion()).importKey(options); context.addCreatedEntity(name, rsaKey); } @@ -153,7 +158,7 @@ public void octKeyImportedWithNameAndParameters(final String name, final int siz .setKeyType(KeyType.OCT_HSM); final ImportKeyOptions options = new ImportKeyOptions(name, key) .setHardwareProtected(true); - final KeyVaultKey rsaKey = context.getClient().importKey(options); + final KeyVaultKey rsaKey = context.getClient(context.getKeyServiceVersion()).importKey(options); context.addCreatedEntity(name, rsaKey); } @@ -161,14 +166,14 @@ public void octKeyImportedWithNameAndParameters(final String name, final int siz public void fetchFirstKeyVersion(final String name) { final List versionsCreated = context.getCreatedEntities().get(name); final String version = versionsCreated.get(0).getProperties().getVersion(); - final KeyVaultKey vaultKey = context.getClient().getKey(name, version); + final KeyVaultKey vaultKey = context.getClient(context.getKeyServiceVersion()).getKey(name, version); context.addFetchedKey(name, vaultKey); assertEquals(version, vaultKey.getProperties().getVersion()); } @When("the last key version of {name} is fetched without providing a version") public void fetchLatestKeyVersion(final String name) { - final KeyVaultKey vaultKey = context.getClient().getKey(name); + final KeyVaultKey vaultKey = context.getClient(context.getKeyServiceVersion()).getKey(name); final List versionsCreated = context.getCreatedEntities().get(name); final String expectedLastVersionId = versionsCreated.get(versionsCreated.size() - 1).getId(); context.addFetchedKey(name, vaultKey); @@ -202,13 +207,14 @@ public void theKeyIsSetToBeEnabledStatus(final boolean enabledStatus) { @And("the key is deleted") public void theKeyIsDeleted() { - final DeletedKey actual = context.getClient().beginDeleteKey(context.getLastResult().getName()).waitForCompletion().getValue(); + final DeletedKey actual = context.getClient(context.getKeyServiceVersion()) + .beginDeleteKey(context.getLastResult().getName()).waitForCompletion().getValue(); context.setLastDeleted(actual); } @When("the key properties are listed") public void theKeyPropertiesAreListed() { - final PagedIterable actual = context.getClient().listPropertiesOfKeys(); + final PagedIterable actual = context.getClient(context.getKeyServiceVersion()).listPropertiesOfKeys(); final List list = actual.stream() .map(KeyProperties::getId) .collect(Collectors.toList()); @@ -246,7 +252,8 @@ public void rsaKeysWithKeyNamePrefixAreCreatedWithBitsSizeAndHSM( public void countKeysWithKeyNamePrefixAreDeleted( final int count, final String prefix) { final List deleted = IntStream.range(0, count).mapToObj(i -> { - final DeletedKey actual = context.getClient().beginDeleteKey(prefix + (i + 1)).waitForCompletion().getValue(); + final DeletedKey actual = context.getClient(context.getKeyServiceVersion()) + .beginDeleteKey(prefix + (i + 1)).waitForCompletion().getValue(); context.setLastDeleted(actual); return actual; }).map(DeletedKey::getRecoveryId).collect(Collectors.toList()); @@ -255,7 +262,7 @@ public void countKeysWithKeyNamePrefixAreDeleted( @When("the deleted key properties are listed") public void theDeletedKeyPropertiesAreListed() { - final PagedIterable actual = context.getClient().listDeletedKeys(); + final PagedIterable actual = context.getClient(context.getKeyServiceVersion()).listDeletedKeys(); final List list = actual.stream() .map(DeletedKey::getRecoveryId) .collect(Collectors.toList()); @@ -265,20 +272,21 @@ public void theDeletedKeyPropertiesAreListed() { @When("the key is recovered") public void theKeyIsRecovered() { final DeletedKey deleted = context.getLastDeleted(); - final KeyVaultKey key = context.getClient().beginRecoverDeletedKey(deleted.getName()).waitForCompletion().getValue(); + final KeyVaultKey key = context.getClient(context.getKeyServiceVersion()) + .beginRecoverDeletedKey(deleted.getName()).waitForCompletion().getValue(); context.addFetchedKey(key.getName(), key); } @When("the key is purged") public void theKeyIsPurged() { final DeletedKey deleted = context.getLastDeleted(); - context.getClient().purgeDeletedKey(deleted.getName()); + context.getClient(context.getKeyServiceVersion()).purgeDeletedKey(deleted.getName()); } @When("the last version of the key is prepared for an update") public void theLastVersionOfTheKeyIsPreparedForAnUpdate() { final KeyVaultKey lastResult = context.getLastResult(); - final KeyProperties updatedProperties = context.getClient() + final KeyProperties updatedProperties = context.getClient(context.getKeyServiceVersion()) .getKey(lastResult.getName(), lastResult.getProperties().getVersion()).getProperties(); context.setUpdateProperties(updatedProperties); } @@ -310,7 +318,7 @@ public void theKeyIsUpdatedToBeEnabledStatus(final boolean enabledStatus) { @When("the key update request is sent") public void theUpdateRequestIsSent() { - final KeyProperties properties = context.getClient() + final KeyProperties properties = context.getClient(context.getKeyServiceVersion()) .updateKeyProperties(context.getUpdateProperties(), context.getUpdateKeyOperations()) .getProperties(); fetchLatestKeyVersion(properties.getName()); @@ -319,7 +327,7 @@ public void theUpdateRequestIsSent() { @When("the created key is used to encrypt {clearText} with {algorithm}") public void theCreatedKeyIsUsedToEncryptClearTextWithAlgorithm(final byte[] text, final EncryptionAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); - context.setCryptographyClient(context.getProvider().getCryptoClient(keyId)); + context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); final EncryptResult encryptResult = context.getCryptographyClient() .encrypt(encryptParams(algorithm, text), Context.NONE); context.setEncryptResult(encryptResult); @@ -328,7 +336,7 @@ public void theCreatedKeyIsUsedToEncryptClearTextWithAlgorithm(final byte[] text @When("the created key is used to sign {clearText} with {signAlgorithm}") public void theCreatedKeyIsUsedToSignClearTextWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); - context.setCryptographyClient(context.getProvider().getCryptoClient(keyId)); + context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); final byte[] digest = hash(text, algorithm.toString()); final SignResult signResult = context.getCryptographyClient() .sign(algorithm, digest); @@ -350,7 +358,7 @@ public void theSignedValueIsNotClearText(final byte[] text) { @And("the encrypted value is decrypted with {algorithm}") public void theEncryptedValueIsDecryptedWithAlgorithm(final EncryptionAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); - context.setCryptographyClient(context.getProvider().getCryptoClient(keyId)); + context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); final DecryptResult decryptResult = context.getCryptographyClient() .decrypt(decryptParams(algorithm, context.getEncryptResult()), Context.NONE); context.setDecryptResult(decryptResult); @@ -368,7 +376,7 @@ public void theEncryptedValueIsDecryptedUsingTheOriginalOctKeyWithAlgorithm(fina @And("the signature of {clearText} is verified with {signAlgorithm}") public void theSignValueIsVerifiedWithAlgorithm(final byte[] text, final SignatureAlgorithm algorithm) { final String keyId = context.getLastResult().getKey().getId(); - context.setCryptographyClient(context.getProvider().getCryptoClient(keyId)); + context.setCryptographyClient(context.getProvider().getCryptoClient(keyId, context.getCryptoServiceVersion())); final byte[] digest = hash(text, algorithm.toString()); final VerifyResult verifyResult = context.getCryptographyClient().verify(algorithm, digest, context.getSignatureResult()); context.setVerifyResult(verifyResult.isValid()); @@ -398,13 +406,14 @@ public void theRsaSignValueIsVerifiedUsingOriginalPublicKeyWithAlgorithm(final b @And("the key named {name} is backed up") public void theKeyNamedNameIsBackedUp(final String name) { - final byte[] bytes = context.getClient().backupKey(name); + final byte[] bytes = context.getClient(context.getKeyServiceVersion()).backupKey(name); context.setBackupBytes(name, bytes); } + @SuppressWarnings({"ResultOfMethodCallIgnored", "resource"}) @And("the key named {name} is backed up to resource") public void theKeyNamedNameIsBackedUpToResource(final String name) throws IOException { - final byte[] bytes = context.getClient().backupKey(name); + final byte[] bytes = context.getClient(context.getKeyServiceVersion()).backupKey(name); final String s = context.getLowkeyVaultManagementClient().unpackBackup(bytes); final File file = new File("/home/esta/IdeaProjects/github/lowkey-vault/lowkey-vault-docker/src/test/resources" + "/json/backups/" + name + ".json"); @@ -416,7 +425,7 @@ public void theKeyNamedNameIsBackedUpToResource(final String name) throws IOExce @And("the key named {name} is restored") public void theKeyNamedNameIsRestored(final String name) { final byte[] bytes = context.getBackupBytes(name); - final KeyVaultKey key = context.getClient().restoreKeyBackup(bytes); + final KeyVaultKey key = context.getClient(context.getKeyServiceVersion()).restoreKeyBackup(bytes); context.addFetchedKey(name, key); } @@ -424,7 +433,7 @@ public void theKeyNamedNameIsRestored(final String name) { public void theKeyIsRestoredFromClasspath(final String name) throws IOException { final String content = readResourceContent("/json/backups/" + name + ".json"); final byte[] bytes = context.getLowkeyVaultManagementClient().compressBackup(content); - final KeyVaultKey key = context.getClient().restoreKeyBackup(bytes); + final KeyVaultKey key = context.getClient(context.getKeyServiceVersion()).restoreKeyBackup(bytes); context.addFetchedKey(key.getName(), key); } 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 076424fa..23b12be4 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 @@ -166,13 +166,13 @@ public void theListedDeletedKeysAreEmpty() { @And("the key named {name} matches the previous backup") public void theKeyNamedNameMatchesThePreviousBackup(final String name) { - final byte[] bytes = context.getClient().backupKey(name); + final byte[] bytes = context.getClient(context.getKeyServiceVersion()).backupKey(name); assertEquals(context.getBackupBytes(name), bytes); } @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().backupKey(name); + final byte[] bytes = context.getClient(context.getKeyServiceVersion()).backupKey(name); final String backup = context.getLowkeyVaultManagementClient().unpackBackup(bytes); final String expected = readResourceContent("/json/backups/" + name + ".json"); assertEquals(expected, backup); diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/ParameterTypeDefs.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/ParameterTypeDefs.java index 55904ce0..ae623efc 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/ParameterTypeDefs.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/ParameterTypeDefs.java @@ -35,6 +35,11 @@ public List keyOperations(final String operations) { .orElse(Collections.emptyList()); } + @ParameterType("(7.2|7.3)") + public String api(final String api) { + return api; + } + @ParameterType("(P-256|P-256K|P-384|P-521)") public KeyCurveName ecCurveName(final String name) { return KeyCurveName.fromString(name); diff --git a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/SecretsStepDefs.java b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/SecretsStepDefs.java index 98967fa8..5148bc29 100644 --- a/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/SecretsStepDefs.java +++ b/lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/steps/SecretsStepDefs.java @@ -27,6 +27,11 @@ public class SecretsStepDefs extends CommonAssertions { @Autowired private SecretTestContext context; + @Given("secret API version {api} is used") + public void apiVersionApiIsUsed(final String version) { + context.setApiVersion(version); + } + @Given("a secret client is created with the vault named {name}") public void theSecretClientIsCreatedWithVaultNameSelected(final String vaultName) { final String vaultAuthority = vaultName + ".localhost:8443"; @@ -45,7 +50,7 @@ public void aSecretNamedSecretNameIsPreparedWithValueSet( public void versionsCountVersionOfTheSecretIsCreated(final int versionsCount) { final KeyVaultSecret secretCreateInfo = context.getCreateSecretOptions(); IntStream.range(0, versionsCount).forEach(i -> { - final KeyVaultSecret secret = context.getClient().setSecret(secretCreateInfo); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()).setSecret(secretCreateInfo); context.addCreatedEntity(secretCreateInfo.getName(), secret); }); } @@ -53,7 +58,7 @@ public void versionsCountVersionOfTheSecretIsCreated(final int versionsCount) { @When("the secret is created") public void secretCreationRequestIsSent() { final KeyVaultSecret secretCreateInfo = context.getCreateSecretOptions(); - final KeyVaultSecret secret = context.getClient().setSecret(secretCreateInfo); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()).setSecret(secretCreateInfo); context.addCreatedEntity(secretCreateInfo.getName(), secret); } @@ -61,14 +66,14 @@ public void secretCreationRequestIsSent() { public void fetchFirstSecretVersion(final String name) { final List versionsCreated = context.getCreatedEntities().get(name); final String version = versionsCreated.get(0).getProperties().getVersion(); - final KeyVaultSecret secret = context.getClient().getSecret(name, version); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()).getSecret(name, version); context.addFetchedSecret(name, secret); assertEquals(version, secret.getProperties().getVersion()); } @When("the last secret version of {name} is fetched without providing a version") public void fetchLatestSecretVersion(final String name) { - final KeyVaultSecret secret = context.getClient().getSecret(name); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()).getSecret(name); final List versionsCreated = context.getCreatedEntities().get(name); final String expectedLastVersionId = versionsCreated.get(versionsCreated.size() - 1).getId(); context.addFetchedSecret(name, secret); @@ -102,7 +107,7 @@ public void theSecretIsSetToBeEnabledStatus(final boolean enabledStatus) { @And("the secret is deleted") public void theSecretIsDeleted() { - final DeletedSecret actual = context.getClient() + final DeletedSecret actual = context.getClient(context.getSecretServiceVersion()) .beginDeleteSecret(context.getLastResult().getName()).waitForCompletion().getValue(); context.setLastDeleted(actual); } @@ -118,7 +123,7 @@ public void secretsWithSecretNamePrefixAreCreatedWithValueSet( @When("the secret properties are listed") public void theSecretPropertiesAreListed() { - final PagedIterable actual = context.getClient().listPropertiesOfSecrets(); + final PagedIterable actual = context.getClient(context.getSecretServiceVersion()).listPropertiesOfSecrets(); final List list = actual.stream() .map(SecretProperties::getId) .collect(Collectors.toList()); @@ -129,7 +134,8 @@ public void theSecretPropertiesAreListed() { public void countSecretsWithKeyNamePrefixAreDeleted( final int count, final String prefix) { final List deleted = IntStream.range(0, count).mapToObj(i -> { - final DeletedSecret actual = context.getClient().beginDeleteSecret(prefix + (i + 1)).waitForCompletion().getValue(); + final DeletedSecret actual = context.getClient(context.getSecretServiceVersion()) + .beginDeleteSecret(prefix + (i + 1)).waitForCompletion().getValue(); context.setLastDeleted(actual); return actual; }).map(DeletedSecret::getRecoveryId).collect(Collectors.toList()); @@ -138,7 +144,7 @@ public void countSecretsWithKeyNamePrefixAreDeleted( @When("the deleted secret properties are listed") public void theDeletedSecretPropertiesAreListed() { - final PagedIterable actual = context.getClient().listDeletedSecrets(); + final PagedIterable actual = context.getClient(context.getSecretServiceVersion()).listDeletedSecrets(); final List list = actual.stream() .map(DeletedSecret::getRecoveryId) .collect(Collectors.toList()); @@ -148,20 +154,21 @@ public void theDeletedSecretPropertiesAreListed() { @When("secret is recovered") public void secretIsRecovered() { final DeletedSecret deleted = context.getLastDeleted(); - final KeyVaultSecret secret = context.getClient().beginRecoverDeletedSecret(deleted.getName()).waitForCompletion().getValue(); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()) + .beginRecoverDeletedSecret(deleted.getName()).waitForCompletion().getValue(); context.addFetchedSecret(secret.getName(), secret); } @When("the secret is purged") public void theSecretIsPurged() { final DeletedSecret deleted = context.getLastDeleted(); - context.getClient().purgeDeletedSecret(deleted.getName()); + context.getClient(context.getSecretServiceVersion()).purgeDeletedSecret(deleted.getName()); } @When("the last version of the secret is prepared for an update") public void theLastVersionOfTheSecretIsPreparedForAnUpdate() { final KeyVaultSecret lastResult = context.getLastResult(); - final SecretProperties updatedProperties = context.getClient() + final SecretProperties updatedProperties = context.getClient(context.getSecretServiceVersion()) .getSecret(lastResult.getName(), lastResult.getProperties().getVersion()).getProperties(); context.setUpdateProperties(updatedProperties); } @@ -188,20 +195,21 @@ public void theSecretIsUpdatedToBeEnabledStatus(final boolean enabledStatus) { @When("the secret update request is sent") public void theUpdateRequestIsSent() { - final SecretProperties properties = context.getClient().updateSecretProperties(context.getUpdateProperties()); + final SecretProperties properties = context.getClient(context.getSecretServiceVersion()) + .updateSecretProperties(context.getUpdateProperties()); fetchLatestSecretVersion(properties.getName()); } @And("the secret named {name} is backed up") public void theSecretNamedNameIsBackedUp(final String name) { - final byte[] bytes = context.getClient().backupSecret(name); + final byte[] bytes = context.getClient(context.getSecretServiceVersion()).backupSecret(name); context.setBackupBytes(name, bytes); } @And("the secret named {name} is restored") public void theSecretNamedNameIsRestored(final String name) { final byte[] bytes = context.getBackupBytes(name); - final KeyVaultSecret secret = context.getClient().restoreSecretBackup(bytes); + final KeyVaultSecret secret = context.getClient(context.getSecretServiceVersion()).restoreSecretBackup(bytes); context.addFetchedSecret(name, secret); } } diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/BackupAndRestoreKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/BackupAndRestoreKeys.feature index ec702d01..24330ed9 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/BackupAndRestoreKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/BackupAndRestoreKeys.feature @@ -2,7 +2,8 @@ Feature: Key backup and restore @Key @KeyImport @KeySign @KeyBackup @KeyRestore @RSA Scenario Outline: RSA_BACKUP_01 An RSA key is imported, backed up, vault is recreated then after restore, the key is verified - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And an RSA key is imported with as name and bits of key size without HSM And the key named is backed up @@ -17,17 +18,24 @@ Feature: Key backup and restore And the key named matches the previous backup Examples: - | keyName | keySize | algorithm | clearText | - | backupRsaKey-1 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | - | backupRsaKey-2 | 2048 | PS384 | | - | backupRsaKey-3 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | - | backupRsaKey-4 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | - | backupRsaKey-5 | 4096 | RS384 | | - | backupRsaKey-6 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | backupRsaKey-01 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupRsaKey-02 | 2048 | PS384 | | + | 7.2 | backupRsaKey-03 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupRsaKey-04 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupRsaKey-05 | 4096 | RS384 | | + | 7.2 | backupRsaKey-06 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupRsaKey-07 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupRsaKey-08 | 2048 | PS384 | | + | 7.3 | backupRsaKey-09 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupRsaKey-10 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupRsaKey-11 | 4096 | RS384 | | + | 7.3 | backupRsaKey-12 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | @Key @KeyImport @KeySign @KeyBackup @KeyRestore @EC Scenario Outline: EC_BACKUP_01 An EC key is imported, backed up, vault is recreated then after restore, the key is verified - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And an EC key is imported with as name and curve without HSM And the key named is backed up @@ -42,20 +50,29 @@ Feature: Key backup and restore And the key named matches the previous backup Examples: - | keyName | curveName | algorithm | clearText | - | backupEc-1 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | - | backupEc-2 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | - | backupEc-3 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | - | backupEc-4 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | - | backupEc-5 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | backupEc-6 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | backupEc-7 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | backupEc-8 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | curveName | algorithm | clearText | + | 7.2 | backupEc-01 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupEc-02 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupEc-03 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupEc-04 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupEc-05 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | backupEc-06 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | backupEc-07 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | backupEc-08 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupEc-09 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupEc-10 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupEc-11 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupEc-12 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupEc-13 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupEc-14 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupEc-15 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupEc-16 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | @Key @KeyImport @KeyEncrypt @KeyBackup @KeyRestore @OCT Scenario Outline: OCT_BACKUP_01 An OCT key is imported, backed up, vault is recreated then after restore, the key is verified - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And an OCT key is imported with as name and bits of key size with HSM And the key named is backed up @@ -70,53 +87,72 @@ Feature: Key backup and restore And the key named matches the previous backup Examples: - | keyName | keySize | algorithm | clearText | - | backupOct-1 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | - | backupOct-2 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | - | backupOct-3 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | - | backupOct-4 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | backupOct-5 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | backupOct-6 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | backupOct-01 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupOct-02 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupOct-03 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | backupOct-04 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | backupOct-05 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | backupOct-06 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupOct-07 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupOct-08 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupOct-09 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | backupOct-10 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupOct-11 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | backupOct-12 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | @Key @KeyImport @KeyEncrypt @KeyBackup @KeyRestore @RSA Scenario Outline: RSA_BACKUP_02 An RSA key is restored from json, backed up, then the backup content is compared to the source - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And the key named is restored from classpath resource When the key named is backed up And the unpacked backup of matches the content of the classpath resource Examples: - | keyName | - | jsonBackupRsa-2048 | - | jsonBackupRsa-3072 | - | jsonBackupRsa-4096 | + | api | keyName | + | 7.2 | jsonBackupRsa-2048-72 | + | 7.2 | jsonBackupRsa-3072-72 | + | 7.2 | jsonBackupRsa-4096-72 | + | 7.3 | jsonBackupRsa-2048-73 | + | 7.3 | jsonBackupRsa-3072-73 | + | 7.3 | jsonBackupRsa-4096-73 | @Key @KeyImport @KeyEncrypt @KeyBackup @KeyRestore @EC Scenario Outline: EC_BACKUP_02 An EC key is restored from json, backed up, then the backup content is compared to the source - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And the key named is restored from classpath resource When the key named is backed up And the unpacked backup of matches the content of the classpath resource Examples: - | keyName | - | jsonBackupEc-256 | - | jsonBackupEc-256k | - | jsonBackupEc-384 | - | jsonBackupEc-521 | + | api | keyName | + | 7.2 | jsonBackupEc-256-72 | + | 7.2 | jsonBackupEc-256k-72 | + | 7.2 | jsonBackupEc-384-72 | + | 7.2 | jsonBackupEc-521-72 | + | 7.3 | jsonBackupEc-256-73 | + | 7.3 | jsonBackupEc-256k-73 | + | 7.3 | jsonBackupEc-384-73 | + | 7.3 | jsonBackupEc-521-73 | @Key @KeyImport @KeyEncrypt @KeyBackup @KeyRestore @OCT Scenario Outline: OCT_BACKUP_02 An OCT key is restored from json, backed up, then the backup content is compared to the source - Given a vault is created with name keys-backup- + Given key API version is used + And a vault is created with name keys-backup- And a key client is created with the vault named keys-backup- And the key named is restored from classpath resource When the key named is backed up And the unpacked backup of matches the content of the classpath resource Examples: - | keyName | - | jsonBackupOct-128 | - | jsonBackupOct-192 | - | jsonBackupOct-256 | + | api | keyName | + | 7.2 | jsonBackupOct-128-72 | + | 7.2 | jsonBackupOct-192-72 | + | 7.2 | jsonBackupOct-256-72 | + | 7.3 | jsonBackupOct-128-73 | + | 7.3 | jsonBackupOct-192-73 | + | 7.3 | jsonBackupOct-256-73 | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/CreateKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/CreateKeys.feature index 59e3132d..955666e1 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/CreateKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/CreateKeys.feature @@ -2,7 +2,8 @@ Feature: Key creation @Key @KeyCreate @RSA Scenario Outline: RSA_CREATE_01 Single versions of RSA keys can be created with the key client - Given a key client is created with the vault named keys-generic + 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 @@ -24,25 +25,40 @@ Feature: Key creation And the key recovery settings are default Examples: - | hsm | keyName | keySize | nBytes | enabledStatus | operations | expires | notBefore | tagMap | - | without | createRsaKey | 2048 | 257 | enabled | null | null | null | null | - | without | createRsaKey4096 | 4096 | 513 | enabled | null | null | null | null | - | without | create-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | - | without | create-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | - | with | createRsaHsmKey | 2048 | 257 | enabled | null | null | null | null | - | with | createRsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | - | with | create-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | - | with | create-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | - | without | createRsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | - | without | createRsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | - | without | createRsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | without | createRsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | - | without | createRsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | - | without | createRsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | + | api | hsm | keyName | keySize | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | without | 72-createRsaKey | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | without | 72-createRsaKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | without | 72-create-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | without | 72-create-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | with | 72-createRsaHsmKey | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | with | 72-createRsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | with | 72-create-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | with | 72-create-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | without | 72-createRsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | without | 72-createRsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | + | 7.2 | without | 72-createRsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | without | 72-createRsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | without | 72-createRsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | + | 7.2 | without | 72-createRsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | + | 7.3 | without | 73-createRsaKey | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | without | 73-createRsaKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | without | 73-create-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | without | 73-create-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | with | 73-createRsaHsmKey | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | with | 73-createRsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | with | 73-create-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | with | 73-create-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | without | 73-createRsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | without | 73-createRsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | + | 7.3 | without | 73-createRsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | without | 73-createRsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | without | 73-createRsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | + | 7.3 | without | 73-createRsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | @Key @KeyCreate @EC Scenario Outline: EC_CREATE_01 Single versions of EC keys can be created with the key client - Given a key client is created with the vault named keys-generic + 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 @@ -64,25 +80,40 @@ Feature: Key creation And the key recovery settings are default Examples: - | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | - | without | createEcKey256 | P-256 | 32 | enabled | null | null | null | null | - | without | createEcKey256k | P-256K | 32 | enabled | null | null | null | null | - | without | createEcKey384 | P-384 | 48 | enabled | null | null | null | null | - | without | createEcKey521 | P-521 | 65 | enabled | null | null | null | null | - | with | createEcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | - | with | createEcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | - | with | createEcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | - | with | createEcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | - | without | createEcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | - | without | createEcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | - | without | createEcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | without | createEcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | - | without | createEcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | - | without | createEcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | api | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | without | 72-createEcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | without | 72-createEcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | without | 72-createEcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | without | 72-createEcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | with | 72-createEcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | with | 72-createEcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | with | 72-createEcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | with | 72-createEcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | without | 72-createEcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | without | 72-createEcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.2 | without | 72-createEcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | without | 72-createEcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | without | 72-createEcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.2 | without | 72-createEcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | 7.3 | without | 73-createEcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | without | 73-createEcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | without | 73-createEcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | without | 73-createEcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | with | 73-createEcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | with | 73-createEcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | with | 73-createEcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | with | 73-createEcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | without | 73-createEcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | without | 73-createEcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.3 | without | 73-createEcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | without | 73-createEcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | without | 73-createEcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.3 | without | 73-createEcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | @Key @KeyCreate @OCT Scenario Outline: OCT_CREATE_01 Single versions of OCT keys can be created with the key client - Given a key client is created with the vault named keys-generic + 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 @@ -105,14 +136,24 @@ Feature: Key creation And the key recovery settings are default Examples: - | hsm | keyName | keySize | enabledStatus | operations | expires | notBefore | tagMap | - | with | createOctKey | 128 | enabled | null | null | null | null | - | with | createOctKey192 | 192 | enabled | null | null | null | null | - | with | createOctKey256 | 256 | enabled | null | null | null | null | - | with | create-oct-key-128 | 128 | enabled | null | null | null | null | - | with | createOctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | - | with | createOctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | - | with | createOctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | with | createOctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | - | with | createOctKeyDates | 128 | enabled | null | 4321 | 1234 | null | - | with | createOctKeyNotEnabled | 128 | not enabled | null | null | null | null | + | api | hsm | keyName | keySize | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | with | 72-createOctKey | 128 | enabled | null | null | null | null | + | 7.2 | with | 72-createOctKey192 | 192 | enabled | null | null | null | null | + | 7.2 | with | 72-createOctKey256 | 256 | enabled | null | null | null | null | + | 7.2 | with | 72-create-oct-key-128 | 128 | enabled | null | null | null | null | + | 7.2 | with | 72-createOctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | with | 72-createOctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | + | 7.2 | with | 72-createOctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | with | 72-createOctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | with | 72-createOctKeyDates | 128 | enabled | null | 4321 | 1234 | null | + | 7.2 | with | 72-createOctKeyNotEnabled | 128 | not enabled | null | null | null | null | + | 7.3 | with | 73-createOctKey | 128 | enabled | null | null | null | null | + | 7.3 | with | 73-createOctKey192 | 192 | enabled | null | null | null | null | + | 7.3 | with | 73-createOctKey256 | 256 | enabled | null | null | null | null | + | 7.3 | with | 73-create-oct-key-128 | 128 | enabled | null | null | null | null | + | 7.3 | with | 73-createOctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | with | 73-createOctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | + | 7.3 | with | 73-createOctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | with | 73-createOctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | with | 73-createOctKeyDates | 128 | enabled | null | 4321 | 1234 | null | + | 7.3 | with | 73-createOctKeyNotEnabled | 128 | not enabled | null | null | null | null | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/DeleteKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/DeleteKeys.feature index 8a290bb3..b3d2aed9 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/DeleteKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/DeleteKeys.feature @@ -2,7 +2,8 @@ Feature: Key delete and recover @Key @KeyCreate @KeyDelete @RSA Scenario Outline: RSA_DELETE_01 Multiple versions of RSA keys are created with the key client then deleted - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an RSA key named is prepared with bits size HSM And version of the RSA key is created When the key is deleted @@ -10,13 +11,16 @@ Feature: Key delete and recover And the key recovery timestamps are default Examples: - | versionsCount | hsm | keyName | keySize | - | 5 | without | deleteRsaKey | 2048 | - | 6 | without | delete-rsa-key-name | 2048 | + | api | versionsCount | hsm | keyName | keySize | + | 7.2 | 5 | without | 72-deleteRsaKey | 2048 | + | 7.2 | 6 | without | 72-delete-rsa-key-name | 2048 | + | 7.3 | 5 | without | 73-deleteRsaKey | 2048 | + | 7.3 | 6 | without | 73-delete-rsa-key-name | 2048 | @Key @KeyCreate @KeyDelete @EC Scenario Outline: EC_DELETE_01 Multiple versions of EC keys are created with the key client then deleted - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an EC key named is prepared with and HSM And version of the EC key is created When the key is deleted @@ -24,13 +28,16 @@ Feature: Key delete and recover And the key recovery timestamps are default Examples: - | versionsCount | hsm | keyName | curveName | - | 5 | without | deleteEcKey256 | P-256 | - | 6 | without | deleteEcKey256k | P-256K | + | api | versionsCount | hsm | keyName | curveName | + | 7.2 | 5 | without | 72-deleteEcKey256 | P-256 | + | 7.2 | 6 | without | 72-deleteEcKey256k | P-256K | + | 7.3 | 5 | without | 73-deleteEcKey256 | P-256 | + | 7.3 | 6 | without | 73-deleteEcKey256k | P-256K | @Key @KeyCreate @KeyDelete @OCT Scenario Outline: OCT_DELETE_01 Multiple versions of OCT keys are created with the key client then deleted - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an OCT key named is prepared with bits size And version of the OCT key is created When the key is deleted @@ -38,13 +45,16 @@ Feature: Key delete and recover And the key recovery timestamps are default Examples: - | versionsCount | keyName | keySize | - | 5 | deleteOctKey | 128 | - | 6 | deleteOctKey192 | 192 | + | api | versionsCount | keyName | keySize | + | 7.2 | 5 | 72-deleteOctKey | 128 | + | 7.2 | 6 | 72-deleteOctKey192 | 192 | + | 7.3 | 5 | 73-deleteOctKey | 128 | + | 7.3 | 6 | 73-deleteOctKey192 | 192 | @Key @KeyCreate @KeyDelete @KeyRecover @RSA Scenario Outline: RSA_RECOVER_01 Multiple versions of RSA keys are created with the key client then deleted and recovered - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an RSA key named is prepared with bits size HSM And version of the RSA key is created And the key is deleted @@ -52,13 +62,16 @@ Feature: Key delete and recover Then the key URL contains the vault url and Examples: - | versionsCount | hsm | keyName | keySize | - | 5 | without | recoverRsaKey | 2048 | - | 6 | without | recover-rsa-key-name | 2048 | + | api | versionsCount | hsm | keyName | keySize | + | 7.2 | 5 | without | 72-recoverRsaKey | 2048 | + | 7.2 | 6 | without | 72-recover-rsa-key-name | 2048 | + | 7.3 | 5 | without | 73-recoverRsaKey | 2048 | + | 7.3 | 6 | without | 73-recover-rsa-key-name | 2048 | @Key @KeyCreate @KeyDelete @KeyRecover @EC Scenario Outline: EC_RECOVER_01 Multiple versions of EC keys are created with the key client then deleted and recovered - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an EC key named is prepared with and HSM And version of the EC key is created And the key is deleted @@ -66,13 +79,16 @@ Feature: Key delete and recover Then the key URL contains the vault url and Examples: - | versionsCount | hsm | keyName | curveName | - | 5 | without | recoverEcKey256 | P-256 | - | 6 | without | recoverEcKey256k | P-256K | + | api | versionsCount | hsm | keyName | curveName | + | 7.2 | 5 | without | 72-recoverEcKey256 | P-256 | + | 7.2 | 6 | without | 72-recoverEcKey256k | P-256K | + | 7.3 | 5 | without | 73-recoverEcKey256 | P-256 | + | 7.3 | 6 | without | 73-recoverEcKey256k | P-256K | @Key @KeyCreate @KeyDelete @KeyRecover @OCT Scenario Outline: OCT_RECOVER_01 Multiple versions of OCT keys are created with the key client then deleted and recovered - Given a key client is created with the vault named keys-delete + Given key API version is used + And a key client is created with the vault named keys-delete And an OCT key named is prepared with bits size And version of the OCT key is created And the key is deleted @@ -80,13 +96,16 @@ Feature: Key delete and recover Then the key URL contains the vault url and Examples: - | versionsCount | keyName | keySize | - | 5 | recoverOctKey | 128 | - | 6 | recoverOctKey192 | 192 | + | api | versionsCount | keyName | keySize | + | 7.2 | 5 | 72-recoverOctKey | 128 | + | 7.2 | 6 | 72-recoverOctKey192 | 192 | + | 7.3 | 5 | 73-recoverOctKey | 128 | + | 7.3 | 6 | 73-recoverOctKey192 | 192 | @Key @KeyCreate @KeyDelete @KeyPurge @RSA Scenario Outline: RSA_PURGE_01 Multiple versions of RSA keys are created with the key client then deleted and purged - Given a vault is created with name keys-purge- + Given key API version is used + And a vault is created with name keys-purge- And a key client is created with the vault named keys-purge- And an RSA key named is prepared with bits size HSM And version of the RSA key is created @@ -98,13 +117,16 @@ Feature: Key delete and recover And the vault named keys-purge- is purged Examples: - | versionsCount | hsm | keyName | keySize | - | 5 | without | purgeRsaKey | 2048 | - | 6 | without | purge-rsa-key-name | 2048 | + | api | versionsCount | hsm | keyName | keySize | + | 7.2 | 5 | without | 72-purgeRsaKey | 2048 | + | 7.2 | 6 | without | 72-purge-rsa-key-name | 2048 | + | 7.3 | 5 | without | 73-purgeRsaKey | 2048 | + | 7.3 | 6 | without | 73-purge-rsa-key-name | 2048 | @Key @KeyCreate @KeyDelete @KeyPurge @EC Scenario Outline: EC_PURGE_01 Multiple versions of EC keys are created with the key client then deleted and purge - Given a vault is created with name keys-purge- + Given key API version is used + And a vault is created with name keys-purge- And a key client is created with the vault named keys-purge- And an EC key named is prepared with and HSM And version of the EC key is created @@ -116,13 +138,16 @@ Feature: Key delete and recover And the vault named keys-purge- is purged Examples: - | versionsCount | hsm | keyName | curveName | - | 5 | without | purgeEcKey256 | P-256 | - | 6 | without | purgeEcKey256k | P-256K | + | api | versionsCount | hsm | keyName | curveName | + | 7.2 | 5 | without | 72-purgeEcKey256 | P-256 | + | 7.2 | 6 | without | 72-purgeEcKey256k | P-256K | + | 7.3 | 5 | without | 73-purgeEcKey256 | P-256 | + | 7.3 | 6 | without | 73-purgeEcKey256k | P-256K | @Key @KeyCreate @KeyDelete @KeyPurge @OCT Scenario Outline: OCT_PURGE_01 Multiple versions of OCT keys are created with the key client then deleted and purge - Given a vault is created with name keys-purge- + Given key API version is used + And a vault is created with name keys-purge- And a key client is created with the vault named keys-purge- And an OCT key named is prepared with bits size And version of the OCT key is created @@ -134,6 +159,8 @@ Feature: Key delete and recover And the vault named keys-purge- is purged Examples: - | versionsCount | keyName | keySize | - | 5 | purgeOctKey | 128 | - | 6 | purgeOctKey192 | 192 | + | api | versionsCount | keyName | keySize | + | 7.2 | 3 | 72-purgeOctKey | 128 | + | 7.2 | 4 | 72-purgeOctKey192 | 192 | + | 7.3 | 3 | 73-purgeOctKey | 128 | + | 7.3 | 4 | 73-purgeOctKey192 | 192 | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/EncryptWithKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/EncryptWithKeys.feature index 8c2c5b8c..abd3cc1b 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/EncryptWithKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/EncryptWithKeys.feature @@ -2,7 +2,8 @@ Feature: Key encrypt and decrypt @Key @KeyCreate @KeyEncrypt @RSA Scenario Outline: RSA_ENCRYPT_01 An RSA key is created with the key client then used for encrypt and decrypt operations - Given a key client is created with the vault named keys-generic + 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 without HSM And the key has encrypt,decrypt,wrapKey,unwrapKey operations granted And the RSA key is created @@ -12,17 +13,24 @@ Feature: Key encrypt and decrypt Then the decrypted value is Examples: - | keyName | keySize | algorithm | clearText | - | encryptRsaKey-1 | 2048 | RSA1_5 | The quick brown fox jumps over the lazy dog. | - | encryptRsaKey-2 | 2048 | RSA1_5 | | - | encryptRsaKey-3 | 2048 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | - | encryptRsaKey-4 | 4096 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | - | encryptRsaKey-5 | 2048 | RSA-OAEP-256 | | - | encryptRsaKey-6 | 4096 | RSA-OAEP-256 | | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | encryptRsaKey-01 | 2048 | RSA1_5 | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptRsaKey-02 | 2048 | RSA1_5 | | + | 7.2 | encryptRsaKey-03 | 2048 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptRsaKey-04 | 4096 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptRsaKey-05 | 2048 | RSA-OAEP-256 | | + | 7.2 | encryptRsaKey-06 | 4096 | RSA-OAEP-256 | | + | 7.3 | encryptRsaKey-07 | 2048 | RSA1_5 | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptRsaKey-08 | 2048 | RSA1_5 | | + | 7.3 | encryptRsaKey-09 | 2048 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptRsaKey-10 | 4096 | RSA-OAEP | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptRsaKey-11 | 2048 | RSA-OAEP-256 | | + | 7.3 | encryptRsaKey-12 | 4096 | RSA-OAEP-256 | | @Key @KeyCreate @KeyEncrypt @OCT Scenario Outline: OCT_ENCRYPT_01 An OCT key is created with the key client then used for encrypt and decrypt operations - Given a key client is created with the vault named keys-generic + 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 has encrypt,decrypt,wrapKey,unwrapKey operations granted And the OCT key is created @@ -32,10 +40,16 @@ Feature: Key encrypt and decrypt Then the decrypted value is Examples: - | keyName | keySize | algorithm | clearText | - | encryptOct-1 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | - | encryptOct-2 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | - | encryptOct-3 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | - | encryptOct-4 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | encryptOct-5 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | encryptOct-6 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | encryptOct-01 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptOct-02 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptOct-03 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | encryptOct-04 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | encryptOct-05 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | encryptOct-06 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | encryptOct-07 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptOct-08 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptOct-09 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | encryptOct-10 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | encryptOct-11 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | encryptOct-12 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/GetKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/GetKeys.feature index 763a3a28..e8b6d606 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/GetKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/GetKeys.feature @@ -2,7 +2,8 @@ Feature: Key get @Key @KeyCreate @KeyGet @RSA Scenario Outline: RSA_GET_01 Multiple versions of RSA keys are created with the key client then the latest is fetched - Given a key client is created with the vault named keys-generic + 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 version of the RSA key is created And the key is set to expire seconds after creation @@ -26,25 +27,40 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | keySize | nBytes | enabledStatus | operations | expires | notBefore | tagMap | - | 2 | without | get01RsaKey | 2048 | 257 | enabled | null | null | null | null | - | 1 | without | get01RsaKey4096 | 4096 | 513 | enabled | null | null | null | null | - | 2 | without | get01-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | - | 1 | without | get01-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | - | 4 | with | get01RsaHsmKey | 2048 | 257 | enabled | null | null | null | null | - | 3 | with | get01RsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | - | 4 | with | get01-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | - | 3 | with | get01-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | - | 4 | without | get01RsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | - | 3 | without | get01RsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | - | 4 | without | get01RsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | 3 | without | get01RsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | - | 4 | without | get01RsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | - | 3 | without | get01RsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | + | api | versionsCount | hsm | keyName | keySize | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | 2 | without | 72-get01RsaKey | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-get01RsaKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | 2 | without | 72-get01-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-get01-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-get01RsaHsmKey | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-get01RsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-get01-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-get01-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.2 | 4 | without | 72-get01RsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | without | 72-get01RsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | + | 7.2 | 4 | without | 72-get01RsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | 3 | without | 72-get01RsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | 4 | without | 72-get01RsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | + | 7.2 | 3 | without | 72-get01RsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | + | 7.3 | 2 | without | 73-get01RsaKey | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-get01RsaKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | 2 | without | 73-get01-rsa-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-get01-rsa-key-name-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-get01RsaHsmKey | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-get01RsaHsmKey4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-get01-rsa-hsm-key-name | 2048 | 257 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-get01-rsa-hsm-key-4096 | 4096 | 513 | enabled | null | null | null | null | + | 7.3 | 4 | without | 73-get01RsaKeyMap1 | 2048 | 257 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | without | 73-get01RsaKeyMap2 | 2048 | 257 | enabled | null | null | null | aKey:aValue | + | 7.3 | 4 | without | 73-get01RsaKeyAllOps | 2048 | 257 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | 3 | without | 73-get01RsaKeyOperations | 2048 | 257 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | 4 | without | 73-get01RsaKeyDates | 2048 | 257 | enabled | null | 4321 | 1234 | null | + | 7.3 | 3 | without | 73-get01RsaKeyNotEnabled | 2048 | 257 | not enabled | null | null | null | null | @Key @KeyCreate @KeyGet @EC Scenario Outline: EC_GET_01 Multiple versions of EC keys are created with the key client then the latest is fetched - Given a key client is created with the vault named keys-generic + 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 version of the EC key is created And the key is set to expire seconds after creation @@ -68,25 +84,40 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | - | 2 | without | get01EcKey256 | P-256 | 32 | enabled | null | null | null | null | - | 1 | without | get01EcKey256k | P-256K | 32 | enabled | null | null | null | null | - | 2 | without | get01EcKey384 | P-384 | 48 | enabled | null | null | null | null | - | 1 | without | get01EcKey521 | P-521 | 65 | enabled | null | null | null | null | - | 4 | with | get01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | - | 3 | with | get01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | - | 4 | with | get01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | - | 3 | with | get01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | - | 4 | without | get01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | - | 3 | without | get01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | - | 4 | without | get01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | 3 | without | get01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | - | 4 | without | get01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | - | 3 | without | get01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | api | versionsCount | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | 2 | without | 72-get01EcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-get01EcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | 2 | without | 72-get01EcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-get01EcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-get01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-get01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-get01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-get01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | 4 | without | 72-get01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | without | 72-get01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.2 | 4 | without | 72-get01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | 3 | without | 72-get01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | 4 | without | 72-get01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.2 | 3 | without | 72-get01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | 7.3 | 2 | without | 73-get01EcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-get01EcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | 2 | without | 73-get01EcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-get01EcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-get01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-get01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-get01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-get01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | 4 | without | 73-get01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | without | 73-get01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.3 | 4 | without | 73-get01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | 3 | without | 73-get01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | 4 | without | 73-get01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.3 | 3 | without | 73-get01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | @Key @KeyCreate @KeyGet @OCT Scenario Outline: OCT_GET_01 Multiple versions of OCT keys are created with the key client then the latest is fetched - Given a key client is created with the vault named keys-generic + 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 version of the OCT key is created And the key is set to expire seconds after creation @@ -111,21 +142,32 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | keySize | enabledStatus | operations | expires | notBefore | tagMap | - | 2 | with | get01OctKey | 128 | enabled | null | null | null | null | - | 1 | with | get01OctKey192 | 192 | enabled | null | null | null | null | - | 2 | with | get01OctKey256 | 256 | enabled | null | null | null | null | - | 1 | with | get01-oct-key-128 | 128 | enabled | null | null | null | null | - | 4 | with | get01OctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | - | 3 | with | get01OctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | - | 4 | with | get01OctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | 3 | with | get01OctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | - | 4 | with | get01OctKeyDates | 128 | enabled | null | 4321 | 1234 | null | - | 3 | with | get01OctKeyNotEnabled | 128 | not enabled | null | null | null | null | + | api | versionsCount | hsm | keyName | keySize | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | 2 | with | 72-get01OctKey | 128 | enabled | null | null | null | null | + | 7.2 | 1 | with | 72-get01OctKey192 | 192 | enabled | null | null | null | null | + | 7.2 | 2 | with | 72-get01OctKey256 | 256 | enabled | null | null | null | null | + | 7.2 | 1 | with | 72-get01-oct-key-128 | 128 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-get01OctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | with | 72-get01OctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | + | 7.2 | 4 | with | 72-get01OctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | 3 | with | 72-get01OctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | 4 | with | 72-get01OctKeyDates | 128 | enabled | null | 4321 | 1234 | null | + | 7.2 | 3 | with | 72-get01OctKeyNotEnabled | 128 | not enabled | null | null | null | null | + | 7.3 | 2 | with | 73-get01OctKey | 128 | enabled | null | null | null | null | + | 7.3 | 1 | with | 73-get01OctKey192 | 192 | enabled | null | null | null | null | + | 7.3 | 2 | with | 73-get01OctKey256 | 256 | enabled | null | null | null | null | + | 7.3 | 1 | with | 73-get01-oct-key-128 | 128 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-get01OctKeyMap1 | 128 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | with | 73-get01OctKeyMap2 | 128 | enabled | null | null | null | aKey:aValue | + | 7.3 | 4 | with | 73-get01OctKeyAllOps | 128 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | 3 | with | 73-get01OctKeyOperations | 128 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | 4 | with | 73-get01OctKeyDates | 128 | enabled | null | 4321 | 1234 | null | + | 7.3 | 3 | with | 73-get01OctKeyNotEnabled | 128 | not enabled | null | null | null | null | @Key @KeyCreate @KeyGet @RSA Scenario Outline: RSA_GET_02 Multiple versions of RSA keys are created with the key client then the first is fetched by version - Given a key client is created with the vault named keys-generic + 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 version of the RSA key is created When the RSA key is created @@ -139,13 +181,16 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | keySize | nBytes | - | 5 | without | get02RsaKey | 2048 | 257 | - | 6 | without | get02-rsa-key-name | 2048 | 257 | + | api | versionsCount | hsm | keyName | keySize | nBytes | + | 7.2 | 2 | without | 72-get02RsaKey | 2048 | 257 | + | 7.2 | 3 | without | 72-get02-rsa-key-name | 2048 | 257 | + | 7.3 | 2 | without | 73-get02RsaKey | 2048 | 257 | + | 7.3 | 3 | without | 73-get02-rsa-key-name | 2048 | 257 | @Key @KeyCreate @KeyGet @EC Scenario Outline: EC_GET_02 Multiple versions of EC keys are created with the key client then the first is fetched by version - Given a key client is created with the vault named keys-generic + 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 version of the EC key is created When the EC key is created @@ -159,13 +204,16 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | curveName | nBytes | - | 5 | without | get02EcKey256 | P-256 | 32 | - | 6 | without | get02EcKey256k | P-256K | 32 | + | api | versionsCount | hsm | keyName | curveName | nBytes | + | 7.2 | 5 | without | 72-get02EcKey256 | P-256 | 32 | + | 7.2 | 6 | without | 72-get02EcKey256k | P-256K | 32 | + | 7.3 | 5 | without | 73-get02EcKey256 | P-256 | 32 | + | 7.3 | 6 | without | 73-get02EcKey256k | P-256K | 32 | @Key @KeyCreate @KeyGet @OCT Scenario Outline: OCT_GET_02 Multiple versions of OCT keys are created with the key client then the first is fetched by version - Given a key client is created with the vault named keys-generic + 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 version of the OCT key is created When the OCT key is created @@ -180,13 +228,16 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | keySize | - | 5 | with | get02OctKey | 128 | - | 6 | with | get02OctKey192 | 192 | + | api | versionsCount | hsm | keyName | keySize | + | 7.2 | 5 | with | 72-get02OctKey | 128 | + | 7.2 | 6 | with | 72-get02OctKey192 | 192 | + | 7.3 | 5 | with | 73-get02OctKey | 128 | + | 7.3 | 6 | with | 73-get02OctKey192 | 192 | @Key @KeyCreate @KeyGet @KeyUpdate @EC Scenario Outline: EC_UPDATE_01 Multiple versions of EC keys are created with the key client then the latest is updated and fetched - Given a key client is created with the vault named keys-generic + 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 version of the EC key is created When the last version of the key is prepared for an update @@ -211,18 +262,32 @@ Feature: Key get And the key recovery settings are default Examples: - | versionsCount | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | - | 2 | without | update01EcKey256 | P-256 | 32 | enabled | null | null | null | null | - | 1 | without | update01EcKey256k | P-256K | 32 | enabled | null | null | null | null | - | 2 | without | update01EcKey384 | P-384 | 48 | enabled | null | null | null | null | - | 1 | without | update01EcKey521 | P-521 | 65 | enabled | null | null | null | null | - | 4 | with | update01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | - | 3 | with | update01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | - | 4 | with | update01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | - | 3 | with | update01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | - | 4 | without | update01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | - | 3 | without | update01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | - | 4 | without | update01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | - | 3 | without | update01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | - | 4 | without | update01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | - | 3 | without | update01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | api | versionsCount | hsm | keyName | curveName | nBytes | enabledStatus | operations | expires | notBefore | tagMap | + | 7.2 | 2 | without | 72-update01EcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-update01EcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | 2 | without | 72-update01EcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | 1 | without | 72-update01EcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-update01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-update01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.2 | 4 | with | 72-update01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.2 | 3 | with | 72-update01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.2 | 4 | without | 72-update01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | without | 72-update01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.2 | 4 | without | 72-update01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.2 | 3 | without | 72-update01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.2 | 4 | without | 72-update01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.2 | 3 | without | 72-update01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | + | 7.3 | 2 | without | 73-update01EcKey256 | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-update01EcKey256k | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | 2 | without | 73-update01EcKey384 | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | 1 | without | 73-update01EcKey521 | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-update01EcKey256Hsm | P-256 | 32 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-update01EcKey256kHsm | P-256K | 32 | enabled | null | null | null | null | + | 7.3 | 4 | with | 73-update01EcKey384Hsm | P-384 | 48 | enabled | null | null | null | null | + | 7.3 | 3 | with | 73-update01EcKey521Hsm | P-521 | 65 | enabled | null | null | null | null | + | 7.3 | 4 | without | 73-update01EcKeyMap1 | P-256 | 32 | enabled | null | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | without | 73-update01EcKeyMap2 | P-256 | 32 | enabled | null | null | null | aKey:aValue | + | 7.3 | 4 | without | 73-update01EcKeyAllOps | P-256 | 32 | enabled | encrypt,decrypt,wrapKey,unwrapKey,sign,verify,import | null | null | null | + | 7.3 | 3 | without | 73-update01EcKeyOperations | P-256 | 32 | enabled | wrapKey,unwrapKey | null | null | null | + | 7.3 | 4 | without | 73-update01EcKeyDates | P-256 | 32 | enabled | null | 4321 | 1234 | null | + | 7.3 | 3 | without | 73-update01EcKeyNotEnabled | P-256 | 32 | not enabled | null | null | null | null | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ImportKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ImportKeys.feature index d36fd564..c8ddf78b 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ImportKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ImportKeys.feature @@ -2,7 +2,8 @@ Feature: Key import @Key @KeyImport @KeySign @RSA Scenario Outline: RSA_IMPORT_01 An RSA key is imported with the key client then used for sign and verify operations - Given a key client is created with the vault named keys-generic + Given key API version is used + And a key client is created with the vault named keys-generic And an RSA key is imported with as name and bits of key size without HSM When the created key is used to sign with And the signed value is not @@ -10,17 +11,24 @@ Feature: Key import Then the signature matches Examples: - | keyName | keySize | algorithm | clearText | - | importRsaKey-1 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | - | importRsaKey-2 | 2048 | PS384 | | - | importRsaKey-3 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | - | importRsaKey-4 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | - | importRsaKey-5 | 4096 | RS384 | | - | importRsaKey-6 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | importRsaKey-01 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importRsaKey-02 | 2048 | PS384 | | + | 7.2 | importRsaKey-03 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importRsaKey-04 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importRsaKey-05 | 4096 | RS384 | | + | 7.2 | importRsaKey-06 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importRsaKey-07 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importRsaKey-08 | 2048 | PS384 | | + | 7.3 | importRsaKey-09 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importRsaKey-10 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importRsaKey-11 | 4096 | RS384 | | + | 7.3 | importRsaKey-12 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | @Key @KeyImport @KeySign @EC Scenario Outline: EC_IMPORT_01 An EC key is imported with the key client then used for sign and verify operations - Given a key client is created with the vault named keys-generic + Given key API version is used + And a key client is created with the vault named keys-generic And an EC key is imported with as name and curve without HSM When the created key is used to sign with And the signed value is not @@ -28,20 +36,29 @@ Feature: Key import Then the signature matches Examples: - | keyName | curveName | algorithm | clearText | - | importEc-1 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | - | importEc-2 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | - | importEc-3 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | - | importEc-4 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | - | importEc-5 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | importEc-6 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | importEc-7 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | importEc-8 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | curveName | algorithm | clearText | + | 7.2 | importEc-01 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importEc-02 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.2 | importEc-03 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importEc-04 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | importEc-05 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | importEc-06 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | importEc-07 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | importEc-08 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importEc-09 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importEc-10 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.3 | importEc-11 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importEc-12 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | importEc-13 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importEc-14 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importEc-15 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importEc-16 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | @Key @KeyImport @KeyEncrypt @OCT Scenario Outline: OCT_IMPORT_01 An OCT key is imported with the key client then used for encrypt and decrypt operations - Given a key client is created with the vault named keys-generic + Given key API version is used + And a key client is created with the vault named keys-generic And an OCT key is imported with as name and bits of key size with HSM When the created key is used to encrypt with And the encrypted value is not @@ -49,10 +66,16 @@ Feature: Key import Then the decrypted value is Examples: - | keyName | keySize | algorithm | clearText | - | importOct-1 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | - | importOct-2 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | - | importOct-3 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | - | importOct-4 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | importOct-5 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | importOct-6 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | importOct-01 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | importOct-02 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | importOct-03 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.2 | importOct-04 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | importOct-05 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | importOct-06 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importOct-07 | 128 | A128CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | importOct-08 | 192 | A192CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | importOct-09 | 256 | A256CBCPAD | The quick brown fox jumps over the lazy dog. | + | 7.3 | importOct-10 | 128 | A128CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importOct-11 | 192 | A192CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | importOct-12 | 256 | A256CBC | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListDeletedKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListDeletedKeys.feature index 34b396a1..d53c9333 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListDeletedKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListDeletedKeys.feature @@ -2,50 +2,67 @@ Feature: Key list deleted @Key @KeyCreate @KeyListDeleted @RSA @CreateVault Scenario Outline: RSA_LIST_DELETED_01 RSA keys are created and deleted with the key client then all are listed as deleted keys - Given a vault is created with name keys-del-rsa- - And a key client is created with the vault named keys-del-rsa- + Given key API version is used + And a vault is created with name keys-del-rsa- + And a key client is created with the vault named keys-del-rsa- And RSA keys with - prefix are created with 2048 bits size without HSM And keys with - prefix are deleted When the deleted key properties are listed Then the listed deleted keys are matching the ones deleted before Examples: - | count | keyName | - | 1 | listRsaKey | - | 2 | list-rsa-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listRsaKey | + | 7.2 | 02 | 2 | list-rsa-key-name | + | 7.3 | 03 | 1 | listRsaKey | + | 7.3 | 04 | 2 | list-rsa-key-name | @Key @KeyCreate @KeyListDeleted @EC @CreateVault Scenario Outline: EC_LIST_DELETED_01 EC keys are created and deleted with the key client then all are listed as deleted keys - Given a vault is created with name keys-del-ec- - And a key client is created with the vault named keys-del-ec- + Given key API version is used + And a vault is created with name keys-del-ec- + And a key client is created with the vault named keys-del-ec- And EC keys with - prefix are created with P-256 and without HSM And keys with - prefix are deleted When the deleted key properties are listed Then the listed deleted keys are matching the ones deleted before Examples: - | count | keyName | - | 1 | listEcKey | - | 2 | list-ec-key-name | - | 3 | listEcKey | - | 5 | list-ec-key-name | - | 25 | listEcKey | - | 42 | list-ec-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listEcKey | + | 7.2 | 02 | 2 | list-ec-key-name | + | 7.2 | 03 | 3 | listEcKey | + | 7.2 | 04 | 5 | list-ec-key-name | + | 7.2 | 05 | 25 | listEcKey | + | 7.2 | 06 | 42 | list-ec-key-name | + | 7.3 | 07 | 1 | listEcKey | + | 7.3 | 08 | 2 | list-ec-key-name | + | 7.3 | 09 | 3 | listEcKey | + | 7.3 | 10 | 5 | list-ec-key-name | + | 7.3 | 11 | 25 | listEcKey | + | 7.3 | 12 | 42 | list-ec-key-name | @Key @KeyCreate @KeyListDeleted @OCT @CreateVault Scenario Outline: OCT_LIST_DELETED_01 OCT keys are created and deleted with the key client then all are listed as deleted keys - Given a vault is created with name keys-del-oct- - And a key client is created with the vault named keys-del-oct- + Given key API version is used + And a vault is created with name keys-del-oct- + And a key client is created with the vault named keys-del-oct- And OCT keys with - prefix are created with 128 bits size And keys with - prefix are deleted When the deleted key properties are listed Then the listed deleted keys are matching the ones deleted before Examples: - | count | keyName | - | 1 | listOctKey | - | 2 | list-oct-key-name | - | 3 | listOctKey | - | 5 | list-oct-key-name | - | 25 | listOctKey | - | 42 | list-oct-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listOctKey | + | 7.2 | 02 | 2 | list-oct-key-name | + | 7.2 | 03 | 3 | listOctKey | + | 7.2 | 04 | 5 | list-oct-key-name | + | 7.2 | 05 | 25 | listOctKey | + | 7.2 | 06 | 42 | list-oct-key-name | + | 7.3 | 07 | 1 | listOctKey | + | 7.3 | 08 | 2 | list-oct-key-name | + | 7.3 | 09 | 3 | listOctKey | + | 7.3 | 10 | 5 | list-oct-key-name | + | 7.3 | 11 | 25 | listOctKey | + | 7.3 | 12 | 42 | list-oct-key-name | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListKeys.feature index 972de60d..c3b2d703 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/ListKeys.feature @@ -2,47 +2,64 @@ Feature: Key list @Key @KeyCreate @KeyList @RSA @CreateVault Scenario Outline: RSA_LIST_01 RSA keys are created with the key client then all are listed - Given a vault is created with name keys-list-rsa- - And a key client is created with the vault named keys-list-rsa- + Given key API version is used + And a vault is created with name keys-list-rsa- + And a key client is created with the vault named keys-list-rsa- And RSA keys with - prefix are created with 2048 bits size without HSM When the key properties are listed Then the listed keys are matching the ones created Examples: - | count | keyName | - | 1 | listRsaKey | - | 2 | list-rsa-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listRsaKey | + | 7.2 | 02 | 2 | list-rsa-key-name | + | 7.3 | 03 | 1 | listRsaKey | + | 7.3 | 04 | 2 | list-rsa-key-name | @Key @KeyCreate @KeyList @EC @CreateVault Scenario Outline: EC_LIST_01 EC keys are created with the key client then all are listed - Given a vault is created with name keys-list-ec- - And a key client is created with the vault named keys-list-ec- + Given key API version is used + And a vault is created with name keys-list-ec- + And a key client is created with the vault named keys-list-ec- And EC keys with - prefix are created with P-256 and without HSM When the key properties are listed Then the listed keys are matching the ones created Examples: - | count | keyName | - | 1 | listEcKey | - | 2 | list-ec-key-name | - | 3 | listEcKey | - | 5 | list-ec-key-name | - | 25 | listEcKey | - | 42 | list-ec-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listEcKey | + | 7.2 | 02 | 2 | list-ec-key-name | + | 7.2 | 03 | 3 | listEcKey | + | 7.2 | 04 | 5 | list-ec-key-name | + | 7.2 | 05 | 25 | listEcKey | + | 7.2 | 06 | 42 | list-ec-key-name | + | 7.3 | 07 | 1 | listEcKey | + | 7.3 | 08 | 2 | list-ec-key-name | + | 7.3 | 09 | 3 | listEcKey | + | 7.3 | 10 | 5 | list-ec-key-name | + | 7.3 | 11 | 25 | listEcKey | + | 7.3 | 12 | 42 | list-ec-key-name | @Key @KeyCreate @KeyList @OCT @CreateVault Scenario Outline: OCT_LIST_01 OCT keys are created with the key client then all are listed - Given a vault is created with name keys-list-oct- - And a key client is created with the vault named keys-list-oct- + Given key API version is used + And a vault is created with name keys-list-oct- + And a key client is created with the vault named keys-list-oct- And OCT keys with - prefix are created with 128 bits size When the key properties are listed Then the listed keys are matching the ones created Examples: - | count | keyName | - | 1 | listOctKey | - | 2 | list-oct-key-name | - | 3 | listOctKey | - | 5 | list-oct-key-name | - | 25 | listOctKey | - | 42 | list-oct-key-name | + | api | index | count | keyName | + | 7.2 | 01 | 1 | listOctKey | + | 7.2 | 02 | 2 | list-oct-key-name | + | 7.2 | 03 | 3 | listOctKey | + | 7.2 | 04 | 5 | list-oct-key-name | + | 7.2 | 05 | 25 | listOctKey | + | 7.2 | 06 | 42 | list-oct-key-name | + | 7.3 | 07 | 1 | listOctKey | + | 7.3 | 08 | 2 | list-oct-key-name | + | 7.3 | 09 | 3 | listOctKey | + | 7.3 | 10 | 5 | list-oct-key-name | + | 7.3 | 11 | 25 | listOctKey | + | 7.3 | 12 | 42 | list-oct-key-name | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/SignWithKeys.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/SignWithKeys.feature index 652e2043..12522047 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/SignWithKeys.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/SignWithKeys.feature @@ -2,7 +2,8 @@ Feature: Key sign and verify @Key @KeyCreate @KeySign @RSA Scenario Outline: RSA_SIGN_01 An RSA key is created with the key client then used for sign and verify operations - Given a key client is created with the vault named keys-generic + 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 without HSM And the key has sign,verify operations granted And the RSA key is created @@ -12,17 +13,24 @@ Feature: Key sign and verify Then the signature matches Examples: - | keyName | keySize | algorithm | clearText | - | signRsaKey-1 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | - | signRsaKey-2 | 2048 | PS384 | | - | signRsaKey-3 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | - | signRsaKey-4 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | - | signRsaKey-5 | 4096 | RS384 | | - | signRsaKey-6 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | api | keyName | keySize | algorithm | clearText | + | 7.2 | signRsaKey-01 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signRsaKey-02 | 2048 | PS384 | | + | 7.2 | signRsaKey-03 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signRsaKey-04 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signRsaKey-05 | 4096 | RS384 | | + | 7.2 | signRsaKey-06 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signRsaKey-07 | 2048 | PS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signRsaKey-08 | 2048 | PS384 | | + | 7.3 | signRsaKey-09 | 2048 | PS512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signRsaKey-10 | 4096 | RS256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signRsaKey-11 | 4096 | RS384 | | + | 7.3 | signRsaKey-12 | 4096 | RS512 | The quick brown fox jumps over the lazy dog. | @Key @KeyCreate @KeySign @EC Scenario Outline: EC_SIGN_01 An EC key is created with the key client then used for sign and verify operations - Given a key client is created with the vault named keys-generic + 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 without HSM And the key has sign,verify operations granted And the EC key is created @@ -32,12 +40,20 @@ Feature: Key sign and verify Then the signature matches Examples: - | keyName | curveName | algorithm | clearText | - | signEc-1 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | - | signEc-2 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | - | signEc-3 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | - | signEc-4 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | - | signEc-5 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | signEc-6 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | signEc-7 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | - | signEc-8 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | api | keyName | curveName | algorithm | clearText | + | 7.2 | signEc-01 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signEc-02 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.2 | signEc-03 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signEc-04 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.2 | signEc-05 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | signEc-06 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | signEc-07 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.2 | signEc-08 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | signEc-09 | P-256 | ES256 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signEc-10 | P-256K | ES256K | The quick brown fox jumps over the lazy dog. | + | 7.3 | signEc-11 | P-384 | ES384 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signEc-12 | P-521 | ES512 | The quick brown fox jumps over the lazy dog. | + | 7.3 | signEc-13 | P-256 | ES256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | signEc-14 | P-256K | ES256K | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | signEc-15 | P-384 | ES384 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | + | 7.3 | signEc-16 | P-521 | ES512 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do. | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/BackupAndRestoreSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/BackupAndRestoreSecrets.feature index a8baf309..938db869 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/BackupAndRestoreSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/BackupAndRestoreSecrets.feature @@ -2,22 +2,29 @@ Feature: Secret backup and restore @Secret @SecretCreate @DeleteVault @SecretBackup @SecretRestore @CreateVault Scenario Outline: SECRET_BACKUP_01 Secrets are created and backed up then vault is deleted and recreated to restore secret - Given a vault is created with name secrets-backup- - And a secret client is created with the vault named secrets-backup- + Given secret API version is used + And a vault is created with name secrets-backup- + And a secret client is created with the vault named secrets-backup- And secrets with - prefix are created valued abc123 And the secret named - is backed up - And the vault named secrets-backup- is deleted - And the vault named secrets-backup- is purged - And a vault is created with name secrets-backup- + And the vault named secrets-backup- is deleted + And the vault named secrets-backup- is purged + And a vault is created with name secrets-backup- When the secret named - is restored Then the last secret version of - is fetched without providing a version And the created secret exists with value: abc123 Examples: - | count | secretName | - | 1 | backupSecret | - | 2 | backup-secret-name | - | 3 | backupSecret | - | 5 | backup-secret-name | - | 25 | backupSecret | - | 42 | backup-secret-name | + | api | index | count | secretName | + | 7.2 | 01 | 1 | 72-backupSecret | + | 7.2 | 02 | 2 | 72-backup-secret-name | + | 7.2 | 03 | 3 | 72-backupSecret | + | 7.2 | 04 | 5 | 72-backup-secret-name | + | 7.2 | 05 | 25 | 72-backupSecret | + | 7.2 | 06 | 42 | 72-backup-secret-name | + | 7.3 | 07 | 1 | 73-backupSecret | + | 7.3 | 08 | 2 | 73-backup-secret-name | + | 7.3 | 09 | 3 | 73-backupSecret | + | 7.3 | 10 | 5 | 73-backup-secret-name | + | 7.3 | 11 | 25 | 73-backupSecret | + | 7.3 | 12 | 42 | 73-backup-secret-name | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/CreateSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/CreateSecrets.feature index b9798122..36850160 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/CreateSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/CreateSecrets.feature @@ -2,7 +2,8 @@ Feature: Secret creation @Secret @SecretCreate Scenario Outline: SECRET_CREATE_01 Single versions of secrets can be created with the secrets client - Given a secret client is created with the vault named secrets-generic + Given secret API version is used + And a secret client is created with the vault named secrets-generic And a secret named and valued is prepared And the secret is set to have as content type And the secret is set to expire seconds after creation @@ -21,16 +22,28 @@ Feature: Secret creation And the secret recovery settings are default Examples: - | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | - | createSecret1 | enabled | text/plain | abc123 | null | null | null | - | createSecret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | create-secret-1 | enabled | text/plain | abc123 | null | null | null | - | create-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | createSecret3 | enabled | text/plain | Lorem ipsum | null | null | null | - | create-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | - | createSecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | - | createSecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | - | createSecretXml | enabled | application/xml | | null | null | null | - | createSecretJson | enabled | application/json | {"value":true} | null | null | null | - | createSecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | - | createSecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | api | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | + | 7.2 | 72-createSecret1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 72-createSecret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 72-create-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 72-create-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 72-createSecret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 72-create-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 72-createSecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.2 | 72-createSecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.2 | 72-createSecretXml | enabled | application/xml | | null | null | null | + | 7.2 | 72-createSecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.2 | 72-createSecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.2 | 72-createSecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | 7.3 | 73-createSecret1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 73-createSecret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 73-create-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 73-create-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 73-createSecret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 73-create-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 73-createSecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.3 | 73-createSecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.3 | 73-createSecretXml | enabled | application/xml | | null | null | null | + | 7.3 | 73-createSecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.3 | 73-createSecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.3 | 73-createSecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/DeleteSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/DeleteSecrets.feature index 8428e8e5..e6b1d0a7 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/DeleteSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/DeleteSecrets.feature @@ -2,7 +2,8 @@ Feature: Secret delete and recover @Secret @SecretCreate @SecretDelete Scenario Outline: SECRET_DELETE_01 Multiple versions of secrets are created with the secret client then deleted - Given a secret client is created with the vault named secrets-delete + Given secret API version is used + And a secret client is created with the vault named secrets-delete And a secret named and valued is prepared And the secret is set to have as content type And version of the secret is created @@ -11,14 +12,18 @@ Feature: Secret delete and recover And the secret recovery timestamps are default Examples: - | versionsCount | secretName | contentType | secretValue | - | 6 | deleteSecret1 | text/plain | abc123 | - | 5 | deleteSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | - | 4 | deleteSecretXml | application/xml | | + | api | versionsCount | secretName | contentType | secretValue | + | 7.2 | 6 | 72-deleteSecret1 | text/plain | abc123 | + | 7.2 | 5 | 72-deleteSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.2 | 4 | 72-deleteSecretXml | application/xml | | + | 7.3 | 6 | 73-deleteSecret1 | text/plain | abc123 | + | 7.3 | 5 | 73-deleteSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.3 | 4 | 73-deleteSecretXml | application/xml | | @Secret @SecretCreate @SecretDelete @SecretRecover Scenario Outline: SECRET_RECOVER_01 Multiple versions of secrets are created with the secret client then deleted and recovered - Given a secret client is created with the vault named secrets-delete + Given secret API version is used + And a secret client is created with the vault named secrets-delete And a secret named and valued is prepared And the secret is set to have as content type And version of the secret is created @@ -27,14 +32,18 @@ Feature: Secret delete and recover Then the secret URL contains the vault url and Examples: - | versionsCount | secretName | contentType | secretValue | - | 6 | recoverSecret1 | text/plain | abc123 | - | 5 | recoverSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | - | 4 | recoverSecretXml | application/xml | | + | api | versionsCount | secretName | contentType | secretValue | + | 7.2 | 6 | 72-recoverSecret1 | text/plain | abc123 | + | 7.2 | 5 | 72-recoverSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.2 | 4 | 72-recoverSecretXml | application/xml | | + | 7.3 | 6 | 73-recoverSecret1 | text/plain | abc123 | + | 7.3 | 5 | 73-recoverSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.3 | 4 | 73-recoverSecretXml | application/xml | | @Secret @SecretCreate @SecretDelete @SecretPurge Scenario Outline: SECRET_PURGE_01 Multiple versions of secrets are created with the secret client then deleted and purged - Given a vault is created with name secrets-purge- + Given secret API version is used + And a vault is created with name secrets-purge- And a secret client is created with the vault named secrets-purge- And a secret named and valued is prepared And the secret is set to have as content type @@ -47,7 +56,10 @@ Feature: Secret delete and recover And the vault named secrets-purge- is purged Examples: - | versionsCount | secretName | contentType | secretValue | - | 6 | purgeSecret1 | text/plain | abc123 | - | 5 | purgeSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | - | 4 | purgeSecretXml | application/xml | | + | api | versionsCount | secretName | contentType | secretValue | + | 7.2 | 6 | 72-purgeSecret1 | text/plain | abc123 | + | 7.2 | 5 | 72-purgeSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.2 | 4 | 72-purgeSecretXml | application/xml | | + | 7.3 | 6 | 73-purgeSecret1 | text/plain | abc123 | + | 7.3 | 5 | 73-purgeSecret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.3 | 4 | 73-purgeSecretXml | application/xml | | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/GetSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/GetSecrets.feature index 3d9e9dee..bcf74f91 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/GetSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/GetSecrets.feature @@ -2,7 +2,8 @@ Feature: Secret get @Secret @SecretCreate @SecretGet Scenario Outline: SECRET_GET_01 Multiple versions of secrets are created with the secret client then the latest is fetched - Given a secret client is created with the vault named secrets-generic + Given secret API version is used + And a secret client is created with the vault named secrets-generic And a secret named and valued is prepared And version of the secret is created And the secret is set to have as content type @@ -23,23 +24,36 @@ Feature: Secret get And the secret recovery settings are default Examples: - | versionsCount | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | - | 2 | get01Secret1 | enabled | text/plain | abc123 | null | null | null | - | 1 | get01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | 2 | get01-secret-1 | enabled | text/plain | abc123 | null | null | null | - | 1 | get01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | 4 | get01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | - | 3 | get01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | - | 4 | get01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | - | 3 | get01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | - | 4 | get01SecretXml | enabled | application/xml | | null | null | null | - | 3 | get01SecretJson | enabled | application/json | {"value":true} | null | null | null | - | 4 | get01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | - | 3 | get01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | api | versionsCount | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | + | 7.2 | 2 | 72-get01Secret1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 1 | 72-get01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 2 | 72-get01-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 1 | 72-get01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 4 | 72-get01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 3 | 72-get01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 4 | 72-get01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | 72-get01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.2 | 4 | 72-get01SecretXml | enabled | application/xml | | null | null | null | + | 7.2 | 3 | 72-get01SecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.2 | 4 | 72-get01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.2 | 3 | 72-get01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | 7.3 | 2 | 73-get01Secret1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 1 | 73-get01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 2 | 73-get01-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 1 | 73-get01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 4 | 73-get01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 3 | 73-get01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 4 | 73-get01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | 73-get01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.3 | 4 | 73-get01SecretXml | enabled | application/xml | | null | null | null | + | 7.3 | 3 | 73-get01SecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.3 | 4 | 73-get01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.3 | 3 | 73-get01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | @Secret @SecretCreate @SecretGet Scenario Outline: SECRET_GET_02 Multiple versions of secrets are created with the secret client then the first is fetched - Given a secret client is created with the vault named secrets-generic + Given secret API version is used + And a secret client is created with the vault named secrets-generic And a secret named and valued is prepared And the secret is set to have as content type And version of the secret is created @@ -52,14 +66,18 @@ Feature: Secret get And the secret recovery settings are default Examples: - | versionsCount | secretName | contentType | secretValue | - | 6 | get02Secret1 | text/plain | abc123 | - | 5 | get02Secret2 | text/plain | The quick brown fox jumps over the lazy dog. | - | 4 | get02SecretXml | application/xml | | + | api | versionsCount | secretName | contentType | secretValue | + | 7.2 | 6 | 72-get02Secret1 | text/plain | abc123 | + | 7.2 | 5 | 72-get02Secret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.2 | 4 | 72-get02SecretXml | application/xml | | + | 7.3 | 6 | 73-get02Secret1 | text/plain | abc123 | + | 7.3 | 5 | 73-get02Secret2 | text/plain | The quick brown fox jumps over the lazy dog. | + | 7.3 | 4 | 73-get02SecretXml | application/xml | | @Secret @SecretCreate @SecretUpdate @SecretGet Scenario Outline: SECRET_UPDATE_01 Multiple versions of secrets are created with the secret client then the latest is updated and fetched - Given a secret client is created with the vault named secrets-generic + Given secret API version is used + And a secret client is created with the vault named secrets-generic And a secret named and valued is prepared And the secret is set to have as content type And version of the secret is created @@ -81,16 +99,28 @@ Feature: Secret get And the secret recovery settings are default Examples: - | versionsCount | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | - | 2 | update01Secret1 | enabled | text/plain | abc123 | null | null | null | - | 1 | update01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | 2 | update01-secret-1 | enabled | text/plain | abc123 | null | null | null | - | 1 | update01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | - | 4 | update01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | - | 3 | update01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | - | 4 | update01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | - | 3 | update01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | - | 4 | update01SecretXml | enabled | application/xml | | null | null | null | - | 3 | update01SecretJson | enabled | application/json | {"value":true} | null | null | null | - | 4 | update01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | - | 3 | update01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | api | versionsCount | secretName | enabledStatus | contentType | secretValue | expires | notBefore | tagMap | + | 7.2 | 2 | 72-update01Secret1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 1 | 72-update01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 2 | 72-update01-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.2 | 1 | 72-update01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.2 | 4 | 72-update01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 3 | 72-update01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.2 | 4 | 72-update01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.2 | 3 | 72-update01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.2 | 4 | 72-update01SecretXml | enabled | application/xml | | null | null | null | + | 7.2 | 3 | 72-update01SecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.2 | 4 | 72-update01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.2 | 3 | 72-update01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | + | 7.3 | 2 | 73-update01Secret1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 1 | 73-update01Secret2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 2 | 73-update01-secret-1 | enabled | text/plain | abc123 | null | null | null | + | 7.3 | 1 | 73-update01-secret-2 | enabled | text/plain | The quick brown fox jumps over the lazy dog. | null | null | null | + | 7.3 | 4 | 73-update01Secret3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 3 | 73-update01-secret-3 | enabled | text/plain | Lorem ipsum | null | null | null | + | 7.3 | 4 | 73-update01SecretMap1 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue,b1:b2 | + | 7.3 | 3 | 73-update01SecretMap2 | enabled | text/plain | 123 XYZ | null | null | aKey:aValue | + | 7.3 | 4 | 73-update01SecretXml | enabled | application/xml | | null | null | null | + | 7.3 | 3 | 73-update01SecretJson | enabled | application/json | {"value":true} | null | null | null | + | 7.3 | 4 | 73-update01SecretDates | enabled | text/plain | Only sometimes. | 4321 | 1234 | null | + | 7.3 | 3 | 73-update01SecretNotEnabled | not enabled | text/plain | Not enabled | null | null | null | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListDeletedSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListDeletedSecrets.feature index 0fa057ae..868cf388 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListDeletedSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListDeletedSecrets.feature @@ -2,18 +2,25 @@ Feature: Secret list deleted @Secret @SecretCreate @SecretListDeleted @CreateVault Scenario Outline: SECRET_LIST_DELETED_01 Secrets are created and deleted with the secret client then all are listed as deleted secrets - Given a vault is created with name secrets-del- - And a secret client is created with the vault named secrets-del- + Given secret API version is used + And a vault is created with name secrets-del- + And a secret client is created with the vault named secrets-del- And secrets with - prefix are created valued abc123 And secrets with - prefix are deleted When the deleted secret properties are listed Then the listed deleted secrets are matching the ones deleted before Examples: - | count | secretName | - | 1 | listSecret | - | 2 | list-secret-name | - | 3 | listSecret | - | 5 | list-secret-name | - | 25 | listSecret | - | 42 | list-secret-name | + | api | index | count | secretName | + | 7.2 | 01 | 1 | listSecret | + | 7.2 | 02 | 2 | list-secret-name | + | 7.2 | 03 | 3 | listSecret | + | 7.2 | 04 | 5 | list-secret-name | + | 7.2 | 05 | 25 | listSecret | + | 7.2 | 06 | 42 | list-secret-name | + | 7.3 | 07 | 1 | listSecret | + | 7.3 | 08 | 2 | list-secret-name | + | 7.3 | 09 | 3 | listSecret | + | 7.3 | 10 | 5 | list-secret-name | + | 7.3 | 11 | 25 | listSecret | + | 7.3 | 12 | 42 | list-secret-name | diff --git a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListSecrets.feature b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListSecrets.feature index e7dfaec2..04502920 100644 --- a/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListSecrets.feature +++ b/lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListSecrets.feature @@ -2,17 +2,24 @@ Feature: Secret list @Secret @SecretCreate @SecretList @CreateVault Scenario Outline: SECRET_LIST_01 Secrets are created with the secret client then all are listed - Given a vault is created with name secrets-list- - And a secret client is created with the vault named secrets-list- + Given secret API version is used + And a vault is created with name secrets-list- + And a secret client is created with the vault named secrets-list- And secrets with - prefix are created valued abc123 When the secret properties are listed Then the listed secrets are matching the ones created Examples: - | count | secretName | - | 1 | listSecret | - | 2 | list-secret-name | - | 3 | listSecret | - | 5 | list-secret-name | - | 25 | listSecret | - | 42 | list-secret-name | + | api | index | count | secretName | + | 7.2 | 01 | 1 | listSecret | + | 7.2 | 02 | 2 | list-secret-name | + | 7.2 | 03 | 3 | listSecret | + | 7.2 | 04 | 5 | list-secret-name | + | 7.2 | 05 | 25 | listSecret | + | 7.2 | 06 | 42 | list-secret-name | + | 7.3 | 07 | 1 | listSecret | + | 7.3 | 08 | 2 | list-secret-name | + | 7.3 | 09 | 3 | listSecret | + | 7.3 | 10 | 5 | list-secret-name | + | 7.3 | 11 | 25 | listSecret | + | 7.3 | 12 | 42 | list-secret-name | diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json similarity index 69% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json index 9ae4fe1b..b466454f 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256.localhost:8443", - "entityId" : "jsonBackupEc-256", + "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256-72.localhost:8443", + "entityId" : "jsonBackupEc-256-72", "entityVersion" : "f16a91376c13478996195a0c2cee39cb", "attributes" : { "enabled" : true, @@ -15,9 +15,9 @@ "crv" : "P-256", "d" : "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256.localhost:8443/keys/jsonBackupEc-256/f16a91376c13478996195a0c2cee39cb", + "kid" : "https://keys-backup-jsonBackupEc-256-72.localhost:8443/keys/jsonBackupEc-256-72/f16a91376c13478996195a0c2cee39cb", "kty" : "EC", "x" : "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", "y" : "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..4e285295 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-73.json @@ -0,0 +1,23 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json similarity index 69% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json index 4fb5ad54..8984af85 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256k.localhost:8443", - "entityId" : "jsonBackupEc-256k", + "vaultBaseUri" : "https://keys-backup-jsonBackupEc-256k-72.localhost:8443", + "entityId" : "jsonBackupEc-256k-72", "entityVersion" : "5425a5872c1f4a9fa96db8f40e6c5d07", "attributes" : { "enabled" : true, @@ -15,9 +15,9 @@ "crv" : "P-256K", "d" : "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-256k.localhost:8443/keys/jsonBackupEc-256k/5425a5872c1f4a9fa96db8f40e6c5d07", + "kid" : "https://keys-backup-jsonBackupEc-256k-72.localhost:8443/keys/jsonBackupEc-256k-72/5425a5872c1f4a9fa96db8f40e6c5d07", "kty" : "EC", "x" : "AOSHhd4mwdHNED-SdhTnMasUYuEwLPWpLb8rNx-NV9OH", "y" : "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..a5fd4441 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-73.json @@ -0,0 +1,23 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json similarity index 71% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json index b788ec1c..c4d1a241 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-384.localhost:8443", - "entityId" : "jsonBackupEc-384", + "vaultBaseUri" : "https://keys-backup-jsonBackupEc-384-72.localhost:8443", + "entityId" : "jsonBackupEc-384-72", "entityVersion" : "7f4c0a2ef5454e07a533e597434984a8", "attributes" : { "enabled" : true, @@ -15,9 +15,9 @@ "crv" : "P-384", "d" : "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-384.localhost:8443/keys/jsonBackupEc-384/7f4c0a2ef5454e07a533e597434984a8", + "kid" : "https://keys-backup-jsonBackupEc-384-72.localhost:8443/keys/jsonBackupEc-384-72/7f4c0a2ef5454e07a533e597434984a8", "kty" : "EC", "x" : "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", "y" : "AKYy8hXwjc0O8mVBXOolUvHklqEV2POLIN6c3EpZIJ-Sz2H_Vce0EoAF320bZhxrfw" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..e27b2a10 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-73.json @@ -0,0 +1,23 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json similarity index 73% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json index a6918152..55e6b332 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupEc-521.localhost:8443", - "entityId" : "jsonBackupEc-521", + "vaultBaseUri" : "https://keys-backup-jsonBackupEc-521-72.localhost:8443", + "entityId" : "jsonBackupEc-521-72", "entityVersion" : "9301e31d18a540e08c68ad5230e60e21", "attributes" : { "enabled" : true, @@ -15,9 +15,9 @@ "crv" : "P-521", "d" : "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupEc-521.localhost:8443/keys/jsonBackupEc-521/9301e31d18a540e08c68ad5230e60e21", + "kid" : "https://keys-backup-jsonBackupEc-521-72.localhost:8443/keys/jsonBackupEc-521-72/9301e31d18a540e08c68ad5230e60e21", "kty" : "EC", "x" : "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", "y" : "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..0c3b2281 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-73.json @@ -0,0 +1,23 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json similarity index 62% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json index 79d47b78..d915b260 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-128.localhost:8443", - "entityId" : "jsonBackupOct-128", + "vaultBaseUri" : "https://keys-backup-jsonBackupOct-128-72.localhost:8443", + "entityId" : "jsonBackupOct-128-72", "entityVersion" : "730490a0df9b4ac78ed675902077a18f", "attributes" : { "enabled" : true, @@ -14,7 +14,7 @@ "keyMaterial" : { "k" : "sx32Vta2Zx1BsdQBY2l5iQ", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-128.localhost:8443/keys/jsonBackupOct-128/730490a0df9b4ac78ed675902077a18f", + "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 new file mode 100644 index 00000000..c070ccdc --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-73.json @@ -0,0 +1,20 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json similarity index 62% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json index 09d9b593..bca9e439 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-192.localhost:8443", - "entityId" : "jsonBackupOct-192", + "vaultBaseUri" : "https://keys-backup-jsonBackupOct-192-72.localhost:8443", + "entityId" : "jsonBackupOct-192-72", "entityVersion" : "55ccab8f94b244ab97d82899340c22dd", "attributes" : { "enabled" : true, @@ -14,7 +14,7 @@ "keyMaterial" : { "k" : "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-192.localhost:8443/keys/jsonBackupOct-192/55ccab8f94b244ab97d82899340c22dd", + "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 new file mode 100644 index 00000000..2fd02eae --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-73.json @@ -0,0 +1,20 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json similarity index 63% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json index 1f1f1465..96e403a7 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupOct-256.localhost:8443", - "entityId" : "jsonBackupOct-256", + "vaultBaseUri" : "https://keys-backup-jsonBackupOct-256-72.localhost:8443", + "entityId" : "jsonBackupOct-256-72", "entityVersion" : "19c6d13acb844fda8ccb14751252433a", "attributes" : { "enabled" : true, @@ -14,7 +14,7 @@ "keyMaterial" : { "k" : "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupOct-256.localhost:8443/keys/jsonBackupOct-256/19c6d13acb844fda8ccb14751252433a", + "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 new file mode 100644 index 00000000..90c4ca01 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-73.json @@ -0,0 +1,20 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json similarity index 88% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json index 32e5bb20..56d1396c 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-2048.localhost:8443", - "entityId" : "jsonBackupRsa-2048", + "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-2048-72.localhost:8443", + "entityId" : "jsonBackupRsa-2048-72", "entityVersion" : "2d93f37afada4679b00b528f7238ad5c", "attributes" : { "enabled" : true, @@ -17,11 +17,11 @@ "dq" : "B7sIKKG4bo4Gvm4xj7KBnlP4X9UQH8FLnisGVe9HqDJH4Zu7d-dj5yHT-HaX07MU5p0r2H26owT829SxZGZB2qR_njnuyt8KnmAdEJPttNkW6L25csAoEzqdexi08wmWH0fAEMVDgeobAg1YtByts-VmajO0TWxYyO1oiJFc6ok", "e" : "AQAB", "key_ops" : [ "sign", "encrypt", "wrapKey" ], - "kid" : "https://keys-backup-jsonBackupRsa-2048.localhost:8443/keys/jsonBackupRsa-2048/2d93f37afada4679b00b528f7238ad5c", + "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" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..268cc230 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-2048-73.json @@ -0,0 +1,27 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json similarity index 91% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json index 987b4c79..cb651411 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-3072.localhost:8443", - "entityId" : "jsonBackupRsa-3072", + "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-3072-72.localhost:8443", + "entityId" : "jsonBackupRsa-3072-72", "entityVersion" : "5ae349c717644397a14df7ece5eda28f", "attributes" : { "enabled" : true, @@ -17,11 +17,11 @@ "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.localhost:8443/keys/jsonBackupRsa-3072/5ae349c717644397a14df7ece5eda28f", + "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" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..16c6ac2d --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-3072-73.json @@ -0,0 +1,27 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096.json b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json similarity index 93% rename from lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096.json rename to lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json index 46d4b750..b0ad62c6 100644 --- a/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096.json +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-72.json @@ -1,6 +1,6 @@ [ { - "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-4096.localhost:8443", - "entityId" : "jsonBackupRsa-4096", + "vaultBaseUri" : "https://keys-backup-jsonBackupRsa-4096-72.localhost:8443", + "entityId" : "jsonBackupRsa-4096-72", "entityVersion" : "6f009cc18cf146cf960a96254e8d3f39", "attributes" : { "enabled" : true, @@ -17,11 +17,11 @@ "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.localhost:8443/keys/jsonBackupRsa-4096/6f009cc18cf146cf960a96254e8d3f39", + "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" } -} ] \ No newline at end of file +} ] 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 new file mode 100644 index 00000000..3671f817 --- /dev/null +++ b/lowkey-vault-docker/src/test/resources/json/backups/jsonBackupRsa-4096-73.json @@ -0,0 +1,27 @@ +[ { + "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" + } +} ] diff --git a/lowkey-vault-testcontainers/build.gradle b/lowkey-vault-testcontainers/build.gradle index 919ccb8e..574c2983 100644 --- a/lowkey-vault-testcontainers/build.gradle +++ b/lowkey-vault-testcontainers/build.gradle @@ -28,6 +28,7 @@ test { useJUnitPlatform() systemProperty("imageVersion", project.version) mustRunAfter(":lowkey-vault-docker:build") + mustRunAfter(":lowkey-vault-docker:test") } publishing { publications {