From d7ed8eb0361e4b97de7f4126d187b8e02812cc51 Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:12:05 +0000 Subject: [PATCH 1/2] Combine auth properties into GALASA_TOKEN and remove client secret Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- README.md | 15 +- docs/generated/errors-list.md | 1 + pkg/auth/authLogin_test.go | 36 +-- pkg/auth/authProperties.go | 67 +++--- pkg/auth/authProperties_test.go | 221 +++++------------- .../templates/galasahome/galasactl.properties | 11 +- pkg/errors/errorMessage.go | 1 + 7 files changed, 115 insertions(+), 237 deletions(-) diff --git a/README.md b/README.md index 833a9607..d1c8ab23 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ The `--galasahome` command-line flag can override the `GALASA_HOME` environment Note: If you change this to a non-existent and/or non-initialised folder path, then you will have to create and re-initialise the folder using the `galasactl local init` command again. That command will respect the `GALASA_HOME` variable and will create the folder and initialise it were it not to exist. +### GALASA_TOKEN +In order to authenticate with a Galasa ecosystem, you will need to create a personal access token from the Galasa web user interface. + +Once a personal access token has been created, you can either store the token in the galasactl.properties file within your Galasa home folder, or set the token as an environment variable named `GALASA_TOKEN`. + ## Syntax The syntax is documented in generated documentation [here](docs/generated/galasactl.md) @@ -95,17 +100,15 @@ If you wish the generated code to depend upon the very latest/bleeding-edge of g Before interacting with a Galasa ecosystem using `galasactl`, you must be authenticated with it. The `auth login` command allows you to log in to an ecosystem provided by your `GALASA_BOOTSTRAP` environment variable or through the `--bootstrap` flag. -Prior to running this command, you must have a `galasactl.properties` file in your `GALASA_HOME` directory, which is automatically created when running `galasactl local init`, that contains a set of `auth` properties with the following format: +Prior to running this command, you must have a `galasactl.properties` file in your `GALASA_HOME` directory, which is automatically created when running `galasactl local init`, that contains a `GALASA_TOKEN` property with the following format: ``` -GALASA_CLIENT_ID= -GALASA_SECRET= -GALASA_ACCESS_TOKEN= +GALASA_TOKEN= ``` -These properties can be retrieved by creating a new personal access token from a Galasa ecosystem's web user interface. +A value for the `GALASA_TOKEN` property can be retrieved by creating a new personal access token from a Galasa ecosystem's web user interface. -If you prefer, these variables can be set as environment variables instead of being read from this file. +If you prefer, this property can be set as an environment variable instead of being read from this file. On a successful login, a `bearer-token.json` file will be created in your `GALASA_HOME` directory. This file will contain a bearer token that `galasactl` will use to authenticate requests when communicating with a Galasa ecosystem. diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index 964a9b10..0a7a57e0 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -124,6 +124,7 @@ The `galasactl` tool can generate the following errors: - GAL1122E: Authentication property {} is not available, which is needed to connect to the Galasa Ecosystem. It either needs to be in a file '{}' or set as an environment variable. - GAL1123E: Failed to read 3270 terminal JSON because the content is in the wrong format. Reason: {} - GAL1124E: Internal Failure. Terminal image could not be encoded into PNG format. Reason: {} +- GAL1125E: Authentication property {} is invalid. Please ensure that it the value is made up of two parts that are separated by a '{}'. - GAL1225E: Failed to open file '{}' cause: {}. Check that this file exists, and that you have read permissions. - GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {} - GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {} diff --git a/pkg/auth/authLogin_test.go b/pkg/auth/authLogin_test.go index 0aecfb9f..2048bd06 100644 --- a/pkg/auth/authLogin_test.go +++ b/pkg/auth/authLogin_test.go @@ -31,7 +31,6 @@ func NewAuthServletMock(t *testing.T, status int, mockResponse string) *httptest requestBodyStr := string(requestBody) assert.Contains(t, requestBodyStr, "client_id") - assert.Contains(t, requestBodyStr, "secret") assert.Contains(t, requestBodyStr, "refresh_token") writer.Header().Set("Content-Type", "application/json") @@ -95,12 +94,9 @@ func TestLoginCreatesBearerTokenFileContainingJWT(t *testing.T) { galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties" mockClientId := "dummyId" - mockSecret := "shhhh" mockRefreshToken := "abcdefg" - mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken)) + tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId + mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) mockResponse := `{"jwt":"blah"}` server := NewAuthServletMock(t, 200, mockResponse) @@ -130,12 +126,9 @@ func TestLoginWithFailedFileWriteReturnsError(t *testing.T) { galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties" mockClientId := "dummyId" - mockSecret := "shhhh" mockRefreshToken := "abcdefg" - mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken)) + tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId + mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) mockFileSystem.VirtualFunction_WriteTextFile = func(path string, contents string) error { return errors.New("simulating a failed write operation") @@ -164,13 +157,9 @@ func TestLoginWithFailedTokenRequestReturnsError(t *testing.T) { galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties" mockClientId := "dummyId" - mockSecret := "shhhh" mockRefreshToken := "abcdefg" - - mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken)) + tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId + mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) mockResponse := `{"error":"something went wrong!"}` server := NewAuthServletMock(t, 500, mockResponse) @@ -194,11 +183,7 @@ func TestLoginWithMissingAuthPropertyReturnsError(t *testing.T) { galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties" - mockClientId := "dummyId" - mockSecret := "shhhh" - mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n", mockClientId, mockSecret)) + mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, "unknown.value=blah") mockResponse := `{"jwt":"blah"}` server := NewAuthServletMock(t, 200, mockResponse) @@ -283,12 +268,9 @@ func TestGetAuthenticatedAPIClientWithUnavailableAPIContinuesWithoutToken(t *tes galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties" mockClientId := "dummyId" - mockSecret := "shhhh" mockRefreshToken := "abcdefg" - mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken)) + tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId + mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) server := NewAuthServletMock(t, 500, "") defer server.Close() diff --git a/pkg/auth/authProperties.go b/pkg/auth/authProperties.go index 4de2b111..6fe16a0e 100644 --- a/pkg/auth/authProperties.go +++ b/pkg/auth/authProperties.go @@ -8,6 +8,7 @@ package auth import ( "log" "path/filepath" + "strings" "github.com/galasa-dev/cli/pkg/files" "github.com/galasa-dev/cli/pkg/galasaapi" @@ -18,40 +19,39 @@ import ( ) const ( - CLIENT_ID_PROPERTY = "GALASA_CLIENT_ID" - SECRET_PROPERTY = "GALASA_SECRET" - ACCESS_TOKEN_PROPERTY = "GALASA_ACCESS_TOKEN" + TOKEN_PROPERTY = "GALASA_TOKEN" + TOKEN_SEPARATOR = ":" ) // Gets authentication properties from the user's galasactl.properties file or from the environment or a mixture. func GetAuthProperties(fileSystem files.FileSystem, galasaHome utils.GalasaHome, env utils.Environment) (galasaapi.AuthProperties, error) { var err error = nil + authProperties := galasaapi.NewAuthProperties() // Work out which file we we want to draw properties from. galasactlPropertiesFilePath := filepath.Join(galasaHome.GetNativeFolderPath(), "galasactl.properties") - // Get the file-based properties if we can - authProperties, fileAccessErr := getAuthPropertiesFromFile(fileSystem, galasactlPropertiesFilePath, env) - if fileAccessErr != nil { - authProperties = *galasaapi.NewAuthProperties() - } + // Get the file-based token property if we can + tokenProperty, fileAccessErr := getPropertyFromFile(fileSystem, galasactlPropertiesFilePath, env, TOKEN_PROPERTY) - // We now have a structure which may be filled-in with values from the file. - // Over-write those values if there is an environment variable set to do that. - authProperties.SetClientId(getPropertyWithOverride(env, authProperties.GetClientId(), galasactlPropertiesFilePath, CLIENT_ID_PROPERTY)) - authProperties.SetRefreshToken(getPropertyWithOverride(env, authProperties.GetRefreshToken(), galasactlPropertiesFilePath, ACCESS_TOKEN_PROPERTY)) - authProperties.SetSecret(getPropertyWithOverride(env, authProperties.GetSecret(), galasactlPropertiesFilePath, SECRET_PROPERTY)) + // Over-write the token property value if there is an environment variable set to do that. + tokenProperty = getPropertyWithOverride(env, tokenProperty, galasactlPropertiesFilePath, TOKEN_PROPERTY) // Make sure all the properties have values that we need. - err = checkPropertyIsSet(authProperties.GetClientId(), CLIENT_ID_PROPERTY, galasactlPropertiesFilePath, fileAccessErr) + err = checkPropertyIsSet(tokenProperty, TOKEN_PROPERTY, galasactlPropertiesFilePath, fileAccessErr) if err == nil { - err = checkPropertyIsSet(authProperties.GetRefreshToken(), ACCESS_TOKEN_PROPERTY, galasactlPropertiesFilePath, fileAccessErr) + var refreshToken string + var clientId string + + // Get the authentication properties from the token + refreshToken, clientId, err = extractPropertiesFromToken(tokenProperty) if err == nil { - err = checkPropertyIsSet(authProperties.GetSecret(), SECRET_PROPERTY, galasactlPropertiesFilePath, fileAccessErr) + authProperties.SetClientId(clientId) + authProperties.SetRefreshToken(refreshToken) } } - return authProperties, err + return *authProperties, err } func checkPropertyIsSet(propertyValue string, propertyName string, galasactlPropertiesFilePath string, fileAccessErr error) error { @@ -84,24 +84,33 @@ func getPropertyWithOverride(env utils.Environment, valueFromFile string, filePa return value } -// Gets authentication properties from the user's galasactl.properties file -func getAuthPropertiesFromFile(fileSystem files.FileSystem, galasactlPropertiesFilePath string, env utils.Environment) (galasaapi.AuthProperties, error) { +// Gets a property from the user's galasactl.properties file +func getPropertyFromFile(fileSystem files.FileSystem, galasactlPropertiesFilePath string, env utils.Environment, propertyName string) (string, error) { var err error = nil - authProperties := galasaapi.NewAuthProperties() - var galasactlProperties props.JavaProperties galasactlProperties, err = props.ReadPropertiesFile(fileSystem, galasactlPropertiesFilePath) - if err == nil { + if err != nil { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_FAILED_TO_READ_FILE, galasactlPropertiesFilePath, err.Error()) + } - if err == nil { - authProperties.SetClientId(galasactlProperties[CLIENT_ID_PROPERTY]) - authProperties.SetSecret(galasactlProperties[SECRET_PROPERTY]) - authProperties.SetRefreshToken(galasactlProperties[ACCESS_TOKEN_PROPERTY]) - } + return galasactlProperties[propertyName], err +} + +func extractPropertiesFromToken(token string) (string, string, error) { + var err error + var refreshToken string + var clientId string + + // The GALASA_TOKEN property should be in the form {GALASA_ACCESS_TOKEN}:{GALASA_CLIENT_ID}, + // so it should split into two parts. + tokenParts := strings.Split(token, TOKEN_SEPARATOR) + if len(tokenParts) == 2 && tokenParts[0] != "" && tokenParts[1] != "" { + refreshToken = tokenParts[0] + clientId = tokenParts[1] } else { - err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_FAILED_TO_READ_FILE, galasactlPropertiesFilePath, err.Error()) + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_BAD_TOKEN_PROPERTY_FORMAT, TOKEN_PROPERTY, TOKEN_SEPARATOR) } - return *authProperties, err + return refreshToken, clientId, err } diff --git a/pkg/auth/authProperties_test.go b/pkg/auth/authProperties_test.go index 2c17c7c1..afb7aeeb 100644 --- a/pkg/auth/authProperties_test.go +++ b/pkg/auth/authProperties_test.go @@ -20,289 +20,173 @@ func TestGetAuthPropertiesWithValidPropertiesUnmarshalsAuthProperties(t *testing mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "dummyId" - secretValue := "dummySecret" accessTokenValue := "abc" + clientIdValue := "dummyId" + tokenPropertyValue := accessTokenValue + TOKEN_SEPARATOR + clientIdValue mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", clientIdValue, secretValue, accessTokenValue)) + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) + // When... authProperties, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and all required properties are present") + assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and the token property is present") assert.Equal(t, clientIdValue, authProperties.GetClientId()) - assert.Equal(t, secretValue, authProperties.GetSecret()) assert.Equal(t, accessTokenValue, authProperties.GetRefreshToken()) } -func TestGetAuthPropertiesWithNoClientIdValidPropertiesUnmarshalsAuthPropertiesFails(t *testing.T) { +func TestGetAuthPropertiesWithNoClientIdInTokenReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - secretValue := "dummySecret" - accessTokenValue := "abc" + tokenPropertyValue := "this-is-my-access-token" mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - // "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", - // clientIdValue, - secretValue, accessTokenValue)) + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) + // When... _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but the client id is missing from the file.") - assert.Contains(t, err.Error(), "GAL1122E") - assert.Contains(t, err.Error(), "GALASA_CLIENT_ID") + assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but is missing part of the token value.") + assert.Contains(t, err.Error(), "GAL1125E") + assert.Contains(t, err.Error(), "GALASA_TOKEN") } -func TestGetAuthPropertiesWithNoSecretIdValidPropertiesUnmarshalsAuthPropertiesFails(t *testing.T) { +func TestGetAuthPropertiesWithSeparatorButNoClientIdReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "my-client-id" - // secretValue := "dummySecret" - accessTokenValue := "abc" + tokenPropertyValue := "my-token" + TOKEN_SEPARATOR mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - // "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", - clientIdValue, - // secretValue, - accessTokenValue)) + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) + // When... _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but the secret is missing from the file.") - assert.Contains(t, err.Error(), "GAL1122E") - assert.Contains(t, err.Error(), "GALASA_SECRET") + assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but is missing the client ID part of the token.") + assert.Contains(t, err.Error(), "GAL1125E") + assert.Contains(t, err.Error(), "GALASA_TOKEN") } -func TestGetAuthPropertiesWithNoRefreshTokenIdValidPropertiesUnmarshalsAuthPropertiesFails(t *testing.T) { +func TestGetAuthPropertiesWithSeparatorButNoAccessTokenReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "my-client-id" - secretValue := "dummySecret" - // accessTokenValue := "abc" + tokenPropertyValue := TOKEN_SEPARATOR + "my-client-id" mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n", - // "GALASA_ACCESS_TOKEN=%s", - clientIdValue, - secretValue, - // accessTokenValue, - )) - // When... - _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) - - // Then... - assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but the secret is missing from the file.") - assert.Contains(t, err.Error(), "GAL1122E") - assert.Contains(t, err.Error(), "GALASA_ACCESS_TOKEN") -} - -func TestGetAuthPropertiesWithEmptyGalasactlPropertiesReturnsError(t *testing.T) { - // Given... - mockFileSystem := files.NewMockFileSystem() - mockEnvironment := utils.NewMockEnv() - mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - - mockFileSystem.WriteTextFile(mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", "") + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) // When... _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.NotNil(t, err, "Should return an error if the galasactl.properties is empty") - assert.ErrorContains(t, err, "GAL1122E") + assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but is missing the access token part of the token.") + assert.Contains(t, err.Error(), "GAL1125E") + assert.Contains(t, err.Error(), "GALASA_TOKEN") } -func TestGetAuthPropertiesWithMissingPropertiesReturnsError(t *testing.T) { +func TestGetAuthPropertiesWithBadlyFormattedTokenReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "dummyId" + tokenPropertyValue := "this:is:a:token:with:too:many:parts" - // Create a galasactl.properties file that is missing the GALASA_SECRET and GALASA_ACCESS_TOKEN properties mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf("GALASA_CLIENT_ID=%s", clientIdValue)) + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) // When... _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.NotNil(t, err, "Should return an error if the galasactl.properties exists and some required properties are missing") - assert.ErrorContains(t, err, "GAL1122E") + assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but the access token is missing from the file.") + assert.Contains(t, err.Error(), "GAL1125E") + assert.Contains(t, err.Error(), "GALASA_TOKEN") } -func TestGetAuthPropertiesWithMissingGalasactlPropertiesFileReturnsError(t *testing.T) { +func TestGetAuthPropertiesWithEmptyGalasactlPropertiesReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") + mockFileSystem.WriteTextFile(mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", "") + // When... _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.NotNil(t, err, "Should return an error if the galasactl.properties does not exist") - assert.ErrorContains(t, err, "GAL1043E") + assert.NotNil(t, err, "Should return an error if the galasactl.properties is empty and an environment variable has not been set") + assert.ErrorContains(t, err, "GAL1122E") } -func TestGetAuthPropertiesEnvVarsOverridesFileValues(t *testing.T) { +func TestGetAuthPropertiesWithMissingTokenPropertyReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "client-id-from-file" - secretValue := "secret-from-file" - accessTokenValue := "token-from-file" - - mockFileSystem.WriteTextFile( - mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", clientIdValue, secretValue, accessTokenValue)) - - clientIdValue = "client-id-from-env-var" - secretValue = "secret-from-env-var" - accessTokenValue = "token-from-env-var" - - mockEnvironment.SetEnv(CLIENT_ID_PROPERTY, clientIdValue) - mockEnvironment.SetEnv(SECRET_PROPERTY, secretValue) - mockEnvironment.SetEnv(ACCESS_TOKEN_PROPERTY, accessTokenValue) + // Create a galasactl.properties file that is missing the GALASA_TOKEN property + mockFileSystem.WriteTextFile(mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", "unknown.value=blah") // When... - authProperties, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) + _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and all required properties are present") - assert.Equal(t, clientIdValue, authProperties.GetClientId()) - assert.Equal(t, secretValue, authProperties.GetSecret()) - assert.Equal(t, accessTokenValue, authProperties.GetRefreshToken()) + assert.NotNil(t, err, "Should return an error if the galasactl.properties exists and is missing a token property") + assert.ErrorContains(t, err, "GAL1122E") } -func TestGetAuthPropertiesClientIDEnvVarOverridesFileValue(t *testing.T) { +func TestGetAuthPropertiesWithMissingGalasactlPropertiesFileReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "client-id-from-file" - secretValue := "secret-from-file" - accessTokenValue := "token-from-file" - - mockFileSystem.WriteTextFile( - mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", clientIdValue, secretValue, accessTokenValue)) - - clientIdValue = "client-id-from-env-var" - // secretValue = "secret-from-env-var" - // accessTokenValue = "token-from-env-var" - - mockEnvironment.SetEnv(CLIENT_ID_PROPERTY, clientIdValue) - // mockEnvironment.SetEnv(SECRET_PROPERTY, secretValue) - // mockEnvironment.SetEnv(ACCESS_TOKEN_PROPERTY, accessTokenValue) - // When... - authProperties, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) + _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) // Then... - assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and all required properties are present") - assert.Equal(t, clientIdValue, authProperties.GetClientId()) - assert.Equal(t, secretValue, authProperties.GetSecret()) - assert.Equal(t, accessTokenValue, authProperties.GetRefreshToken()) + assert.NotNil(t, err, "Should return an error if the galasactl.properties does not exist") + assert.ErrorContains(t, err, "GAL1043E") } -func TestGetAuthPropertiesSecretEnvVarOverridesFileValue(t *testing.T) { +func TestGetAuthPropertiesTokenEnvVarOverridesFileValue(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem() mockEnvironment := utils.NewMockEnv() mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "client-id-from-file" - secretValue := "secret-from-file" accessTokenValue := "token-from-file" - - mockFileSystem.WriteTextFile( - mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", clientIdValue, secretValue, accessTokenValue)) - - // clientIdValue = "client-id-from-env-var" - secretValue = "secret-from-env-var" - // accessTokenValue = "token-from-env-var" - - // mockEnvironment.SetEnv(CLIENT_ID_PROPERTY, clientIdValue) - mockEnvironment.SetEnv(SECRET_PROPERTY, secretValue) - // mockEnvironment.SetEnv(ACCESS_TOKEN_PROPERTY, accessTokenValue) - - // When... - authProperties, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) - - // Then... - assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and all required properties are present") - assert.Equal(t, clientIdValue, authProperties.GetClientId()) - assert.Equal(t, secretValue, authProperties.GetSecret()) - assert.Equal(t, accessTokenValue, authProperties.GetRefreshToken()) -} - -func TestGetAuthPropertiesRefreshTokenEnvVarOverridesFileValue(t *testing.T) { - // Given... - mockFileSystem := files.NewMockFileSystem() - mockEnvironment := utils.NewMockEnv() - mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") - clientIdValue := "client-id-from-file" - secretValue := "secret-from-file" - accessTokenValue := "token-from-file" + tokenPropertyValue := accessTokenValue + TOKEN_SEPARATOR + clientIdValue mockFileSystem.WriteTextFile( mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", - fmt.Sprintf( - "GALASA_CLIENT_ID=%s\n"+ - "GALASA_SECRET=%s\n"+ - "GALASA_ACCESS_TOKEN=%s", clientIdValue, secretValue, accessTokenValue)) + fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue)) - // clientIdValue = "client-id-from-env-var" - // secretValue = "secret-from-env-var" accessTokenValue = "token-from-env-var" + clientIdValue = "client-id-from-env-var" + tokenPropertyValue = accessTokenValue + TOKEN_SEPARATOR + clientIdValue - // mockEnvironment.SetEnv(CLIENT_ID_PROPERTY, clientIdValue) - // mockEnvironment.SetEnv(SECRET_PROPERTY, secretValue) - mockEnvironment.SetEnv(ACCESS_TOKEN_PROPERTY, accessTokenValue) + mockEnvironment.SetEnv(TOKEN_PROPERTY, tokenPropertyValue) // When... authProperties, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) @@ -310,6 +194,5 @@ func TestGetAuthPropertiesRefreshTokenEnvVarOverridesFileValue(t *testing.T) { // Then... assert.Nil(t, err, "Should not return an error if the galasactl.properties exists and all required properties are present") assert.Equal(t, clientIdValue, authProperties.GetClientId()) - assert.Equal(t, secretValue, authProperties.GetSecret()) assert.Equal(t, accessTokenValue, authProperties.GetRefreshToken()) } diff --git a/pkg/embedded/templates/galasahome/galasactl.properties b/pkg/embedded/templates/galasahome/galasactl.properties index bf057960..0388c89d 100644 --- a/pkg/embedded/templates/galasahome/galasactl.properties +++ b/pkg/embedded/templates/galasahome/galasactl.properties @@ -1,8 +1,7 @@ -# The following properties allow the galasactl tool to communicate with the Galasa Ecosystem. +# The GALASA_TOKEN property allows the galasactl tool to communicate with the Galasa Ecosystem. # -# To obtain these values, log in to the Galasa web user interface and allocate a new personal -# access token. +# To obtain a value for this property, log in to the Galasa web user interface and allocate a new personal +# access token. Once a personal access token has been allocated, please follow the instructions on the web +# user interface to copy the token into this file or set it as an environment variable. # -# GALASA_ACCESS_TOKEN="my-access-token" -# GALASA_CLIENT_ID="my-client-id" -# GALASA_SECRET="my-secret" +# GALASA_TOKEN=example:token diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index aae18c46..da1238a6 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -210,6 +210,7 @@ var ( GALASA_ERROR_AUTH_PROPERTY_NOT_AVAILABLE = NewMessageType("GAL1122E: Authentication property %s is not available, which is needed to connect to the Galasa Ecosystem. It either needs to be in a file '%s' or set as an environment variable.", 1122, STACK_TRACE_NOT_WANTED) GALASA_ERROR_BAD_TERMINAL_JSON_FORMAT = NewMessageType("GAL1123E: Failed to read 3270 terminal JSON because the content is in the wrong format. Reason: %s", 1123, STACK_TRACE_WANTED) GALASA_ERROR_PNG_ENCODING_FAILED = NewMessageType("GAL1124E: Internal Failure. Terminal image could not be encoded into PNG format. Reason: %s", 1124, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_BAD_TOKEN_PROPERTY_FORMAT = NewMessageType("GAL1125E: Authentication property %s is invalid. Please ensure that it the value is made up of two parts that are separated by a '%s'.", 1125, STACK_TRACE_NOT_WANTED) GALASA_ERROR_FAILED_TO_OPEN_GZIP_FILE = NewMessageType("GAL1225E: Failed to open file '%s' cause: %v. Check that this file exists, and that you have read permissions.", 1225, STACK_TRACE_NOT_WANTED) GALASA_ERROR_FAILED_TO_SETUP_READER_GZIP_FILE = NewMessageType("GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: %s error: %v", 1226, STACK_TRACE_NOT_WANTED) GALASA_ERROR_FAILED_TO_UNCOMPRESS_GZIP_FILE = NewMessageType("GAL1227E: Internal failure. Contents of gzip could not be decoded. %v error: %v", 1227, STACK_TRACE_NOT_WANTED) From 79f9433696f9a60f6ba4249266c267d0e90f73bd Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:15:15 +0000 Subject: [PATCH 2/2] Add unit test for GALASA_TOKEN=: case Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- pkg/auth/authProperties_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkg/auth/authProperties_test.go b/pkg/auth/authProperties_test.go index afb7aeeb..ecb49780 100644 --- a/pkg/auth/authProperties_test.go +++ b/pkg/auth/authProperties_test.go @@ -58,6 +58,25 @@ func TestGetAuthPropertiesWithNoClientIdInTokenReturnsError(t *testing.T) { assert.Contains(t, err.Error(), "GALASA_TOKEN") } +func TestGetAuthPropertiesWithOnlySeparatorReturnsError(t *testing.T) { + // Given... + mockFileSystem := files.NewMockFileSystem() + mockEnvironment := utils.NewMockEnv() + mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "") + + mockFileSystem.WriteTextFile( + mockGalasaHome.GetNativeFolderPath()+"/galasactl.properties", + fmt.Sprintf("GALASA_TOKEN=%s", TOKEN_SEPARATOR)) + + // When... + _, err := GetAuthProperties(mockFileSystem, mockGalasaHome, mockEnvironment) + + // Then... + assert.NotNil(t, err, "Should return an error as the galasactl.properties exists but is missing the access token and client ID parts of the token.") + assert.Contains(t, err.Error(), "GAL1125E") + assert.Contains(t, err.Error(), "GALASA_TOKEN") +} + func TestGetAuthPropertiesWithSeparatorButNoClientIdReturnsError(t *testing.T) { // Given... mockFileSystem := files.NewMockFileSystem()