diff --git a/common/src/main/java/com/scalar/dl/ledger/error/CommonError.java b/common/src/main/java/com/scalar/dl/ledger/error/CommonError.java index f015bc24..43618cd4 100644 --- a/common/src/main/java/com/scalar/dl/ledger/error/CommonError.java +++ b/common/src/main/java/com/scalar/dl/ledger/error/CommonError.java @@ -214,6 +214,12 @@ public enum CommonError implements ScalarDlError { "", ""), + // + // Errors for SECRET_NOT_FOUND(415) + // + SECRET_NOT_FOUND( + StatusCode.SECRET_NOT_FOUND, "001", "The specified secret is not found.", "", ""), + // // Errors for DATABASE_ERROR(500) // diff --git a/common/src/main/java/com/scalar/dl/ledger/service/Constants.java b/common/src/main/java/com/scalar/dl/ledger/service/Constants.java deleted file mode 100644 index 12d88c06..00000000 --- a/common/src/main/java/com/scalar/dl/ledger/service/Constants.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.scalar.dl.ledger.service; - -public class Constants { - public static final String KEYS_JSON_NAME = "_keys"; -} diff --git a/common/src/main/java/com/scalar/dl/ledger/service/StatusCode.java b/common/src/main/java/com/scalar/dl/ledger/service/StatusCode.java index 0d7937b5..2c7090ad 100644 --- a/common/src/main/java/com/scalar/dl/ledger/service/StatusCode.java +++ b/common/src/main/java/com/scalar/dl/ledger/service/StatusCode.java @@ -126,6 +126,9 @@ public enum StatusCode { /** StatusCode: 414. This indicates that the argument is invalid. */ INVALID_ARGUMENT(414), + /** StatusCode: 415. This indicates that the given secret is not found. */ + SECRET_NOT_FOUND(415), + /** * StatusCode: 500. This indicates that the system encountered a database error such as IO error. */ diff --git a/ledger/src/integration-test/java/com/scalar/dl/ledger/service/LedgerServiceEndToEndTest.java b/ledger/src/integration-test/java/com/scalar/dl/ledger/service/LedgerServiceEndToEndTest.java index f3df6246..b2aab831 100644 --- a/ledger/src/integration-test/java/com/scalar/dl/ledger/service/LedgerServiceEndToEndTest.java +++ b/ledger/src/integration-test/java/com/scalar/dl/ledger/service/LedgerServiceEndToEndTest.java @@ -92,6 +92,7 @@ import com.scalar.dl.ledger.exception.ContractContextException; import com.scalar.dl.ledger.exception.LedgerException; import com.scalar.dl.ledger.exception.MissingContractException; +import com.scalar.dl.ledger.exception.MissingSecretException; import com.scalar.dl.ledger.exception.SignatureException; import com.scalar.dl.ledger.model.CertificateRegistrationRequest; import com.scalar.dl.ledger.model.ContractExecutionRequest; @@ -2495,7 +2496,7 @@ public void execute_HmacConfiguredAndValidHmacSignatureGiven_ShouldExecuteProper } @Test - public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProperly() { + public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldThrowSignatureException() { // Arrange Properties props2 = createProperties(); props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod()); @@ -2530,6 +2531,43 @@ public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProp assertThat(thrown).isExactlyInstanceOf(SignatureException.class); } + @Test + public void + execute_HmacConfiguredAndValidDigitalSignatureGiven_ShouldThrowMissingSecretException() { + // Arrange + Properties props2 = createProperties(); + props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod()); + props2.put(LedgerConfig.AUTHENTICATION_HMAC_CIPHER_KEY, SOME_CIPHER_KEY); + createServices(new LedgerConfig(props2)); + String nonce = UUID.randomUUID().toString(); + JsonNode contractArgument = + mapper + .createObjectNode() + .put(ASSET_ATTRIBUTE_NAME, SOME_ASSET_ID_1) + .put(AMOUNT_ATTRIBUTE_NAME, SOME_AMOUNT_1); + String argument = Argument.format(contractArgument, nonce, Collections.emptyList()); + + byte[] serialized = + ContractExecutionRequest.serialize(CREATE_CONTRACT_ID1, argument, ENTITY_ID_A, KEY_VERSION); + ContractExecutionRequest request = + new ContractExecutionRequest( + nonce, + ENTITY_ID_A, + KEY_VERSION, + CREATE_CONTRACT_ID1, + argument, + Collections.emptyList(), + null, + dsSigner1.sign(serialized), + null); + + // Act + Throwable thrown = catchThrowable(() -> ledgerService.execute(request)); + + // Assert + assertThat(thrown).isExactlyInstanceOf(MissingSecretException.class); + } + @Test public void execute_AuditorEnabledAndValidAuditorHmacSignatureGiven_ShouldExecuteProperly() { // Arrange diff --git a/ledger/src/main/java/com/scalar/dl/ledger/crypto/SecretManager.java b/ledger/src/main/java/com/scalar/dl/ledger/crypto/SecretManager.java index 93068f88..fb4d7d4e 100644 --- a/ledger/src/main/java/com/scalar/dl/ledger/crypto/SecretManager.java +++ b/ledger/src/main/java/com/scalar/dl/ledger/crypto/SecretManager.java @@ -9,6 +9,7 @@ import com.scalar.dl.ledger.database.SecretRegistry; import com.scalar.dl.ledger.error.CommonError; import com.scalar.dl.ledger.exception.DatabaseException; +import com.scalar.dl.ledger.exception.MissingSecretException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import javax.annotation.concurrent.Immutable; @@ -56,11 +57,12 @@ public SecretManager( * @param entry a {@code SecretEntry} */ public void register(SecretEntry entry) { - SecretEntry existing = registry.lookup(entry.getKey()); - if (existing != null) { + try { + registry.lookup(entry.getKey()); throw new DatabaseException(CommonError.SECRET_ALREADY_REGISTERED); + } catch (MissingSecretException e) { + registry.bind(entry); } - registry.bind(entry); } /** diff --git a/ledger/src/main/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistry.java b/ledger/src/main/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistry.java index 4571c27c..cfe5750c 100644 --- a/ledger/src/main/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistry.java +++ b/ledger/src/main/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistry.java @@ -17,10 +17,10 @@ import com.scalar.dl.ledger.database.SecretRegistry; import com.scalar.dl.ledger.error.CommonError; import com.scalar.dl.ledger.exception.DatabaseException; +import com.scalar.dl.ledger.exception.MissingSecretException; import com.scalar.dl.ledger.exception.UnexpectedValueException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.nio.charset.StandardCharsets; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @Immutable @@ -72,7 +72,6 @@ public void unbind(SecretEntry.Key key) { } @Override - @Nullable public SecretEntry lookup(SecretEntry.Key key) { Get get = new Get( @@ -81,11 +80,17 @@ public SecretEntry lookup(SecretEntry.Key key) { .withConsistency(Consistency.SEQUENTIAL) .forTable(TABLE); + Result result; try { - return storage.get(get).map(this::toSecretEntry).orElse(null); + result = + storage + .get(get) + .orElseThrow(() -> new MissingSecretException(CommonError.SECRET_NOT_FOUND)); } catch (ExecutionException e) { throw new DatabaseException(CommonError.GETTING_SECRET_KEY_FAILED, e, e.getMessage()); } + + return toSecretEntry(result); } private SecretEntry toSecretEntry(Result result) { diff --git a/ledger/src/main/java/com/scalar/dl/ledger/exception/MissingSecretException.java b/ledger/src/main/java/com/scalar/dl/ledger/exception/MissingSecretException.java new file mode 100644 index 00000000..8b0a7aeb --- /dev/null +++ b/ledger/src/main/java/com/scalar/dl/ledger/exception/MissingSecretException.java @@ -0,0 +1,19 @@ +package com.scalar.dl.ledger.exception; + +import com.scalar.dl.ledger.error.ScalarDlError; +import com.scalar.dl.ledger.service.StatusCode; + +public class MissingSecretException extends DatabaseException { + + public MissingSecretException(String message) { + super(message, StatusCode.SECRET_NOT_FOUND); + } + + public MissingSecretException(String message, Throwable cause) { + super(message, cause, StatusCode.SECRET_NOT_FOUND); + } + + public MissingSecretException(ScalarDlError error, Object... args) { + super(error.buildMessage(args), error.getStatusCode()); + } +} diff --git a/ledger/src/test/java/com/scalar/dl/ledger/crypto/SecretManagerTest.java b/ledger/src/test/java/com/scalar/dl/ledger/crypto/SecretManagerTest.java index 49f2f7fa..194a3d41 100644 --- a/ledger/src/test/java/com/scalar/dl/ledger/crypto/SecretManagerTest.java +++ b/ledger/src/test/java/com/scalar/dl/ledger/crypto/SecretManagerTest.java @@ -13,6 +13,7 @@ import com.google.common.cache.CacheBuilder; import com.scalar.dl.ledger.database.SecretRegistry; import com.scalar.dl.ledger.exception.DatabaseException; +import com.scalar.dl.ledger.exception.MissingSecretException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -43,7 +44,8 @@ public void setUp() { @Test public void register_ProperSecretEntryGiven_ShouldCallBind() { // Arrange - when(registry.lookup(entry.getKey())).thenReturn(null); + MissingSecretException toThrow = mock(MissingSecretException.class); + when(registry.lookup(entry.getKey())).thenThrow(toThrow); // Act manager.register(entry); diff --git a/ledger/src/test/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistryTest.java b/ledger/src/test/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistryTest.java index fb9619cc..46bb36c6 100644 --- a/ledger/src/test/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistryTest.java +++ b/ledger/src/test/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistryTest.java @@ -22,6 +22,7 @@ import com.scalar.dl.ledger.crypto.Cipher; import com.scalar.dl.ledger.crypto.SecretEntry; import com.scalar.dl.ledger.exception.DatabaseException; +import com.scalar.dl.ledger.exception.MissingSecretException; import java.nio.charset.StandardCharsets; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -153,18 +154,17 @@ public void lookup_ValidArgumentGiven_ShouldLookupProperly() throws ExecutionExc } @Test - public void lookup_ValidArgumentGivenButEmptyResultReturned_ShouldReturnNull() + public void lookup_ValidArgumentGivenButEmptyResultReturned_ShouldThrowMissingSecretException() throws ExecutionException { // Arrange SecretEntry.Key key = new SecretEntry.Key(SOME_ENTITY_ID, SOME_KEY_VERSION); when(storage.get(any(Get.class))).thenReturn(Optional.empty()); // Act Assert - SecretEntry actual = registry.lookup(key); + assertThatThrownBy(() -> registry.lookup(key)).isInstanceOf(MissingSecretException.class); // Assert verify(storage).get(any(Get.class)); - assertThat(actual).isNull(); } @Test