Skip to content

Commit

Permalink
Changes to support GLP IAM API Client
Browse files Browse the repository at this point in the history
In this PR we add support for GLP IAM API Clients.  We retain support
for GLCS API Clients.  We also retain support for "non-api vended" GLCS
API Clients for backwards compatibility.

We've added a new provider parameter "iam_version" with the
corresponding env-var HPEGL_IAM_VERSION to designate which version of
IAM is being used:
- glcs
- glp

The default value of this parameter is glp.  We've added a function to
validate the value of "iam_version".

We've updated the "description" field for "api_vended_service_client"
and "tenant_id" in light of the support for GLP IAM.

The issuertoken package has been modified to generate the correct URL
and the correct set of http request parameters depending on which type
of IAM is being used.

We've had to update various unit-tests and the generated mocks.

Signed-off-by: Eamonn O'Toole <[email protected]>
  • Loading branch information
eamonnotoole committed May 16, 2024
1 parent 6aa29f7 commit 1aeeded
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 34 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/hewlettpackard/hpegl-provider-lib
go 1.18

require (
github.com/davecgh/go-spew v1.1.1
github.com/golang/mock v1.6.0
github.com/golangci/golangci-lint v1.43.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.1
Expand Down Expand Up @@ -35,7 +36,6 @@ require (
github.com/charithe/durationcheck v0.0.9 // indirect
github.com/chavacava/garif v0.0.0-20221024190013-b3ef35877348 // indirect
github.com/daixiang0/gci v0.9.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingajkin/go-header v0.4.2 // indirect
github.com/esimonov/ifshort v1.0.4 // indirect
github.com/ettle/strcase v0.1.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions pkg/mocks/IdentityAPI_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 44 additions & 5 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
// (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP

package provider

Expand All @@ -11,6 +11,16 @@ import (
"github.com/hewlettpackard/hpegl-provider-lib/pkg/registration"
)

const (
// IAMVersionGLCS is the IAM version for GLCS
IAMVersionGLCS = "glcs"
// IAMVersionGLP is the IAM version for GLP
IAMVersionGLP = "glp"
)

// Update this list with any new IAM versions
var iamVersionList = [...]string{IAMVersionGLCS, IAMVersionGLP}

// ConfigureFunc is a type definition of a function that returns a ConfigureContextFunc object
// A function of this type is passed in to NewProviderFunc below
type ConfigureFunc func(p *schema.Provider) schema.ConfigureContextFunc
Expand Down Expand Up @@ -81,9 +91,18 @@ func Schema() map[string]*schema.Schema {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("HPEGL_IAM_SERVICE_URL", "https://client.greenlake.hpe.com/api/iam"),
Description: `The IAM service URL to be used to generate tokens. In the case of API-vended API clients
(the default) then this should be set to the "issuer url" for the client. In the case of non-API-vended
API clients use the appropriate GL "client" URL. Can be set by HPEGL_IAM_SERVICE_URL env-var`,
Description: `The IAM service URL to be used to generate tokens. In the case of GLCS API clients
(the default) then this should be set to the "issuer url" for the client. In the case of GLP
API clients use the appropriate "Token URL" from the API screen. Can be set by HPEGL_IAM_SERVICE_URL env-var`,
}

providerSchema["iam_version"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("HPEGL_IAM_VERSION", IAMVersionGLCS),
ValidateFunc: ValidateIAMVersion,
Description: `The IAM version to be used. Can be set by HPEGL_IAM_VERSION env-var. Valid values are:
` + fmt.Sprintf("%v", iamVersionList) + `The default is ` + IAMVersionGLCS + `.`,
}

providerSchema["api_vended_service_client"] = &schema.Schema{
Expand All @@ -98,7 +117,7 @@ func Schema() map[string]*schema.Schema {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("HPEGL_TENANT_ID", ""),
Description: "The tenant-id to be used, can be set by HPEGL_TENANT_ID env-var",
Description: "The tenant-id to be used for GLCS IAM, can be set by HPEGL_TENANT_ID env-var",
}

providerSchema["user_id"] = &schema.Schema{
Expand Down Expand Up @@ -137,3 +156,23 @@ func convertToTypeSet(r *schema.Resource) *schema.Schema {
Elem: r,
}
}

// ValidateIAMVersion is a ValidateFunc for the "iam_version" field in the provider schema
func ValidateIAMVersion(v interface{}, k string) ([]string, []error) {
// check that v is in iamVersionList
found := false
for _, version := range iamVersionList {
if version == v.(string) {
found = true
break
}
}

// add error if not found
es := make([]error, 0)
if !found {
es = append(es, fmt.Errorf("IAM version must be one of %v", iamVersionList))
}

return []string{}, es
}
43 changes: 41 additions & 2 deletions pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
// (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP

package provider

Expand All @@ -8,8 +8,9 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hewlettpackard/hpegl-provider-lib/pkg/registration"
"github.com/stretchr/testify/assert"

"github.com/hewlettpackard/hpegl-provider-lib/pkg/registration"
)

func testResource() *schema.Resource {
Expand Down Expand Up @@ -170,3 +171,41 @@ func TestNewProviderFunc(t *testing.T) {
})
}
}

