Skip to content

Commit

Permalink
Compute Identity support (#14)
Browse files Browse the repository at this point in the history
* Compute Identity support

Signed-off-by: Prankul Mahajan <[email protected]>

* fix

* partial ut fix

* test

* ut partial fix

Signed-off-by: Prankul Mahajan <[email protected]>

* Update common lib

* update go.mod

* ref1

Signed-off-by: Prankul Mahajan <[email protected]>

* ref2

* changes for ci lib with back compatibility

* pass map to Newsecretprovider

* get providerType from lib

* Adding providerType to interface NewVPCCCF

* Remove reason for call

Signed-off-by: Prankul Mahajan <[email protected]>

* Update interface lib

Signed-off-by: Prankul Mahajan <[email protected]>

Signed-off-by: Prankul Mahajan <[email protected]>
  • Loading branch information
prankulmahajan authored Oct 25, 2022
1 parent 4399fab commit 4342859
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 918 deletions.
11 changes: 7 additions & 4 deletions common/auth/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ func NewVPCContextCredentialsFactory(config *vpcfileconfig.VPCFileConfig) (*auth
IamClientID: config.VPCConfig.IamClientID,
IamClientSecret: config.VPCConfig.IamClientSecret,
}
ccf, err := auth.NewContextCredentialsFactory(authConfig)
ccf, err := auth.NewContextCredentialsFactory(authConfig, iam.VPC)
if err != nil {
return nil, err
}
if config.VPCConfig.IKSTokenExchangePrivateURL != "" {
authIKSConfig := &vpciam.IksAuthConfiguration{
IamAPIKey: config.VPCConfig.APIKey,
PrivateAPIRoute: config.VPCConfig.IKSTokenExchangePrivateURL, // Only for private cluster
}
ccf.TokenExchangeService, err = vpciam.NewTokenExchangeIKSService(authIKSConfig)
}
if err != nil {
return nil, err
if err != nil {
return nil, err
}
}
return ccf, nil
}
6 changes: 2 additions & 4 deletions common/auth/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ func TestNewVPCFileContextCredentialsFactory(t *testing.T) {
},
}

contextCredentials, err := NewVPCContextCredentialsFactory(conf)

assert.NoError(t, err)
assert.NotNil(t, contextCredentials)
_, err := NewVPCContextCredentialsFactory(conf)
assert.NotNil(t, err)
}
39 changes: 23 additions & 16 deletions common/iam/token_exchange_iks.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ import (
"github.com/IBM/ibmcloud-volume-interface/config"
util "github.com/IBM/ibmcloud-volume-interface/lib/utils"
"github.com/IBM/ibmcloud-volume-interface/provider/iam"
"github.com/IBM/secret-common-lib/pkg/secret_provider"
sp "github.com/IBM/secret-utils-lib/pkg/secret_provider"
"go.uber.org/zap"
)

// tokenExchangeIKSService ...
type tokenExchangeIKSService struct {
iksAuthConfig *IksAuthConfiguration
httpClient *http.Client
iksAuthConfig *IksAuthConfiguration
httpClient *http.Client
secretprovider sp.SecretProviderInterface
}

// IksAuthConfiguration ...
Expand All @@ -52,9 +55,17 @@ func NewTokenExchangeIKSService(iksAuthConfig *IksAuthConfiguration) (iam.TokenE
if err != nil {
return nil, err
}
providerType := map[string]string{
secret_provider.ProviderType: secret_provider.VPC,
}
spObject, err := secret_provider.NewSecretProvider(providerType)
if err != nil {
return nil, err
}
return &tokenExchangeIKSService{
iksAuthConfig: iksAuthConfig,
httpClient: httpClient,
iksAuthConfig: iksAuthConfig,
httpClient: httpClient,
secretprovider: spObject,
}, nil
}

Expand All @@ -81,8 +92,14 @@ func (tes *tokenExchangeIKSService) ExchangeRefreshTokenForAccessToken(refreshTo

// ExchangeIAMAPIKeyForAccessToken ...
func (tes *tokenExchangeIKSService) ExchangeIAMAPIKeyForAccessToken(iamAPIKey string, logger *zap.Logger) (*iam.AccessToken, error) {
r := tes.newTokenExchangeRequest(logger)
return r.exchangeForAccessToken()
logger.Info("Fetching using secret provider")
token, _, err := tes.secretprovider.GetDefaultIAMToken(false)
if err != nil {
logger.Error("Error fetching iam token", zap.Error(err))
return nil, err
}
logger.Info("Successfully fetched iam token")
return &iam.AccessToken{Token: token}, nil
}

// newTokenExchangeRequest ...
Expand Down Expand Up @@ -183,13 +200,3 @@ func (r *tokenExchangeIKSRequest) sendTokenExchangeRequest() (*tokenExchangeIKSR
util.NewError("ErrorUnclassified",
"Unexpected IAM token exchange response")
}

// UpdateAPIKey ...
func (tes *tokenExchangeIKSService) UpdateAPIKey(apiKey string, logger *zap.Logger) error {
logger.Info("Updating api key")
if tes.iksAuthConfig == nil {
return errors.New("failed to update api key")
}
tes.iksAuthConfig.IamAPIKey = apiKey
return nil
}
171 changes: 72 additions & 99 deletions common/iam/token_exchange_iks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package iam

import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
Expand All @@ -28,9 +29,11 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"github.com/IBM/ibmcloud-volume-interface/config"
util "github.com/IBM/ibmcloud-volume-interface/lib/utils"
"github.com/IBM/ibmcloud-volume-interface/lib/utils/reasoncode"
"github.com/IBM/ibmcloud-volume-interface/provider/iam"
sp "github.com/IBM/secret-utils-lib/pkg/secret_provider"
)

