From d7f1e3aec3903efd2270ae5aacf342af39c53d38 Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Mon, 2 Aug 2021 17:01:07 -0500 Subject: [PATCH 1/6] fix: remove VSI use-case from ComputeResourceAuthenticator This commit removes the aspects of the new ComputeResourceAuthenticator related to the VPC/VSI use-case. We're doing this due to some changes in the design of that use-case. With these changes, the ComputeResourceAuthenticator will support only the IKS file-based use-case. --- v5/core/authenticator_factory_test.go | 7 +- v5/core/common_test.go | 1 - v5/core/constants.go | 30 ++- v5/core/cr_authenticator.go | 280 ++++++++------------------ v5/core/cr_authenticator_test.go | 266 +++++++++++------------- v5/resources/my-credentials.env | 1 - 6 files changed, 214 insertions(+), 371 deletions(-) diff --git a/v5/core/authenticator_factory_test.go b/v5/core/authenticator_factory_test.go index 0905862..be58eaf 100644 --- a/v5/core/authenticator_factory_test.go +++ b/v5/core/authenticator_factory_test.go @@ -18,7 +18,6 @@ package core import ( "os" - "path" "testing" "github.com/stretchr/testify/assert" @@ -31,9 +30,7 @@ import ( // clearTestVCAP() func TestGetAuthenticatorFromEnvironment1(t *testing.T) { - pwd, _ := os.Getwd() - credentialFilePath := path.Join(pwd, "/../resources/my-credentials.env") - os.Setenv("IBM_CREDENTIALS_FILE", credentialFilePath) + os.Setenv("IBM_CREDENTIALS_FILE", "../resources/my-credentials.env") authenticator, err := GetAuthenticatorFromEnvironment("service-1") assert.Nil(t, err) @@ -67,7 +64,6 @@ func TestGetAuthenticatorFromEnvironment1(t *testing.T) { assert.True(t, ok) assert.NotNil(t, crAuthenticator) assert.Equal(t, "crtoken.txt", crAuthenticator.CRTokenFilename) - assert.Equal(t, "http://1.1.1.1", crAuthenticator.InstanceMetadataServiceURL) assert.Equal(t, "iam-user1", crAuthenticator.IAMProfileName) assert.Equal(t, "iam-id1", crAuthenticator.IAMProfileID) assert.Equal(t, "https://iamhost/iam/api", crAuthenticator.URL) @@ -109,7 +105,6 @@ func TestGetAuthenticatorFromEnvironment2(t *testing.T) { assert.True(t, ok) assert.NotNil(t, crAuthenticator) assert.Equal(t, "crtoken.txt", crAuthenticator.CRTokenFilename) - assert.Equal(t, "http://2.2.2.2", crAuthenticator.InstanceMetadataServiceURL) assert.Equal(t, "iam-user2", crAuthenticator.IAMProfileName) assert.Equal(t, "iam-id2", crAuthenticator.IAMProfileID) assert.Equal(t, "https://iamhost/iam/api", crAuthenticator.URL) diff --git a/v5/core/common_test.go b/v5/core/common_test.go index 2162653..499aee6 100644 --- a/v5/core/common_test.go +++ b/v5/core/common_test.go @@ -69,7 +69,6 @@ var testEnvironment = map[string]string{ "SERVICE6_SCOPE": "A B C D", "SERVICE7_AUTH_TYPE": "crauth", "SERVICE7_CR_TOKEN_FILENAME": "crtoken.txt", - "SERVICE7_INSTANCE_METADATA_SERVICE_URL": "http://2.2.2.2", "SERVICE7_IAM_PROFILE_NAME": "iam-user2", "SERVICE7_IAM_PROFILE_ID": "iam-id2", "SERVICE7_AUTH_URL": "https://iamhost/iam/api", diff --git a/v5/core/constants.go b/v5/core/constants.go index e1ae651..19e167d 100644 --- a/v5/core/constants.go +++ b/v5/core/constants.go @@ -35,20 +35,19 @@ const ( PROPNAME_SVC_RETRY_INTERVAL = "RETRY_INTERVAL" // Authenticator properties. - PROPNAME_AUTH_TYPE = "AUTH_TYPE" - PROPNAME_USERNAME = "USERNAME" - PROPNAME_PASSWORD = "PASSWORD" - PROPNAME_BEARER_TOKEN = "BEARER_TOKEN" - PROPNAME_AUTH_URL = "AUTH_URL" - PROPNAME_AUTH_DISABLE_SSL = "AUTH_DISABLE_SSL" - PROPNAME_APIKEY = "APIKEY" - PROPNAME_CLIENT_ID = "CLIENT_ID" - PROPNAME_CLIENT_SECRET = "CLIENT_SECRET" - PROPNAME_SCOPE = "SCOPE" - PROPNAME_CRTOKEN_FILENAME = "CR_TOKEN_FILENAME" - PROPNAME_INSTANCE_METADATA_SERVICE_URL = "INSTANCE_METADATA_SERVICE_URL" - PROPNAME_IAM_PROFILE_NAME = "IAM_PROFILE_NAME" - PROPNAME_IAM_PROFILE_ID = "IAM_PROFILE_ID" + PROPNAME_AUTH_TYPE = "AUTH_TYPE" + PROPNAME_USERNAME = "USERNAME" + PROPNAME_PASSWORD = "PASSWORD" + PROPNAME_BEARER_TOKEN = "BEARER_TOKEN" + PROPNAME_AUTH_URL = "AUTH_URL" + PROPNAME_AUTH_DISABLE_SSL = "AUTH_DISABLE_SSL" + PROPNAME_APIKEY = "APIKEY" + PROPNAME_CLIENT_ID = "CLIENT_ID" + PROPNAME_CLIENT_SECRET = "CLIENT_SECRET" + PROPNAME_SCOPE = "SCOPE" + PROPNAME_CRTOKEN_FILENAME = "CR_TOKEN_FILENAME" + PROPNAME_IAM_PROFILE_NAME = "IAM_PROFILE_NAME" + PROPNAME_IAM_PROFILE_ID = "IAM_PROFILE_ID" // SSL error SSL_CERTIFICATION_ERROR = "x509: certificate" @@ -76,7 +75,6 @@ const ( ERRORMSG_CREATE_RETRYABLE_REQ = "An error occurred while creating a retryable http Request: %s" ERRORMSG_UNEXPECTED_STATUS_CODE = "Unexpected HTTP status code %d (%s)" ERRORMSG_UNMARSHAL_AUTH_RESPONSE = "error unmarshalling authentication response: %s" - ERRORMSG_UNABLE_RETRIEVE_CRTOKEN = "unable to retrieve the CR token value from current compute resource" + ERRORMSG_UNABLE_RETRIEVE_CRTOKEN = "unable to retrieve compute resource token value: %s" ERRORMSG_IAM_GETTOKEN_ERROR = "IAM 'get token' error, status code %d received from '%s': %s" - ERRORMSG_IMDS_OPERATION_ERROR = "Instance Metadata Service error, status code %d received from '%s': %s" ) diff --git a/v5/core/cr_authenticator.go b/v5/core/cr_authenticator.go index 37d72f4..243c761 100644 --- a/v5/core/cr_authenticator.go +++ b/v5/core/cr_authenticator.go @@ -43,11 +43,6 @@ type ComputeResourceAuthenticator struct { // Default value: "/var/run/secrets/tokens/vault-token" CRTokenFilename string - // [optional] The base endpoint URL to be used for invoking operations of the compute resource's - // local Instance Metadata Service (applies to VSI-managed compute resources). - // Default value: "http://169.254.169.254" - InstanceMetadataServiceURL string - // [optional] The name of the linked trusted IAM profile to be used when obtaining the IAM access token // (a CR token might map to multiple IAM profiles). // One of IAMProfileName or IAMProfileID must be specified. @@ -101,40 +96,87 @@ type ComputeResourceAuthenticator struct { const ( defaultCRTokenFilename = "/var/run/secrets/tokens/vault-token" - defaultImdsEndpoint = "http://169.254.169.254" - imdsVersionDate = "2021-07-15" - imdsMetadataFlavor = "ibm" - iamGrantTypeCRToken = "urn:ibm:params:oauth:grant-type:cr-token" // #nosec G101 - crtokenLifetime = 300 + iamGrantTypeCRToken = "urn:ibm:params:oauth:grant-type:cr-token" // #nosec G101 ) var craRequestTokenMutex sync.Mutex -// NewComputeResourceAuthenticator constructs a new ComputeResourceAuthenticator instance with the supplied values and -// invokes the ComputeResourceAuthenticator's Validate() method. -func NewComputeResourceAuthenticator(crtokenFilename string, instanceMetadataServiceURL, iamProfileName string, iamProfileID string, - url string, clientID string, clientSecret string, disableSSLVerification bool, scope string, - headers map[string]string) (*ComputeResourceAuthenticator, error) { - authenticator := &ComputeResourceAuthenticator{ - CRTokenFilename: crtokenFilename, - InstanceMetadataServiceURL: instanceMetadataServiceURL, - IAMProfileName: iamProfileName, - IAMProfileID: iamProfileID, - URL: url, - ClientID: clientID, - ClientSecret: clientSecret, - DisableSSLVerification: disableSSLVerification, - Scope: scope, - Headers: headers, - } +// ComputeResourceAuthenticatorBuilder is used to construct an instance of the ComputeResourceAuthenticator +type ComputeResourceAuthenticatorBuilder struct { + ComputeResourceAuthenticator +} + +// NewComputeResourceAuthenticatorBuilder returns a new builder struct that +// can be used to construct a ComputeResourceAuthenticator instance. +func NewComputeResourceAuthenticatorBuilder() *ComputeResourceAuthenticatorBuilder { + return &ComputeResourceAuthenticatorBuilder{} +} + +// SetCRTokenFilename sets the CRTokenFilename field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetCRTokenFilename(s string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.CRTokenFilename = s + return builder +} + +// SetIAMProfileName sets the IAMProfileName field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetIAMProfileName(s string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.IAMProfileName = s + return builder +} + +// SetIAMProfileID sets the IAMProfileID field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetIAMProfileID(s string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.IAMProfileID = s + return builder +} + +// SetURL sets the URL field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetURL(s string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.URL = s + return builder +} + +// SetClientIDSecret sets the ClientID and ClientSecret fields in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetClientIDSecret(clientID, clientSecret string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.ClientID = clientID + builder.ComputeResourceAuthenticator.ClientSecret = clientSecret + return builder +} + +// SetDisableSSLVerification sets the DisableSSLVerification field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetDisableSSLVerification(b bool) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.DisableSSLVerification = b + return builder +} + +// SetScope sets the Scope field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetScope(s string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.Scope = s + return builder +} + +// SetHeaders sets the Headers field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetHeaders(headers map[string]string) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.Headers = headers + return builder +} + +// SetClient sets the Client field in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) SetClient(client *http.Client) *ComputeResourceAuthenticatorBuilder { + builder.ComputeResourceAuthenticator.Client = client + return builder +} + +// Build() returns a validated instance of the ComputeResourceAuthenticator with the config that was set in the builder. +func (builder *ComputeResourceAuthenticatorBuilder) Build() (*ComputeResourceAuthenticator, error) { // Make sure the config is valid. - err := authenticator.Validate() + err := builder.ComputeResourceAuthenticator.Validate() if err != nil { return nil, err } - return authenticator, nil + return &builder.ComputeResourceAuthenticator, nil } // newComputeResourceAuthenticatorFromMap constructs a new ComputeResourceAuthenticator instance from a map containing @@ -144,23 +186,21 @@ func newComputeResourceAuthenticatorFromMap(properties map[string]string) (authe return nil, fmt.Errorf(ERRORMSG_PROPS_MAP_NIL) } - // Grab the AUTH_DISABLE_SSL string property and convert to a boolean. + // Grab the AUTH_DISABLE_SSL string property and convert to a boolean value. disableSSL, err := strconv.ParseBool(properties[PROPNAME_AUTH_DISABLE_SSL]) if err != nil { disableSSL = false } - authenticator, err = NewComputeResourceAuthenticator( - properties[PROPNAME_CRTOKEN_FILENAME], - properties[PROPNAME_INSTANCE_METADATA_SERVICE_URL], - properties[PROPNAME_IAM_PROFILE_NAME], - properties[PROPNAME_IAM_PROFILE_ID], - properties[PROPNAME_AUTH_URL], - properties[PROPNAME_CLIENT_ID], - properties[PROPNAME_CLIENT_SECRET], - disableSSL, - properties[PROPNAME_SCOPE], - nil) + authenticator, err = NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(properties[PROPNAME_CRTOKEN_FILENAME]). + SetIAMProfileName(properties[PROPNAME_IAM_PROFILE_NAME]). + SetIAMProfileID(properties[PROPNAME_IAM_PROFILE_ID]). + SetURL(properties[PROPNAME_AUTH_URL]). + SetClientIDSecret(properties[PROPNAME_CLIENT_ID], properties[PROPNAME_CLIENT_SECRET]). + SetDisableSSLVerification(disableSSL). + SetScope(properties[PROPNAME_SCOPE]). + Build() return } @@ -301,7 +341,7 @@ func (authenticator *ComputeResourceAuthenticator) RequestToken() (*IamTokenServ crToken, err := authenticator.retrieveCRToken() if crToken == "" { if err == nil { - err = fmt.Errorf(ERRORMSG_UNABLE_RETRIEVE_CRTOKEN + ": reason unknown") + err = fmt.Errorf(ERRORMSG_UNABLE_RETRIEVE_CRTOKEN, "reason unknown") } return nil, NewAuthenticationError(&DetailedResponse{}, err) } @@ -430,44 +470,8 @@ func (authenticator *ComputeResourceAuthenticator) RequestToken() (*IamTokenServ return tokenResponse, nil } -// retrieveCRToken retrieves the CR token for the current compute resource. +// retrieveCRToken tries to read the CR token value from the local file system. func (authenticator *ComputeResourceAuthenticator) retrieveCRToken() (crToken string, err error) { - var errs []error - var crTokenErr error - - // 1. IKS use-case - // First, try to read the CR token value from a local file. - crToken, crTokenErr = authenticator.readCRTokenFromFile() - if crTokenErr != nil { - errs = append(errs, crTokenErr) - } - - // 2. VPC/VSI use-case - // Next, try to obtain the CR token value from the Instance Metadata Service. - if crToken == "" { - crToken, crTokenErr = authenticator.retrieveCRTokenFromIMDS() - if crTokenErr != nil { - errs = append(errs, crTokenErr) - } - } - - // If we're going to return "" for the crToken (an error condition), then - // gather up each of the error objects that resulted from each attempt at - // retrieving the CR token value as we want to present the entire list - // to the caller. - if crToken == "" { - var errorMsgs []string - for _, e := range errs { - errorMsgs = append(errorMsgs, "\t"+e.Error()) - } - err = fmt.Errorf(ERRORMSG_UNABLE_RETRIEVE_CRTOKEN + "\n" + strings.Join(errorMsgs, "\n")) - } - - return -} - -// readCRTokenFromFile tries to read the CR token value from the local file system. -func (authenticator *ComputeResourceAuthenticator) readCRTokenFromFile() (crToken string, err error) { // Use the default filename if one wasn't supplied by the user. crTokenFilename := authenticator.CRTokenFilename @@ -480,124 +484,14 @@ func (authenticator *ComputeResourceAuthenticator) readCRTokenFromFile() (crToke // Read the entire file into a byte slice, then convert to string. var bytes []byte bytes, err = ioutil.ReadFile(crTokenFilename) - if err == nil { - crToken = string(bytes) - GetLogger().Debug("Successfully read CR token from file: %s\n", crTokenFilename) - } else { - GetLogger().Debug("Failed to read CR token value from file %s: %s\n", crTokenFilename, err.Error()) - } - - return -} - -// This struct models the response to the "create_access_token" operation (PUT instance_identity/v1/token). -type instanceIdentityToken struct { - AccessToken string `json:"access_token"` - - // The following fields are also present in the response, but - // we don't need these fields because we're going to use the access token - // (CR token value) immediately and will never cache it. - // CreatedAt string `json:"created_at"` - // ExpiresAt string `json:"expires_at"` - // ExpiresIn int64 `json:"expires_in"` -} - -// retrieveCRTokenFromIMDS tries to retrieve the CR token value by invoking -// the "create_access_token" operation on the compute resource's -// local Instance Metadata Service. -func (authenticator *ComputeResourceAuthenticator) retrieveCRTokenFromIMDS() (crToken string, err error) { - var operationPath string = "instance_identity/v1/token" - - url := authenticator.InstanceMetadataServiceURL - if url == "" { - url = defaultImdsEndpoint - } else { - // Canonicalize the URL by removing the operation path if it was specified by the user. - url = strings.TrimSuffix(url, operationPath) - } - - // Set up the request to invoke the "create_access_token" operation. - builder := NewRequestBuilder(PUT) - _, err = builder.ResolveRequestURL(url, operationPath, nil) - if err != nil { - return - } - - // Set the params and request body. - builder.AddQuery("version", imdsVersionDate) - builder.AddHeader(CONTENT_TYPE, APPLICATION_JSON) - builder.AddHeader(Accept, APPLICATION_JSON) - builder.AddHeader("Metadata-Flavor", imdsMetadataFlavor) - - requestBody := fmt.Sprintf(`{"expires_in": %d}`, crtokenLifetime) - _, _ = builder.SetBodyContentString(requestBody) - - req, err := builder.Build() - if err != nil { - return - } - - // Create a Client with 30 sec timeout. - client := &http.Client{ - Timeout: time.Second * 30, - } - - // If debug is enabled, then dump the request. - if GetLogger().IsLogLevelEnabled(LevelDebug) { - buf, dumpErr := httputil.DumpRequestOut(req, req.Body != nil) - if dumpErr == nil { - GetLogger().Debug("Request:\n%s\n", string(buf)) - } else { - GetLogger().Debug(fmt.Sprintf("error while attempting to log outbound request: %s", dumpErr.Error())) - } - } - - // Invoke the request. - GetLogger().Debug("Invoking IMDS 'create_access_token' operation: %s", builder.URL) - resp, err := client.Do(req) if err != nil { + err = fmt.Errorf(ERRORMSG_UNABLE_RETRIEVE_CRTOKEN, err.Error()) + GetLogger().Debug(err.Error()) return } - GetLogger().Debug("Returned from IMDS 'create_access_token' operation, received status code %d", resp.StatusCode) - - // If debug is enabled, then dump the response. - if GetLogger().IsLogLevelEnabled(LevelDebug) { - buf, dumpErr := httputil.DumpResponse(resp, resp.Body != nil) - if dumpErr == nil { - GetLogger().Debug("Response:\n%s\n", string(buf)) - } else { - GetLogger().Debug(fmt.Sprintf("error while attempting to log inbound response: %s", dumpErr.Error())) - } - } - - // Check for a bad status code and handle the operation error. - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - buff := new(bytes.Buffer) - _, _ = buff.ReadFrom(resp.Body) - resp.Body.Close() - - // Create a DetailedResponse to be included in the error below. - detailedResponse := &DetailedResponse{ - StatusCode: resp.StatusCode, - Headers: resp.Header, - RawResult: buff.Bytes(), - } - - imdsErrorMsg := string(detailedResponse.RawResult) - if imdsErrorMsg == "" { - imdsErrorMsg = "IMDS error response not available" - } - err = fmt.Errorf(ERRORMSG_IMDS_OPERATION_ERROR, detailedResponse.StatusCode, builder.URL, imdsErrorMsg) - return - } - - // IMDS operation invocation must have worked, so unmarshal the response and retrieve the CR token. - createTokenResponse := &instanceIdentityToken{} - _ = json.NewDecoder(resp.Body).Decode(createTokenResponse) - defer resp.Body.Close() - // The CR token value is returned in the "access_token" field of the response object. - crToken = createTokenResponse.AccessToken + crToken = string(bytes) + GetLogger().Debug("Successfully read CR token from file: %s\n", crTokenFilename) return } diff --git a/v5/core/cr_authenticator_test.go b/v5/core/cr_authenticator_test.go index 18dc89a..98c2be0 100644 --- a/v5/core/cr_authenticator_test.go +++ b/v5/core/cr_authenticator_test.go @@ -1,4 +1,4 @@ -// +build all slow auth +// +build all auth package core @@ -17,7 +17,6 @@ package core // limitations under the License. import ( - "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -42,29 +41,30 @@ const ( craTestRefreshToken string = "Xj7Gle500MachEOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" ) -// Struct that models the request body for the "create_access_token" operation -type instanceIdentityTokenPrototype struct { - ExpiresIn int `json:"expires_in"` -} - func TestCraCtorErrors(t *testing.T) { var err error var auth *ComputeResourceAuthenticator // Error: missing IAMProfileName and IBMProfileID. - auth, err = NewComputeResourceAuthenticator("", "", "", "", "", "", "", false, "", nil) + auth, err = NewComputeResourceAuthenticatorBuilder().Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientID. - auth, err = NewComputeResourceAuthenticator("", "", craMockIAMProfileName, "", "", "", "client-secret", false, "", nil) + auth, err = NewComputeResourceAuthenticatorBuilder(). + SetIAMProfileName(craMockIAMProfileName). + SetClientIDSecret("", craMockClientSecret). + Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientSecret. - auth, err = NewComputeResourceAuthenticator("", "", "", "iam-id-123", "", "client-id", "", false, "", nil) + auth, err = NewComputeResourceAuthenticatorBuilder(). + SetIAMProfileID(craMockIAMProfileID). + SetClientIDSecret(craMockClientID, ""). + Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) @@ -79,12 +79,13 @@ func TestCraCtorSuccess(t *testing.T) { // Success - only required params // 1. only IAMProfileName - auth, err = NewComputeResourceAuthenticator("", "", craMockIAMProfileName, "", "", "", "", false, "", nil) + auth, err = NewComputeResourceAuthenticatorBuilder(). + SetIAMProfileName(craMockIAMProfileName). + Build() assert.Nil(t, err) assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, "", auth.InstanceMetadataServiceURL) assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) assert.Equal(t, "", auth.IAMProfileID) assert.Equal(t, "", auth.URL) @@ -94,12 +95,13 @@ func TestCraCtorSuccess(t *testing.T) { assert.Nil(t, auth.Headers) // 2. only IAMProfileID - auth, err = NewComputeResourceAuthenticator("", "", "", craMockIAMProfileID, "", "", "", false, "", nil) + auth, err = NewComputeResourceAuthenticatorBuilder(). + SetIAMProfileID(craMockIAMProfileID). + Build() assert.Nil(t, err) assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, "", auth.InstanceMetadataServiceURL) assert.Equal(t, "", auth.IAMProfileName) assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, "", auth.URL) @@ -109,18 +111,25 @@ func TestCraCtorSuccess(t *testing.T) { assert.Nil(t, auth.Headers) // Success - all parameters - auth, err = NewComputeResourceAuthenticator("cr-token-file", "http://1.1.1.1", craMockIAMProfileName, craMockIAMProfileID, - defaultIamTokenServerEndpoint, "client-id", "client-secret", true, "scope1", expectedHeaders) + auth, err = NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename("cr-token-file"). + SetIAMProfileName(craMockIAMProfileName). + SetIAMProfileID(craMockIAMProfileID). + SetURL(defaultIamTokenServerEndpoint). + SetClientIDSecret(craMockClientID, craMockClientSecret). + SetDisableSSLVerification(true). + SetScope("scope1"). + SetHeaders(expectedHeaders). + Build() assert.Nil(t, err) assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) assert.Equal(t, "cr-token-file", auth.CRTokenFilename) - assert.Equal(t, "http://1.1.1.1", auth.InstanceMetadataServiceURL) assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, defaultIamTokenServerEndpoint, auth.URL) - assert.Equal(t, "client-id", auth.ClientID) - assert.Equal(t, "client-secret", auth.ClientSecret) + assert.Equal(t, craMockClientID, auth.ClientID) + assert.Equal(t, craMockClientSecret, auth.ClientSecret) assert.Equal(t, true, auth.DisableSSLVerification) assert.Equal(t, expectedHeaders, auth.Headers) } @@ -146,7 +155,7 @@ func TestCraCtorFromMapErrors(t *testing.T) { // Error: missing ClientID. configProps = map[string]string{ PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, - PROPNAME_CLIENT_SECRET: "client-secret", + PROPNAME_CLIENT_SECRET: craMockClientSecret, } auth, err = newComputeResourceAuthenticatorFromMap(configProps) assert.NotNil(t, err) @@ -155,8 +164,8 @@ func TestCraCtorFromMapErrors(t *testing.T) { // Error: missing ClientSecret. configProps = map[string]string{ - PROPNAME_IAM_PROFILE_ID: "iam-id-123", - PROPNAME_CLIENT_ID: "client-id", + PROPNAME_IAM_PROFILE_ID: craMockIAMProfileID, + PROPNAME_CLIENT_ID: craMockClientID, } auth, err = newComputeResourceAuthenticatorFromMap(configProps) assert.NotNil(t, err) @@ -178,7 +187,6 @@ func TestCraCtorFromMapSuccess(t *testing.T) { assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, "", auth.InstanceMetadataServiceURL) assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) assert.Equal(t, "", auth.IAMProfileID) assert.Equal(t, "", auth.URL) @@ -196,7 +204,6 @@ func TestCraCtorFromMapSuccess(t *testing.T) { assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, "", auth.InstanceMetadataServiceURL) assert.Equal(t, "", auth.IAMProfileName) assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, "", auth.URL) @@ -207,59 +214,37 @@ func TestCraCtorFromMapSuccess(t *testing.T) { // Success - all params configProps = map[string]string{ - PROPNAME_CRTOKEN_FILENAME: "cr-token-file", - PROPNAME_INSTANCE_METADATA_SERVICE_URL: "http://1.1.1.1", - PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, - PROPNAME_IAM_PROFILE_ID: "iam-id-123", - PROPNAME_AUTH_URL: defaultIamTokenServerEndpoint, - PROPNAME_CLIENT_ID: "client-id", - PROPNAME_CLIENT_SECRET: "client-secret", - PROPNAME_AUTH_DISABLE_SSL: "true", - PROPNAME_SCOPE: "scope1", + PROPNAME_CRTOKEN_FILENAME: craMockCRTokenFile, + PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, + PROPNAME_IAM_PROFILE_ID: craMockIAMProfileID, + PROPNAME_AUTH_URL: defaultIamTokenServerEndpoint, + PROPNAME_CLIENT_ID: craMockClientID, + PROPNAME_CLIENT_SECRET: craMockClientSecret, + PROPNAME_AUTH_DISABLE_SSL: "true", + PROPNAME_SCOPE: "scope1", } auth, err = newComputeResourceAuthenticatorFromMap(configProps) assert.Nil(t, err) assert.NotNil(t, auth) assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) - assert.Equal(t, "cr-token-file", auth.CRTokenFilename) - assert.Equal(t, "http://1.1.1.1", auth.InstanceMetadataServiceURL) + assert.Equal(t, craMockCRTokenFile, auth.CRTokenFilename) assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) - assert.Equal(t, "iam-id-123", auth.IAMProfileID) + assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, defaultIamTokenServerEndpoint, auth.URL) - assert.Equal(t, "client-id", auth.ClientID) - assert.Equal(t, "client-secret", auth.ClientSecret) + assert.Equal(t, craMockClientID, auth.ClientID) + assert.Equal(t, craMockClientSecret, auth.ClientSecret) assert.Equal(t, true, auth.DisableSSLVerification) assert.Nil(t, auth.Headers) } // startMockServer will start a mock server endpoint that supports both the // Instance Metadata Service and IAM operations that we'll need to call. -func startMockServer(t *testing.T) *httptest.Server { +func startMockIAMServer(t *testing.T) *httptest.Server { // Create the mock server. server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { operationPath := req.URL.EscapedPath() - method := req.Method - - if operationPath == "/instance_identity/v1/token" { - // If this is an invocation of the IMDS "create_access_token" operation, - // then validate it a bit and then send back a good response. - assert.Equal(t, "PUT", method) - assert.Equal(t, imdsVersionDate, req.URL.Query()["version"][0]) - assert.Equal(t, APPLICATION_JSON, req.Header.Get("Accept")) - assert.Equal(t, APPLICATION_JSON, req.Header.Get("Content-Type")) - assert.Equal(t, imdsMetadataFlavor, req.Header.Get("Metadata-Flavor")) - // Read and unmarshal the request body. - requestBody := &instanceIdentityTokenPrototype{} - _ = json.NewDecoder(req.Body).Decode(requestBody) - defer req.Body.Close() - - assert.NotNil(t, requestBody) - assert.Equal(t, crtokenLifetime, requestBody.ExpiresIn) - - res.WriteHeader(http.StatusOK) - fmt.Fprintf(res, `{"access_token":"%s"}`, craTestCRToken1) - } else if operationPath == "/identity/token" { + if operationPath == "/identity/token" { // If this is an invocation of the IAM "get_token" operation, // then validate it a bit and then send back a good response. assert.Equal(t, APPLICATION_JSON, req.Header.Get("Accept")) @@ -284,7 +269,7 @@ func startMockServer(t *testing.T) *httptest.Server { // 1. whether to return the first or second access token. // 2. whether we should validate the basic-auth header. // 3. whether we should return a bad status code. - // Yes, this is kinda subversive but sometimes we need to be creative on these big jobs :) + // Yes, this is kinda subversive, but sometimes we need to be creative on these big jobs :) scope := req.FormValue("scope") if scope == "send-second-token" { @@ -324,54 +309,26 @@ func startMockServer(t *testing.T) *httptest.Server { return server } -func TestCraRetrieveCRTokenFromFileSuccess(t *testing.T) { +func TestCraRetrieveCRTokenSuccess(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) // Set the authenticator to read the CR token from our mock file. auth := &ComputeResourceAuthenticator{ CRTokenFilename: craMockCRTokenFile, } - crToken, err := auth.readCRTokenFromFile() + crToken, err := auth.retrieveCRToken() assert.Nil(t, err) assert.Equal(t, craTestCRToken1, crToken) } -func TestCraRetrieveCRTokenFromFileFail(t *testing.T) { +func TestCraRetrieveCRTokenFail(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) // Use a non-existent cr token file. auth := &ComputeResourceAuthenticator{ CRTokenFilename: "bogus-cr-token-file", } - crToken, err := auth.readCRTokenFromFile() - assert.NotNil(t, err) - assert.Equal(t, "", crToken) - t.Logf("Expected error: %s", err.Error()) -} - -func TestCraRetrieveCRTokenFromIMDSSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) - - server := startMockServer(t) - defer server.Close() - - // Set up the authenticator to use the mock server. - auth := &ComputeResourceAuthenticator{ - InstanceMetadataServiceURL: server.URL, - } - crToken, err := auth.retrieveCRTokenFromIMDS() - assert.Nil(t, err) - assert.Equal(t, craTestCRToken1, crToken) -} - -func TestCraRetrieveCRTokenFromIMDSFail(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) - - // Set up the authenticator to use a bogus IMDS endpoint. - auth := &ComputeResourceAuthenticator{ - InstanceMetadataServiceURL: "http://bogus.imds.endpoint", - } - crToken, err := auth.retrieveCRTokenFromIMDS() + crToken, err := auth.retrieveCRToken() assert.NotNil(t, err) assert.Equal(t, "", crToken) t.Logf("Expected error: %s", err.Error()) @@ -380,7 +337,7 @@ func TestCraRetrieveCRTokenFromIMDSFail(t *testing.T) { func TestCraGetTokenSuccess(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() auth := &ComputeResourceAuthenticator{ @@ -427,7 +384,7 @@ func TestCraGetTokenSuccess(t *testing.T) { func TestCraRequestTokenSuccess(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() auth := &ComputeResourceAuthenticator{ @@ -448,17 +405,17 @@ func TestCraRequestTokenSuccess(t *testing.T) { func TestCraAuthenticateSuccess(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() - // Set up the authenticator to use the mock server for the IMDS and IAM operations. - auth := &ComputeResourceAuthenticator{ - IAMProfileID: craMockIAMProfileID, - InstanceMetadataServiceURL: server.URL, - URL: server.URL, - } - err := auth.Validate() + // Set up the authenticator to use the cr token file. + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(craMockCRTokenFile). + SetIAMProfileID(craMockIAMProfileID). + SetURL(server.URL). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Create a new Request object to simulate an API request that needs authentication. builder, err := NewRequestBuilder("GET").ConstructHTTPURL("https://myservice.localhost/api/v1", nil, nil) @@ -477,6 +434,9 @@ func TestCraAuthenticateSuccess(t *testing.T) { assert.Equal(t, "Bearer "+craTestAccessToken1, authHeader) // Call Authenticate again to make sure we used the cached access token. + // We'll do this by setting scope to request the second token, + // we'll expect the first token to be returned which verifies that we didn't + // call the IAM "get token" operation again. auth.Scope = "send-second-token" err = auth.Authenticate(request) assert.Nil(t, err) @@ -494,16 +454,15 @@ func TestCraAuthenticateSuccess(t *testing.T) { func TestCraAuthenticateFailNoCRToken(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - // Set up the authenticator with both a bogus cr token filename and IMDS endpoint + // Set up the authenticator with a bogus cr token filename // so that we can't successfully retrieve a CR Token value. - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: "bogus-cr-token-file", - InstanceMetadataServiceURL: "http://bogus.imds.endpoint", - IAMProfileName: craMockIAMProfileName, - URL: "https://bogus.iam.endpoint", - } - err := auth.Validate() + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename("bogus-cr-token-file"). + SetIAMProfileName(craMockIAMProfileName). + SetURL("https://bogus.iam.endpoint"). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Create a new Request object to simulate an API request that needs authentication. builder, err := NewRequestBuilder("GET").ConstructHTTPURL("https://myservice.localhost/api/v1", nil, nil) @@ -516,30 +475,30 @@ func TestCraAuthenticateFailNoCRToken(t *testing.T) { // Try to authenticate the request (should fail) err = auth.Authenticate(request) - // Validate the resulting error is a valid + // Validate the resulting error is a valid AuthenticationError. assert.NotNil(t, err) t.Logf("Expected error: %s\n", err.Error()) authErr, ok := err.(*AuthenticationError) assert.True(t, ok) assert.NotNil(t, authErr) assert.EqualValues(t, authErr, err) - // The casted error should match the original error message + // The auth error should match the original error message assert.Equal(t, err.Error(), authErr.Error()) } func TestCraAuthenticateFailIAM(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() - // Setup the authenticator to get the CR token from our mock server, - // and set scope to cause the mock server to send a bad status code for the IAM call. + // Set up the authenticator to use our mock cr token file, + // and set scope to cause the mock IAM server to send a bad status code for the IAM call. auth := &ComputeResourceAuthenticator{ - InstanceMetadataServiceURL: server.URL, - IAMProfileName: craMockIAMProfileName, - URL: server.URL, - Scope: "status-bad-request", + CRTokenFilename: craMockCRTokenFile, + IAMProfileName: craMockIAMProfileName, + URL: server.URL, + Scope: "status-bad-request", } err := auth.Validate() assert.Nil(t, err) @@ -568,7 +527,7 @@ func TestCraAuthenticateFailIAM(t *testing.T) { func TestCraBackgroundTokenRefreshSuccess(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() auth := &ComputeResourceAuthenticator{ @@ -606,7 +565,7 @@ func TestCraBackgroundTokenRefreshSuccess(t *testing.T) { func TestCraBackgroundTokenRefreshFail(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() auth := &ComputeResourceAuthenticator{ @@ -656,18 +615,17 @@ func TestCraBackgroundTokenRefreshFail(t *testing.T) { func TestCraClientIdAndSecret(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, - ClientID: craMockClientID, - ClientSecret: craMockClientSecret, - URL: server.URL, - } - err := auth.Validate() + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(craMockCRTokenFile). + SetIAMProfileName(craMockIAMProfileName). + SetClientIDSecret(craMockClientID, craMockClientSecret). + SetURL(server.URL). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Force the first fetch and verify we got the first access token. auth.Scope = "check-basic-auth" @@ -679,17 +637,17 @@ func TestCraClientIdAndSecret(t *testing.T) { func TestCraDisableSSL(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, - URL: server.URL, - DisableSSLVerification: true, - } - err := auth.Validate() + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(craMockCRTokenFile). + SetIAMProfileName(craMockIAMProfileName). + SetURL(server.URL). + SetDisableSSLVerification(true). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() @@ -708,21 +666,21 @@ func TestCraDisableSSL(t *testing.T) { func TestCraUserHeaders(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() headers := make(map[string]string) headers["User-Header-1"] = "Value-1" headers["Host"] = "iam.cloud.ibm.com" - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, - URL: server.URL, - Headers: headers, - } - err := auth.Validate() + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(craMockCRTokenFile). + SetIAMProfileName(craMockIAMProfileName). + SetURL(server.URL). + SetHeaders(headers). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Force the first fetch and verify we got the first access token. auth.Scope = "check-user-headers" @@ -734,16 +692,16 @@ func TestCraUserHeaders(t *testing.T) { func TestCraGetTokenTimeout(t *testing.T) { GetLogger().SetLogLevel(craTestLogLevel) - server := startMockServer(t) + server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, - URL: server.URL, - } - err := auth.Validate() + auth, err := NewComputeResourceAuthenticatorBuilder(). + SetCRTokenFilename(craMockCRTokenFile). + SetIAMProfileName(craMockIAMProfileName). + SetURL(server.URL). + Build() assert.Nil(t, err) + assert.NotNil(t, auth) // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() diff --git a/v5/resources/my-credentials.env b/v5/resources/my-credentials.env index b4c1a0b..8112f5b 100644 --- a/v5/resources/my-credentials.env +++ b/v5/resources/my-credentials.env @@ -58,7 +58,6 @@ SERVICE6_SCOPE=scope1 scope2 scope3 # Service configured with CRA SERVICE7_AUTH_TYPE=CraUth SERVICE7_CR_TOKEN_FILENAME=crtoken.txt -SERVICE7_INSTANCE_METADATA_SERVICE_URL=http://1.1.1.1 SERVICE7_IAM_PROFILE_NAME=iam-user1 SERVICE7_IAM_PROFILE_ID=iam-id1 SERVICE7_AUTH_URL=https://iamhost/iam/api From cb0a798c97cc738c92a04d82980d1ee7a65b2aa6 Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Tue, 3 Aug 2021 08:15:03 -0500 Subject: [PATCH 2/6] chore: update .secrets.baseline --- .secrets.baseline | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index fd0b38f..84780a2 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "package-lock.json|go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2021-07-27T13:23:24Z", + "generated_at": "2021-08-03T13:14:10Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -96,7 +96,7 @@ "hashed_secret": "bc2f74c22f98f7b6ffbc2f67453dbfa99bce9a32", "is_secret": false, "is_verified": false, - "line_number": 512, + "line_number": 519, "type": "Secret Keyword", "verified_result": null } @@ -238,7 +238,7 @@ "hashed_secret": "2a68d46242baf9214502d1dc240a9075a7c6ed55", "is_secret": false, "is_verified": false, - "line_number": 77, + "line_number": 76, "type": "Secret Keyword", "verified_result": null } @@ -418,15 +418,15 @@ "hashed_secret": "3c81615afb40d1889fc2e1fff551a8b59b4e80ce", "is_secret": false, "is_verified": false, - "line_number": 107, + "line_number": 99, "type": "Secret Keyword", "verified_result": null }, { - "hashed_secret": "7ea6be9eecb6605329a1b1870c2fd2af9b896991", + "hashed_secret": "8b142a91cfb6e617618ad437cedf74a6745f8926", "is_secret": false, "is_verified": false, - "line_number": 125, + "line_number": 142, "type": "Secret Keyword", "verified_result": null } @@ -436,23 +436,15 @@ "hashed_secret": "c8f0df25bade89c1873f5f01b85bcfb921443ac6", "is_secret": false, "is_verified": false, - "line_number": 40, + "line_number": 39, "type": "JSON Web Token", "verified_result": null }, - { - "hashed_secret": "a0281cd072cea8e80e7866b05dc124815760b6c9", - "is_secret": false, - "is_verified": false, - "line_number": 216, - "type": "Secret Keyword", - "verified_result": null - }, { "hashed_secret": "1cef53b6f5a230f88f15e2213a257fe72d9545fd", "is_secret": false, "is_verified": false, - "line_number": 666, + "line_number": 222, "type": "Secret Keyword", "verified_result": null }, @@ -460,7 +452,7 @@ "hashed_secret": "de82580fcac7f6e7df7c1a14f47ff060f098826f", "is_secret": false, "is_verified": false, - "line_number": 740, + "line_number": 571, "type": "Secret Keyword", "verified_result": null } @@ -664,7 +656,7 @@ "hashed_secret": "4e44e97dae1aa4e93c01536f48bbd8602133a86d", "is_secret": false, "is_verified": false, - "line_number": 67, + "line_number": 66, "type": "Secret Keyword", "verified_result": null }, @@ -672,7 +664,7 @@ "hashed_secret": "9e2659aa7e2b335ec6bdcf180f3b6f41f5191af5", "is_secret": false, "is_verified": false, - "line_number": 72, + "line_number": 71, "type": "Secret Keyword", "verified_result": null } @@ -728,7 +720,7 @@ } ] }, - "version": "0.13.1+ibm.39.dss", + "version": "0.13.1+ibm.40.dss", "word_list": { "file": null, "hash": null From ebc48000b0238753b0285c82ad45364775db290a Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Tue, 3 Aug 2021 16:39:28 -0500 Subject: [PATCH 3/6] refactor: update authenticator with latest design changes --- v5/core/authenticator_factory.go | 6 +- v5/core/authenticator_factory_test.go | 44 +-- v5/core/common_test.go | 2 +- v5/core/constants.go | 2 +- ...nticator.go => container_authenticator.go} | 100 +++--- ...est.go => container_authenticator_test.go} | 318 +++++++++--------- v5/resources/my-credentials.env | 2 +- 7 files changed, 236 insertions(+), 238 deletions(-) rename v5/core/{cr_authenticator.go => container_authenticator.go} (78%) rename v5/core/{cr_authenticator_test.go => container_authenticator_test.go} (62%) diff --git a/v5/core/authenticator_factory.go b/v5/core/authenticator_factory.go index 5aae075..b434906 100644 --- a/v5/core/authenticator_factory.go +++ b/v5/core/authenticator_factory.go @@ -34,7 +34,7 @@ func GetAuthenticatorFromEnvironment(credentialKey string) (authenticator Authen if properties[PROPNAME_APIKEY] != "" { authType = AUTHTYPE_IAM } else { - authType = AUTHTYPE_CRAUTH + authType = AUTHTYPE_CONTAINER } } @@ -45,8 +45,8 @@ func GetAuthenticatorFromEnvironment(credentialKey string) (authenticator Authen authenticator, err = newBearerTokenAuthenticatorFromMap(properties) } else if strings.EqualFold(authType, AUTHTYPE_IAM) { authenticator, err = newIamAuthenticatorFromMap(properties) - } else if strings.EqualFold(authType, AUTHTYPE_CRAUTH) { - authenticator, err = newComputeResourceAuthenticatorFromMap(properties) + } else if strings.EqualFold(authType, AUTHTYPE_CONTAINER) { + authenticator, err = newContainerAuthenticatorFromMap(properties) } else if strings.EqualFold(authType, AUTHTYPE_CP4D) { authenticator, err = newCloudPakForDataAuthenticatorFromMap(properties) } else if strings.EqualFold(authType, AUTHTYPE_NOAUTH) { diff --git a/v5/core/authenticator_factory_test.go b/v5/core/authenticator_factory_test.go index be58eaf..ca94d15 100644 --- a/v5/core/authenticator_factory_test.go +++ b/v5/core/authenticator_factory_test.go @@ -59,18 +59,18 @@ func TestGetAuthenticatorFromEnvironment1(t *testing.T) { authenticator, err = GetAuthenticatorFromEnvironment("service7") assert.Nil(t, err) assert.NotNil(t, authenticator) - assert.Equal(t, AUTHTYPE_CRAUTH, authenticator.AuthenticationType()) - crAuthenticator, ok := authenticator.(*ComputeResourceAuthenticator) + assert.Equal(t, AUTHTYPE_CONTAINER, authenticator.AuthenticationType()) + containerAuth, ok := authenticator.(*ContainerAuthenticator) assert.True(t, ok) - assert.NotNil(t, crAuthenticator) - assert.Equal(t, "crtoken.txt", crAuthenticator.CRTokenFilename) - assert.Equal(t, "iam-user1", crAuthenticator.IAMProfileName) - assert.Equal(t, "iam-id1", crAuthenticator.IAMProfileID) - assert.Equal(t, "https://iamhost/iam/api", crAuthenticator.URL) - assert.Equal(t, "iam-client1", crAuthenticator.ClientID) - assert.Equal(t, "iam-secret1", crAuthenticator.ClientSecret) - assert.True(t, crAuthenticator.DisableSSLVerification) - assert.Equal(t, "scope1", crAuthenticator.Scope) + assert.NotNil(t, containerAuth) + assert.Equal(t, "crtoken.txt", containerAuth.CRTokenFilename) + assert.Equal(t, "iam-user1", containerAuth.IAMProfileName) + assert.Equal(t, "iam-id1", containerAuth.IAMProfileID) + assert.Equal(t, "https://iamhost/iam/api", containerAuth.URL) + assert.Equal(t, "iam-client1", containerAuth.ClientID) + assert.Equal(t, "iam-secret1", containerAuth.ClientSecret) + assert.True(t, containerAuth.DisableSSLVerification) + assert.Equal(t, "scope1", containerAuth.Scope) os.Unsetenv("IBM_CREDENTIALS_FILE") } @@ -100,18 +100,18 @@ func TestGetAuthenticatorFromEnvironment2(t *testing.T) { authenticator, err = GetAuthenticatorFromEnvironment("service7") assert.Nil(t, err) assert.NotNil(t, authenticator) - assert.Equal(t, AUTHTYPE_CRAUTH, authenticator.AuthenticationType()) - crAuthenticator, ok := authenticator.(*ComputeResourceAuthenticator) + assert.Equal(t, AUTHTYPE_CONTAINER, authenticator.AuthenticationType()) + containerAuth, ok := authenticator.(*ContainerAuthenticator) assert.True(t, ok) - assert.NotNil(t, crAuthenticator) - assert.Equal(t, "crtoken.txt", crAuthenticator.CRTokenFilename) - assert.Equal(t, "iam-user2", crAuthenticator.IAMProfileName) - assert.Equal(t, "iam-id2", crAuthenticator.IAMProfileID) - assert.Equal(t, "https://iamhost/iam/api", crAuthenticator.URL) - assert.Equal(t, "iam-client2", crAuthenticator.ClientID) - assert.Equal(t, "iam-secret2", crAuthenticator.ClientSecret) - assert.False(t, crAuthenticator.DisableSSLVerification) - assert.Equal(t, "scope2 scope3", crAuthenticator.Scope) + assert.NotNil(t, containerAuth) + assert.Equal(t, "crtoken.txt", containerAuth.CRTokenFilename) + assert.Equal(t, "iam-user2", containerAuth.IAMProfileName) + assert.Equal(t, "iam-id2", containerAuth.IAMProfileID) + assert.Equal(t, "https://iamhost/iam/api", containerAuth.URL) + assert.Equal(t, "iam-client2", containerAuth.ClientID) + assert.Equal(t, "iam-secret2", containerAuth.ClientSecret) + assert.False(t, containerAuth.DisableSSLVerification) + assert.Equal(t, "scope2 scope3", containerAuth.Scope) clearTestEnvironment() } diff --git a/v5/core/common_test.go b/v5/core/common_test.go index 499aee6..6d0187a 100644 --- a/v5/core/common_test.go +++ b/v5/core/common_test.go @@ -67,7 +67,7 @@ var testEnvironment = map[string]string{ "SERVICE6_AUTH_TYPE": "iam", "SERVICE6_APIKEY": "my-api-key", "SERVICE6_SCOPE": "A B C D", - "SERVICE7_AUTH_TYPE": "crauth", + "SERVICE7_AUTH_TYPE": "container", "SERVICE7_CR_TOKEN_FILENAME": "crtoken.txt", "SERVICE7_IAM_PROFILE_NAME": "iam-user2", "SERVICE7_IAM_PROFILE_ID": "iam-id2", diff --git a/v5/core/constants.go b/v5/core/constants.go index 19e167d..60d087a 100644 --- a/v5/core/constants.go +++ b/v5/core/constants.go @@ -21,7 +21,7 @@ const ( AUTHTYPE_NOAUTH = "noAuth" AUTHTYPE_IAM = "iam" AUTHTYPE_CP4D = "cp4d" - AUTHTYPE_CRAUTH = "crAuth" + AUTHTYPE_CONTAINER = "container" // Names of properties that can be defined as part of an external configuration (credential file, env vars, etc.). // Example: export MYSERVICE_URL=https://myurl diff --git a/v5/core/cr_authenticator.go b/v5/core/container_authenticator.go similarity index 78% rename from v5/core/cr_authenticator.go rename to v5/core/container_authenticator.go index 243c761..2c9dad7 100644 --- a/v5/core/cr_authenticator.go +++ b/v5/core/container_authenticator.go @@ -28,29 +28,26 @@ import ( "time" ) -// ComputeResourceAuthenticator implements an IAM-based authentication schema where by it +// ContainerAuthenticator implements an IAM-based authentication schema whereby it // retrieves a "compute resource token" from the local compute resource (VM) // and uses that to obtain an IAM access token by invoking the IAM "get token" operation with grant-type=cr-token. // The resulting IAM access token is then added to outbound requests in an Authorization header // of the form: -// // Authorization: Bearer // -type ComputeResourceAuthenticator struct { +type ContainerAuthenticator struct { // [optional] The name of the file containing the injected CR token value (applies to // IKS-managed compute resources). // Default value: "/var/run/secrets/tokens/vault-token" CRTokenFilename string - // [optional] The name of the linked trusted IAM profile to be used when obtaining the IAM access token - // (a CR token might map to multiple IAM profiles). + // [optional] The name of the linked trusted IAM profile to be used when obtaining the IAM access token. // One of IAMProfileName or IAMProfileID must be specified. // Default value: "" IAMProfileName string - // [optional] The id of the linked trusted IAM profile to be used when obtaining the IAM access token - // (a CR token might map to multiple IAM profiles). + // [optional] The id of the linked trusted IAM profile to be used when obtaining the IAM access token. // One of IAMProfileName or IAMProfileID must be specified. // Default value: "" IAMProfileID string @@ -101,87 +98,87 @@ const ( var craRequestTokenMutex sync.Mutex -// ComputeResourceAuthenticatorBuilder is used to construct an instance of the ComputeResourceAuthenticator -type ComputeResourceAuthenticatorBuilder struct { - ComputeResourceAuthenticator +// ContainerAuthenticatorBuilder is used to construct an instance of the ContainerAuthenticator +type ContainerAuthenticatorBuilder struct { + ContainerAuthenticator } -// NewComputeResourceAuthenticatorBuilder returns a new builder struct that -// can be used to construct a ComputeResourceAuthenticator instance. -func NewComputeResourceAuthenticatorBuilder() *ComputeResourceAuthenticatorBuilder { - return &ComputeResourceAuthenticatorBuilder{} +// NewContainerAuthenticatorBuilder returns a new builder struct that +// can be used to construct a ContainerAuthenticator instance. +func NewContainerAuthenticatorBuilder() *ContainerAuthenticatorBuilder { + return &ContainerAuthenticatorBuilder{} } // SetCRTokenFilename sets the CRTokenFilename field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetCRTokenFilename(s string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.CRTokenFilename = s +func (builder *ContainerAuthenticatorBuilder) SetCRTokenFilename(s string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.CRTokenFilename = s return builder } // SetIAMProfileName sets the IAMProfileName field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetIAMProfileName(s string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.IAMProfileName = s +func (builder *ContainerAuthenticatorBuilder) SetIAMProfileName(s string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.IAMProfileName = s return builder } // SetIAMProfileID sets the IAMProfileID field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetIAMProfileID(s string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.IAMProfileID = s +func (builder *ContainerAuthenticatorBuilder) SetIAMProfileID(s string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.IAMProfileID = s return builder } // SetURL sets the URL field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetURL(s string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.URL = s +func (builder *ContainerAuthenticatorBuilder) SetURL(s string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.URL = s return builder } // SetClientIDSecret sets the ClientID and ClientSecret fields in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetClientIDSecret(clientID, clientSecret string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.ClientID = clientID - builder.ComputeResourceAuthenticator.ClientSecret = clientSecret +func (builder *ContainerAuthenticatorBuilder) SetClientIDSecret(clientID, clientSecret string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.ClientID = clientID + builder.ContainerAuthenticator.ClientSecret = clientSecret return builder } // SetDisableSSLVerification sets the DisableSSLVerification field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetDisableSSLVerification(b bool) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.DisableSSLVerification = b +func (builder *ContainerAuthenticatorBuilder) SetDisableSSLVerification(b bool) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.DisableSSLVerification = b return builder } // SetScope sets the Scope field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetScope(s string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.Scope = s +func (builder *ContainerAuthenticatorBuilder) SetScope(s string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.Scope = s return builder } // SetHeaders sets the Headers field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetHeaders(headers map[string]string) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.Headers = headers +func (builder *ContainerAuthenticatorBuilder) SetHeaders(headers map[string]string) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.Headers = headers return builder } // SetClient sets the Client field in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) SetClient(client *http.Client) *ComputeResourceAuthenticatorBuilder { - builder.ComputeResourceAuthenticator.Client = client +func (builder *ContainerAuthenticatorBuilder) SetClient(client *http.Client) *ContainerAuthenticatorBuilder { + builder.ContainerAuthenticator.Client = client return builder } -// Build() returns a validated instance of the ComputeResourceAuthenticator with the config that was set in the builder. -func (builder *ComputeResourceAuthenticatorBuilder) Build() (*ComputeResourceAuthenticator, error) { +// Build() returns a validated instance of the ContainerAuthenticator with the config that was set in the builder. +func (builder *ContainerAuthenticatorBuilder) Build() (*ContainerAuthenticator, error) { // Make sure the config is valid. - err := builder.ComputeResourceAuthenticator.Validate() + err := builder.ContainerAuthenticator.Validate() if err != nil { return nil, err } - return &builder.ComputeResourceAuthenticator, nil + return &builder.ContainerAuthenticator, nil } -// newComputeResourceAuthenticatorFromMap constructs a new ComputeResourceAuthenticator instance from a map containing +// newContainerAuthenticatorFromMap constructs a new ContainerAuthenticator instance from a map containing // configuration properties. -func newComputeResourceAuthenticatorFromMap(properties map[string]string) (authenticator *ComputeResourceAuthenticator, err error) { +func newContainerAuthenticatorFromMap(properties map[string]string) (authenticator *ContainerAuthenticator, err error) { if properties == nil { return nil, fmt.Errorf(ERRORMSG_PROPS_MAP_NIL) } @@ -192,7 +189,7 @@ func newComputeResourceAuthenticatorFromMap(properties map[string]string) (authe disableSSL = false } - authenticator, err = NewComputeResourceAuthenticatorBuilder(). + authenticator, err = NewContainerAuthenticatorBuilder(). SetCRTokenFilename(properties[PROPNAME_CRTOKEN_FILENAME]). SetIAMProfileName(properties[PROPNAME_IAM_PROFILE_NAME]). SetIAMProfileID(properties[PROPNAME_IAM_PROFILE_ID]). @@ -206,8 +203,8 @@ func newComputeResourceAuthenticatorFromMap(properties map[string]string) (authe } // AuthenticationType returns the authentication type for this authenticator. -func (*ComputeResourceAuthenticator) AuthenticationType() string { - return AUTHTYPE_CRAUTH +func (*ContainerAuthenticator) AuthenticationType() string { + return AUTHTYPE_CONTAINER } // Authenticate adds IAM authentication information to the request. @@ -216,7 +213,7 @@ func (*ComputeResourceAuthenticator) AuthenticationType() string { // // Authorization: Bearer // -func (authenticator *ComputeResourceAuthenticator) Authenticate(request *http.Request) error { +func (authenticator *ContainerAuthenticator) Authenticate(request *http.Request) error { token, err := authenticator.GetToken() if err != nil { return err @@ -227,7 +224,7 @@ func (authenticator *ComputeResourceAuthenticator) Authenticate(request *http.Re } // getTokenData returns the tokenData field from the authenticator with synchronization. -func (authenticator *ComputeResourceAuthenticator) getTokenData() *iamTokenData { +func (authenticator *ContainerAuthenticator) getTokenData() *iamTokenData { authenticator.tokenDataMutex.Lock() defer authenticator.tokenDataMutex.Unlock() @@ -235,7 +232,7 @@ func (authenticator *ComputeResourceAuthenticator) getTokenData() *iamTokenData } // setTokenData sets the 'tokenData' field in the authenticator with synchronization. -func (authenticator *ComputeResourceAuthenticator) setTokenData(tokenData *iamTokenData) { +func (authenticator *ContainerAuthenticator) setTokenData(tokenData *iamTokenData) { authenticator.tokenDataMutex.Lock() defer authenticator.tokenDataMutex.Unlock() @@ -246,7 +243,7 @@ func (authenticator *ComputeResourceAuthenticator) setTokenData(tokenData *iamTo // // Ensures that one of IAMProfileName or IAMProfileID are specified, and the ClientId and ClientSecret pair are // mutually inclusive. -func (authenticator *ComputeResourceAuthenticator) Validate() error { +func (authenticator *ContainerAuthenticator) Validate() error { // Check to make sure that one of IAMProfileName or IAMProfileID are specified. if authenticator.IAMProfileName == "" && authenticator.IAMProfileID == "" { @@ -273,7 +270,7 @@ func (authenticator *ComputeResourceAuthenticator) Validate() error { // GetToken returns an access token to be used in an Authorization header. // Whenever a new token is needed (when a token doesn't yet exist or the existing token has expired), // a new access token is fetched from the token server. -func (authenticator *ComputeResourceAuthenticator) GetToken() (string, error) { +func (authenticator *ContainerAuthenticator) GetToken() (string, error) { if authenticator.getTokenData() == nil || !authenticator.getTokenData().isTokenValid() { GetLogger().Debug("Performing synchronous token fetch...") // synchronously request the token @@ -302,7 +299,7 @@ func (authenticator *ComputeResourceAuthenticator) GetToken() (string, error) { // a valid cached access token. // If yes, then nothing else needs to be done. // If no, then a blocking request is made to obtain a new IAM access token. -func (authenticator *ComputeResourceAuthenticator) synchronizedRequestToken() error { +func (authenticator *ContainerAuthenticator) synchronizedRequestToken() error { craRequestTokenMutex.Lock() defer craRequestTokenMutex.Unlock() // if cached token is still valid, then just continue to use it @@ -316,7 +313,7 @@ func (authenticator *ComputeResourceAuthenticator) synchronizedRequestToken() er // invokeRequestTokenData requests a new token from the IAM token server and // unmarshals the response to produce the authenticator's 'tokenData' field (cache). // Returns an error if the token was unable to be fetched, otherwise returns nil. -func (authenticator *ComputeResourceAuthenticator) invokeRequestTokenData() error { +func (authenticator *ContainerAuthenticator) invokeRequestTokenData() error { tokenResponse, err := authenticator.RequestToken() if err != nil { return err @@ -333,7 +330,7 @@ func (authenticator *ComputeResourceAuthenticator) invokeRequestTokenData() erro // RequestToken first retrieves a CR token value from the current compute resource, then uses // that to obtain a new IAM access token from the IAM token server. -func (authenticator *ComputeResourceAuthenticator) RequestToken() (*IamTokenServerResponse, error) { +func (authenticator *ContainerAuthenticator) RequestToken() (*IamTokenServerResponse, error) { var err error var operationPath string = "/identity/token" @@ -446,6 +443,7 @@ func (authenticator *ComputeResourceAuthenticator) RequestToken() (*IamTokenServ buff := new(bytes.Buffer) _, _ = buff.ReadFrom(resp.Body) resp.Body.Close() + /* #nosec G104 */ // Create a DetailedResponse to be included in the error below. detailedResponse := &DetailedResponse{ @@ -471,7 +469,7 @@ func (authenticator *ComputeResourceAuthenticator) RequestToken() (*IamTokenServ } // retrieveCRToken tries to read the CR token value from the local file system. -func (authenticator *ComputeResourceAuthenticator) retrieveCRToken() (crToken string, err error) { +func (authenticator *ContainerAuthenticator) retrieveCRToken() (crToken string, err error) { // Use the default filename if one wasn't supplied by the user. crTokenFilename := authenticator.CRTokenFilename diff --git a/v5/core/cr_authenticator_test.go b/v5/core/container_authenticator_test.go similarity index 62% rename from v5/core/cr_authenticator_test.go rename to v5/core/container_authenticator_test.go index 98c2be0..c7d9787 100644 --- a/v5/core/cr_authenticator_test.go +++ b/v5/core/container_authenticator_test.go @@ -29,64 +29,64 @@ import ( const ( // To enable debug logging during test execution, set this to "LevelDebug" - craTestLogLevel LogLevel = LevelError - craMockCRTokenFile string = "../resources/cr-token.txt" - craMockIAMProfileName string = "iam-user-123" - craMockIAMProfileID string = "iam-id-123" - craMockClientID string = "client-id-1" - craMockClientSecret string = "client-secret-1" - craTestCRToken1 string = "cr-token-1" - craTestAccessToken1 string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" - craTestAccessToken2 string = "3yJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" - craTestRefreshToken string = "Xj7Gle500MachEOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" + containerAuthTestLogLevel LogLevel = LevelError + containerAuthMockCRTokenFile string = "../resources/cr-token.txt" + containerAuthMockIAMProfileName string = "iam-user-123" + containerAuthMockIAMProfileID string = "iam-id-123" + containerAuthMockClientID string = "client-id-1" + containerAuthMockClientSecret string = "client-secret-1" + containerAuthTestCRToken1 string = "cr-token-1" + containerAuthTestAccessToken1 string = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" + containerAuthTestAccessToken2 string = "3yJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" + containerAuthTestRefreshToken string = "Xj7Gle500MachEOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImhlbGxvIiwicm9sZSI6InVzZXIiLCJwZXJtaXNzaW9ucyI6WyJhZG1pbmlzdHJhdG9yIiwiZGVwbG95bWVudF9hZG1pbiJdLCJzdWIiOiJoZWxsbyIsImlzcyI6IkpvaG4iLCJhdWQiOiJEU1giLCJ1aWQiOiI5OTkiLCJpYXQiOjE1NjAyNzcwNTEsImV4cCI6MTU2MDI4MTgxOSwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4In0.cIodB4I6CCcX8vfIImz7Cytux3GpWyObt9Gkur5g1QI" ) -func TestCraCtorErrors(t *testing.T) { +func TestContainerAuthCtorErrors(t *testing.T) { var err error - var auth *ComputeResourceAuthenticator + var auth *ContainerAuthenticator // Error: missing IAMProfileName and IBMProfileID. - auth, err = NewComputeResourceAuthenticatorBuilder().Build() + auth, err = NewContainerAuthenticatorBuilder().Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientID. - auth, err = NewComputeResourceAuthenticatorBuilder(). - SetIAMProfileName(craMockIAMProfileName). - SetClientIDSecret("", craMockClientSecret). + auth, err = NewContainerAuthenticatorBuilder(). + SetIAMProfileName(containerAuthMockIAMProfileName). + SetClientIDSecret("", containerAuthMockClientSecret). Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientSecret. - auth, err = NewComputeResourceAuthenticatorBuilder(). - SetIAMProfileID(craMockIAMProfileID). - SetClientIDSecret(craMockClientID, ""). + auth, err = NewContainerAuthenticatorBuilder(). + SetIAMProfileID(containerAuthMockIAMProfileID). + SetClientIDSecret(containerAuthMockClientID, ""). Build() assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) } -func TestCraCtorSuccess(t *testing.T) { +func TestContainerAuthCtorSuccess(t *testing.T) { var err error - var auth *ComputeResourceAuthenticator + var auth *ContainerAuthenticator var expectedHeaders = map[string]string{ "header1": "value1", } // Success - only required params // 1. only IAMProfileName - auth, err = NewComputeResourceAuthenticatorBuilder(). - SetIAMProfileName(craMockIAMProfileName). + auth, err = NewContainerAuthenticatorBuilder(). + SetIAMProfileName(containerAuthMockIAMProfileName). Build() assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) + assert.Equal(t, containerAuthMockIAMProfileName, auth.IAMProfileName) assert.Equal(t, "", auth.IAMProfileID) assert.Equal(t, "", auth.URL) assert.Equal(t, "", auth.ClientID) @@ -95,15 +95,15 @@ func TestCraCtorSuccess(t *testing.T) { assert.Nil(t, auth.Headers) // 2. only IAMProfileID - auth, err = NewComputeResourceAuthenticatorBuilder(). - SetIAMProfileID(craMockIAMProfileID). + auth, err = NewContainerAuthenticatorBuilder(). + SetIAMProfileID(containerAuthMockIAMProfileID). Build() assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) assert.Equal(t, "", auth.IAMProfileName) - assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) + assert.Equal(t, containerAuthMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, "", auth.URL) assert.Equal(t, "", auth.ClientID) assert.Equal(t, "", auth.ClientSecret) @@ -111,83 +111,83 @@ func TestCraCtorSuccess(t *testing.T) { assert.Nil(t, auth.Headers) // Success - all parameters - auth, err = NewComputeResourceAuthenticatorBuilder(). + auth, err = NewContainerAuthenticatorBuilder(). SetCRTokenFilename("cr-token-file"). - SetIAMProfileName(craMockIAMProfileName). - SetIAMProfileID(craMockIAMProfileID). + SetIAMProfileName(containerAuthMockIAMProfileName). + SetIAMProfileID(containerAuthMockIAMProfileID). SetURL(defaultIamTokenServerEndpoint). - SetClientIDSecret(craMockClientID, craMockClientSecret). + SetClientIDSecret(containerAuthMockClientID, containerAuthMockClientSecret). SetDisableSSLVerification(true). SetScope("scope1"). SetHeaders(expectedHeaders). Build() assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) assert.Equal(t, "cr-token-file", auth.CRTokenFilename) - assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) - assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) + assert.Equal(t, containerAuthMockIAMProfileName, auth.IAMProfileName) + assert.Equal(t, containerAuthMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, defaultIamTokenServerEndpoint, auth.URL) - assert.Equal(t, craMockClientID, auth.ClientID) - assert.Equal(t, craMockClientSecret, auth.ClientSecret) + assert.Equal(t, containerAuthMockClientID, auth.ClientID) + assert.Equal(t, containerAuthMockClientSecret, auth.ClientSecret) assert.Equal(t, true, auth.DisableSSLVerification) assert.Equal(t, expectedHeaders, auth.Headers) } -func TestCraCtorFromMapErrors(t *testing.T) { +func TestContainerAuthCtorFromMapErrors(t *testing.T) { var err error - var auth *ComputeResourceAuthenticator + var auth *ContainerAuthenticator var configProps map[string]string // Error: nil config map - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing IAMProfileName and IAMProfileID configProps = map[string]string{} - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientID. configProps = map[string]string{ - PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, - PROPNAME_CLIENT_SECRET: craMockClientSecret, + PROPNAME_IAM_PROFILE_NAME: containerAuthMockIAMProfileName, + PROPNAME_CLIENT_SECRET: containerAuthMockClientSecret, } - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) // Error: missing ClientSecret. configProps = map[string]string{ - PROPNAME_IAM_PROFILE_ID: craMockIAMProfileID, - PROPNAME_CLIENT_ID: craMockClientID, + PROPNAME_IAM_PROFILE_ID: containerAuthMockIAMProfileID, + PROPNAME_CLIENT_ID: containerAuthMockClientID, } - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.NotNil(t, err) assert.Nil(t, auth) t.Logf("Expected error: %s", err.Error()) } -func TestCraCtorFromMapSuccess(t *testing.T) { +func TestContainerAuthCtorFromMapSuccess(t *testing.T) { var err error - var auth *ComputeResourceAuthenticator + var auth *ContainerAuthenticator var configProps map[string]string // Success - only required params // 1. only IAMProfileName configProps = map[string]string{ - PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, + PROPNAME_IAM_PROFILE_NAME: containerAuthMockIAMProfileName, } - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) - assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) + assert.Equal(t, containerAuthMockIAMProfileName, auth.IAMProfileName) assert.Equal(t, "", auth.IAMProfileID) assert.Equal(t, "", auth.URL) assert.Equal(t, "", auth.ClientID) @@ -197,15 +197,15 @@ func TestCraCtorFromMapSuccess(t *testing.T) { // 2. only IAMProfileID configProps = map[string]string{ - PROPNAME_IAM_PROFILE_ID: craMockIAMProfileID, + PROPNAME_IAM_PROFILE_ID: containerAuthMockIAMProfileID, } - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) assert.Equal(t, "", auth.CRTokenFilename) assert.Equal(t, "", auth.IAMProfileName) - assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) + assert.Equal(t, containerAuthMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, "", auth.URL) assert.Equal(t, "", auth.ClientID) assert.Equal(t, "", auth.ClientSecret) @@ -214,25 +214,25 @@ func TestCraCtorFromMapSuccess(t *testing.T) { // Success - all params configProps = map[string]string{ - PROPNAME_CRTOKEN_FILENAME: craMockCRTokenFile, - PROPNAME_IAM_PROFILE_NAME: craMockIAMProfileName, - PROPNAME_IAM_PROFILE_ID: craMockIAMProfileID, + PROPNAME_CRTOKEN_FILENAME: containerAuthMockCRTokenFile, + PROPNAME_IAM_PROFILE_NAME: containerAuthMockIAMProfileName, + PROPNAME_IAM_PROFILE_ID: containerAuthMockIAMProfileID, PROPNAME_AUTH_URL: defaultIamTokenServerEndpoint, - PROPNAME_CLIENT_ID: craMockClientID, - PROPNAME_CLIENT_SECRET: craMockClientSecret, + PROPNAME_CLIENT_ID: containerAuthMockClientID, + PROPNAME_CLIENT_SECRET: containerAuthMockClientSecret, PROPNAME_AUTH_DISABLE_SSL: "true", PROPNAME_SCOPE: "scope1", } - auth, err = newComputeResourceAuthenticatorFromMap(configProps) + auth, err = newContainerAuthenticatorFromMap(configProps) assert.Nil(t, err) assert.NotNil(t, auth) - assert.Equal(t, AUTHTYPE_CRAUTH, auth.AuthenticationType()) - assert.Equal(t, craMockCRTokenFile, auth.CRTokenFilename) - assert.Equal(t, craMockIAMProfileName, auth.IAMProfileName) - assert.Equal(t, craMockIAMProfileID, auth.IAMProfileID) + assert.Equal(t, AUTHTYPE_CONTAINER, auth.AuthenticationType()) + assert.Equal(t, containerAuthMockCRTokenFile, auth.CRTokenFilename) + assert.Equal(t, containerAuthMockIAMProfileName, auth.IAMProfileName) + assert.Equal(t, containerAuthMockIAMProfileID, auth.IAMProfileID) assert.Equal(t, defaultIamTokenServerEndpoint, auth.URL) - assert.Equal(t, craMockClientID, auth.ClientID) - assert.Equal(t, craMockClientSecret, auth.ClientSecret) + assert.Equal(t, containerAuthMockClientID, auth.ClientID) + assert.Equal(t, containerAuthMockClientSecret, auth.ClientSecret) assert.Equal(t, true, auth.DisableSSLVerification) assert.Nil(t, auth.Headers) } @@ -249,7 +249,7 @@ func startMockIAMServer(t *testing.T) *httptest.Server { // then validate it a bit and then send back a good response. assert.Equal(t, APPLICATION_JSON, req.Header.Get("Accept")) assert.Equal(t, FORM_URL_ENCODED_HEADER, req.Header.Get("Content-Type")) - assert.Equal(t, craTestCRToken1, req.FormValue("cr_token")) + assert.Equal(t, containerAuthTestCRToken1, req.FormValue("cr_token")) assert.Equal(t, iamGrantTypeCRToken, req.FormValue("grant_type")) iamProfileID := req.FormValue("profile_id") @@ -262,7 +262,7 @@ func startMockIAMServer(t *testing.T) *httptest.Server { // This is the access token we'll send back in the mock response. // We'll default to token 1, then see if the caller asked for token 2 // via the scope setting below. - accessToken := craTestAccessToken1 + accessToken := containerAuthTestAccessToken1 // We'll use the scope value to control the behavior of this mock endpoint so that we can force // certain things to happen: @@ -273,12 +273,12 @@ func startMockIAMServer(t *testing.T) *httptest.Server { scope := req.FormValue("scope") if scope == "send-second-token" { - accessToken = craTestAccessToken2 + accessToken = containerAuthTestAccessToken2 } else if scope == "check-basic-auth" { username, password, ok := req.BasicAuth() assert.True(t, ok) - assert.Equal(t, craMockClientID, username) - assert.Equal(t, craMockClientSecret, password) + assert.Equal(t, containerAuthMockClientID, username) + assert.Equal(t, containerAuthMockClientSecret, password) } else if scope == "check-user-headers" { assert.Equal(t, "Value-1", req.Header.Get("User-Header-1")) assert.Equal(t, "iam.cloud.ibm.com", req.Host) @@ -295,7 +295,7 @@ func startMockIAMServer(t *testing.T) *httptest.Server { switch statusCode { case http.StatusOK: fmt.Fprintf(res, `{"access_token": "%s", "token_type": "Bearer", "expires_in": 3600, "expiration": %d, "refresh_token": "%s"}`, - accessToken, expiration, craTestRefreshToken) + accessToken, expiration, containerAuthTestRefreshToken) case http.StatusBadRequest: fmt.Fprintf(res, `Sorry, bad request!`) @@ -309,23 +309,23 @@ func startMockIAMServer(t *testing.T) *httptest.Server { return server } -func TestCraRetrieveCRTokenSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthRetrieveCRTokenSuccess(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) // Set the authenticator to read the CR token from our mock file. - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, } crToken, err := auth.retrieveCRToken() assert.Nil(t, err) - assert.Equal(t, craTestCRToken1, crToken) + assert.Equal(t, containerAuthTestCRToken1, crToken) } -func TestCraRetrieveCRTokenFail(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthRetrieveCRTokenFail(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) // Use a non-existent cr token file. - auth := &ComputeResourceAuthenticator{ + auth := &ContainerAuthenticator{ CRTokenFilename: "bogus-cr-token-file", } crToken, err := auth.retrieveCRToken() @@ -334,15 +334,15 @@ func TestCraRetrieveCRTokenFail(t *testing.T) { t.Logf("Expected error: %s", err.Error()) } -func TestCraGetTokenSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthGetTokenSuccess(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, + IAMProfileName: containerAuthMockIAMProfileName, URL: server.URL, } err := auth.Validate() @@ -359,8 +359,8 @@ func TestCraGetTokenSuccess(t *testing.T) { // Verify that the access token was returned by GetToken() and also // stored in the authenticator's tokenData field as well. assert.NotNil(t, auth.getTokenData()) - assert.Equal(t, craTestAccessToken1, accessToken) - assert.Equal(t, craTestAccessToken1, auth.getTokenData().AccessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, auth.getTokenData().AccessToken) // Call GetToken() again and verify that we get the cached value. // Note: we'll Set Scope so that if the IAM operation is actually called again, @@ -368,28 +368,28 @@ func TestCraGetTokenSuccess(t *testing.T) { auth.Scope = "send-second-token" accessToken, err = auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Force expiration and verify that GetToken() fetched the second access token. auth.getTokenData().Expiration = GetCurrentTime() - 1 auth.IAMProfileName = "" - auth.IAMProfileID = craMockIAMProfileID + auth.IAMProfileID = containerAuthMockIAMProfileID accessToken, err = auth.GetToken() assert.Nil(t, err) assert.NotNil(t, auth.getTokenData()) - assert.Equal(t, craTestAccessToken2, accessToken) - assert.Equal(t, craTestAccessToken2, auth.getTokenData().AccessToken) + assert.Equal(t, containerAuthTestAccessToken2, accessToken) + assert.Equal(t, containerAuthTestAccessToken2, auth.getTokenData().AccessToken) } -func TestCraRequestTokenSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthRequestTokenSuccess(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, + IAMProfileName: containerAuthMockIAMProfileName, URL: server.URL, } err := auth.Validate() @@ -399,19 +399,19 @@ func TestCraRequestTokenSuccess(t *testing.T) { tokenResponse, err := auth.RequestToken() assert.Nil(t, err) assert.NotNil(t, tokenResponse) - assert.Equal(t, craTestRefreshToken, tokenResponse.RefreshToken) + assert.Equal(t, containerAuthTestRefreshToken, tokenResponse.RefreshToken) } -func TestCraAuthenticateSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthAuthenticateSuccess(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() // Set up the authenticator to use the cr token file. - auth, err := NewComputeResourceAuthenticatorBuilder(). - SetCRTokenFilename(craMockCRTokenFile). - SetIAMProfileID(craMockIAMProfileID). + auth, err := NewContainerAuthenticatorBuilder(). + SetCRTokenFilename(containerAuthMockCRTokenFile). + SetIAMProfileID(containerAuthMockIAMProfileID). SetURL(server.URL). Build() assert.Nil(t, err) @@ -431,7 +431,7 @@ func TestCraAuthenticateSuccess(t *testing.T) { // Verify that it succeeded. assert.Nil(t, err) authHeader := request.Header.Get("Authorization") - assert.Equal(t, "Bearer "+craTestAccessToken1, authHeader) + assert.Equal(t, "Bearer "+containerAuthTestAccessToken1, authHeader) // Call Authenticate again to make sure we used the cached access token. // We'll do this by setting scope to request the second token, @@ -441,24 +441,24 @@ func TestCraAuthenticateSuccess(t *testing.T) { err = auth.Authenticate(request) assert.Nil(t, err) authHeader = request.Header.Get("Authorization") - assert.Equal(t, "Bearer "+craTestAccessToken1, authHeader) + assert.Equal(t, "Bearer "+containerAuthTestAccessToken1, authHeader) // Force expiration and verify that Authenticate() fetched the second access token. auth.getTokenData().Expiration = GetCurrentTime() - 1 err = auth.Authenticate(request) assert.Nil(t, err) authHeader = request.Header.Get("Authorization") - assert.Equal(t, "Bearer "+craTestAccessToken2, authHeader) + assert.Equal(t, "Bearer "+containerAuthTestAccessToken2, authHeader) } -func TestCraAuthenticateFailNoCRToken(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthAuthenticateFailNoCRToken(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) // Set up the authenticator with a bogus cr token filename // so that we can't successfully retrieve a CR Token value. - auth, err := NewComputeResourceAuthenticatorBuilder(). + auth, err := NewContainerAuthenticatorBuilder(). SetCRTokenFilename("bogus-cr-token-file"). - SetIAMProfileName(craMockIAMProfileName). + SetIAMProfileName(containerAuthMockIAMProfileName). SetURL("https://bogus.iam.endpoint"). Build() assert.Nil(t, err) @@ -486,17 +486,17 @@ func TestCraAuthenticateFailNoCRToken(t *testing.T) { assert.Equal(t, err.Error(), authErr.Error()) } -func TestCraAuthenticateFailIAM(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthAuthenticateFailIAM(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() // Set up the authenticator to use our mock cr token file, // and set scope to cause the mock IAM server to send a bad status code for the IAM call. - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, + IAMProfileName: containerAuthMockIAMProfileName, URL: server.URL, Scope: "status-bad-request", } @@ -524,15 +524,15 @@ func TestCraAuthenticateFailIAM(t *testing.T) { assert.Equal(t, http.StatusBadRequest, authErr.Response.StatusCode) } -func TestCraBackgroundTokenRefreshSuccess(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthBackgroundTokenRefreshSuccess(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, + IAMProfileName: containerAuthMockIAMProfileName, URL: server.URL, } err := auth.Validate() @@ -541,7 +541,7 @@ func TestCraBackgroundTokenRefreshSuccess(t *testing.T) { // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Now simulate being in the refresh window where the token is not expired but still needs to be refreshed. auth.getTokenData().RefreshTime = GetCurrentTime() - 1 @@ -553,24 +553,24 @@ func TestCraBackgroundTokenRefreshSuccess(t *testing.T) { auth.Scope = "send-second-token" accessToken, err = auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Wait for the background thread to finish. time.Sleep(2 * time.Second) accessToken, err = auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken2, accessToken) + assert.Equal(t, containerAuthTestAccessToken2, accessToken) } -func TestCraBackgroundTokenRefreshFail(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthBackgroundTokenRefreshFail(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth := &ComputeResourceAuthenticator{ - CRTokenFilename: craMockCRTokenFile, - IAMProfileName: craMockIAMProfileName, + auth := &ContainerAuthenticator{ + CRTokenFilename: containerAuthMockCRTokenFile, + IAMProfileName: containerAuthMockIAMProfileName, URL: server.URL, } err := auth.Validate() @@ -579,7 +579,7 @@ func TestCraBackgroundTokenRefreshFail(t *testing.T) { // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Now simulate being in the refresh window where the token is not expired but still needs to be refreshed. auth.getTokenData().RefreshTime = GetCurrentTime() - 1 @@ -590,7 +590,7 @@ func TestCraBackgroundTokenRefreshFail(t *testing.T) { auth.Scope = "status-unauthorized" accessToken, err = auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Wait for the background thread to finish. time.Sleep(2 * time.Second) @@ -601,7 +601,7 @@ func TestCraBackgroundTokenRefreshFail(t *testing.T) { // that we had previously cached. accessToken, err = auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Next, simulate the expiration of the token, then we should expect // an error from GetToken(). @@ -612,16 +612,16 @@ func TestCraBackgroundTokenRefreshFail(t *testing.T) { t.Logf("Expected error: %s\n", err.Error()) } -func TestCraClientIdAndSecret(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthClientIdAndSecret(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth, err := NewComputeResourceAuthenticatorBuilder(). - SetCRTokenFilename(craMockCRTokenFile). - SetIAMProfileName(craMockIAMProfileName). - SetClientIDSecret(craMockClientID, craMockClientSecret). + auth, err := NewContainerAuthenticatorBuilder(). + SetCRTokenFilename(containerAuthMockCRTokenFile). + SetIAMProfileName(containerAuthMockIAMProfileName). + SetClientIDSecret(containerAuthMockClientID, containerAuthMockClientSecret). SetURL(server.URL). Build() assert.Nil(t, err) @@ -631,18 +631,18 @@ func TestCraClientIdAndSecret(t *testing.T) { auth.Scope = "check-basic-auth" accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) } -func TestCraDisableSSL(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthDisableSSL(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth, err := NewComputeResourceAuthenticatorBuilder(). - SetCRTokenFilename(craMockCRTokenFile). - SetIAMProfileName(craMockIAMProfileName). + auth, err := NewContainerAuthenticatorBuilder(). + SetCRTokenFilename(containerAuthMockCRTokenFile). + SetIAMProfileName(containerAuthMockIAMProfileName). SetURL(server.URL). SetDisableSSLVerification(true). Build() @@ -652,7 +652,7 @@ func TestCraDisableSSL(t *testing.T) { // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Next, verify that the authenticator's Client is configured correctly. assert.NotNil(t, auth.Client) @@ -663,8 +663,8 @@ func TestCraDisableSSL(t *testing.T) { assert.True(t, transport.TLSClientConfig.InsecureSkipVerify) } -func TestCraUserHeaders(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthUserHeaders(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() @@ -673,9 +673,9 @@ func TestCraUserHeaders(t *testing.T) { headers["User-Header-1"] = "Value-1" headers["Host"] = "iam.cloud.ibm.com" - auth, err := NewComputeResourceAuthenticatorBuilder(). - SetCRTokenFilename(craMockCRTokenFile). - SetIAMProfileName(craMockIAMProfileName). + auth, err := NewContainerAuthenticatorBuilder(). + SetCRTokenFilename(containerAuthMockCRTokenFile). + SetIAMProfileName(containerAuthMockIAMProfileName). SetURL(server.URL). SetHeaders(headers). Build() @@ -686,18 +686,18 @@ func TestCraUserHeaders(t *testing.T) { auth.Scope = "check-user-headers" accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) } -func TestCraGetTokenTimeout(t *testing.T) { - GetLogger().SetLogLevel(craTestLogLevel) +func TestContainerAuthGetTokenTimeout(t *testing.T) { + GetLogger().SetLogLevel(containerAuthTestLogLevel) server := startMockIAMServer(t) defer server.Close() - auth, err := NewComputeResourceAuthenticatorBuilder(). - SetCRTokenFilename(craMockCRTokenFile). - SetIAMProfileName(craMockIAMProfileName). + auth, err := NewContainerAuthenticatorBuilder(). + SetCRTokenFilename(containerAuthMockCRTokenFile). + SetIAMProfileName(containerAuthMockIAMProfileName). SetURL(server.URL). Build() assert.Nil(t, err) @@ -706,7 +706,7 @@ func TestCraGetTokenTimeout(t *testing.T) { // Force the first fetch and verify we got the first access token. accessToken, err := auth.GetToken() assert.Nil(t, err) - assert.Equal(t, craTestAccessToken1, accessToken) + assert.Equal(t, containerAuthTestAccessToken1, accessToken) // Next, tell the mock server to sleep for a bit, force the expiration of the token, // and configure the client with a short timeout. diff --git a/v5/resources/my-credentials.env b/v5/resources/my-credentials.env index 8112f5b..94c0c0b 100644 --- a/v5/resources/my-credentials.env +++ b/v5/resources/my-credentials.env @@ -56,7 +56,7 @@ SERVICE6_AUTH_URL=https://iamhost/iam/api SERVICE6_SCOPE=scope1 scope2 scope3 # Service configured with CRA -SERVICE7_AUTH_TYPE=CraUth +SERVICE7_AUTH_TYPE=conTaIneR SERVICE7_CR_TOKEN_FILENAME=crtoken.txt SERVICE7_IAM_PROFILE_NAME=iam-user1 SERVICE7_IAM_PROFILE_ID=iam-id1 From 45a927c07eba5c25a44eb2a2fed317eb557b0165 Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Tue, 3 Aug 2021 16:40:31 -0500 Subject: [PATCH 4/6] chore: update .secrets.baseline --- .secrets.baseline | 94 ++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 84780a2..fb3e06a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "package-lock.json|go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2021-08-03T13:14:10Z", + "generated_at": "2021-08-03T21:39:38Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -289,26 +289,62 @@ "verified_result": null }, { - "hashed_secret": "4c3d172901e4d1bddd4f67c2e5f659882d175fa2", + "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", "is_secret": false, "is_verified": false, - "line_number": 24, + "line_number": 44, "type": "Secret Keyword", "verified_result": null }, { - "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", + "hashed_secret": "8318df9ecda039deac9868adf1944a29a95c7114", "is_secret": false, "is_verified": false, - "line_number": 44, + "line_number": 46, + "type": "Secret Keyword", + "verified_result": null + } + ], + "v5/core/container_authenticator.go": [ + { + "hashed_secret": "3c81615afb40d1889fc2e1fff551a8b59b4e80ce", + "is_secret": false, + "is_verified": false, + "line_number": 96, "type": "Secret Keyword", "verified_result": null }, { - "hashed_secret": "8318df9ecda039deac9868adf1944a29a95c7114", + "hashed_secret": "8b142a91cfb6e617618ad437cedf74a6745f8926", "is_secret": false, "is_verified": false, - "line_number": 46, + "line_number": 139, + "type": "Secret Keyword", + "verified_result": null + } + ], + "v5/core/container_authenticator_test.go": [ + { + "hashed_secret": "c8f0df25bade89c1873f5f01b85bcfb921443ac6", + "is_secret": false, + "is_verified": false, + "line_number": 39, + "type": "JSON Web Token", + "verified_result": null + }, + { + "hashed_secret": "f0048c1e535178d8ba9760fd4139c2554ac53d99", + "is_secret": false, + "is_verified": false, + "line_number": 222, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "10ef99be8df801b05b5933e121e85385edf6b98a", + "is_secret": false, + "is_verified": false, + "line_number": 571, "type": "Secret Keyword", "verified_result": null } @@ -413,50 +449,6 @@ "verified_result": null } ], - "v5/core/cr_authenticator.go": [ - { - "hashed_secret": "3c81615afb40d1889fc2e1fff551a8b59b4e80ce", - "is_secret": false, - "is_verified": false, - "line_number": 99, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "8b142a91cfb6e617618ad437cedf74a6745f8926", - "is_secret": false, - "is_verified": false, - "line_number": 142, - "type": "Secret Keyword", - "verified_result": null - } - ], - "v5/core/cr_authenticator_test.go": [ - { - "hashed_secret": "c8f0df25bade89c1873f5f01b85bcfb921443ac6", - "is_secret": false, - "is_verified": false, - "line_number": 39, - "type": "JSON Web Token", - "verified_result": null - }, - { - "hashed_secret": "1cef53b6f5a230f88f15e2213a257fe72d9545fd", - "is_secret": false, - "is_verified": false, - "line_number": 222, - "type": "Secret Keyword", - "verified_result": null - }, - { - "hashed_secret": "de82580fcac7f6e7df7c1a14f47ff060f098826f", - "is_secret": false, - "is_verified": false, - "line_number": 571, - "type": "Secret Keyword", - "verified_result": null - } - ], "v5/core/gzip_test.go": [ { "hashed_secret": "4912eabc958e1d066ed0b9c041a1a5f2eeb19f05", From 232435112caeca9b31f6c104e8d5460fa03a02ca Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Tue, 3 Aug 2021 16:55:43 -0500 Subject: [PATCH 5/6] docs: update Authentication.md with design changes --- Authentication.md | 56 +++++++++++++---------------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/Authentication.md b/Authentication.md index a12d7b7..a717a22 100644 --- a/Authentication.md +++ b/Authentication.md @@ -256,18 +256,18 @@ service := exampleservicev1.NewExampleServiceV1(options) ``` -## Compute Resource Authentication -The `ComputeResourceAuthenticator` is intended to be used by application code -running inside a compute resource (a VM) in which a secure compute resource -token (CR token) has been injected by the compute resource -provider (e.g. IBM Kubernetes Service (IKS), VPC Gen2 Virtual Server Instances (VSI), etc.). +## Container Authentication +The `ContainerAuthenticator` is intended to be used by application code +running inside a compute resource managed by the IBM Kubernetes Service (IKS) +in which a secure compute resource token (CR token) has been stored in a file +within the compute resource's local file system. The CR token is similar to an IAM apikey except that it is managed automatically by -the compute resource provider. +the compute resource provider (IKS). This allows the application developer to: - avoid storing credentials in application code, configuraton files or a password vault - avoid managing or rotating credentials -The `ComputeResourceAuthenticator` will retrieve the CR token from +The `ContainerAuthenticator` will retrieve the CR token from the compute resource in which the application is running, and will then perform the necessary interactions with the IAM token service to obtain an IAM access token using the IAM "get token" operation with grant-type `cr-token`. @@ -278,39 +278,12 @@ The IAM access token is added to each outbound request in the `Authorization` he Authorization: Bearer ``` -### Compute Resource Token Retrieval Algorithm -The `ComputeResourceAuthenticator` will retrieve a fresh CR token value each time it needs -to obtain a new access token from the IAM token service. -It will do this according to the following algorithm: - -1. First, the authenticator will attempt to read the CR token value from a file in the -compute resource's local file system. -By default, the authenticator will use the filename `/var/run/secrets/tokens/vault-token`, but this -can be overridden by setting the `CRTokenFilename` property (described below). -If a suitable CR token value is obtained from this step, then the authenticator will use this value. -Otherwise, the authenticator will proceed to step 2 below. - -2. If no CR token was obtained from step 1 above, then the authenticator will attempt to invoke the -`PUT /instance_identity/v1/token` (aka `create_access_token`) operation from the compute resource's -local Instance Metadata Service. The CR token value is obtained from the `access_token` -field of the operation response. -By default, the authenticator will use `http://169.254.169.254` as the base endpoint URL for this -invocation, but this can be overridden by setting the -`InstanceMetadataServiceURL` property (described below). -If a suitable CR token value is obtained from this step, then the authenticator will use this value. -Otherwise, an error is reported and the authentication fails. - ### Properties -- CRTokenFilename: (optional) the name of the file containing the injected CR token value -(applies to the IKS use-case). +- CRTokenFilename: (optional) the name of the file containing the injected CR token value. If not specified, then `/var/run/secrets/tokens/vault-token` is used as the default value. The application must have `read` permissions on the file containing the CR token value. -- InstanceMetadataServiceURL: (optional) the base endpoint URL to be used for invoking -operations of the compute resource's local Instance Metadata Service (applies to the VSI use-case). -If not specified, then `http://169.254.169.254` is used as the default value. - - IAMProfileName: (optional) the name of the linked trusted IAM profile to be used when obtaining the IAM access token (a CR token might map to multiple IAM profiles). One of `IAMProfileName` or `IAMProfileID` must be specified. @@ -320,10 +293,11 @@ IAM access token (a CR token might map to multiple IAM profiles). One of `IAMProfileName` or `IAMProfileID` must be specified. - URL: (optional) The base endpoint URL of the IAM token service. -The default value of this property is the "prod" IAM token service endpoint (`https://iam.cloud.ibm.com`). +The default value of this property is the "prod" IAM token service endpoint +(`https://iam.cloud.ibm.com`). - ClientId/ClientSecret: (optional) The `ClientId` and `ClientSecret` fields are used to form a -"basic auth" Authorization header for interactions with the IAM token server. If neither field +"basic auth" Authorization header for interactions with the IAM token service. If neither field is specified, then no Authorization header will be sent with token server requests. These fields are optional, but must be specified together. @@ -347,9 +321,9 @@ import { } ... // Create the authenticator. -authenticator := &core.ComputeResourceAuthenticator{ - IAMProfileName: "iam-user123", -} +authenticator := core.NewContainerAuthenticatorBuilder(). + SetIAMProfileName("iam-user123"). + Build() // Create the service options struct. options := &exampleservicev1.ExampleServiceV1Options{ @@ -365,7 +339,7 @@ service := exampleservicev1.NewExampleServiceV1(options) ### Configuration example External configuration: ``` -export EXAMPLE_SERVICE_AUTH_TYPE=crauth +export EXAMPLE_SERVICE_AUTH_TYPE=container export EXAMPLE_SERVICE_IAM_PROFILE_NAME=iam-user123 ``` Application code: From be39beb3fdf9cdcf530f024520c97d8b155fffbd Mon Sep 17 00:00:00 2001 From: Phil Adams Date: Tue, 3 Aug 2021 17:01:28 -0500 Subject: [PATCH 6/6] chore: update .secrets.baseline --- .secrets.baseline | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index fb3e06a..c976f8b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "package-lock.json|go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2021-08-03T21:39:38Z", + "generated_at": "2021-08-03T22:01:08Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -70,7 +70,7 @@ "hashed_secret": "98635b2eaa2379f28cd6d72a38299f286b81b459", "is_secret": false, "is_verified": false, - "line_number": 411, + "line_number": 385, "type": "Secret Keyword", "verified_result": null }, @@ -78,7 +78,7 @@ "hashed_secret": "91dfd9ddb4198affc5c194cd8ce6d338fde470e2", "is_secret": false, "is_verified": false, - "line_number": 464, + "line_number": 438, "type": "Secret Keyword", "verified_result": null }, @@ -86,7 +86,7 @@ "hashed_secret": "47fcf185ee7e15fe05cae31fbe9e4ebe4a06a40d", "is_secret": false, "is_verified": false, - "line_number": 470, + "line_number": 444, "type": "Secret Keyword", "verified_result": null }