Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send user-agent header with auth token requests #216

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion core/container_authenticator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package core

// (C) Copyright IBM Corp. 2021, 2023..
// (C) Copyright IBM Corp. 2021, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -86,6 +86,10 @@ type ContainerAuthenticator struct {
Client *http.Client
clientInit sync.Once

// The User-Agent header value to be included with each token request.
userAgent string
userAgentInit sync.Once

// The cached IAM access token and its expiration time.
tokenData *iamTokenData

Expand Down Expand Up @@ -199,6 +203,14 @@ func (authenticator *ContainerAuthenticator) client() *http.Client {
return authenticator.Client
}

// getUserAgent returns the User-Agent header value to be included in each token request invoked by the authenticator.
func (authenticator *ContainerAuthenticator) getUserAgent() string {
authenticator.userAgentInit.Do(func() {
authenticator.userAgent = fmt.Sprintf("%s/%s-%s %s", sdkName, "container-authenticator", __VERSION__, SystemInfo())
})
return authenticator.userAgent
}

// newContainerAuthenticatorFromMap constructs a new ContainerAuthenticator instance from a map containing
// configuration properties.
func newContainerAuthenticatorFromMap(properties map[string]string) (authenticator *ContainerAuthenticator, err error) {
Expand Down Expand Up @@ -392,6 +404,7 @@ func (authenticator *ContainerAuthenticator) RequestToken() (*IamTokenServerResp

builder.AddHeader(CONTENT_TYPE, FORM_URL_ENCODED_HEADER)
builder.AddHeader(Accept, APPLICATION_JSON)
builder.AddHeader(headerNameUserAgent, authenticator.getUserAgent())
builder.AddFormData("grant_type", "", "", iamGrantTypeCRToken) // #nosec G101
builder.AddFormData("cr_token", "", "", crToken)

Expand Down
3 changes: 3 additions & 0 deletions core/container_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"

"testing"
"time"
Expand Down Expand Up @@ -257,6 +258,8 @@ 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.True(t, strings.HasPrefix(req.Header.Get(headerNameUserAgent),
fmt.Sprintf("%s/%s", sdkName, "container-authenticator")))
assert.Equal(t, containerAuthTestCRToken1, req.FormValue("cr_token"))
assert.Equal(t, iamGrantTypeCRToken, req.FormValue("grant_type"))

Expand Down
17 changes: 15 additions & 2 deletions core/cp4d_authenticator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package core

// (C) Copyright IBM Corp. 2019, 2021.
// (C) Copyright IBM Corp. 2019, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -57,6 +57,10 @@ type CloudPakForDataAuthenticator struct {
Client *http.Client
clientInit sync.Once

// The User-Agent header value to be included with each token request.
userAgent string
userAgentInit sync.Once

// The cached token and expiration time.
tokenData *cp4dTokenData

Expand Down Expand Up @@ -182,6 +186,14 @@ func (authenticator *CloudPakForDataAuthenticator) client() *http.Client {
return authenticator.Client
}

// getUserAgent returns the User-Agent header value to be included in each token request invoked by the authenticator.
func (authenticator *CloudPakForDataAuthenticator) getUserAgent() string {
authenticator.userAgentInit.Do(func() {
authenticator.userAgent = fmt.Sprintf("%s/%s-%s %s", sdkName, "cp4d-authenticator", __VERSION__, SystemInfo())
})
return authenticator.userAgent
}

// Authenticate adds the bearer token (obtained from the token server) to the
// specified request.
//
Expand Down Expand Up @@ -304,8 +316,9 @@ func (authenticator *CloudPakForDataAuthenticator) requestToken() (tokenResponse
builder.AddHeader(headerName, headerValue)
}

// Add the Content-Type header.
// Add the Content-Type and User-Agent headers.
builder.AddHeader("Content-Type", "application/json")
builder.AddHeader(headerNameUserAgent, authenticator.getUserAgent())

// Add the request body to request.
_, err = builder.SetBodyContentJSON(body)
Expand Down
4 changes: 3 additions & 1 deletion core/cp4d_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package core

// (C) Copyright IBM Corp. 2019, 2021.
// (C) Copyright IBM Corp. 2019, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -585,6 +585,8 @@ func TestCp4dUserHeaders(t *testing.T) {
assert.Equal(t, "Value1", r.Header.Get("Header1"))
assert.Equal(t, "Value2", r.Header.Get("Header2"))
assert.Equal(t, "cp4d.cloud.ibm.com", r.Host)
assert.True(t, strings.HasPrefix(r.Header.Get(headerNameUserAgent),
fmt.Sprintf("%s/%s", sdkName, "cp4d-authenticator")))

w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{ "_messageCode_":"200", "message":"success", "token":"%s"}`, cp4dUsernameApikey1)
Expand Down
13 changes: 13 additions & 0 deletions core/iam_authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ type IamAuthenticator struct {
Client *http.Client
clientInit sync.Once

// The User-Agent header value to be included with each token request.
userAgent string
userAgentInit sync.Once

// The cached token and expiration time.
tokenData *iamTokenData

Expand Down Expand Up @@ -190,6 +194,14 @@ func (authenticator *IamAuthenticator) client() *http.Client {
return authenticator.Client
}

// getUserAgent returns the User-Agent header value to be included in each token request invoked by the authenticator.
func (authenticator *IamAuthenticator) getUserAgent() string {
authenticator.userAgentInit.Do(func() {
authenticator.userAgent = fmt.Sprintf("%s/%s-%s %s", sdkName, "iam-authenticator", __VERSION__, SystemInfo())
})
return authenticator.userAgent
}

// NewIamAuthenticator constructs a new IamAuthenticator instance.
// Deprecated - use the IamAuthenticatorBuilder instead.
func NewIamAuthenticator(apiKey string, url string, clientId string, clientSecret string,
Expand Down Expand Up @@ -413,6 +425,7 @@ func (authenticator *IamAuthenticator) RequestToken() (*IamTokenServerResponse,

builder.AddHeader(CONTENT_TYPE, "application/x-www-form-urlencoded")
builder.AddHeader(Accept, APPLICATION_JSON)
builder.AddHeader(headerNameUserAgent, authenticator.getUserAgent())
builder.AddFormData("response_type", "", "", "cloud_iam")

if authenticator.ApiKey != "" {
Expand Down
2 changes: 2 additions & 0 deletions core/iam_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,8 @@ func TestIamUserHeaders(t *testing.T) {
assert.False(t, ok)
assert.Equal(t, "Value1", r.Header.Get("Header1"))
assert.Equal(t, "Value2", r.Header.Get("Header2"))
assert.True(t, strings.HasPrefix(r.Header.Get(headerNameUserAgent),
fmt.Sprintf("%s/%s", sdkName, "iam-authenticator")))
assert.Equal(t, "iam.cloud.ibm.com", r.Host)
}))
defer server.Close()
Expand Down
15 changes: 14 additions & 1 deletion core/mcsp_authenticator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package core

// (C) Copyright IBM Corp. 2023.
// (C) Copyright IBM Corp. 2023, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -49,6 +49,10 @@ type MCSPAuthenticator struct {
Client *http.Client
clientInit sync.Once

// The User-Agent header value to be included with each token request.
userAgent string
userAgentInit sync.Once

// The cached token and expiration time.
tokenData *mcspTokenData

Expand Down Expand Up @@ -136,6 +140,14 @@ func (authenticator *MCSPAuthenticator) client() *http.Client {
return authenticator.Client
}

// getUserAgent returns the User-Agent header value to be included in each token request invoked by the authenticator.
func (authenticator *MCSPAuthenticator) getUserAgent() string {
authenticator.userAgentInit.Do(func() {
authenticator.userAgent = fmt.Sprintf("%s/%s-%s %s", sdkName, "mcsp-authenticator", __VERSION__, SystemInfo())
})
return authenticator.userAgent
}

// newMCSPAuthenticatorFromMap constructs a new MCSPAuthenticator instance from a map.
func newMCSPAuthenticatorFromMap(properties map[string]string) (authenticator *MCSPAuthenticator, err error) {
if properties == nil {
Expand Down Expand Up @@ -280,6 +292,7 @@ func (authenticator *MCSPAuthenticator) RequestToken() (*MCSPTokenServerResponse

builder.AddHeader(CONTENT_TYPE, APPLICATION_JSON)
builder.AddHeader(Accept, APPLICATION_JSON)
builder.AddHeader(headerNameUserAgent, authenticator.getUserAgent())
requestBody := fmt.Sprintf(`{"apikey":"%s"}`, authenticator.ApiKey)
_, _ = builder.SetBodyContentString(requestBody)

Expand Down
4 changes: 3 additions & 1 deletion core/mcsp_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package core

// (C) Copyright IBM Corp. 2023.
// (C) Copyright IBM Corp. 2023, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -467,6 +467,8 @@ func TestMCSPUserHeaders(t *testing.T) {
fmt.Fprintf(w, `{"token":"%s","token_type":"jwt","expires_in":7200}`, mcspAuthTestAccessToken1)
assert.Equal(t, "Value1", r.Header.Get("Header1"))
assert.Equal(t, "Value2", r.Header.Get("Header2"))
assert.True(t, strings.HasPrefix(r.Header.Get(headerNameUserAgent),
fmt.Sprintf("%s/%s", sdkName, "mcsp-authenticator")))
assert.Equal(t, "mcsp.cloud.ibm.com", r.Host)
}))
defer server.Close()
Expand Down
15 changes: 14 additions & 1 deletion core/vpc_instance_authenticator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package core

// (C) Copyright IBM Corp. 2021, 2023.
// (C) Copyright IBM Corp. 2021, 2024.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -58,6 +58,10 @@ type VpcInstanceAuthenticator struct {
Client *http.Client
clientInit sync.Once

// The User-Agent header value to be included with each token request.
userAgent string
userAgentInit sync.Once

// The cached IAM access token and its expiration time.
tokenData *iamTokenData

Expand Down Expand Up @@ -133,6 +137,14 @@ func (authenticator *VpcInstanceAuthenticator) client() *http.Client {
return authenticator.Client
}

// getUserAgent returns the User-Agent header value to be included in each token request invoked by the authenticator.
func (authenticator *VpcInstanceAuthenticator) getUserAgent() string {
authenticator.userAgentInit.Do(func() {
authenticator.userAgent = fmt.Sprintf("%s/%s-%s %s", sdkName, "vpc-instance-authenticator", __VERSION__, SystemInfo())
})
return authenticator.userAgent
}

// url returns the authenticator's URL property after potentially initializing it.
func (authenticator *VpcInstanceAuthenticator) url() string {
authenticator.urlInit.Do(func() {
Expand Down Expand Up @@ -332,6 +344,7 @@ func (authenticator *VpcInstanceAuthenticator) retrieveIamAccessToken(
builder.AddQuery("version", vpcauthMetadataServiceVersion)
builder.AddHeader(CONTENT_TYPE, APPLICATION_JSON)
builder.AddHeader(Accept, APPLICATION_JSON)
builder.AddHeader(headerNameUserAgent, authenticator.getUserAgent())
builder.AddHeader("Authorization", "Bearer "+instanceIdentityToken)

// Next, construct the optional request body to specify the linked IAM profile.
Expand Down
3 changes: 3 additions & 0 deletions core/vpc_instance_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"time"

"github.com/go-openapi/strfmt"
Expand Down Expand Up @@ -293,6 +294,8 @@ func startMockVPCServer(t *testing.T, scenario string) *httptest.Server {
assert.NotEmpty(t, req.URL.Query().Get("version"))
assert.Equal(t, APPLICATION_JSON, req.Header.Get("Accept"))
assert.Equal(t, APPLICATION_JSON, req.Header.Get("Content-Type"))
assert.True(t, strings.HasPrefix(req.Header.Get(headerNameUserAgent),
fmt.Sprintf("%s/%s", sdkName, "vpc-instance-authenticator")))
assert.Equal(t, expectedAuthorizationHeader, req.Header.Get("Authorization"))

// Models a trusted profile (includes both CRN and ID fields).
Expand Down