Skip to content

Commit b34f68a

Browse files
authored
Update auth property format to GALASA_TOKEN and remove client secret from token (#211)
* Combine auth properties into GALASA_TOKEN and remove client secret Signed-off-by: Eamonn Mansour <[email protected]> * Add unit test for GALASA_TOKEN=: case Signed-off-by: Eamonn Mansour <[email protected]> --------- Signed-off-by: Eamonn Mansour <[email protected]>
1 parent 0c7a34b commit b34f68a

File tree

7 files changed

+124
-227
lines changed

7 files changed

+124
-227
lines changed

README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ The `--galasahome` command-line flag can override the `GALASA_HOME` environment
1818
Note: If you change this to a non-existent and/or non-initialised folder path, then
1919
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.
2020

21+
### GALASA_TOKEN
22+
In order to authenticate with a Galasa ecosystem, you will need to create a personal access token from the Galasa web user interface.
23+
24+
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`.
25+
2126

2227
## Syntax
2328
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
95100

96101
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.
97102

98-
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:
103+
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:
99104

100105
```
101-
GALASA_CLIENT_ID=<a client identifier>
102-
GALASA_SECRET=<a client secret>
103-
GALASA_ACCESS_TOKEN=<a personal access token>
106+
GALASA_TOKEN=<your personal access token>
104107
```
105108

106-
These properties can be retrieved by creating a new personal access token from a Galasa ecosystem's web user interface.
109+
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.
107110

108-
If you prefer, these variables can be set as environment variables instead of being read from this file.
111+
If you prefer, this property can be set as an environment variable instead of being read from this file.
109112

110113
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.
111114

docs/generated/errors-list.md

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ The `galasactl` tool can generate the following errors:
124124
- 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.
125125
- GAL1123E: Failed to read 3270 terminal JSON because the content is in the wrong format. Reason: {}
126126
- GAL1124E: Internal Failure. Terminal image could not be encoded into PNG format. Reason: {}
127+
- GAL1125E: Authentication property {} is invalid. Please ensure that it the value is made up of two parts that are separated by a '{}'.
127128
- GAL1225E: Failed to open file '{}' cause: {}. Check that this file exists, and that you have read permissions.
128129
- GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {}
129130
- GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {}

pkg/auth/authLogin_test.go

+9-27
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ func NewAuthServletMock(t *testing.T, status int, mockResponse string) *httptest
3131

3232
requestBodyStr := string(requestBody)
3333
assert.Contains(t, requestBodyStr, "client_id")
34-
assert.Contains(t, requestBodyStr, "secret")
3534
assert.Contains(t, requestBodyStr, "refresh_token")
3635

3736
writer.Header().Set("Content-Type", "application/json")
@@ -95,12 +94,9 @@ func TestLoginCreatesBearerTokenFileContainingJWT(t *testing.T) {
9594
galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties"
9695

9796
mockClientId := "dummyId"
98-
mockSecret := "shhhh"
9997
mockRefreshToken := "abcdefg"
100-
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf(
101-
"GALASA_CLIENT_ID=%s\n"+
102-
"GALASA_SECRET=%s\n"+
103-
"GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken))
98+
tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId
99+
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue))
104100

105101
mockResponse := `{"jwt":"blah"}`
106102
server := NewAuthServletMock(t, 200, mockResponse)
@@ -130,12 +126,9 @@ func TestLoginWithFailedFileWriteReturnsError(t *testing.T) {
130126
galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties"
131127

132128
mockClientId := "dummyId"
133-
mockSecret := "shhhh"
134129
mockRefreshToken := "abcdefg"
135-
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf(
136-
"GALASA_CLIENT_ID=%s\n"+
137-
"GALASA_SECRET=%s\n"+
138-
"GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken))
130+
tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId
131+
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue))
139132

140133
mockFileSystem.VirtualFunction_WriteTextFile = func(path string, contents string) error {
141134
return errors.New("simulating a failed write operation")
@@ -164,13 +157,9 @@ func TestLoginWithFailedTokenRequestReturnsError(t *testing.T) {
164157
galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties"
165158

166159
mockClientId := "dummyId"
167-
mockSecret := "shhhh"
168160
mockRefreshToken := "abcdefg"
169-
170-
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf(
171-
"GALASA_CLIENT_ID=%s\n"+
172-
"GALASA_SECRET=%s\n"+
173-
"GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken))
161+
tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId
162+
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue))
174163

175164
mockResponse := `{"error":"something went wrong!"}`
176165
server := NewAuthServletMock(t, 500, mockResponse)
@@ -194,11 +183,7 @@ func TestLoginWithMissingAuthPropertyReturnsError(t *testing.T) {
194183

195184
galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties"
196185

197-
mockClientId := "dummyId"
198-
mockSecret := "shhhh"
199-
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf(
200-
"GALASA_CLIENT_ID=%s\n"+
201-
"GALASA_SECRET=%s\n", mockClientId, mockSecret))
186+
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, "unknown.value=blah")
202187

203188
mockResponse := `{"jwt":"blah"}`
204189
server := NewAuthServletMock(t, 200, mockResponse)
@@ -283,12 +268,9 @@ func TestGetAuthenticatedAPIClientWithUnavailableAPIContinuesWithoutToken(t *tes
283268
galasactlPropertiesFilePath := mockGalasaHome.GetNativeFolderPath() + "/galasactl.properties"
284269

285270
mockClientId := "dummyId"
286-
mockSecret := "shhhh"
287271
mockRefreshToken := "abcdefg"
288-
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf(
289-
"GALASA_CLIENT_ID=%s\n"+
290-
"GALASA_SECRET=%s\n"+
291-
"GALASA_ACCESS_TOKEN=%s", mockClientId, mockSecret, mockRefreshToken))
272+
tokenPropertyValue := mockRefreshToken + TOKEN_SEPARATOR + mockClientId
273+
mockFileSystem.WriteTextFile(galasactlPropertiesFilePath, fmt.Sprintf("GALASA_TOKEN=%s", tokenPropertyValue))
292274

293275
server := NewAuthServletMock(t, 500, "")
294276
defer server.Close()

pkg/auth/authProperties.go

+38-29
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package auth
88
import (
99
"log"
1010
"path/filepath"
11+
"strings"
1112

1213
"github.com/galasa-dev/cli/pkg/files"
1314
"github.com/galasa-dev/cli/pkg/galasaapi"
@@ -18,40 +19,39 @@ import (
1819
)
1920

2021
const (
21-
CLIENT_ID_PROPERTY = "GALASA_CLIENT_ID"
22-
SECRET_PROPERTY = "GALASA_SECRET"
23-
ACCESS_TOKEN_PROPERTY = "GALASA_ACCESS_TOKEN"
22+
TOKEN_PROPERTY = "GALASA_TOKEN"
23+
TOKEN_SEPARATOR = ":"
2424
)
2525

2626
// Gets authentication properties from the user's galasactl.properties file or from the environment or a mixture.
2727
func GetAuthProperties(fileSystem files.FileSystem, galasaHome utils.GalasaHome, env utils.Environment) (galasaapi.AuthProperties, error) {
2828
var err error = nil
29+
authProperties := galasaapi.NewAuthProperties()
2930

3031
// Work out which file we we want to draw properties from.
3132
galasactlPropertiesFilePath := filepath.Join(galasaHome.GetNativeFolderPath(), "galasactl.properties")
3233

33-
// Get the file-based properties if we can
34-
authProperties, fileAccessErr := getAuthPropertiesFromFile(fileSystem, galasactlPropertiesFilePath, env)
35-
if fileAccessErr != nil {
36-
authProperties = *galasaapi.NewAuthProperties()
37-
}
34+
// Get the file-based token property if we can
35+
tokenProperty, fileAccessErr := getPropertyFromFile(fileSystem, galasactlPropertiesFilePath, env, TOKEN_PROPERTY)
3836

39-
// We now have a structure which may be filled-in with values from the file.
40-
// Over-write those values if there is an environment variable set to do that.
41-
authProperties.SetClientId(getPropertyWithOverride(env, authProperties.GetClientId(), galasactlPropertiesFilePath, CLIENT_ID_PROPERTY))
42-
authProperties.SetRefreshToken(getPropertyWithOverride(env, authProperties.GetRefreshToken(), galasactlPropertiesFilePath, ACCESS_TOKEN_PROPERTY))
43-
authProperties.SetSecret(getPropertyWithOverride(env, authProperties.GetSecret(), galasactlPropertiesFilePath, SECRET_PROPERTY))
37+
// Over-write the token property value if there is an environment variable set to do that.
38+
tokenProperty = getPropertyWithOverride(env, tokenProperty, galasactlPropertiesFilePath, TOKEN_PROPERTY)
4439

4540
// Make sure all the properties have values that we need.
46-
err = checkPropertyIsSet(authProperties.GetClientId(), CLIENT_ID_PROPERTY, galasactlPropertiesFilePath, fileAccessErr)
41+
err = checkPropertyIsSet(tokenProperty, TOKEN_PROPERTY, galasactlPropertiesFilePath, fileAccessErr)
4742
if err == nil {
48-
err = checkPropertyIsSet(authProperties.GetRefreshToken(), ACCESS_TOKEN_PROPERTY, galasactlPropertiesFilePath, fileAccessErr)
43+
var refreshToken string
44+
var clientId string
45+
46+
// Get the authentication properties from the token
47+
refreshToken, clientId, err = extractPropertiesFromToken(tokenProperty)
4948
if err == nil {
50-
err = checkPropertyIsSet(authProperties.GetSecret(), SECRET_PROPERTY, galasactlPropertiesFilePath, fileAccessErr)
49+
authProperties.SetClientId(clientId)
50+
authProperties.SetRefreshToken(refreshToken)
5151
}
5252
}
5353

54-
return authProperties, err
54+
return *authProperties, err
5555
}
5656

5757
func checkPropertyIsSet(propertyValue string, propertyName string, galasactlPropertiesFilePath string, fileAccessErr error) error {
@@ -84,24 +84,33 @@ func getPropertyWithOverride(env utils.Environment, valueFromFile string, filePa
8484
return value
8585
}
8686

87-
// Gets authentication properties from the user's galasactl.properties file
88-
func getAuthPropertiesFromFile(fileSystem files.FileSystem, galasactlPropertiesFilePath string, env utils.Environment) (galasaapi.AuthProperties, error) {
87+
// Gets a property from the user's galasactl.properties file
88+
func getPropertyFromFile(fileSystem files.FileSystem, galasactlPropertiesFilePath string, env utils.Environment, propertyName string) (string, error) {
8989
var err error = nil
90-
authProperties := galasaapi.NewAuthProperties()
91-
9290
var galasactlProperties props.JavaProperties
9391
galasactlProperties, err = props.ReadPropertiesFile(fileSystem, galasactlPropertiesFilePath)
94-
if err == nil {
92+
if err != nil {
93+
err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_FAILED_TO_READ_FILE, galasactlPropertiesFilePath, err.Error())
94+
}
9595

96-
if err == nil {
97-
authProperties.SetClientId(galasactlProperties[CLIENT_ID_PROPERTY])
98-
authProperties.SetSecret(galasactlProperties[SECRET_PROPERTY])
99-
authProperties.SetRefreshToken(galasactlProperties[ACCESS_TOKEN_PROPERTY])
100-
}
96+
return galasactlProperties[propertyName], err
97+
}
98+
99+
func extractPropertiesFromToken(token string) (string, string, error) {
100+
var err error
101+
var refreshToken string
102+
var clientId string
103+
104+
// The GALASA_TOKEN property should be in the form {GALASA_ACCESS_TOKEN}:{GALASA_CLIENT_ID},
105+
// so it should split into two parts.
106+
tokenParts := strings.Split(token, TOKEN_SEPARATOR)
101107

108+
if len(tokenParts) == 2 && tokenParts[0] != "" && tokenParts[1] != "" {
109+
refreshToken = tokenParts[0]
110+
clientId = tokenParts[1]
102111
} else {
103-
err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_FAILED_TO_READ_FILE, galasactlPropertiesFilePath, err.Error())
112+
err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_BAD_TOKEN_PROPERTY_FORMAT, TOKEN_PROPERTY, TOKEN_SEPARATOR)
104113
}
105114

106-
return *authProperties, err
115+
return refreshToken, clientId, err
107116
}

0 commit comments

Comments
 (0)