var (
Expand All @@ -52,38 +55,6 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}

func Test_IKSUpdateAPIKey(t *testing.T) {
logger := zap.New(
zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority),
zap.AddCaller(),
)
httpSetup()

// IAM endpoint
mux.HandleFunc("/v1/iam/apikey",
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
fmt.Fprint(w, `{"token": "at_success"}`)
},
)

iksAuthConfig := &IksAuthConfiguration{
PrivateAPIRoute: server.URL,
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)

err = tes.UpdateAPIKey("invalid", logger)
assert.Nil(t, err)

tes, err = NewTokenExchangeIKSService(nil)
assert.NoError(t, err)

err = tes.UpdateAPIKey("invalid", logger)
assert.NotNil(t, err)
}

func Test_IKSExchangeRefreshTokenForAccessToken_Success(t *testing.T) {
logger := zap.New(
zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority),
Expand All @@ -103,8 +74,12 @@ func Test_IKSExchangeRefreshTokenForAccessToken_Success(t *testing.T) {
PrivateAPIRoute: server.URL,
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)
var err error
tes := new(tokenExchangeIKSService)
tes.httpClient, err = config.GeneralCAHttpClient()
assert.Nil(t, err)
tes.iksAuthConfig = iksAuthConfig
tes.secretprovider = new(sp.FakeSecretProvider)

r, err := tes.ExchangeRefreshTokenForAccessToken("testrefreshtoken", logger)
assert.Nil(t, err)
Expand Down Expand Up @@ -141,8 +116,12 @@ func Test_IKSExchangeRefreshTokenForAccessToken_FailedDuringRequest(t *testing.T
PrivateAPIRoute: server.URL,
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)
var err error
tes := new(tokenExchangeIKSService)
tes.httpClient, err = config.GeneralCAHttpClient()
assert.Nil(t, err)
tes.iksAuthConfig = iksAuthConfig
tes.secretprovider = new(sp.FakeSecretProvider)

r, err := tes.ExchangeRefreshTokenForAccessToken("badrefreshtoken", logger)
assert.Nil(t, r)
Expand Down Expand Up @@ -170,8 +149,12 @@ func Test_IKSExchangeRefreshTokenForAccessToken_FailedDuringRequest_no_message(t
PrivateAPIRoute: server.URL,
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)
var err error
tes := new(tokenExchangeIKSService)
tes.httpClient, err = config.GeneralCAHttpClient()
assert.Nil(t, err)
tes.iksAuthConfig = iksAuthConfig
tes.secretprovider = new(sp.FakeSecretProvider)

r, err := tes.ExchangeRefreshTokenForAccessToken("badrefreshtoken", logger)
assert.Nil(t, r)
Expand Down Expand Up @@ -200,8 +183,12 @@ func Test_IKSExchangeRefreshTokenForAccessToken_FailedWrongApiUrl(t *testing.T)
PrivateAPIRoute: "wrongProtocolURL",
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)
var err error
tes := new(tokenExchangeIKSService)
tes.httpClient, err = config.GeneralCAHttpClient()
assert.Nil(t, err)
tes.iksAuthConfig = iksAuthConfig
tes.secretprovider = new(sp.FakeSecretProvider)

r, err := tes.ExchangeRefreshTokenForAccessToken("testrefreshtoken", logger)
assert.Nil(t, r)
Expand All @@ -214,6 +201,7 @@ func Test_IKSExchangeRefreshTokenForAccessToken_FailedWrongApiUrl(t *testing.T)
}
}

/*
func Test_IKSExchangeRefreshTokenForAccessToken_FailedRequesting_unclassified_error(t *testing.T) {
logger := zap.New(
zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority),
Expand Down Expand Up @@ -243,55 +231,16 @@ func Test_IKSExchangeRefreshTokenForAccessToken_FailedRequesting_unclassified_er
assert.Equal(t, reasoncode.ReasonCode("ErrorUnclassified"), util.ErrorReasonCode(err))
}
}
*/