func TestValidateIAMVersion(t *testing.T) {
t.Parallel()
testcases := []struct {
name string
version string
hasError bool
}{
{
name: "valid IAM version GLCS",
version: IAMVersionGLCS,
hasError: false,
},
{
name: "valid IAM version GLP",
version: IAMVersionGLP,
hasError: false,
},
{
name: "invalid IAM version",
version: "invalid",
hasError: true,
},
}

for _, testcase := range testcases {
tc := testcase
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
_, es := ValidateIAMVersion(tc.version, "iam_version")
if tc.hasError {
assert.NotEmpty(t, es)
} else {
assert.Empty(t, es)
}
})
}
}
7 changes: 4 additions & 3 deletions pkg/token/httpclient/httpclient.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
// (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP

package httpclient

Expand Down Expand Up @@ -33,11 +33,12 @@ func New(identityServiceURL string, vendedServiceClient bool, passedInToken stri
}
}

func (c *Client) GenerateToken(ctx context.Context, tenantID, clientID, clientSecret string) (string, error) {
func (c *Client) GenerateToken(ctx context.Context, tenantID, clientID, clientSecret, iamVersion string) (string, error) {
// we don't have a passed-in token, so we need to actually generate a token
if c.passedInToken == "" {
if c.vendedServiceClient {
token, err := issuertoken.GenerateToken(ctx, tenantID, clientID, clientSecret, c.identityServiceURL, c.httpClient)
token, err := issuertoken.GenerateToken(
ctx, clientID, clientSecret, c.identityServiceURL, iamVersion, c.httpClient)

return token, err
}
Expand Down
12 changes: 7 additions & 5 deletions pkg/token/httpclient/httpclient_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) Copyright 2021 Hewlett Packard Enterprise Development LP
// (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP

//nolint:structcheck
package httpclient
Expand All @@ -11,9 +11,11 @@ import (
"net/http"
"testing"

"github.com/stretchr/testify/assert"

"github.com/hewlettpackard/hpegl-provider-lib/pkg/provider"
"github.com/hewlettpackard/hpegl-provider-lib/pkg/token/identitytoken"
"github.com/hewlettpackard/hpegl-provider-lib/pkg/token/issuertoken"
"github.com/stretchr/testify/assert"
)

type testCaseIssuer struct {
Expand Down Expand Up @@ -205,7 +207,7 @@ func TestGenerateToken(t *testing.T) {

c = createTestClient(tc.url, "", tc.statusCode, tc.token, true)

token, err := c.GenerateToken(tc.ctx, "", "", "")
token, err := c.GenerateToken(tc.ctx, "", "", "", provider.IAMVersionGLCS)
if tc.err != nil {
assert.EqualError(t, err, tc.err.Error())
}
Expand All @@ -219,7 +221,7 @@ func TestGenerateToken(t *testing.T) {

c = createTestClient(tc.url, "", tc.statusCode, tc.token, false)

token, err := c.GenerateToken(tc.ctx, "", "", "")
token, err := c.GenerateToken(tc.ctx, "", "", "", provider.IAMVersionGLCS)
if tc.err != nil {
assert.EqualError(t, err, tc.err.Error())
}
Expand All @@ -232,7 +234,7 @@ func TestGenerateTokenPassedInToken(t *testing.T) {
t.Parallel()
c := createTestClient("", "testToken", http.StatusAccepted, nil, true)

token, err := c.GenerateToken(context.Background(), "", "", "")
token, err := c.GenerateToken(context.Background(), "", "", "", "")
assert.Equal(t, "testToken", token)
assert.NoError(t, err)
}
46 changes: 37 additions & 9 deletions pkg/token/issuertoken/issuertoken.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (C) Copyright 2021-2023 Hewlett Packard Enterprise Development LP
// (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP

package issuertoken

Expand All @@ -11,6 +11,7 @@ import (
"net/url"
"strings"

"github.com/hewlettpackard/hpegl-provider-lib/pkg/provider"
tokenutil "github.com/hewlettpackard/hpegl-provider-lib/pkg/token/token-util"
)

Expand All @@ -27,25 +28,26 @@ type TokenResponse struct {

func GenerateToken(
ctx context.Context,
tenantID,
clientID,
clientSecret string,
identityServiceURL string,
iamVersion string,
httpClient tokenutil.HttpClient,
) (string, error) {
params := url.Values{}
params.Add("client_id", clientID)
params.Add("client_secret", clientSecret)
params.Add("grant_type", "client_credentials")
params.Add("scope", "hpe-tenant")
// Generate the parameters and URL for the request
params, clientURL, err := generateParamsAndURL(clientID, clientSecret, identityServiceURL, iamVersion)
if err != nil {
return "", err
}

url := fmt.Sprintf("%s/v1/token", identityServiceURL)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(params.Encode()))
// Create the request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, clientURL, strings.NewReader(params.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Execute the request, with retries
resp, err := tokenutil.DoRetries(func() (*http.Response, error) {
return httpClient.Do(req)
}, retryLimit)
Expand Down Expand Up @@ -73,3 +75,29 @@ func GenerateToken(

return token.AccessToken, nil
}

// generateParamsAndURL generates the parameters and URL for the request
func generateParamsAndURL(clientID, clientSecret, identityServiceURL, iamVersion string) (url.Values, string, error) {
params := url.Values{}

// Add common parameters for an API Client
params.Add("client_id", clientID)
params.Add("client_secret", clientSecret)
params.Add("grant_type", "client_credentials")

// Add specific parameters and generate URL for the IAM version
var clientURL string
switch iamVersion {
case provider.IAMVersionGLCS:
params.Add("scope", "hpe-tenant")
clientURL = fmt.Sprintf("%s/v1/token", identityServiceURL)

case provider.IAMVersionGLP:
clientURL = identityServiceURL

default:
return nil, "", fmt.Errorf("invalid IAM version")
}

return params, clientURL, nil
}
49 changes: 49 additions & 0 deletions pkg/token/issuertoken/issuertoken_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// (C) Copyright 2024 Hewlett Packard Enterprise Development LP

package issuertoken

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/hewlettpackard/hpegl-provider-lib/pkg/provider"
)

func TestGenerateParamsAndURL(t *testing.T) {
t.Parallel()
testcases := []struct {
name string
iamVersion string
hasError bool
}{
{
name: "valid IAM version GLCS",
iamVersion: provider.IAMVersionGLCS,
hasError: false,
},
{
name: "valid IAM version GLP",
iamVersion: provider.IAMVersionGLP,
hasError: false,
},
{
name: "invalid IAM version",
iamVersion: "invalid",
hasError: true,
},
}

for _, testcase := range testcases {
tc := testcase
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
_, _, err := generateParamsAndURL("clientID", "clientSecret", "identityServiceURL", tc.iamVersion)
if tc.hasError {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
}
})
}
}
Loading

0 comments on commit 1aeeded

Please sign in to comment.