Skip to content

Commit

Permalink
Add basic support for 7.4 API version (#701)
Browse files Browse the repository at this point in the history
- Adds new constants for 7.4 API
- Moves controller logic introduced by 7.3 API controllers to new common controllers
- Adds new controllers for 7.4 API endpoint mappings
- Adjusts converter API version mappings to include 7.4 API
- Adds new unit and integration test cases covering the 7.4 API
- Adds new end-to-end test cases covering the 7.4 API
- Introduces random URI usage in integration tests to isolate test cases better
- Removes 7.2 API variants of unnecessary end-to-end tests
- Changes Lowkey Vault Client implementation to automatically use 7.4 API
- Updates readme to mention the 7.4 API support

#697
{minor}

Signed-off-by: Esta Nagy <nagyesta@gmail.com>
  • Loading branch information
nagyesta authored Sep 9, 2023
1 parent 6969998 commit 7d70f78
Show file tree
Hide file tree
Showing 86 changed files with 5,958 additions and 558 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo

### Keys

- API version supported: ```7.2```, partially ```7.3```
- API version supported: ```7.2```, partially ```7.3```, ```7.4```
- Create key (```RSA```, ```EC```, ```OCT```)
- Including metadata
- Import key (```RSA```, ```EC```, ```OCT```)
Expand Down Expand Up @@ -128,7 +128,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo

### Secrets

- API version supported: ```7.2```, ```7.3```
- API version supported: ```7.2```, ```7.3```, ```7.4```
- Set secret
- Including metadata
- Get available secret versions
Expand All @@ -147,7 +147,7 @@ Lowkey Vault is far from supporting all Azure Key Vault features. The list suppo

### Certificates

- API version supported: ```7.3```
- API version supported: ```7.3```, ```7.4```
- Create certificate
- Self-signed only
- Using `PKCS12` (`.pfx`) or `PEM` (`.pem`) formats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,13 @@
public interface ApiVersionAware {

/**
* Supported API version collection containing only 7.2.
* Supported API version collection containing all 7.3+ versions.
*/
SortedSet<String> V7_2 = new TreeSet<>(Set.of(ApiConstants.V_7_2));
SortedSet<String> V7_3_AND_LATER = new TreeSet<>(Set.of(ApiConstants.V_7_3, ApiConstants.V_7_4));
/**
* Supported API version collection containing only 7.3.
* Supported API version collection containing all versions (7.2, 7.3 and 7.4).
*/
SortedSet<String> V7_3 = new TreeSet<>(Set.of(ApiConstants.V_7_3));
/**
* Supported API version collection containing both 7.2 and 7.3.
*/
SortedSet<String> V7_2_AND_V7_3 = new TreeSet<>(Set.of(ApiConstants.V_7_2, ApiConstants.V_7_3));
SortedSet<String> ALL_VERSIONS = new TreeSet<>(Set.of(ApiConstants.V_7_2, ApiConstants.V_7_3, ApiConstants.V_7_4));

SortedSet<String> supportedVersions();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.github.nagyesta.lowkeyvault.controller.common;

import com.github.nagyesta.lowkeyvault.mapper.common.AliasAwareConverter;
import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Update;
import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId;
import com.github.nagyesta.lowkeyvault.service.vault.VaultService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.validation.annotation.Validated;

import java.net.URI;

@Slf4j
public abstract class CommonKeyPolicyController extends BaseKeyController {

public CommonKeyPolicyController(@NonNull final KeyConverterRegistry registry, @NonNull final VaultService vaultService) {
super(registry, vaultService);
}

public ResponseEntity<KeyRotationPolicyModel> getRotationPolicy(
@Valid @Pattern(regexp = NAME_PATTERN) final String keyName,
final URI baseUri) {
log.info("Received request to {} get rotation policy: {} using API version: {}",
baseUri.toString(), keyName, apiVersion());
return getRotationPolicyResponseEntity(getVaultByUri(baseUri), entityId(baseUri, keyName), baseUri);
}

public ResponseEntity<KeyRotationPolicyModel> updateRotationPolicy(
@Valid @Pattern(regexp = NAME_PATTERN) final String keyName,
final URI baseUri,
@NonNull @Valid @Validated(Update.class) final KeyRotationPolicyModel request) {
log.info("Received request to {} update rotation policy: {} using API version: {}",
baseUri.toString(), keyName, apiVersion());
final KeyEntityId keyEntityId = entityId(baseUri, keyName);
request.setKeyEntityId(keyEntityId);
final RotationPolicy rotationPolicy = registry().rotationPolicyEntityConverter(apiVersion()).convert(request);
final KeyVaultFake keyVaultFake = getVaultByUri(baseUri);
keyVaultFake.setRotationPolicy(rotationPolicy);
return getRotationPolicyResponseEntity(keyVaultFake, keyEntityId, baseUri);
}

private ResponseEntity<KeyRotationPolicyModel> getRotationPolicyResponseEntity(
final KeyVaultFake keyVaultFake, final KeyEntityId keyEntityId, final URI baseUri) {
final ReadOnlyRotationPolicy policy = keyVaultFake.rotationPolicy(keyEntityId);
final AliasAwareConverter<ReadOnlyRotationPolicy, KeyRotationPolicyModel> converter = registry()
.rotationPolicyModelConverter(apiVersion());
return ResponseEntity.ok(converter.convert(policy, baseUri));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.github.nagyesta.lowkeyvault.controller.common;

import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry;
import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupList;
import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupModel;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel;
import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId;
import com.github.nagyesta.lowkeyvault.service.vault.VaultService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;

import java.net.URI;
import java.util.Optional;

@Slf4j
public abstract class CommonPolicyAwareKeyBackupRestoreController extends CommonKeyBackupRestoreController {

public CommonPolicyAwareKeyBackupRestoreController(
@NonNull final KeyConverterRegistry registry, @NonNull final VaultService vaultService) {
super(registry, vaultService);
}

@Override
protected KeyBackupModel backupEntity(final KeyEntityId entityId) {
final KeyBackupModel keyBackupModel = super.backupEntity(entityId);
final KeyBackupList value = keyBackupModel.getValue();
final ReadOnlyRotationPolicy rotationPolicy = getVaultByUri(entityId.vault()).rotationPolicy(entityId);
value.setKeyRotationPolicy(registry().rotationPolicyModelConverter(apiVersion()).convert(rotationPolicy, entityId.vault()));
return keyBackupModel;
}

@Override
protected KeyVaultKeyModel restoreEntity(final KeyBackupModel backupModel) {
final KeyVaultKeyModel keyVaultKeyModel = super.restoreEntity(backupModel);
final URI baseUri = getSingleBaseUri(backupModel);
final String entityName = getSingleEntityName(backupModel);
final KeyEntityId keyEntityId = entityId(baseUri, entityName);
final KeyVaultFake vaultByUri = getVaultByUri(baseUri);
final KeyRotationPolicyModel keyRotationPolicy = backupModel.getValue().getKeyRotationPolicy();
Optional.ofNullable(keyRotationPolicy)
.map(r -> {
r.setKeyEntityId(keyEntityId);
return r;
})
.map(r -> registry().rotationPolicyEntityConverter(apiVersion()).convert(r))
.ifPresent(vaultByUri::setRotationPolicy);
return keyVaultKeyModel;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

@Data
@Builder
class PaginationContext {
public class PaginationContext {
private static final String AND = "&";
private static final String EQUALS = "=";
private static final String QUESTION_MARK = "?";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package com.github.nagyesta.lowkeyvault.controller.v7_3;

import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyBackupRestoreController;
import com.github.nagyesta.lowkeyvault.controller.common.CommonPolicyAwareKeyBackupRestoreController;
import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry;
import com.github.nagyesta.lowkeyvault.model.common.ApiConstants;
import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupList;
import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupModel;
import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyVaultKeyModel;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel;
import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId;
import com.github.nagyesta.lowkeyvault.service.vault.VaultService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
Expand All @@ -23,7 +18,6 @@
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.util.Optional;

import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_3;
import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_3;
Expand All @@ -34,7 +28,7 @@
@Validated
@DependsOn({"keyModelConverter", "keyRotationPolicyEntityConverter"})
@Component("KeyBackupRestoreControllerV73")
public class KeyBackupRestoreController extends CommonKeyBackupRestoreController {
public class KeyBackupRestoreController extends CommonPolicyAwareKeyBackupRestoreController {

@Autowired
public KeyBackupRestoreController(@NonNull final KeyConverterRegistry registry, @NonNull final VaultService vaultService) {
Expand All @@ -61,33 +55,6 @@ public ResponseEntity<KeyVaultKeyModel> restore(@RequestAttribute(name = ApiCons
return super.restore(baseUri, keyBackupModel);
}

@Override
protected KeyBackupModel backupEntity(final KeyEntityId entityId) {
final KeyBackupModel keyBackupModel = super.backupEntity(entityId);
final KeyBackupList value = keyBackupModel.getValue();
final ReadOnlyRotationPolicy rotationPolicy = getVaultByUri(entityId.vault()).rotationPolicy(entityId);
value.setKeyRotationPolicy(registry().rotationPolicyModelConverter(apiVersion()).convert(rotationPolicy, entityId.vault()));
return keyBackupModel;
}

@Override
protected KeyVaultKeyModel restoreEntity(final KeyBackupModel backupModel) {
final KeyVaultKeyModel keyVaultKeyModel = super.restoreEntity(backupModel);
final URI baseUri = getSingleBaseUri(backupModel);
final String entityName = getSingleEntityName(backupModel);
final KeyEntityId keyEntityId = entityId(baseUri, entityName);
final KeyVaultFake vaultByUri = getVaultByUri(baseUri);
final KeyRotationPolicyModel keyRotationPolicy = backupModel.getValue().getKeyRotationPolicy();
Optional.ofNullable(keyRotationPolicy)
.map(r -> {
r.setKeyEntityId(keyEntityId);
return r;
})
.map(r -> registry().rotationPolicyEntityConverter(apiVersion()).convert(r))
.ifPresent(vaultByUri::setRotationPolicy);
return keyVaultKeyModel;
}

@Override
protected String apiVersion() {
return V_7_3;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package com.github.nagyesta.lowkeyvault.controller.v7_3;

import com.github.nagyesta.lowkeyvault.controller.common.BaseKeyController;
import com.github.nagyesta.lowkeyvault.mapper.common.AliasAwareConverter;
import com.github.nagyesta.lowkeyvault.controller.common.CommonKeyPolicyController;
import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry;
import com.github.nagyesta.lowkeyvault.model.common.ApiConstants;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel;
import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Update;
import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake;
import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyRotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.RotationPolicy;
import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId;
import com.github.nagyesta.lowkeyvault.service.vault.VaultService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
Expand All @@ -30,7 +25,7 @@
@RestController
@Validated
@Component("KeyPolicyControllerV73")
public class KeyPolicyController extends BaseKeyController {
public class KeyPolicyController extends CommonKeyPolicyController {

public KeyPolicyController(@NonNull final KeyConverterRegistry registry, @NonNull final VaultService vaultService) {
super(registry, vaultService);
Expand All @@ -42,9 +37,7 @@ public KeyPolicyController(@NonNull final KeyConverterRegistry registry, @NonNul
public ResponseEntity<KeyRotationPolicyModel> getRotationPolicy(
@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName,
@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) {
log.info("Received request to {} get rotation policy: {} using API version: {}",
baseUri.toString(), keyName, apiVersion());
return getRotationPolicyResponseEntity(getVaultByUri(baseUri), entityId(baseUri, keyName), baseUri);
return super.getRotationPolicy(keyName, baseUri);
}

@PutMapping(value = {"/keys/{keyName}/rotationpolicy", "/keys/{keyName}/rotationpolicy/"},
Expand All @@ -55,26 +48,12 @@ public ResponseEntity<KeyRotationPolicyModel> updateRotationPolicy(
@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String keyName,
@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri,
@NonNull @Valid @Validated(Update.class) @RequestBody final KeyRotationPolicyModel request) {
log.info("Received request to {} update rotation policy: {} using API version: {}",
baseUri.toString(), keyName, apiVersion());
final KeyEntityId keyEntityId = entityId(baseUri, keyName);
request.setKeyEntityId(keyEntityId);
final RotationPolicy rotationPolicy = registry().rotationPolicyEntityConverter(apiVersion()).convert(request);
final KeyVaultFake keyVaultFake = getVaultByUri(baseUri);
keyVaultFake.setRotationPolicy(rotationPolicy);
return getRotationPolicyResponseEntity(keyVaultFake, keyEntityId, baseUri);
return super.updateRotationPolicy(keyName, baseUri, request);
}

@Override
protected String apiVersion() {
return V_7_3;
}

private ResponseEntity<KeyRotationPolicyModel> getRotationPolicyResponseEntity(
final KeyVaultFake keyVaultFake, final KeyEntityId keyEntityId, final URI baseUri) {
final ReadOnlyRotationPolicy policy = keyVaultFake.rotationPolicy(keyEntityId);
final AliasAwareConverter<ReadOnlyRotationPolicy, KeyRotationPolicyModel> converter = registry()
.rotationPolicyModelConverter(apiVersion());
return ResponseEntity.ok(converter.convert(policy, baseUri));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.github.nagyesta.lowkeyvault.controller.v7_4;

import com.github.nagyesta.lowkeyvault.controller.common.CommonCertificateBackupRestoreController;
import com.github.nagyesta.lowkeyvault.mapper.common.registry.CertificateConverterRegistry;
import com.github.nagyesta.lowkeyvault.model.common.ApiConstants;
import com.github.nagyesta.lowkeyvault.model.common.backup.CertificateBackupModel;
import com.github.nagyesta.lowkeyvault.model.v7_3.certificate.KeyVaultCertificateModel;
import com.github.nagyesta.lowkeyvault.service.vault.VaultService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
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 java.net.URI;

import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.API_VERSION_7_4;
import static com.github.nagyesta.lowkeyvault.model.common.ApiConstants.V_7_4;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

@Slf4j
@RestController
@Validated
@DependsOn({"certificateBackupConverter", "certificateModelConverter"})
@Component("CertificateBackupRestoreControllerV74")
public class CertificateBackupRestoreController extends CommonCertificateBackupRestoreController {

@Autowired
public CertificateBackupRestoreController(
@NonNull final CertificateConverterRegistry registry, @NonNull final VaultService vaultService) {
super(registry, vaultService);
}

@Override
@PostMapping(value = {"/certificates/{certificateName}/backup", "/certificates/{certificateName}/backup/"},
params = API_VERSION_7_4,
produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CertificateBackupModel> backup(@PathVariable @Valid @Pattern(regexp = NAME_PATTERN) final String certificateName,
@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri) {
return super.backup(certificateName, baseUri);
}

@Override
@PostMapping(value = {"/certificates/restore", "/certificates/restore/"},
params = API_VERSION_7_4,
consumes = APPLICATION_JSON_VALUE,
produces = APPLICATION_JSON_VALUE)
public ResponseEntity<KeyVaultCertificateModel> restore(@RequestAttribute(name = ApiConstants.REQUEST_BASE_URI) final URI baseUri,
@Valid @RequestBody final CertificateBackupModel certificateBackupModel) {
return super.restore(baseUri, certificateBackupModel);
}

@Override
protected String apiVersion() {
return V_7_4;
}
}
Loading

0 comments on commit 7d70f78

Please sign in to comment.