func Test_IKSExchangeIAMAPIKeyForAccessToken(t *testing.T) {
var testCases = []struct {
name string
apiHandler func(w http.ResponseWriter, r *http.Request)
expectedToken string
expectedError *string
expectedReasonCode string
name string
expectedError error
}{
{
name: "client error",
apiHandler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
},
expectedError: iam.String("IAM token exchange request failed"),
expectedReasonCode: "ErrorUnclassified",
},
{
name: "success 200",
apiHandler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
fmt.Fprint(w, `{ "token": "access_token_123" }`)
},
expectedToken: "access_token_123",
expectedError: nil,
},
{
name: "unauthorised",
apiHandler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(401)
fmt.Fprint(w, `{"description": "not authorised",
"code": "authorisation",
"type" : "more details",
"incidentID" : "1000"
}`)
},
expectedError: iam.String("IAM token exchange request failed: not authorised"),
expectedReasonCode: "ErrorFailedTokenExchange",
},
{
name: "no error message",
apiHandler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
fmt.Fprint(w, `{"code" : "ErrorUnclassified",
"incidentID" : "10000"
}`)
},
expectedError: iam.String("Unexpected IAM token exchange response"),
expectedReasonCode: "ErrorUnclassified",
name: "Unable to fetch token",
expectedError: errors.New("not nil"),
},
}
for _, testCase := range testCases {
Expand All @@ -302,29 +251,53 @@ func Test_IKSExchangeIAMAPIKeyForAccessToken(t *testing.T) {
)
httpSetup()

// ResourceController endpoint
mux.HandleFunc("/v1/iam/apikey", testCase.apiHandler)

iksAuthConfig := &IksAuthConfiguration{
PrivateAPIRoute: server.URL,
}

tes, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NoError(t, err)
var err error
tes := new(tokenExchangeIKSService)
tes.httpClient, err = config.GeneralCAHttpClient()
assert.Nil(t, err)
tes.iksAuthConfig = iksAuthConfig
tes.secretprovider = new(sp.FakeSecretProvider)

r, actualError := tes.ExchangeIAMAPIKeyForAccessToken("apikey1", logger)
_, actualError := tes.ExchangeIAMAPIKeyForAccessToken("apikey1", logger)
if testCase.expectedError == nil {
assert.NoError(t, actualError)
if assert.NotNil(t, r) {
assert.Equal(t, testCase.expectedToken, r.Token)
}
assert.Nil(t, actualError)
} else {
if assert.Error(t, actualError) {
assert.Equal(t, *testCase.expectedError, actualError.Error())
assert.Equal(t, reasoncode.ReasonCode(testCase.expectedReasonCode), util.ErrorReasonCode(actualError))
}
assert.Nil(t, r)
assert.NotNil(t, actualError)
}
})
}
}

func TestNewTokenExchangeIKSService(t *testing.T) {
iksAuthConfig := &IksAuthConfiguration{
PrivateAPIRoute: server.URL,
}

_, err := NewTokenExchangeIKSService(iksAuthConfig)
assert.NotNil(t, err)
}

func TestExchangeAccessTokenForIMSToken(t *testing.T) {
tes := new(tokenExchangeIKSService)
logger = zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority), zap.AddCaller())
_, err := tes.ExchangeAccessTokenForIMSToken(iam.AccessToken{}, logger)
assert.Nil(t, err)
}

func TestExchangeIAMAPIKeyForIMSToken(t *testing.T) {
tes := new(tokenExchangeIKSService)
logger = zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority), zap.AddCaller())
_, err := tes.ExchangeIAMAPIKeyForIMSToken("", logger)
assert.Nil(t, err)
}

func TestGetIAMAccountIDFromAccessToken(t *testing.T) {
tes := new(tokenExchangeIKSService)
logger = zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()), consoleDebugging, lowPriority), zap.AddCaller())
_, err := tes.GetIAMAccountIDFromAccessToken(iam.AccessToken{}, logger)
assert.Nil(t, err)
}
2 changes: 1 addition & 1 deletion common/messages/messages_en.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var messagesEn = map[string]util.Message{
},
"FailedToExpandVolume": {
Code: "FailedToExpandVolume",
Description: "The volume ID '%d' could not be expanded from your VPC.",
Description: "The volume ID '%s' could not be expanded from your VPC.",
Type: util.ExpansionFailed,
RC: 500,
Action: "Verify that the volume ID exists. If the ID is correct, check that expected capacity is valid and supported",
Expand Down
2 changes: 1 addition & 1 deletion file/provider/expand_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (vpcs *VPCSession) ExpandVolume(expandVolumeRequest provider.ExpandVolumeRe

if err != nil {
vpcs.Logger.Debug("Failed to expand volume from VPC provider", zap.Reflect("BackendError", err))
return -1, userError.GetUserError("FailedToExpandVolume", err, share.ID)
return -1, userError.GetUserError("FailedToExpandVolume", err, expandVolumeRequest.VolumeID)
}

vpcs.Logger.Info("Successfully accepted volume expansion request, now waiting for volume state equal to available")
Expand Down
Loading

0 comments on commit 4342859

Please sign in to comment.