From be4e99128c1ac62ecaad8969cce4ae84b381225d Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:12:57 +0000 Subject: [PATCH] feat: Add description, lastUpdatedTime, and lastUpdatedBy to secrets Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- .secrets.baseline | 8 +- .../internal/TokenPayloadValidator.java | 20 +- .../api/common/ServletErrorMessage.java | 3 +- .../resources/BaseResourceValidator.java | 46 +++ .../api/common/resources/Secret.java | 9 +- .../framework/api/common/BaseServletTest.java | 1 + .../src/main/resources/openapi.yaml | 18 +- .../api/resources/ResourcesServlet.java | 9 +- .../processors/GalasaPropertyProcessor.java | 2 +- .../processors/GalasaSecretProcessor.java | 39 ++- .../processors/IGalasaResourceProcessor.java | 3 +- .../api/resources/routes/ResourcesRoute.java | 28 +- .../api/resources/ResourcesServletTest.java | 30 +- .../resources/mocks/MockResourcesServlet.java | 8 + .../GalasaPropertyProcessorTest.java | 48 ++-- .../processors/GalasaSecretProcessorTest.java | 218 ++++++++++---- .../resources/routes/TestResourcesRoute.java | 72 +++-- .../framework/api/secrets/SecretsServlet.java | 11 +- .../internal/SecretRequestValidator.java | 15 +- .../UpdateSecretRequestValidator.java | 6 +- .../internal/routes/AbstractSecretsRoute.java | 43 ++- .../internal/routes/SecretDetailsRoute.java | 17 +- .../secrets/internal/routes/SecretsRoute.java | 13 +- .../api/secrets/internal/MockCredentials.java | 36 +++ .../internal/SecretDetailsRouteTest.java | 271 +++++++++++++----- .../secrets/internal/SecretsRouteTest.java | 224 +++++++++++---- .../secrets/internal/SecretsServletTest.java | 41 ++- .../api/secrets/mocks/MockSecretsServlet.java | 14 +- .../spi/creds/AbstractCredentials.java | 80 ++++++ .../framework/spi/creds/Credentials.java | 1 - .../framework/spi/creds/CredentialsToken.java | 5 +- .../spi/creds/CredentialsUsername.java | 4 +- .../creds/CredentialsUsernamePassword.java | 6 +- .../spi/creds/CredentialsUsernameToken.java | 6 +- .../framework/mocks/MockCredentials.java | 18 -- .../main/java/dev/galasa/ICredentials.java | 10 + 36 files changed, 1058 insertions(+), 325 deletions(-) create mode 100644 galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/BaseResourceValidator.java create mode 100644 galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/AbstractCredentials.java delete mode 100644 galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/mocks/MockCredentials.java diff --git a/.secrets.baseline b/.secrets.baseline index 750f2ecf9..2882e431f 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -119,7 +119,7 @@ "hashed_secret": "0ea7458942ab65e0a340cf4fd28ca00d93c494f3", "is_secret": false, "is_verified": false, - "line_number": 321, + "line_number": 710, "type": "Secret Keyword", "verified_result": null } @@ -129,7 +129,7 @@ "hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13", "is_secret": false, "is_verified": false, - "line_number": 285, + "line_number": 293, "type": "Secret Keyword", "verified_result": null }, @@ -137,7 +137,7 @@ "hashed_secret": "89e7fc0c50091804bfeb26cddefc0e701dd60fab", "is_secret": false, "is_verified": false, - "line_number": 724, + "line_number": 732, "type": "Secret Keyword", "verified_result": null } @@ -147,7 +147,7 @@ "hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13", "is_secret": false, "is_verified": false, - "line_number": 636, + "line_number": 670, "type": "Secret Keyword", "verified_result": null } diff --git a/galasa-parent/dev.galasa.framework.api.authentication/src/main/java/dev/galasa/framework/api/authentication/internal/TokenPayloadValidator.java b/galasa-parent/dev.galasa.framework.api.authentication/src/main/java/dev/galasa/framework/api/authentication/internal/TokenPayloadValidator.java index 1b0fb6763..3ddf109e9 100644 --- a/galasa-parent/dev.galasa.framework.api.authentication/src/main/java/dev/galasa/framework/api/authentication/internal/TokenPayloadValidator.java +++ b/galasa-parent/dev.galasa.framework.api.authentication/src/main/java/dev/galasa/framework/api/authentication/internal/TokenPayloadValidator.java @@ -9,12 +9,13 @@ import dev.galasa.framework.api.common.IBeanValidator; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.ServletError; +import dev.galasa.framework.api.common.resources.BaseResourceValidator; import static dev.galasa.framework.api.common.ServletErrorMessage.*; import javax.servlet.http.HttpServletResponse; -public class TokenPayloadValidator implements IBeanValidator { +public class TokenPayloadValidator extends BaseResourceValidator implements IBeanValidator { @Override public void validate(TokenPayload tokenPayload) throws InternalServletException { @@ -36,21 +37,4 @@ public void validate(TokenPayload tokenPayload) throws InternalServletException throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); } } - - /** - * Checks whether a given string contains only alphanumeric characters, '-', and '_' - * - * @param str the string to validate - * @return true if the string contains only alphanumeric characters, '-', and '_', or false otherwise - */ - private boolean isAlphanumWithDashes(String str) { - boolean isValid = true; - for (char c : str.toCharArray()) { - if (!Character.isLetterOrDigit(c) && c != '-' && c != '_') { - isValid = false; - break; - } - } - return isValid; - } } diff --git a/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/ServletErrorMessage.java b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/ServletErrorMessage.java index 4a59d2913..86e6b4fe3 100644 --- a/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/ServletErrorMessage.java +++ b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/ServletErrorMessage.java @@ -128,7 +128,7 @@ public enum ServletErrorMessage { GAL5082_NO_LOGINID_PARAM_PROVIDED (5082, "E: A request to get the user details failed. The request did not supply a ‘loginId’ filter. A ‘loginId’ query parameter with a value of : ‘me’ was expected. This problem is caused by the client program sending a bad request. Please report this problem to the owner of your client program."), // Secrets APIs... - GAL5092_INVALID_SECRET_NAME_PROVIDED (5092, "E: Invalid secret name provided. The name of a Galasa secret cannot be empty or contain only spaces or tabs. Check your request payload and try again."), + GAL5092_INVALID_SECRET_NAME_PROVIDED (5092, "E: Invalid secret name provided. The name of a Galasa secret cannot be empty or contain only spaces or tabs, and must only contain characters in the Latin-1 character set. Check your request payload and try again."), GAL5093_ERROR_SECRET_NOT_FOUND (5093, "E: Unable to retrieve a secret with the given name. No such secret exists. Check your request query parameters and try again."), GAL5094_FAILED_TO_GET_SECRET_FROM_CREDS (5094, "E: Failed to retrieve a secret with the given name from the credentials store. The credentials store might be badly configured or could be experiencing a temporary issue. Report the problem to your Galasa Ecosystem owner."), GAL5095_ERROR_PASSWORD_AND_TOKEN_PROVIDED (5095, "E: Invalid secret payload provided. The ''password'' and ''token'' fields are mutually exclusive and cannot be provided in the same secret. Check your request payload and try again."), @@ -138,6 +138,7 @@ public enum ServletErrorMessage { GAL5099_ERROR_MISSING_REQUIRED_SECRET_FIELD (5099, "E: Invalid secret payload provided. The ''{0}'' type was provided but the required ''{1}'' field was missing. Check your request payload and try again."), GAL5100_ERROR_UNEXPECTED_SECRET_FIELD_PROVIDED (5100, "E: Invalid secret payload provided. An unexpected field was given to update a ''{0}'' secret. Only the following fields can be provided to update this secret: ''{1}''. Check your request payload and try again."), GAL5101_ERROR_UNEXPECTED_SECRET_TYPE_DETECTED (5101, "E: Unknown secret type detected. A secret retrieved from the credentials store is in an unknown or unsupported format. Report the problem to your Galasa Ecosystem owner."), + GAL5102_INVALID_SECRET_DESCRIPTION_PROVIDED (5102, "E: Invalid secret description provided. The description should not only contain spaces or tabs. When provided, it must contain characters in the Latin-1 character set. Report the problem to your Galasa Ecosystem owner."), ; diff --git a/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/BaseResourceValidator.java b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/BaseResourceValidator.java new file mode 100644 index 000000000..cab2bc07b --- /dev/null +++ b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/BaseResourceValidator.java @@ -0,0 +1,46 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.framework.api.common.resources; + +/** + * A base validator class that contains commonly-used validation methods + */ +public class BaseResourceValidator { + + /** + * Checks whether a given string is in valid Latin-1 format (e.g. characters in the range 0 - 255) + * + * @param str the string to validate + * @return true if the string is in valid Latin-1 format, or false otherwise + */ + public boolean isLatin1(String str) { + boolean isValidLatin1 = true; + for (char i = 0; i < str.length(); i++) { + if (str.charAt(i) > 255) { + isValidLatin1 = false; + break; + } + } + return isValidLatin1; + } + + /** + * Checks whether a given string contains only alphanumeric characters, '-', and '_' + * + * @param str the string to validate + * @return true if the string contains only alphanumeric characters, '-', and '_', or false otherwise + */ + public boolean isAlphanumWithDashes(String str) { + boolean isValid = true; + for (char c : str.toCharArray()) { + if (!Character.isLetterOrDigit(c) && c != '-' && c != '_') { + isValid = false; + break; + } + } + return isValid; + } +} diff --git a/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/Secret.java b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/Secret.java index 90f7d5888..272e1e8fe 100644 --- a/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/Secret.java +++ b/galasa-parent/dev.galasa.framework.api.common/src/main/java/dev/galasa/framework/api/common/resources/Secret.java @@ -10,6 +10,7 @@ import dev.galasa.framework.api.common.ServletError; import dev.galasa.framework.spi.creds.CredentialsException; import dev.galasa.framework.spi.creds.ICredentialsService; +import dev.galasa.framework.spi.utils.ITimeService; import static dev.galasa.framework.api.common.ServletErrorMessage.*; @@ -19,11 +20,13 @@ public class Secret { private String secretId; private ICredentialsService credentialsService; + private ITimeService timeService; private ICredentials value; - public Secret(ICredentialsService credentialsService, String secretName) { + public Secret(ICredentialsService credentialsService, String secretName, ITimeService timeService) { this.secretId = secretName; this.credentialsService = credentialsService; + this.timeService = timeService; } public boolean existsInCredentialsStore() { @@ -39,8 +42,10 @@ public void loadValueFromCredentialsStore() throws InternalServletException { } } - public void setSecretToCredentialsStore(ICredentials newValue) throws InternalServletException { + public void setSecretToCredentialsStore(ICredentials newValue, String username) throws InternalServletException { try { + newValue.setLastUpdatedTime(timeService.now()); + newValue.setLastUpdatedByUser(username); credentialsService.setCredentials(secretId, newValue); } catch (CredentialsException e) { ServletError error = new ServletError(GAL5077_FAILED_TO_SET_SECRET); diff --git a/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/BaseServletTest.java b/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/BaseServletTest.java index 93b8163d7..b37ba0e26 100644 --- a/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/BaseServletTest.java +++ b/galasa-parent/dev.galasa.framework.api.common/src/testFixtures/java/dev/galasa/framework/api/common/BaseServletTest.java @@ -28,6 +28,7 @@ public class BaseServletTest { // "iat": 1516239022 // } public static final String DUMMY_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0UmVxdWVzdG9yIiwic3ViIjoicmVxdWVzdG9ySWQiLCJuYW1lIjoiSmFjayBTa2VsbGluZ3RvbiIsImlhdCI6MTUxNjIzOTAyMn0.kW1arFknbywrtRrxsLjB2MiXcM6oSgnUrOpuAlE5dhk"; //Dummy JWT + public static final String JWT_USERNAME = "testRequestor"; protected static final GalasaGson gson = new GalasaGson(); diff --git a/galasa-parent/dev.galasa.framework.api.openapi/src/main/resources/openapi.yaml b/galasa-parent/dev.galasa.framework.api.openapi/src/main/resources/openapi.yaml index 07d2372c8..adf6411ff 100644 --- a/galasa-parent/dev.galasa.framework.api.openapi/src/main/resources/openapi.yaml +++ b/galasa-parent/dev.galasa.framework.api.openapi/src/main/resources/openapi.yaml @@ -2042,7 +2042,20 @@ components: name: type: string description: | - The name of the Galasa Secret to perform a resource action on. + The name that identifies the Galasa Secret. + description: + type: string + description: | + The description to be associated with the Galasa Secret. + lastUpdatedTime: + type: string + format: date-time + description: | + The timestamp at which the Galasa Secret was last updated. + lastUpdatedBy: + type: string + description: | + The ID of the last user that updated the Galasa Secret. encoding: type: string description: | @@ -2109,6 +2122,9 @@ components: name: type: string description: The name of the secret to create or update + description: + type: string + description: The description to associate with the secret to create or update type: type: string description: The type of the secret to create or update diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/ResourcesServlet.java b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/ResourcesServlet.java index 645687562..c22bc2833 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/ResourcesServlet.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/ResourcesServlet.java @@ -18,11 +18,15 @@ import dev.galasa.framework.FileSystem; import dev.galasa.framework.IFileSystem; import dev.galasa.framework.api.common.BaseServlet; +import dev.galasa.framework.api.common.Environment; +import dev.galasa.framework.api.common.SystemEnvironment; import dev.galasa.framework.api.common.resources.CPSFacade; import dev.galasa.framework.api.resources.routes.ResourcesRoute; import dev.galasa.framework.spi.ConfigurationPropertyStoreException; import dev.galasa.framework.spi.IFramework; import dev.galasa.framework.spi.creds.CredentialsException; +import dev.galasa.framework.spi.utils.ITimeService; +import dev.galasa.framework.spi.utils.SystemTimeService; /* * Proxy Servlet for the /resources/* endpoints */ @@ -38,6 +42,9 @@ public class ResourcesServlet extends BaseServlet { protected Log logger = LogFactory.getLog(this.getClass()); protected IFileSystem fileSystem = new FileSystem(); + + protected ITimeService timeService = new SystemTimeService(); + protected Environment env = new SystemEnvironment(); protected IFramework getFramework() { return this.framework; @@ -54,7 +61,7 @@ public void init() throws ServletException { super.init(); try { - addRoute(new ResourcesRoute(getResponseBuilder(), new CPSFacade(framework), framework.getCredentialsService())); + addRoute(new ResourcesRoute(getResponseBuilder(), new CPSFacade(framework), framework.getCredentialsService(), timeService, env)); } catch (ConfigurationPropertyStoreException | CredentialsException e) { logger.error("Failed to initialise the Resources servlet", e); throw new ServletException("Failed to initialise the Resources servlet", e); diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessor.java b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessor.java index ea93ab5ce..17bfe23b6 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessor.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessor.java @@ -36,7 +36,7 @@ public GalasaPropertyProcessor(CPSFacade cps) { } @Override - public List processResource(JsonObject resource, ResourceAction action) throws InternalServletException { + public List processResource(JsonObject resource, ResourceAction action, String username) throws InternalServletException { List errors = checkGalasaPropertyJsonStructure(resource, action); try { if (errors.isEmpty()) { diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessor.java b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessor.java index 068a8efc9..11a36c8d6 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessor.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessor.java @@ -26,8 +26,10 @@ import dev.galasa.ICredentials; import dev.galasa.framework.api.beans.generated.GalasaSecret; import dev.galasa.framework.api.beans.generated.GalasaSecretdata; +import dev.galasa.framework.api.beans.generated.GalasaSecretmetadata; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.ServletError; +import dev.galasa.framework.api.common.resources.BaseResourceValidator; import dev.galasa.framework.api.common.resources.GalasaSecretType; import dev.galasa.framework.api.common.resources.ResourceAction; import dev.galasa.framework.api.common.resources.Secret; @@ -36,6 +38,7 @@ import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; import dev.galasa.framework.spi.creds.CredentialsUsernameToken; import dev.galasa.framework.spi.creds.ICredentialsService; +import dev.galasa.framework.spi.utils.ITimeService; /** * Processor class to handle creating, updating, and deleting GalasaSecret resources @@ -47,20 +50,24 @@ public class GalasaSecretProcessor extends AbstractGalasaResourceProcessor imple private static final List SUPPORTED_ENCODING_SCHEMES = List.of("base64"); private ICredentialsService credentialsService; + private ITimeService timeService; - public GalasaSecretProcessor(ICredentialsService credentialsService) { + private BaseResourceValidator validator = new BaseResourceValidator(); + + public GalasaSecretProcessor(ICredentialsService credentialsService, ITimeService timeService) { this.credentialsService = credentialsService; + this.timeService = timeService; } @Override - public List processResource(JsonObject resourceJson, ResourceAction action) throws InternalServletException { + public List processResource(JsonObject resourceJson, ResourceAction action, String username) throws InternalServletException { logger.info("Processing GalasaSecret resource"); List errors = checkGalasaSecretJsonStructure(resourceJson, action); if (errors.isEmpty()) { logger.info("GalasaSecret validated successfully"); GalasaSecret galasaSecret = gson.fromJson(resourceJson, GalasaSecret.class); String credentialsId = galasaSecret.getmetadata().getname(); - Secret secret = new Secret(credentialsService, credentialsId); + Secret secret = new Secret(credentialsService, credentialsId, timeService); if (action == DELETE) { logger.info("Deleting secret from credentials store"); @@ -77,12 +84,13 @@ public List processResource(JsonObject resourceJson, ResourceAction acti throw new InternalServletException(error, HttpServletResponse.SC_NOT_FOUND); } - GalasaSecretType secretType = GalasaSecretType.getFromString(galasaSecret.getmetadata().gettype().toString()); + GalasaSecretmetadata metadata = galasaSecret.getmetadata(); + GalasaSecretType secretType = GalasaSecretType.getFromString(metadata.gettype().toString()); GalasaSecretdata decodedData = decodeSecretData(galasaSecret); - ICredentials credentials = getCredentialsFromSecret(secretType, decodedData); + ICredentials credentials = getCredentialsFromSecret(secretType, decodedData, metadata); logger.info("Setting secret in credentials store"); - secret.setSecretToCredentialsStore(credentials); + secret.setSecretToCredentialsStore(credentials, username); logger.info("Secret set in credentials store OK"); } logger.info("Processed GalasaSecret resource OK"); @@ -140,7 +148,11 @@ private List checkGalasaSecretJsonStructure(JsonObject secretJson, Resou return validationErrors; } - private ICredentials getCredentialsFromSecret(GalasaSecretType secretType, GalasaSecretdata decodedData) { + private ICredentials getCredentialsFromSecret( + GalasaSecretType secretType, + GalasaSecretdata decodedData, + GalasaSecretmetadata metadata + ) { ICredentials credentials = null; switch (secretType) { case USERNAME: @@ -158,6 +170,10 @@ private ICredentials getCredentialsFromSecret(GalasaSecretType secretType, Galas default: break; } + + if (credentials != null) { + credentials.setDescription(metadata.getdescription()); + } return credentials; } @@ -170,6 +186,15 @@ private void validateSecretMetadata(JsonObject secretJson, List validati validationErrors.add(new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST).getMessage()); } + // If a description is provided, check that it is valid + if (metadata.has("description")) { + String description = metadata.get("description").getAsString(); + if (description.isBlank() || !validator.isLatin1(description)) { + ServletError error = new ServletError(GAL5102_INVALID_SECRET_DESCRIPTION_PROVIDED); + validationErrors.add(new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST).getMessage()); + } + } + // Check if the given secret type is a valid type if (metadata.has("type")) { GalasaSecretType secretType = GalasaSecretType.getFromString(metadata.get("type").getAsString()); diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/IGalasaResourceProcessor.java b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/IGalasaResourceProcessor.java index 5679b1c34..58a0b401e 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/IGalasaResourceProcessor.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/processors/IGalasaResourceProcessor.java @@ -19,8 +19,9 @@ public interface IGalasaResourceProcessor { * * @param resourceJson the resource to perform an action on * @param action the action to perform + * @param username the username of the user performing the action * @return a list of validation errors encountered when processing the given JSON payload * @throws InternalServletException if there was an issue processing the resource */ - List processResource(JsonObject resourceJson, ResourceAction action) throws InternalServletException; + List processResource(JsonObject resourceJson, ResourceAction action, String username) throws InternalServletException; } diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/routes/ResourcesRoute.java b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/routes/ResourcesRoute.java index ee5e6ebb5..89442da82 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/routes/ResourcesRoute.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/main/java/dev/galasa/framework/api/resources/routes/ResourcesRoute.java @@ -23,7 +23,9 @@ import com.google.gson.JsonObject; import dev.galasa.framework.api.common.BaseRoute; +import dev.galasa.framework.api.common.Environment; import dev.galasa.framework.api.common.InternalServletException; +import dev.galasa.framework.api.common.JwtWrapper; import dev.galasa.framework.api.common.QueryParameters; import dev.galasa.framework.api.common.ResponseBuilder; import dev.galasa.framework.api.common.ServletError; @@ -37,6 +39,7 @@ import dev.galasa.framework.spi.FrameworkException; import dev.galasa.framework.spi.creds.ICredentialsService; import dev.galasa.framework.spi.utils.GalasaGson; +import dev.galasa.framework.spi.utils.ITimeService; public class ResourcesRoute extends BaseRoute{ @@ -50,11 +53,20 @@ public class ResourcesRoute extends BaseRoute{ protected List errors = new ArrayList(); - public ResourcesRoute(ResponseBuilder responseBuilder, CPSFacade cps, ICredentialsService credentialsService) { + private Environment env; + + public ResourcesRoute( + ResponseBuilder responseBuilder, + CPSFacade cps, + ICredentialsService credentialsService, + ITimeService timeService, + Environment env + ) { super(responseBuilder, path); + this.env = env; resourceProcessors.put(GALASA_PROPERTY, new GalasaPropertyProcessor(cps)); - resourceProcessors.put(GALASA_SECRET, new GalasaSecretProcessor(credentialsService)); + resourceProcessors.put(GALASA_SECRET, new GalasaSecretProcessor(credentialsService, timeService)); } @Override @@ -63,7 +75,9 @@ public HttpServletResponse handlePostRequest(String pathInfo, QueryParameters qu logger.info("ResourcesRoute - handlePostRequest() entered"); JsonObject jsonBody = parseRequestBody(request, JsonObject.class); - List errorsList = processRequest(jsonBody); + + String requestUsername = new JwtWrapper(request, env).getUsername(); + List errorsList = processRequest(jsonBody, requestUsername); if (errorsList.size() >0){ response = getResponseBuilder().buildResponse(request, response, "application/json", getErrorsAsJson(errorsList), HttpServletResponse.SC_BAD_REQUEST); } else { @@ -76,12 +90,12 @@ public HttpServletResponse handlePostRequest(String pathInfo, QueryParameters qu } - protected List processRequest(JsonObject body) throws InternalServletException{ + protected List processRequest(JsonObject body, String username) throws InternalServletException{ String actionStr = body.get("action").getAsString().toLowerCase().trim(); ResourceAction action = ResourceAction.getFromString(actionStr); if (action != null){ JsonArray jsonArray = body.get("data").getAsJsonArray(); - processDataArray(jsonArray, action); + processDataArray(jsonArray, action, username); } else { ServletError error = new ServletError(GAL5025_UNSUPPORTED_ACTION); throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); @@ -105,7 +119,7 @@ protected String getErrorsAsJson(List errorsList){ return gson.toJson(json); } - protected void processDataArray(JsonArray jsonArray, ResourceAction action) throws InternalServletException{ + protected void processDataArray(JsonArray jsonArray, ResourceAction action, String username) throws InternalServletException{ for (JsonElement element: jsonArray) { try { checkJsonElementIsValidJSON(element); @@ -118,7 +132,7 @@ protected void processDataArray(JsonArray jsonArray, ResourceAction action) thro throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); } - errors.addAll(resourceProcessors.get(kind).processResource(resource, action)); + errors.addAll(resourceProcessors.get(kind).processResource(resource, action, username)); } catch (InternalServletException s) { errors.add(s.getMessage()); diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/ResourcesServletTest.java b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/ResourcesServletTest.java index 3d306a87c..a267ce0bf 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/ResourcesServletTest.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/ResourcesServletTest.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.*; import java.io.PrintWriter; +import java.time.Instant; import java.util.List; import java.util.Map; @@ -21,6 +22,7 @@ import com.google.gson.JsonObject; import dev.galasa.framework.api.common.BaseServletTest; +import dev.galasa.framework.api.common.EnvironmentVariables; import dev.galasa.framework.api.common.ResponseBuilder; import dev.galasa.framework.api.common.mocks.MockEnvironment; import dev.galasa.framework.api.common.mocks.MockFramework; @@ -28,7 +30,7 @@ import dev.galasa.framework.api.common.mocks.MockHttpServletResponse; import dev.galasa.framework.api.common.mocks.MockIConfigurationPropertyStoreService; import dev.galasa.framework.api.common.mocks.MockServletOutputStream; - +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.resources.mocks.MockResourcesServlet; import dev.galasa.framework.spi.ConfigurationPropertyStoreException; import dev.galasa.framework.spi.IConfigurationPropertyStoreService; @@ -43,6 +45,8 @@ public class ResourcesServletTest extends BaseServletTest { HttpServletRequest req; HttpServletResponse resp; + private Map headers = Map.of("Authorization", "Bearer " + BaseServletTest.DUMMY_JWT); + private class MockICPSServiceWithError extends MockIConfigurationPropertyStoreService { protected MockICPSServiceWithError(String namespace){ super.namespaceInput= namespace; @@ -55,13 +59,6 @@ public void deleteProperty(@NotNull String name) throws ConfigurationPropertySto } protected void setServlet(String namespace){ - this.servlet = new MockResourcesServlet(); - servlet.setResponseBuilder(new ResponseBuilder(new MockEnvironment())); - - ServletOutputStream outStream = new MockServletOutputStream(); - PrintWriter writer = new PrintWriter(outStream); - this.resp = new MockHttpServletResponse(writer, outStream); - IConfigurationPropertyStoreService cpsstore; if (namespace != null){ cpsstore = new MockIConfigurationPropertyStoreService(namespace); @@ -69,17 +66,28 @@ protected void setServlet(String namespace){ cpsstore = new MockICPSServiceWithError("framework"); } IFramework framework = new MockFramework(cpsstore); - this.servlet.setFramework(framework); + + MockEnvironment env = new MockEnvironment(); + env.setenv(EnvironmentVariables.GALASA_USERNAME_CLAIMS, "preferred_username"); + + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + this.servlet = new MockResourcesServlet(framework, env, timeService); + + servlet.setResponseBuilder(new ResponseBuilder(env)); + + ServletOutputStream outStream = new MockServletOutputStream(); + PrintWriter writer = new PrintWriter(outStream); + this.resp = new MockHttpServletResponse(writer, outStream); } protected void setServlet(String path,String namespace, Map parameterMap){ setServlet(namespace); - this.req = new MockHttpServletRequest(parameterMap,path); + this.req = new MockHttpServletRequest(parameterMap, path, headers); } protected void setServlet( String path,String namespace, JsonObject requestBody, String method){ setServlet(namespace); - this.req = new MockHttpServletRequest(path, gson.toJson(requestBody), method); + this.req = new MockHttpServletRequest(path, gson.toJson(requestBody), method, headers); } protected void setServlet( String path,String namespace, JsonObject requestBody, String method, Map headerMap) { diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/mocks/MockResourcesServlet.java b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/mocks/MockResourcesServlet.java index 89f5c63f5..29e1a5ff0 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/mocks/MockResourcesServlet.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/mocks/MockResourcesServlet.java @@ -7,11 +7,19 @@ import dev.galasa.framework.IFileSystem; import dev.galasa.framework.api.common.mocks.IServletUnderTest; +import dev.galasa.framework.api.common.mocks.MockEnvironment; +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.resources.ResourcesServlet; import dev.galasa.framework.spi.IFramework; public class MockResourcesServlet extends ResourcesServlet implements IServletUnderTest{ + public MockResourcesServlet(IFramework framework, MockEnvironment env, MockTimeService timeService) { + this.framework = framework; + this.env = env; + this.timeService = timeService; + } + @Override public void setFramework(IFramework framework) { super.setFramework(framework); diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessorTest.java b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessorTest.java index 363d33989..8105f0e9d 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessorTest.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaPropertyProcessorTest.java @@ -24,6 +24,7 @@ public class GalasaPropertyProcessorTest extends ResourcesServletTest { @Test public void testProcessGalasaPropertyValidPropertyReturnsOK() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "myvalue"; @@ -34,7 +35,7 @@ public void testProcessGalasaPropertyValidPropertyReturnsOK() throws Exception { JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - propertyProcessor.processResource(propertyJson, APPLY); + propertyProcessor.processResource(propertyJson, APPLY, username); //Then... checkPropertyInNamespace(namespace,propertyname,value); @@ -43,6 +44,7 @@ public void testProcessGalasaPropertyValidPropertyReturnsOK() throws Exception { @Test public void testProcessGalasaPropertyPropertyWithNewNamespaceReturnsOK() throws Exception { //Given... + String username = "myuser"; String namespace = "newnamespace"; String propertyname = "property.name"; String value = "myvalue"; @@ -53,7 +55,7 @@ public void testProcessGalasaPropertyPropertyWithNewNamespaceReturnsOK() throws JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - propertyProcessor.processResource(propertyJson, APPLY); + propertyProcessor.processResource(propertyJson, APPLY, username); //Then... checkPropertyInNamespace(namespace,propertyname,value); @@ -62,6 +64,7 @@ public void testProcessGalasaPropertyPropertyWithNewNamespaceReturnsOK() throws @Test public void testProcessGalasaPropertyInvalidPropertyNameReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property1!"; String value = "myvalue"; @@ -72,7 +75,7 @@ public void testProcessGalasaPropertyInvalidPropertyNameReturnsError() throws Ex JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... @@ -85,6 +88,7 @@ public void testProcessGalasaPropertyInvalidPropertyNameReturnsError() throws Ex @Test public void testProcessGalasaPropertyPropertyNameWithTrailingDotReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name."; String value = "myvalue"; @@ -95,7 +99,7 @@ public void testProcessGalasaPropertyPropertyNameWithTrailingDotReturnsError() t JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -107,6 +111,7 @@ public void testProcessGalasaPropertyPropertyNameWithTrailingDotReturnsError() t @Test public void testProcessGalasaPropertyPropertyNameWithLeadingDotReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = ".property.name"; String value = "myvalue"; @@ -117,7 +122,7 @@ public void testProcessGalasaPropertyPropertyNameWithLeadingDotReturnsError() th JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -129,6 +134,7 @@ public void testProcessGalasaPropertyPropertyNameWithLeadingDotReturnsError() th @Test public void testProcessGalasaPropertyBadPropertyNameReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property"; String value = "myvalue"; @@ -139,7 +145,7 @@ public void testProcessGalasaPropertyBadPropertyNameReturnsError() throws Except JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -151,6 +157,7 @@ public void testProcessGalasaPropertyBadPropertyNameReturnsError() throws Except @Test public void testProcessGalasaPropertyMissingPropertyNameReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = ""; String value = "myvalue"; @@ -161,7 +168,7 @@ public void testProcessGalasaPropertyMissingPropertyNameReturnsError() throws Ex JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -173,6 +180,7 @@ public void testProcessGalasaPropertyMissingPropertyNameReturnsError() throws Ex @Test public void testProcessGalasaPropertyMissingPropertyNamespaceReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = ""; String propertyname = "property.name"; String value = "myvalue"; @@ -183,7 +191,7 @@ public void testProcessGalasaPropertyMissingPropertyNamespaceReturnsError() thro JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -195,6 +203,7 @@ public void testProcessGalasaPropertyMissingPropertyNamespaceReturnsError() thro @Test public void testProcessGalasaPropertyBadNamespaceReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "namespace@"; String propertyname = "property.name"; String value = "myvalue"; @@ -205,7 +214,7 @@ public void testProcessGalasaPropertyBadNamespaceReturnsError() throws Exception JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -217,6 +226,7 @@ public void testProcessGalasaPropertyBadNamespaceReturnsError() throws Exception @Test public void testProcessGalasaPropertyNamespaceWithTrailingDotReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "namespace."; String propertyname = "property.name"; String value = "myvalue"; @@ -227,7 +237,7 @@ public void testProcessGalasaPropertyNamespaceWithTrailingDotReturnsError() thro JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -239,6 +249,7 @@ public void testProcessGalasaPropertyNamespaceWithTrailingDotReturnsError() thro @Test public void testProcessGalasaPropertyNamespaceWithLeadingDotReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = ".namespace"; String propertyname = "property.name"; String value = "myvalue"; @@ -249,7 +260,7 @@ public void testProcessGalasaPropertyNamespaceWithLeadingDotReturnsError() throw JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -261,6 +272,7 @@ public void testProcessGalasaPropertyNamespaceWithLeadingDotReturnsError() throw @Test public void testProcessGalasaPropertyMissingPropertyValueReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = ""; @@ -271,7 +283,7 @@ public void testProcessGalasaPropertyMissingPropertyValueReturnsError() throws E JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -284,6 +296,7 @@ public void testProcessGalasaPropertyMissingPropertyValueReturnsError() throws E @Test public void testProcessGalasaPropertyEmptyFieldsReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = ""; String propertyname = ""; String value = ""; @@ -294,7 +307,7 @@ public void testProcessGalasaPropertyEmptyFieldsReturnsError() throws Exception JsonObject propertyJson = generatePropertyJson(namespace, propertyname, value, "galasa-dev/v1alpha1"); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -308,6 +321,7 @@ public void testProcessGalasaPropertyEmptyFieldsReturnsError() throws Exception @Test public void testProcessGalasaPropertyNoMetadataOrDataReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = ""; String propertyname = ""; String value = ""; @@ -319,7 +333,7 @@ public void testProcessGalasaPropertyNoMetadataOrDataReturnsError() throws Excep JsonObject propertyJson = JsonParser.parseString(jsonString).getAsJsonObject(); //When... - List errors = propertyProcessor.processResource(propertyJson, APPLY); + List errors = propertyProcessor.processResource(propertyJson, APPLY, username); //Then... assertThat(errors).isNotNull(); @@ -334,6 +348,7 @@ public void testProcessGalasaPropertyNoMetadataOrDataReturnsError() throws Excep @Test public void testProcessGalasaPropertyMissingApiVersionReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -345,7 +360,7 @@ public void testProcessGalasaPropertyMissingApiVersionReturnsError() throws Exce //When... Throwable thrown = catchThrowable(() -> { - propertyProcessor.processResource(propertyJson, APPLY); + propertyProcessor.processResource(propertyJson, APPLY, username); }); //Then... @@ -357,6 +372,7 @@ public void testProcessGalasaPropertyMissingApiVersionReturnsError() throws Exce @Test public void testProcessGalasaPropertyBadJsonReturnsError() throws Exception { //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -369,7 +385,7 @@ public void testProcessGalasaPropertyBadJsonReturnsError() throws Exception { //When... Throwable thrown = catchThrowable(() -> { - propertyProcessor.processResource(propertyJson, APPLY); + propertyProcessor.processResource(propertyJson, APPLY, username); }); //Then... diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessorTest.java b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessorTest.java index eb8767ded..4bbe382d3 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessorTest.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/processors/GalasaSecretProcessorTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.*; import static dev.galasa.framework.api.common.resources.ResourceAction.*; +import java.time.Instant; import java.util.Base64; import java.util.HashMap; import java.util.List; @@ -21,6 +22,7 @@ import dev.galasa.ICredentials; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.mocks.MockCredentialsService; +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.resources.ResourcesServletTest; import dev.galasa.framework.spi.creds.CredentialsToken; import dev.galasa.framework.spi.creds.CredentialsUsername; @@ -29,17 +31,29 @@ public class GalasaSecretProcessorTest extends ResourcesServletTest { - private JsonObject generateSecretJson(String secretName, String type, String encoding, String username, String password) { + private JsonObject generateSecretJson(String secretName, String type, String encoding, String username, String password) { return generateSecretJson(secretName, type, encoding, username, password, null); - } + } + + private JsonObject generateSecretJson( + String secretName, + String type, + String encoding, + String username, + String password, + String description + ) { + return generateSecretJson(secretName, type, encoding, username, password, null, description); + } - private JsonObject generateSecretJson( + private JsonObject generateSecretJson( String secretName, String type, String encoding, String username, String password, - String token + String token, + String description ) { JsonObject secretJson = new JsonObject(); secretJson.addProperty("apiVersion", "galasa-dev/v1alpha1"); @@ -49,6 +63,10 @@ private JsonObject generateSecretJson( secretMetadata.addProperty("name", secretName); secretMetadata.addProperty("type", type); + if (description != null) { + secretMetadata.addProperty("description", description); + } + if (encoding != null) { secretMetadata.addProperty("encoding", encoding); } @@ -82,14 +100,16 @@ private JsonObject generateSecretJson( // "username": "a-username" // } // } - return secretJson; - } + return secretJson; + } @Test public void testApplySecretWithMissingNameReturnsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -101,7 +121,7 @@ public void testApplySecretWithMissingNameReturnsError() throws Exception { secretJson.get("metadata").getAsJsonObject().remove("name"); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(1); @@ -113,8 +133,10 @@ public void testApplySecretWithMissingNameReturnsError() throws Exception { @Test public void testApplySecretWithMissingSecretTypeReturnsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -126,7 +148,7 @@ public void testApplySecretWithMissingSecretTypeReturnsError() throws Exception secretJson.get("metadata").getAsJsonObject().remove("type"); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(1); @@ -138,8 +160,10 @@ public void testApplySecretWithMissingSecretTypeReturnsError() throws Exception @Test public void testApplySecretWithMissingDataThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -152,7 +176,7 @@ public void testApplySecretWithMissingDataThrowsError() throws Exception { // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, APPLY); + secretProcessor.processResource(secretJson, APPLY, requestUsername); }, InternalServletException.class); // Then... @@ -165,8 +189,10 @@ public void testApplySecretWithMissingDataThrowsError() throws Exception { @Test public void testApplySecretWithMissingMetadataThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -179,7 +205,7 @@ public void testApplySecretWithMissingMetadataThrowsError() throws Exception { // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, APPLY); + secretProcessor.processResource(secretJson, APPLY, requestUsername); }, InternalServletException.class); // Then... @@ -192,8 +218,10 @@ public void testApplySecretWithMissingMetadataThrowsError() throws Exception { @Test public void testApplySecretWithMissingApiVersionThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -206,7 +234,7 @@ public void testApplySecretWithMissingApiVersionThrowsError() throws Exception { // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, APPLY); + secretProcessor.processResource(secretJson, APPLY, requestUsername); }, InternalServletException.class); // Then... @@ -219,8 +247,10 @@ public void testApplySecretWithMissingApiVersionThrowsError() throws Exception { @Test public void testApplySecretWithMissingUsernamePasswordFieldsReturnsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; @@ -229,7 +259,7 @@ public void testApplySecretWithMissingUsernamePasswordFieldsReturnsError() throw JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(1); @@ -241,8 +271,10 @@ public void testApplySecretWithMissingUsernamePasswordFieldsReturnsError() throw @Test public void testApplySecretWithUnsupportedEncodingReturnsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = "UNKNOWN!!!"; @@ -251,7 +283,7 @@ public void testApplySecretWithUnsupportedEncodingReturnsError() throws Exceptio JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(1); @@ -262,8 +294,10 @@ public void testApplySecretWithUnsupportedEncodingReturnsError() throws Exceptio @Test public void testApplySecretWithUnknownSecretTypeReturnsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UNKNOWN TYPE!"; String encoding = null; @@ -272,7 +306,7 @@ public void testApplySecretWithUnknownSecretTypeReturnsError() throws Exception JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(1); @@ -283,8 +317,10 @@ public void testApplySecretWithUnknownSecretTypeReturnsError() throws Exception @Test public void testApplySecretWithNoNameAndUnknownSecretTypeAndUnknownEncodingReturnsMultipleErrors() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UNKNOWN TYPE!"; String encoding = "UNKNOWN ENCODING!"; @@ -296,7 +332,7 @@ public void testApplySecretWithNoNameAndUnknownSecretTypeAndUnknownEncodingRetur secretJson.get("metadata").getAsJsonObject().remove("name"); // When... - List errors = secretProcessor.processResource(secretJson, APPLY); + List errors = secretProcessor.processResource(secretJson, APPLY, requestUsername); // Then... assertThat(errors).hasSize(3); @@ -312,17 +348,21 @@ public void testApplySecretWithNoNameAndUnknownSecretTypeAndUnknownEncodingRetur @Test public void testCreateUsernamePasswordSecretSetsCredentialsOk() throws Exception { // Given... + Instant lastUpdatedTime = Instant.EPOCH; + MockTimeService mockTimeService = new MockTimeService(lastUpdatedTime); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; String encoding = null; String username = "my-username"; String password = "a-password"; - JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password); + String description = "my new credentials"; + JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password, description); // When... - List errors = secretProcessor.processResource(secretJson, CREATE); + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -331,13 +371,18 @@ public void testCreateUsernamePasswordSecretSetsCredentialsOk() throws Exception assertThat(credentials).isNotNull(); assertThat(credentials.getUsername()).isEqualTo(username); assertThat(credentials.getPassword()).isEqualTo(password); + assertThat(credentials.getDescription()).isEqualTo(description); + assertThat(credentials.getLastUpdatedTime()).isEqualTo(lastUpdatedTime); + assertThat(credentials.getLastUpdatedByUser()).isEqualTo(requestUsername); } @Test public void testCreateEncodedUsernamePasswordSecretSetsCredentialsOk() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernamePassword"; @@ -352,7 +397,7 @@ public void testCreateEncodedUsernamePasswordSecretSetsCredentialsOk() throws Ex JsonObject secretJson = generateSecretJson(secretName, type, encoding, encodedUsername, encodedPassword); // When... - List errors = secretProcessor.processResource(secretJson, CREATE); + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -367,8 +412,10 @@ public void testCreateEncodedUsernamePasswordSecretSetsCredentialsOk() throws Ex @Test public void testCreateEncodedTokenSecretSetsCredentialsOk() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Token"; @@ -378,10 +425,10 @@ public void testCreateEncodedTokenSecretSetsCredentialsOk() throws Exception { String token = "my-token"; String encodedToken = encoder.encodeToString(token.getBytes()); - JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, encodedToken); + JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, encodedToken, null); // When... - List errors = secretProcessor.processResource(secretJson, CREATE); + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -395,8 +442,10 @@ public void testCreateEncodedTokenSecretSetsCredentialsOk() throws Exception { @Test public void testCreateEncodedUsernameTokenSecretSetsCredentialsOk() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "UsernameToken"; @@ -408,10 +457,10 @@ public void testCreateEncodedUsernameTokenSecretSetsCredentialsOk() throws Excep String encodedUsername = encoder.encodeToString(username.getBytes()); String encodedToken = encoder.encodeToString(token.getBytes()); - JsonObject secretJson = generateSecretJson(secretName, type, encoding, encodedUsername, null, encodedToken); + JsonObject secretJson = generateSecretJson(secretName, type, encoding, encodedUsername, null, encodedToken, null); // When... - List errors = secretProcessor.processResource(secretJson, CREATE); + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -426,8 +475,10 @@ public void testCreateEncodedUsernameTokenSecretSetsCredentialsOk() throws Excep @Test public void testCreateEncodedUsernameSecretSetsCredentialsOk() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Username"; @@ -440,7 +491,7 @@ public void testCreateEncodedUsernameSecretSetsCredentialsOk() throws Exception JsonObject secretJson = generateSecretJson(secretName, type, encoding, encodedUsername, null, null); // When... - List errors = secretProcessor.processResource(secretJson, CREATE); + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -454,6 +505,7 @@ public void testCreateEncodedUsernameSecretSetsCredentialsOk() throws Exception @Test public void testDeleteSecretDeletesCredentialsOk() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); String secretName = "ABC"; String username = "my-username"; Map existingCreds = new HashMap<>(); @@ -461,7 +513,8 @@ public void testDeleteSecretDeletesCredentialsOk() throws Exception { existingCreds.put("another-secret", new CredentialsUsername("another-username")); MockCredentialsService mockCreds = new MockCredentialsService(existingCreds); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String type = "Username"; String encoding = null; @@ -470,7 +523,7 @@ public void testDeleteSecretDeletesCredentialsOk() throws Exception { // When... assertThat(mockCreds.getAllCredentials()).hasSize(2); assertThat(mockCreds.getCredentials(secretName)).isNotNull(); - List errors = secretProcessor.processResource(secretJson, DELETE); + List errors = secretProcessor.processResource(secretJson, DELETE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -481,6 +534,7 @@ public void testDeleteSecretDeletesCredentialsOk() throws Exception { @Test public void testDeleteSecretDoesNotInsistOnData() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); String secretName = "ABC"; String username = "my-username"; Map existingCreds = new HashMap<>(); @@ -488,7 +542,8 @@ public void testDeleteSecretDoesNotInsistOnData() throws Exception { existingCreds.put("another-secret", new CredentialsUsername("another-username")); MockCredentialsService mockCreds = new MockCredentialsService(existingCreds); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String type = "Username"; String encoding = null; @@ -500,7 +555,7 @@ public void testDeleteSecretDoesNotInsistOnData() throws Exception { // When... assertThat(mockCreds.getAllCredentials()).hasSize(2); assertThat(mockCreds.getCredentials(secretName)).isNotNull(); - List errors = secretProcessor.processResource(secretJson, DELETE); + List errors = secretProcessor.processResource(secretJson, DELETE, requestUsername); // Then... assertThat(errors).isEmpty(); @@ -511,11 +566,13 @@ public void testDeleteSecretDoesNotInsistOnData() throws Exception { @Test public void testCreateSecretThatAlreadyExistsThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); Map credsMap = new HashMap<>(); credsMap.put("ABC", new CredentialsUsername("my-username")); MockCredentialsService mockCreds = new MockCredentialsService(credsMap); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Username"; String encoding = null; @@ -525,7 +582,7 @@ public void testCreateSecretThatAlreadyExistsThrowsError() throws Exception { // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, CREATE); + secretProcessor.processResource(secretJson, CREATE, requestUsername); }, InternalServletException.class); // Then... @@ -537,18 +594,20 @@ public void testCreateSecretThatAlreadyExistsThrowsError() throws Exception { @Test public void testUpdateSecretThatDoesNotExistThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Token"; String encoding = null; String token = "another-token"; - JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, token); + JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, token, null); // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, UPDATE); + secretProcessor.processResource(secretJson, UPDATE, requestUsername); }, InternalServletException.class); // Then... @@ -560,20 +619,22 @@ public void testUpdateSecretThatDoesNotExistThrowsError() throws Exception { @Test public void testApplySecretWithFailingCredsServiceThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); mockCreds.setThrowError(true); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Token"; String encoding = null; String token = "a-token"; - JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, token); + JsonObject secretJson = generateSecretJson(secretName, type, encoding, null, null, token, null); // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, APPLY); + secretProcessor.processResource(secretJson, APPLY, requestUsername); }, InternalServletException.class); // Then... @@ -585,10 +646,12 @@ public void testApplySecretWithFailingCredsServiceThrowsError() throws Exception @Test public void testDeleteSecretWithFailingCredsServiceThrowsError() throws Exception { // Given... + MockTimeService mockTimeService = new MockTimeService(Instant.EPOCH); MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); mockCreds.setThrowError(true); - GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; String secretName = "ABC"; String type = "Token"; String encoding = null; @@ -598,7 +661,7 @@ public void testDeleteSecretWithFailingCredsServiceThrowsError() throws Exceptio // When... InternalServletException thrown = catchThrowableOfType(() -> { - secretProcessor.processResource(secretJson, DELETE); + secretProcessor.processResource(secretJson, DELETE, requestUsername); }, InternalServletException.class); // Then... @@ -606,4 +669,57 @@ public void testDeleteSecretWithFailingCredsServiceThrowsError() throws Exceptio checkErrorStructure(thrown.getMessage(), 5078, "GAL5078E", "Failed to delete a secret with the given ID from the credentials store"); } + + @Test + public void testCreateUsernamePasswordSecretWithBlankDescriptionThrowsError() throws Exception { + // Given... + Instant lastUpdatedTime = Instant.EPOCH; + MockTimeService mockTimeService = new MockTimeService(lastUpdatedTime); + MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; + String secretName = "ABC"; + String type = "UsernamePassword"; + String encoding = null; + String username = "my-username"; + String password = "a-password"; + String description = " "; + JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password, description); + + // When... + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); + + // Then... + assertThat(errors).hasSize(1); + checkErrorStructure(errors.get(0), 5102, "GAL5102E", + "Invalid secret description provided"); + } + + @Test + public void testCreateUsernamePasswordSecretWithNonLatin1DescriptionThrowsError() throws Exception { + // Given... + Instant lastUpdatedTime = Instant.EPOCH; + MockTimeService mockTimeService = new MockTimeService(lastUpdatedTime); + MockCredentialsService mockCreds = new MockCredentialsService(new HashMap<>()); + GalasaSecretProcessor secretProcessor = new GalasaSecretProcessor(mockCreds, mockTimeService); + String requestUsername = "myuser"; + String secretName = "ABC"; + String type = "UsernamePassword"; + String encoding = null; + String username = "my-username"; + String password = "a-password"; + + // Latin-1 characters are in the 0-255 range, so set one that is outside this range + char nonLatin1Character = (char) 300; + String description = "this is my bad description " + nonLatin1Character; + JsonObject secretJson = generateSecretJson(secretName, type, encoding, username, password, description); + + // When... + List errors = secretProcessor.processResource(secretJson, CREATE, requestUsername); + + // Then... + assertThat(errors).hasSize(1); + checkErrorStructure(errors.get(0), 5102, "GAL5102E", + "Invalid secret description provided"); + } } diff --git a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/routes/TestResourcesRoute.java b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/routes/TestResourcesRoute.java index 81994a37e..446f293fc 100644 --- a/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/routes/TestResourcesRoute.java +++ b/galasa-parent/dev.galasa.framework.api.resources/src/test/java/dev/galasa/framework/api/resources/routes/TestResourcesRoute.java @@ -120,18 +120,19 @@ public void TestPathRegexMultipleForwardSlashPathReturnsFalse(){ @Test public void TestProcessDataArrayBadJsonArrayReturnsError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString = "[{},{},{}]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -143,18 +144,19 @@ public void TestProcessDataArrayBadJsonArrayReturnsError() throws Exception{ @Test public void TestProcessDataArrayBadJsonReturnsError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString = "[{\"kind\":\"GalasaProperty\",\"apiVersion\":\"galasa-dev/v1alpha1\","+namespace+"."+propertyname+":"+value+"}]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -166,18 +168,19 @@ public void TestProcessDataArrayBadJsonReturnsError() throws Exception{ @Test public void TestProcessDataArrayBadKindReturnsError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString = "[{\"kind\":\"GalasaProperly\",\"apiVersion\":\"v1alpha1\","+namespace+"."+propertyname+":"+value+"}]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -189,16 +192,17 @@ public void TestProcessDataArrayBadKindReturnsError() throws Exception{ @Test public void TestProcessDataArrayNullJsonObjectReturnsError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString = "[null]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -209,17 +213,18 @@ public void TestProcessDataArrayNullJsonObjectReturnsError() throws Exception{ @Test public void TestProcessDataArrayCorrectJSONReturnsOK() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); JsonArray propertyJson = generatePropertyArrayJson(namespace,propertyname,value,"galasa-dev/v1alpha1"); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -230,19 +235,20 @@ public void TestProcessDataArrayCorrectJSONReturnsOK() throws Exception{ @Test public void TestProcessDataArrayThreeBadJsonReturnsErrors() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString = "[null, {\"kind\":\"GalasaProperty\",\"apiVersion\":\"galasa-dev/v1alpha1\","+namespace+"."+propertyname+":"+value+"},"+ "{\"kind\":\"GalasaProperly\",\"apiVersion\":\"v1alpha1\","+namespace+"."+propertyname+":"+value+"},{}]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, APPLY); + resourcesRoute.processDataArray(propertyJson, APPLY, username); List errors = resourcesRoute.errors; //Then... @@ -257,6 +263,7 @@ public void TestProcessDataArrayThreeBadJsonReturnsErrors() throws Exception{ @Test public void TestProcessDataArrayCreateWithOneExistingRecordJSONReturnsOneError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -265,13 +272,13 @@ public void TestProcessDataArrayCreateWithOneExistingRecordJSONReturnsOneError() setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString ="["+ generatePropertyJson(namespace,propertyname,value,"galasa-dev/v1alpha1"); jsonString = jsonString+","+ generatePropertyJson(namespace,propertyNameTwo,valueTwo,"galasa-dev/v1alpha1") +"]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, CREATE); + resourcesRoute.processDataArray(propertyJson, CREATE, username); List errors = resourcesRoute.errors; //Then... @@ -285,6 +292,7 @@ public void TestProcessDataArrayCreateWithOneExistingRecordJSONReturnsOneError() @Test public void TestProcessDataArrayCreateWithTwoExistingRecordsJSONReturnsTwoErrors() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.1"; String value = "value"; @@ -293,13 +301,13 @@ public void TestProcessDataArrayCreateWithTwoExistingRecordsJSONReturnsTwoErrors setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString ="["+ generatePropertyJson(namespace,propertyname,value,"galasa-dev/v1alpha1"); jsonString = jsonString+","+ generatePropertyJson(namespace,propertyNameTwo,valueTwo,"galasa-dev/v1alpha1") +"]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, CREATE); + resourcesRoute.processDataArray(propertyJson, CREATE, username); List errors = resourcesRoute.errors; //Then... @@ -315,6 +323,7 @@ public void TestProcessDataArrayCreateWithTwoExistingRecordsJSONReturnsTwoErrors @Test public void TestProcessDataArrayUpdateWithOneNewRecordJSONReturnsOneError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -323,13 +332,13 @@ public void TestProcessDataArrayUpdateWithOneNewRecordJSONReturnsOneError() thro setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString ="["+ generatePropertyJson(namespace,propertyname,value,"galasa-dev/v1alpha1"); jsonString = jsonString+","+ generatePropertyJson(namespace,propertyNameTwo,valueTwo,"galasa-dev/v1alpha1") +"]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, UPDATE); + resourcesRoute.processDataArray(propertyJson, UPDATE, username); List errors = resourcesRoute.errors; //Then... @@ -342,6 +351,7 @@ public void TestProcessDataArrayUpdateWithOneNewRecordJSONReturnsOneError() thro @Test public void TestProcessDataArrayUpdateWithTwoNewRecordsJSONReturnsTwoError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -350,13 +360,13 @@ public void TestProcessDataArrayUpdateWithTwoNewRecordsJSONReturnsTwoError() thr setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); String jsonString ="["+ generatePropertyJson(namespace,propertyname,value,"galasa-dev/v1alpha1"); jsonString = jsonString+","+ generatePropertyJson(namespace,propertyNameTwo,valueTwo,"galasa-dev/v1alpha1") +"]"; JsonArray propertyJson = JsonParser.parseString(jsonString).getAsJsonArray(); //When... - resourcesRoute.processDataArray(propertyJson, UPDATE); + resourcesRoute.processDataArray(propertyJson, UPDATE, username); List errors = resourcesRoute.errors; //Then... @@ -374,6 +384,7 @@ public void TestProcessDataArrayUpdateWithTwoNewRecordsJSONReturnsTwoError() thr @Test public void TestProcessRequestApplyActionReturnsOK() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -381,11 +392,11 @@ public void TestProcessRequestApplyActionReturnsOK() throws Exception{ setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); JsonObject requestJson = generateRequestJson(action, namespace,propertyname,value,"galasa-dev/v1alpha1"); //When... - resourcesRoute.processRequest(requestJson); + resourcesRoute.processRequest(requestJson, username); List errors = resourcesRoute.errors; //Then... @@ -396,6 +407,7 @@ public void TestProcessRequestApplyActionReturnsOK() throws Exception{ @Test public void TestProcessRequestCreateActionReturnsOK() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -403,11 +415,11 @@ public void TestProcessRequestCreateActionReturnsOK() throws Exception{ setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); JsonObject jsonString = generateRequestJson(action, namespace,propertyname,value,"galasa-dev/v1alpha1"); //When... - resourcesRoute.processRequest(jsonString); + resourcesRoute.processRequest(jsonString, username); List errors = resourcesRoute.errors; //Then... @@ -418,6 +430,7 @@ public void TestProcessRequestCreateActionReturnsOK() throws Exception{ @Test public void TestProcessRequestUpdateActionReturnsOK() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.1"; String value = "value"; @@ -425,11 +438,11 @@ public void TestProcessRequestUpdateActionReturnsOK() throws Exception{ setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); JsonObject jsonString = generateRequestJson(action, namespace,propertyname,value,"galasa-dev/v1alpha1"); //When... - resourcesRoute.processRequest(jsonString); + resourcesRoute.processRequest(jsonString, username); List errors = resourcesRoute.errors; //Then... @@ -440,6 +453,7 @@ public void TestProcessRequestUpdateActionReturnsOK() throws Exception{ @Test public void TestProcessRequestBadActionReturnsError() throws Exception{ //Given... + String username = "myuser"; String namespace = "framework"; String propertyname = "property.name"; String value = "value"; @@ -447,12 +461,12 @@ public void TestProcessRequestBadActionReturnsError() throws Exception{ setServlet(namespace); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); JsonObject jsonString = generateRequestJson(action, namespace,propertyname,value,"galasa-dev/v1alpha1"); //When... Throwable thrown = catchThrowable(() -> { - resourcesRoute.processRequest(jsonString); + resourcesRoute.processRequest(jsonString, username); }); //Then... @@ -973,7 +987,7 @@ public void TestGetErrorsAsJsonReturnsJsonString() throws Exception{ setServlet("framework"); MockResourcesServlet servlet = getServlet(); CPSFacade cps = new CPSFacade(servlet.getFramework()); - ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null); + ResourcesRoute resourcesRoute = new ResourcesRoute(null, cps, null, null, null); // When... String json = resourcesRoute.getErrorsAsJson(errors); diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/SecretsServlet.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/SecretsServlet.java index 7a894e08e..151883711 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/SecretsServlet.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/SecretsServlet.java @@ -12,11 +12,15 @@ import org.apache.commons.logging.LogFactory; import dev.galasa.framework.api.common.BaseServlet; +import dev.galasa.framework.api.common.Environment; +import dev.galasa.framework.api.common.SystemEnvironment; import dev.galasa.framework.api.secrets.internal.routes.SecretDetailsRoute; import dev.galasa.framework.api.secrets.internal.routes.SecretsRoute; import dev.galasa.framework.spi.IFramework; import dev.galasa.framework.spi.creds.CredentialsException; import dev.galasa.framework.spi.creds.ICredentialsService; +import dev.galasa.framework.spi.utils.ITimeService; +import dev.galasa.framework.spi.utils.SystemTimeService; import javax.servlet.Servlet; import javax.servlet.ServletException; @@ -31,6 +35,9 @@ public class SecretsServlet extends BaseServlet { @Reference protected IFramework framework; + protected Environment env = new SystemEnvironment(); + protected ITimeService timeService = new SystemTimeService(); + private static final long serialVersionUID = 1L; private Log logger = LogFactory.getLog(this.getClass()); @@ -41,8 +48,8 @@ public void init() throws ServletException { try { ICredentialsService credentialsService = framework.getCredentialsService(); - addRoute(new SecretsRoute(getResponseBuilder(), credentialsService)); - addRoute(new SecretDetailsRoute(getResponseBuilder(), credentialsService)); + addRoute(new SecretsRoute(getResponseBuilder(), credentialsService, env, timeService)); + addRoute(new SecretDetailsRoute(getResponseBuilder(), credentialsService, env, timeService)); } catch (CredentialsException e) { throw new ServletException("Failed to initialise the Secrets servlet"); } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/SecretRequestValidator.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/SecretRequestValidator.java index 9af466efc..2ee427c33 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/SecretRequestValidator.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/SecretRequestValidator.java @@ -17,8 +17,9 @@ import dev.galasa.framework.api.common.IBeanValidator; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.ServletError; +import dev.galasa.framework.api.common.resources.BaseResourceValidator; -public class SecretRequestValidator implements IBeanValidator { +public class SecretRequestValidator extends BaseResourceValidator implements IBeanValidator { @Override public void validate(SecretRequest secretRequest) throws InternalServletException { @@ -28,11 +29,13 @@ public void validate(SecretRequest secretRequest) throws InternalServletExceptio // Check that the secret has been given a name String secretName = secretRequest.getname(); - if (secretName == null || secretName.isBlank()) { + if (secretName == null || secretName.isBlank() || !isLatin1(secretName)) { ServletError error = new ServletError(GAL5092_INVALID_SECRET_NAME_PROVIDED); throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); } + validateDescription(secretRequest.getdescription()); + // Password and token are mutually exclusive, so error if both are provided if (password != null && token != null) { ServletError error = new ServletError(GAL5095_ERROR_PASSWORD_AND_TOKEN_PROVIDED); @@ -77,4 +80,12 @@ private void validateField(String value, String encoding) throws InternalServlet throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); } } + + protected void validateDescription(String description) throws InternalServletException { + if (description != null && (description.isBlank() || !isLatin1(description))) { + ServletError error = new ServletError(GAL5102_INVALID_SECRET_DESCRIPTION_PROVIDED); + throw new InternalServletException(error, HttpServletResponse.SC_BAD_REQUEST); + } + } + } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/UpdateSecretRequestValidator.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/UpdateSecretRequestValidator.java index 6da2cfc11..ba600f2b6 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/UpdateSecretRequestValidator.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/UpdateSecretRequestValidator.java @@ -49,6 +49,8 @@ private void validateCreateSecretRequest(SecretRequest secretRequest) throws Int SecretRequestpassword password = secretRequest.getpassword(); SecretRequesttoken token = secretRequest.gettoken(); + validateDescription(secretRequest.getdescription()); + // Password and token are mutually exclusive, so error if both are provided if (password != null && token != null) { ServletError error = new ServletError(GAL5095_ERROR_PASSWORD_AND_TOKEN_PROVIDED); @@ -69,6 +71,8 @@ private void validateUpdateSecretRequest(SecretRequest secretRequest) throws Int SecretRequestpassword password = secretRequest.getpassword(); SecretRequesttoken token = secretRequest.gettoken(); + validateDescription(secretRequest.getdescription()); + // Password and token are mutually exclusive, so error if both are provided if (password != null && token != null) { ServletError error = new ServletError(GAL5095_ERROR_PASSWORD_AND_TOKEN_PROVIDED); @@ -112,7 +116,7 @@ private void checkProvidedSecretFieldsAreRelevant(GalasaSecretType secretType, S JsonObject secretRequestJson = gson.toJsonTree(secretRequest).getAsJsonObject(); Set secretRequestFields = secretRequestJson.keySet() .stream() - .filter(key -> !key.equals("name") && !key.equals("type")) + .filter(key -> !key.equals("name") && !key.equals("type") && !key.equals("description")) .collect(Collectors.toSet()); List requiredTypeFields = Arrays.asList(secretType.getRequiredDataFields()); diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/AbstractSecretsRoute.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/AbstractSecretsRoute.java index 416396b10..55b29dc64 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/AbstractSecretsRoute.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/AbstractSecretsRoute.java @@ -8,9 +8,11 @@ import static dev.galasa.framework.api.common.ServletErrorMessage.*; import static dev.galasa.framework.api.beans.generated.GalasaSecretType.*; +import java.time.Instant; import java.util.Base64; import java.util.Map; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import dev.galasa.ICredentials; @@ -26,7 +28,9 @@ import dev.galasa.framework.api.beans.generated.SecretRequesttoken; import dev.galasa.framework.api.beans.generated.SecretRequestusername; import dev.galasa.framework.api.common.BaseRoute; +import dev.galasa.framework.api.common.Environment; import dev.galasa.framework.api.common.InternalServletException; +import dev.galasa.framework.api.common.JwtWrapper; import dev.galasa.framework.api.common.ResponseBuilder; import dev.galasa.framework.api.common.ServletError; import dev.galasa.framework.api.common.resources.GalasaSecretType; @@ -34,11 +38,15 @@ import dev.galasa.framework.spi.creds.CredentialsUsername; import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; import dev.galasa.framework.spi.creds.CredentialsUsernameToken; +import dev.galasa.framework.spi.utils.ITimeService; public abstract class AbstractSecretsRoute extends BaseRoute { private static final String DEFAULT_RESPONSE_ENCODING = "base64"; + private Environment env; + protected ITimeService timeService; + private static final Map, GalasaSecretType> credentialsToSecretTypes = Map.of( CredentialsUsername.class, GalasaSecretType.USERNAME, CredentialsToken.class, GalasaSecretType.TOKEN, @@ -46,8 +54,10 @@ public abstract class AbstractSecretsRoute extends BaseRoute { CredentialsUsernameToken.class, GalasaSecretType.USERNAME_TOKEN ); - public AbstractSecretsRoute(ResponseBuilder responseBuilder, String path) { + public AbstractSecretsRoute(ResponseBuilder responseBuilder, String path, Environment env, ITimeService timeService) { super(responseBuilder, path); + this.env = env; + this.timeService = timeService; } protected GalasaSecret createGalasaSecretFromCredentials(String secretName, ICredentials credentials) throws InternalServletException { @@ -57,7 +67,7 @@ protected GalasaSecret createGalasaSecretFromCredentials(String secretName, ICre metadata.setname(secretName); metadata.setencoding(DEFAULT_RESPONSE_ENCODING); setSecretTypeValuesFromCredentials(metadata, data, credentials); - + setSecretMetadata(metadata, credentials.getDescription(), credentials.getLastUpdatedByUser(), credentials.getLastUpdatedTime()); GalasaSecret secret = new GalasaSecret(); secret.setApiVersion(GalasaSecretType.DEFAULT_API_VERSION); secret.setdata(data); @@ -66,7 +76,13 @@ protected GalasaSecret createGalasaSecretFromCredentials(String secretName, ICre return secret; } - protected ICredentials decodeCredentialsFromSecretPayload(SecretRequest secretRequest) throws InternalServletException { + protected ICredentials buildDecodedCredentialsToSet(SecretRequest secretRequest, String lastUpdatedByUser) throws InternalServletException { + ICredentials decodedSecret = decodeCredentialsFromSecretPayload(secretRequest); + setSecretMetadataProperties(decodedSecret, secretRequest.getdescription(), lastUpdatedByUser); + return decodedSecret; + } + + private ICredentials decodeCredentialsFromSecretPayload(SecretRequest secretRequest) throws InternalServletException { ICredentials credentials = null; SecretRequestusername username = secretRequest.getusername(); SecretRequestpassword password = secretRequest.getpassword(); @@ -142,6 +158,15 @@ private void setSecretTypeValuesFromCredentials(GalasaSecretmetadata metadata, G } } + private void setSecretMetadata(GalasaSecretmetadata metadata, String description, String username, Instant timestamp) { + metadata.setdescription(description); + metadata.setLastUpdatedBy(username); + + if (timestamp != null) { + metadata.setLastUpdatedTime(timestamp.toString()); + } + } + private String encodeValue(String value) { String encodedValue = value; if (DEFAULT_RESPONSE_ENCODING.equals("base64")) { @@ -157,4 +182,16 @@ protected GalasaSecretType getSecretType(ICredentials existingSecret) { } return existingSecretType; } + + protected String getUsernameFromRequestJwt(HttpServletRequest request) throws InternalServletException { + return new JwtWrapper(request, env).getUsername(); + } + + protected void setSecretMetadataProperties(ICredentials secret, String description, String lastUpdatedByUser) { + if (description != null && !description.isBlank()) { + secret.setDescription(description); + } + secret.setLastUpdatedByUser(lastUpdatedByUser); + secret.setLastUpdatedTime(timeService.now()); + } } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretDetailsRoute.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretDetailsRoute.java index e08e3018a..3673d762e 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretDetailsRoute.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretDetailsRoute.java @@ -23,6 +23,7 @@ import dev.galasa.framework.api.beans.generated.SecretRequestpassword; import dev.galasa.framework.api.beans.generated.SecretRequesttoken; import dev.galasa.framework.api.beans.generated.SecretRequestusername; +import dev.galasa.framework.api.common.Environment; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.QueryParameters; import dev.galasa.framework.api.common.ResponseBuilder; @@ -37,6 +38,7 @@ import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; import dev.galasa.framework.spi.creds.CredentialsUsernameToken; import dev.galasa.framework.spi.creds.ICredentialsService; +import dev.galasa.framework.spi.utils.ITimeService; public class SecretDetailsRoute extends AbstractSecretsRoute { @@ -52,8 +54,13 @@ public class SecretDetailsRoute extends AbstractSecretsRoute { private Log logger = LogFactory.getLog(getClass()); - public SecretDetailsRoute(ResponseBuilder responseBuilder, ICredentialsService credentialsService) { - super(responseBuilder, PATH_PATTERN); + public SecretDetailsRoute( + ResponseBuilder responseBuilder, + ICredentialsService credentialsService, + Environment env, + ITimeService timeService + ) { + super(responseBuilder, PATH_PATTERN, env, timeService); this.credentialsService = credentialsService; } @@ -85,6 +92,7 @@ public HttpServletResponse handlePutRequest( checkRequestHasContent(request); String secretName = getSecretNameFromPath(pathInfo); + String lastUpdatedByUser = getUsernameFromRequestJwt(request); SecretRequest secretPayload = parseRequestBody(request, SecretRequest.class); ICredentials existingSecret = credentialsService.getCredentials(secretName); @@ -95,16 +103,17 @@ public HttpServletResponse handlePutRequest( int responseCode = HttpServletResponse.SC_NO_CONTENT; if (existingSecret == null) { // No secret with the given name exists, so create a new one - decodedSecret = decodeCredentialsFromSecretPayload(secretPayload); + decodedSecret = buildDecodedCredentialsToSet(secretPayload, lastUpdatedByUser); responseCode = HttpServletResponse.SC_CREATED; } else if (secretPayload.gettype() != null) { // When a secret type is given, all relevant fields for that type are required, // so overwrite the existing secret to change its type - decodedSecret = decodeCredentialsFromSecretPayload(secretPayload); + decodedSecret = buildDecodedCredentialsToSet(secretPayload, lastUpdatedByUser); } else { // A secret already exists and no type was given, so just update the secret by // overriding its existing values with the values provided in the request decodedSecret = getOverriddenSecret(existingSecretType, existingSecret, secretPayload); + setSecretMetadataProperties(decodedSecret, secretPayload.getdescription(), lastUpdatedByUser); } credentialsService.setCredentials(secretName, decodedSecret); diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretsRoute.java b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretsRoute.java index 445168a51..4aa45227d 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretsRoute.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/main/java/dev/galasa/framework/api/secrets/internal/routes/SecretsRoute.java @@ -22,6 +22,7 @@ import dev.galasa.ICredentials; import dev.galasa.framework.api.beans.generated.GalasaSecret; import dev.galasa.framework.api.beans.generated.SecretRequest; +import dev.galasa.framework.api.common.Environment; import dev.galasa.framework.api.common.InternalServletException; import dev.galasa.framework.api.common.QueryParameters; import dev.galasa.framework.api.common.ResponseBuilder; @@ -29,6 +30,7 @@ import dev.galasa.framework.api.secrets.internal.SecretRequestValidator; import dev.galasa.framework.spi.FrameworkException; import dev.galasa.framework.spi.creds.ICredentialsService; +import dev.galasa.framework.spi.utils.ITimeService; public class SecretsRoute extends AbstractSecretsRoute { @@ -40,8 +42,12 @@ public class SecretsRoute extends AbstractSecretsRoute { private Log logger = LogFactory.getLog(getClass()); - public SecretsRoute(ResponseBuilder responseBuilder, ICredentialsService credentialsService) { - super(responseBuilder, PATH_PATTERN); + public SecretsRoute( + ResponseBuilder responseBuilder, + ICredentialsService credentialsService, + Environment env, + ITimeService timeService) { + super(responseBuilder, PATH_PATTERN, env, timeService); this.credentialsService = credentialsService; } @@ -93,7 +99,8 @@ public HttpServletResponse handlePostRequest( } logger.info("Setting secret in credentials store"); - ICredentials decodedSecret = decodeCredentialsFromSecretPayload(secretPayload); + String lastUpdatedByUser = getUsernameFromRequestJwt(request); + ICredentials decodedSecret = buildDecodedCredentialsToSet(secretPayload, lastUpdatedByUser); credentialsService.setCredentials(secretName, decodedSecret); logger.info("Secret set in credentials store OK"); diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/MockCredentials.java b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/MockCredentials.java index 8034b1bf9..0f7925f5b 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/MockCredentials.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/MockCredentials.java @@ -5,6 +5,7 @@ */ package dev.galasa.framework.api.secrets.internal; +import java.time.Instant; import java.util.Properties; import dev.galasa.ICredentials; @@ -15,4 +16,39 @@ class MockCredentials implements ICredentials { public Properties toProperties(String credentialsId) { throw new UnsupportedOperationException("Unimplemented method 'toProperties'"); } + + @Override + public void setDescription(String description) { + throw new UnsupportedOperationException("Unimplemented method 'setDescription'"); + } + + @Override + public void setLastUpdatedByUser(String username) { + throw new UnsupportedOperationException("Unimplemented method 'setLastUpdatedByUser'"); + } + + @Override + public void setLastUpdatedTime(Instant time) { + throw new UnsupportedOperationException("Unimplemented method 'setLastUpdatedTime'"); + } + + @Override + public String getDescription() { + throw new UnsupportedOperationException("Unimplemented method 'getDescription'"); + } + + @Override + public String getLastUpdatedByUser() { + throw new UnsupportedOperationException("Unimplemented method 'getLastUpdatedByUser'"); + } + + @Override + public Instant getLastUpdatedTime() { + throw new UnsupportedOperationException("Unimplemented method 'getLastUpdatedTime'"); + } + + @Override + public Properties getMetadataProperties(String credentialsId) { + throw new UnsupportedOperationException("Unimplemented method 'getMetadataProperties'"); + } } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretDetailsRouteTest.java b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretDetailsRouteTest.java index eef30d7bf..df075a054 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretDetailsRouteTest.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretDetailsRouteTest.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.*; import static dev.galasa.framework.api.common.resources.GalasaSecretType.*; +import java.time.Instant; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -25,6 +26,7 @@ import dev.galasa.framework.api.common.mocks.MockFramework; import dev.galasa.framework.api.common.mocks.MockHttpServletRequest; import dev.galasa.framework.api.common.mocks.MockHttpServletResponse; +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.secrets.internal.routes.SecretDetailsRoute; import dev.galasa.framework.api.secrets.mocks.MockSecretsServlet; import dev.galasa.framework.spi.creds.CredentialsToken; @@ -37,7 +39,7 @@ public class SecretDetailsRouteTest extends SecretsServletTest { @Test public void testSecretDetailsRouteRegexMatchesExpectedPaths() throws Exception { // Given... - Pattern routePattern = new SecretDetailsRoute(null, null).getPath(); + Pattern routePattern = new SecretDetailsRoute(null, null, null, null).getPath(); // Then... // The servlet's whiteboard pattern will match /secrets, so this route should @@ -69,9 +71,10 @@ public void testGetSecretByNameReturnsSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -98,9 +101,10 @@ public void testGetNonExistantSecretByNameReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -128,9 +132,10 @@ public void testGetSecretByNameWithFailingCredsStoreReturnsError() throws Except MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); mockRequest.setQueryParameter("name", secretName); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); @@ -157,9 +162,10 @@ public void testDeleteSecretDeletesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); mockRequest.setMethod(HttpMethod.DELETE.toString()); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); @@ -184,9 +190,10 @@ public void testDeleteNonExistantSecretReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); mockRequest.setMethod(HttpMethod.DELETE.toString()); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); @@ -215,9 +222,10 @@ public void testDeleteSecretWithFailingCredsStoreReturnsError() throws Exception MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, REQUEST_HEADERS); mockRequest.setMethod(HttpMethod.DELETE.toString()); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); @@ -253,10 +261,10 @@ public void testUpdateSecretUsernameUpdatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -297,10 +305,10 @@ public void testUpdateSecretUsernamePasswordUpdatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -338,10 +346,10 @@ public void testUpdateTokenSecretUpdatesValueOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -379,10 +387,10 @@ public void testUpdateUsernameTokenSecretUpdatesValueOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -420,10 +428,10 @@ public void testUpdateUsernameSecretUpdatesValueOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -461,10 +469,10 @@ public void testUpdateSecretToTokenChangesSecretTypeOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -502,10 +510,10 @@ public void testUpdateSecretWithUnknownTypeReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -541,10 +549,10 @@ public void testUpdateSecretWithTypeAndMissingFieldsReturnsError() throws Except MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -580,10 +588,10 @@ public void testUpdateSecretWithPasswordAndTokenPayloadReturnsError() throws Exc MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -621,10 +629,10 @@ public void testUpdateSecretWithMixedEncodingUpdatesSecretOk() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -658,10 +666,10 @@ public void testUpdateNonExistantSecretCreatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -697,10 +705,10 @@ public void testUpdateNonExistantSecretWithPasswordAndTokenPayloadReturnsError() MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -730,10 +738,10 @@ public void testUpdateNonExistantSecretWithPasswordOnlyPayloadReturnsError() thr MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -763,10 +771,10 @@ public void testUpdateSecretWithUnknownTokenEncodingReturnsError() throws Except MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -795,10 +803,10 @@ public void testUpdateSecretWithUnknownUsernameEncodingReturnsError() throws Exc MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -830,10 +838,10 @@ public void testUpdateSecretWithUnexpectedFieldsReturnsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -867,10 +875,10 @@ public void testUpdateSecretWithTooManyFieldsReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -903,10 +911,10 @@ public void testUpdateSecretWithUnsupportedTypeReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/" + secretName); - mockRequest.setMethod(HttpMethod.PUT.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -920,5 +928,134 @@ public void testUpdateSecretWithUnsupportedTypeReturnsError() throws Exception { checkErrorStructure(outStream.toString(), 5101, "GAL5101E", "Unknown secret type detected"); } + + @Test + public void testUpdateSecretWithBlankDescriptionReturnsError() throws Exception { + // Given... + Map creds = new HashMap<>(); + String secretName = "BOB"; + String oldToken = "my-old-token"; + String newToken = "my-new-token"; + String newDescription = " "; + + creds.put(secretName, new CredentialsToken(oldToken)); + + JsonObject secretJson = new JsonObject(); + secretJson.add("token", createSecretJson(newToken)); + secretJson.addProperty("description", newDescription); + + String secretJsonStr = gson.toJson(secretJson); + + MockCredentialsService credsService = new MockCredentialsService(creds); + MockFramework mockFramework = new MockFramework(credsService); + + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); + + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + ServletOutputStream outStream = servletResponse.getOutputStream(); + + // When... + servlet.init(); + servlet.doPut(mockRequest, servletResponse); + + // Then... + assertThat(servletResponse.getStatus()).isEqualTo(400); + checkErrorStructure(outStream.toString(), 5102, "GAL5102E", + "Invalid secret description provided"); + } + + @Test + public void testUpdateSecretWithNonLatin1DescriptionReturnsError() throws Exception { + // Given... + Map creds = new HashMap<>(); + String secretName = "BOB"; + String oldToken = "my-old-token"; + String newToken = "my-new-token"; + + // Latin-1 characters are in the range 0-255, so get one that is outside this range + char nonLatin1Character = (char)300; + String description = Character.toString(nonLatin1Character) + " more text here!"; + + creds.put(secretName, new CredentialsToken(oldToken)); + + JsonObject secretJson = new JsonObject(); + secretJson.add("token", createSecretJson(newToken)); + secretJson.addProperty("description", description); + + String secretJsonStr = gson.toJson(secretJson); + + MockCredentialsService credsService = new MockCredentialsService(creds); + MockFramework mockFramework = new MockFramework(credsService); + + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); + + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + ServletOutputStream outStream = servletResponse.getOutputStream(); + + // When... + servlet.init(); + servlet.doPut(mockRequest, servletResponse); + + // Then... + assertThat(servletResponse.getStatus()).isEqualTo(400); + checkErrorStructure(outStream.toString(), 5102, "GAL5102E", + "Invalid secret description provided"); + } + + @Test + public void testUpdateSecretWithValidLatin1DescriptionUpdatesSecret() throws Exception { + // Given... + Map creds = new HashMap<>(); + String secretName = "BOB"; + String oldToken = "my-old-token"; + String newToken = "my-new-token"; + + Instant lastUpdatedTime = Instant.EPOCH; + + // Latin-1 characters are in the range 0-255 + char latin1Character = (char)255; + String description = Character.toString(latin1Character) + " more text here!"; + + creds.put(secretName, new CredentialsToken(oldToken)); + + JsonObject secretJson = new JsonObject(); + secretJson.add("token", createSecretJson(newToken)); + secretJson.addProperty("description", description); + + String secretJsonStr = gson.toJson(secretJson); + + MockCredentialsService credsService = new MockCredentialsService(creds); + MockFramework mockFramework = new MockFramework(credsService); + + MockTimeService timeService = new MockTimeService(lastUpdatedTime); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/" + secretName, secretJsonStr, HttpMethod.PUT.toString(), REQUEST_HEADERS); + + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + ServletOutputStream outStream = servletResponse.getOutputStream(); + + // When... + servlet.init(); + servlet.doPut(mockRequest, servletResponse); + + // Then... + assertThat(servletResponse.getStatus()).isEqualTo(204); + assertThat(outStream.toString()).isEmpty(); + + assertThat(credsService.getAllCredentials()).hasSize(1); + CredentialsToken updatedCredentials = (CredentialsToken) credsService.getCredentials(secretName); + assertThat(updatedCredentials).isNotNull(); + assertThat(updatedCredentials.getToken()).isEqualTo(newToken.getBytes()); + assertThat(updatedCredentials.getDescription()).isEqualTo(description); + assertThat(updatedCredentials.getLastUpdatedTime()).isEqualTo(lastUpdatedTime); + assertThat(updatedCredentials.getLastUpdatedByUser()).isEqualTo(JWT_USERNAME); + } } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsRouteTest.java b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsRouteTest.java index 0d07f1ee8..428e74495 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsRouteTest.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsRouteTest.java @@ -7,6 +7,7 @@ import static org.assertj.core.api.Assertions.*; +import java.time.Instant; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -25,18 +26,20 @@ import dev.galasa.framework.api.common.mocks.MockFramework; import dev.galasa.framework.api.common.mocks.MockHttpServletRequest; import dev.galasa.framework.api.common.mocks.MockHttpServletResponse; +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.secrets.internal.routes.SecretsRoute; import dev.galasa.framework.api.secrets.mocks.MockSecretsServlet; import dev.galasa.framework.spi.creds.CredentialsToken; import dev.galasa.framework.spi.creds.CredentialsUsername; import dev.galasa.framework.spi.creds.CredentialsUsernamePassword; +import dev.galasa.framework.spi.creds.CredentialsUsernameToken; public class SecretsRouteTest extends SecretsServletTest { @Test public void testSecretsRouteRegexMatchesExpectedPaths() throws Exception { // Given... - Pattern routePattern = new SecretsRoute(null, null).getPath(); + Pattern routePattern = new SecretsRoute(null, null, null, null).getPath(); // Then... // The servlet's whiteboard pattern will match /secrets, so the secrets route @@ -56,23 +59,52 @@ public void testGetSecretsReturnsAllSecretsOk() throws Exception { String secretName1 = "BOB"; String username1 = "my-username"; String password1 = "not-a-password"; + String description1 = "this is my first secret"; + String lastUser1 = "user1"; + Instant lastUpdated1 = Instant.EPOCH; String secretName2 = "ITS_BOB_AGAIN"; String username2 = "another-username"; + String description2 = "this is my second secret"; + String lastUser2 = "user2"; + Instant lastUpdated2 = Instant.EPOCH.plusMillis(1); String secretName3 = "not-b0b"; String token3 = "this-is-a-token"; - creds.put(secretName1, new CredentialsUsernamePassword(username1, password1)); - creds.put(secretName2, new CredentialsUsername(username2)); + String secretName4 = "new-bob"; + String username4 = "this-is-yet-another-username"; + String token4 = "this-is-another-token"; + String lastUser4 = "user4"; + Instant lastUpdated4 = Instant.EPOCH.plusMillis(4); + + ICredentials secret1 = new CredentialsUsernamePassword(username1, password1); + secret1.setDescription(description1); + secret1.setLastUpdatedByUser(lastUser1); + secret1.setLastUpdatedTime(lastUpdated1); + + ICredentials secret2 = new CredentialsUsername(username2); + secret2.setDescription(description2); + secret2.setLastUpdatedByUser(lastUser2); + secret2.setLastUpdatedTime(lastUpdated2); + + ICredentials secret4 = new CredentialsUsernameToken(username4, token4); + secret4.setLastUpdatedByUser(username4); + secret4.setLastUpdatedByUser(lastUser4); + secret4.setLastUpdatedTime(lastUpdated4); + + creds.put(secretName1, secret1); + creds.put(secretName2, secret2); creds.put(secretName3, new CredentialsToken(token3)); + creds.put(secretName4, secret4); MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/"); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -83,8 +115,9 @@ public void testGetSecretsReturnsAllSecretsOk() throws Exception { // Then... JsonArray expectedJson = new JsonArray(); - expectedJson.add(generateSecretJson(secretName2, "Username", username2, null, null)); - expectedJson.add(generateSecretJson(secretName1, "UsernamePassword", username1, password1, null)); + expectedJson.add(generateSecretJson(secretName4, "UsernameToken", username4, null, token4, null, lastUser4, lastUpdated4)); + expectedJson.add(generateSecretJson(secretName2, "Username", username2, null, null, description2, lastUser2, lastUpdated2)); + expectedJson.add(generateSecretJson(secretName1, "UsernamePassword", username1, password1, null, description1, lastUser1, lastUpdated1)); expectedJson.add(generateSecretJson(secretName3, "Token", null, null, token3)); String output = outStream.toString(); @@ -102,9 +135,10 @@ public void testGetSecretsWithUnknownSecretTypeReturnsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest("/"); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -136,10 +170,10 @@ public void testCreateUsernamePasswordSecretCreatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -175,10 +209,10 @@ public void testCreateTokenSecretCreatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -215,10 +249,10 @@ public void testCreateBase64EncodedSecretCreatesSecretOk() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -254,10 +288,10 @@ public void testCreateBase64EncodedSecretWithBadlyEncodedDataThrowsError() throw MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -290,10 +324,10 @@ public void testCreateSecretWithPasswordAndTokenThrowsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -324,10 +358,10 @@ public void testCreateSecretWithMissingSecretNameThrowsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -360,10 +394,10 @@ public void testCreateSecretWithBlankSecretNameThrowsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -396,10 +430,10 @@ public void testCreateSecretWithMissingUsernameValueThrowsError() throws Excepti MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -432,10 +466,10 @@ public void testCreateSecretWithMissingPasswordValueThrowsError() throws Excepti MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -468,10 +502,10 @@ public void testCreateSecretWithMissingTokenValueThrowsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -504,10 +538,10 @@ public void testCreateSecretWithBlankUsernameValueThrowsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -540,10 +574,10 @@ public void testCreateSecretWithBlankPasswordValueThrowsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -574,10 +608,10 @@ public void testCreateSecretWithBlankTokenValueThrowsError() throws Exception { MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -609,10 +643,10 @@ public void testCreateSecretWithUnknownEncodingValueThrowsError() throws Excepti MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -643,10 +677,10 @@ public void testCreateSecretWithPasswordAndMissingUsernameThrowsError() throws E MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -677,10 +711,10 @@ public void testCreateSecretWithExistingSecretNameThrowsError() throws Exception MockCredentialsService credsService = new MockCredentialsService(creds); MockFramework mockFramework = new MockFramework(credsService); - MockSecretsServlet servlet = new MockSecretsServlet(mockFramework); + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); - MockHttpServletRequest mockRequest = new MockHttpServletRequest(secretJsonStr, "/"); - mockRequest.setMethod(HttpMethod.POST.toString()); + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); MockHttpServletResponse servletResponse = new MockHttpServletResponse(); ServletOutputStream outStream = servletResponse.getOutputStream(); @@ -695,4 +729,80 @@ public void testCreateSecretWithExistingSecretNameThrowsError() throws Exception checkErrorStructure(output, 5075, "GAL5075E", "A secret with the provided name already exists"); } + + @Test + public void testCreateSecretWithNonLatin1SecretNameThrowsError() throws Exception { + // Given... + Map creds = new HashMap<>(); + + // Latin-1 characters are in the range 0-255, so get one that is outside this range + char nonLatin1Character = (char)300; + String secretName = "MY-EXISTING-SECRET" + nonLatin1Character; + + + JsonObject secretJson = new JsonObject(); + secretJson.addProperty("name", secretName); + secretJson.add("username", createSecretJson("my-new-username")); + String secretJsonStr = gson.toJson(secretJson); + + MockCredentialsService credsService = new MockCredentialsService(creds); + MockFramework mockFramework = new MockFramework(credsService); + + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); + + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + ServletOutputStream outStream = servletResponse.getOutputStream(); + + // When... + servlet.init(); + servlet.doPost(mockRequest, servletResponse); + + // Then... + String output = outStream.toString(); + assertThat(servletResponse.getStatus()).isEqualTo(400); + checkErrorStructure(output, 5092, "GAL5092E", + "Invalid secret name provided"); + } + + @Test + public void testCreateSecretWithNonLatin1DescriptionThrowsError() throws Exception { + // Given... + Map creds = new HashMap<>(); + + // Latin-1 characters are in the range 0-255, so get one that is outside this range + char nonLatin1Character = (char)300; + String description = Character.toString(nonLatin1Character) + " more text here!"; + String secretName = "MY-EXISTING-SECRET"; + + + JsonObject secretJson = new JsonObject(); + secretJson.addProperty("name", secretName); + secretJson.addProperty("description", description); + secretJson.add("username", createSecretJson("my-new-username")); + String secretJsonStr = gson.toJson(secretJson); + + MockCredentialsService credsService = new MockCredentialsService(creds); + MockFramework mockFramework = new MockFramework(credsService); + + MockTimeService timeService = new MockTimeService(Instant.EPOCH); + MockSecretsServlet servlet = new MockSecretsServlet(mockFramework, timeService); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest("/", secretJsonStr, HttpMethod.POST.toString(), REQUEST_HEADERS); + + MockHttpServletResponse servletResponse = new MockHttpServletResponse(); + ServletOutputStream outStream = servletResponse.getOutputStream(); + + // When... + servlet.init(); + servlet.doPost(mockRequest, servletResponse); + + // Then... + String output = outStream.toString(); + assertThat(servletResponse.getStatus()).isEqualTo(400); + checkErrorStructure(output, 5102, "GAL5102E", + "Invalid secret description provided"); + } } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsServletTest.java b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsServletTest.java index 1e22ea485..ec45ba01e 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsServletTest.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/internal/SecretsServletTest.java @@ -5,8 +5,10 @@ */ package dev.galasa.framework.api.secrets.internal; +import java.time.Instant; import java.util.Base64; import java.util.Base64.Encoder; +import java.util.Map; import com.google.gson.JsonObject; @@ -15,6 +17,8 @@ public class SecretsServletTest extends BaseServletTest { + protected static final Map REQUEST_HEADERS = Map.of("Authorization", "Bearer " + BaseServletTest.DUMMY_JWT); + protected JsonObject createSecretJson(String value, String encoding) { JsonObject secretJson = new JsonObject(); if (value != null) { @@ -38,11 +42,24 @@ protected JsonObject generateSecretJson( String username, String password, String token + ) { + return generateSecretJson(secretName, type, username, password, token, null, null, null); + } + + protected JsonObject generateSecretJson( + String secretName, + String type, + String username, + String password, + String token, + String description, + String lastUpdatedUser, + Instant lastUpdatedTime ) { JsonObject secretJson = new JsonObject(); secretJson.addProperty("apiVersion", GalasaSecretType.DEFAULT_API_VERSION); - secretJson.add("metadata", generateExpectedMetadata(secretName, type)); + secretJson.add("metadata", generateExpectedMetadata(secretName, type, description, lastUpdatedUser, lastUpdatedTime)); secretJson.add("data", generateExpectedData(username, password, token)); secretJson.addProperty("kind", "GalasaSecret"); @@ -50,11 +67,31 @@ protected JsonObject generateSecretJson( return secretJson; } - private JsonObject generateExpectedMetadata(String secretName, String type) { + private JsonObject generateExpectedMetadata( + String secretName, + String type, + String description, + String lastUpdatedUser, + Instant lastUpdatedTime + ) { JsonObject metadata = new JsonObject(); metadata.addProperty("name", secretName); + if (lastUpdatedTime != null) { + metadata.addProperty("lastUpdatedTime", lastUpdatedTime.toString()); + } + + if (lastUpdatedUser != null) { + metadata.addProperty("lastUpdatedBy", lastUpdatedUser); + } + metadata.addProperty("encoding", "base64"); + + if (description != null) { + metadata.addProperty("description", description); + } + metadata.addProperty("type", type); + return metadata; } diff --git a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/mocks/MockSecretsServlet.java b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/mocks/MockSecretsServlet.java index 6ceb170df..ed3108cb9 100644 --- a/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/mocks/MockSecretsServlet.java +++ b/galasa-parent/dev.galasa.framework.api.secrets/src/test/java/dev/galasa/framework/api/secrets/mocks/MockSecretsServlet.java @@ -5,20 +5,26 @@ */ package dev.galasa.framework.api.secrets.mocks; -import dev.galasa.framework.api.common.Environment; +import dev.galasa.framework.api.common.EnvironmentVariables; import dev.galasa.framework.api.common.ResponseBuilder; import dev.galasa.framework.api.common.mocks.MockEnvironment; import dev.galasa.framework.api.common.mocks.MockFramework; +import dev.galasa.framework.api.common.mocks.MockTimeService; import dev.galasa.framework.api.secrets.SecretsServlet; +import dev.galasa.framework.spi.utils.ITimeService; public class MockSecretsServlet extends SecretsServlet { - public MockSecretsServlet(MockFramework framework) { - this(framework, new MockEnvironment()); + public MockSecretsServlet(MockFramework framework, MockTimeService mockTimeService) { + this(framework, new MockEnvironment(), mockTimeService); } - public MockSecretsServlet(MockFramework framework, Environment env) { + public MockSecretsServlet(MockFramework framework, MockEnvironment env, ITimeService timeService) { + env.setenv(EnvironmentVariables.GALASA_USERNAME_CLAIMS, "preferred_username"); + this.framework = framework; + this.env = env; + this.timeService = timeService; setResponseBuilder(new ResponseBuilder(env)); } } diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/AbstractCredentials.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/AbstractCredentials.java new file mode 100644 index 000000000..636269bee --- /dev/null +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/AbstractCredentials.java @@ -0,0 +1,80 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package dev.galasa.framework.spi.creds; + +import java.time.Instant; +import java.util.Properties; + +import javax.crypto.spec.SecretKeySpec; + +import dev.galasa.ICredentials; + +/** + * An abstract class where common credential-related details are stored. + */ +public abstract class AbstractCredentials extends Credentials implements ICredentials { + + protected static final String CREDS_PROPERTY_PREFIX = "secure.credentials."; + + private String description; + private String lastUpdatedByUser; + private Instant lastUpdatedTime; + + public AbstractCredentials(SecretKeySpec key) throws CredentialsException { + super(key); + } + + public AbstractCredentials() { + super(); + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setLastUpdatedByUser(String username) { + this.lastUpdatedByUser = username; + } + + @Override + public void setLastUpdatedTime(Instant time) { + this.lastUpdatedTime = time; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getLastUpdatedByUser() { + return lastUpdatedByUser; + } + + @Override + public Instant getLastUpdatedTime() { + return lastUpdatedTime; + } + + @Override + public Properties getMetadataProperties(String credentialsId) { + Properties properties = new Properties(); + if (description != null) { + properties.put(CREDS_PROPERTY_PREFIX + credentialsId + ".description", description); + } + + if (lastUpdatedTime != null) { + properties.put(CREDS_PROPERTY_PREFIX + credentialsId + ".lastUpdated.time", lastUpdatedTime.toString()); + } + + if (lastUpdatedByUser != null) { + properties.put(CREDS_PROPERTY_PREFIX + credentialsId + ".lastUpdated.user", lastUpdatedByUser); + } + return properties; + } +} diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/Credentials.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/Credentials.java index ccc96ecf1..f57df24a8 100644 --- a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/Credentials.java +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/Credentials.java @@ -64,5 +64,4 @@ protected byte[] decrypt(String encrypted) throws CredentialsException { throw new CredentialsException("Unable to decrypt credentials", e); } } - } diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsToken.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsToken.java index a5a41850f..40f76c47f 100644 --- a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsToken.java +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsToken.java @@ -11,7 +11,7 @@ import dev.galasa.ICredentialsToken; -public class CredentialsToken extends Credentials implements ICredentialsToken { +public class CredentialsToken extends AbstractCredentials implements ICredentialsToken { private final byte[] token; public CredentialsToken(String plainTextToken) { @@ -36,8 +36,7 @@ public byte[] getToken() { @Override public Properties toProperties(String credentialsId) { Properties credsProperties = new Properties(); - credsProperties.setProperty("secure.credentials." + credentialsId + ".token" , new String(this.token)); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".token" , new String(this.token)); return credsProperties; } - } diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsername.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsername.java index 9e232ffc5..c9e1ad438 100644 --- a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsername.java +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsername.java @@ -12,7 +12,7 @@ import dev.galasa.ICredentialsUsername; -public class CredentialsUsername extends Credentials implements ICredentialsUsername { +public class CredentialsUsername extends AbstractCredentials implements ICredentialsUsername { private String username; public CredentialsUsername(String plainTextUsername) { @@ -36,7 +36,7 @@ public String getUsername() { @Override public Properties toProperties(String credentialsId) { Properties credsProperties = new Properties(); - credsProperties.setProperty("secure.credentials." + credentialsId + ".username" , this.username); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".username" , this.username); return credsProperties; } diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernamePassword.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernamePassword.java index 811509ed3..efa4eb03a 100644 --- a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernamePassword.java +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernamePassword.java @@ -12,7 +12,7 @@ import dev.galasa.ICredentialsUsernamePassword; -public class CredentialsUsernamePassword extends Credentials implements ICredentialsUsernamePassword { +public class CredentialsUsernamePassword extends AbstractCredentials implements ICredentialsUsernamePassword { private String username; private String password; @@ -48,8 +48,8 @@ public String getPassword() { @Override public Properties toProperties(String credentialsId) { Properties credsProperties = new Properties(); - credsProperties.setProperty("secure.credentials." + credentialsId + ".username" , this.username); - credsProperties.setProperty("secure.credentials." + credentialsId + ".password" , this.password); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".username" , this.username); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".password" , this.password); return credsProperties; } } diff --git a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernameToken.java b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernameToken.java index 37fb3c3cd..e11678409 100644 --- a/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernameToken.java +++ b/galasa-parent/dev.galasa.framework/src/main/java/dev/galasa/framework/spi/creds/CredentialsUsernameToken.java @@ -12,7 +12,7 @@ import dev.galasa.ICredentialsUsernameToken; -public class CredentialsUsernameToken extends Credentials implements ICredentialsUsernameToken { +public class CredentialsUsernameToken extends AbstractCredentials implements ICredentialsUsernameToken { private String username; private byte[] token; @@ -48,8 +48,8 @@ public byte[] getToken() { @Override public Properties toProperties(String credentialsId) { Properties credsProperties = new Properties(); - credsProperties.setProperty("secure.credentials." + credentialsId + ".username" , this.username); - credsProperties.setProperty("secure.credentials." + credentialsId + ".token" , new String(this.token)); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".username" , this.username); + credsProperties.setProperty(CREDS_PROPERTY_PREFIX + credentialsId + ".token" , new String(this.token)); return credsProperties; } } diff --git a/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/mocks/MockCredentials.java b/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/mocks/MockCredentials.java deleted file mode 100644 index 1abc94cbb..000000000 --- a/galasa-parent/dev.galasa.framework/src/test/java/dev/galasa/framework/mocks/MockCredentials.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ -package dev.galasa.framework.mocks; - -import java.util.Properties; - -import dev.galasa.ICredentials; - -class MockCredentials implements ICredentials { - - @Override - public Properties toProperties(String credentialsId) { - throw new UnsupportedOperationException("Unimplemented method 'toProperties'"); - } -}; \ No newline at end of file diff --git a/galasa-parent/dev.galasa/src/main/java/dev/galasa/ICredentials.java b/galasa-parent/dev.galasa/src/main/java/dev/galasa/ICredentials.java index 4369db949..35db1ec2b 100644 --- a/galasa-parent/dev.galasa/src/main/java/dev/galasa/ICredentials.java +++ b/galasa-parent/dev.galasa/src/main/java/dev/galasa/ICredentials.java @@ -5,8 +5,18 @@ */ package dev.galasa; +import java.time.Instant; import java.util.Properties; public interface ICredentials { Properties toProperties(String credentialsId); + Properties getMetadataProperties(String credentialsId); + + void setDescription(String description); + void setLastUpdatedByUser(String username); + void setLastUpdatedTime(Instant time); + + String getDescription(); + String getLastUpdatedByUser(); + Instant getLastUpdatedTime(); }