From 76bb0116a2468786cda32fafc14161f1e0686644 Mon Sep 17 00:00:00 2001 From: Kevin DeJong Date: Wed, 4 Sep 2019 11:03:37 -0500 Subject: [PATCH] Support storing the policy hash in database Signed-off-by: Kevin DeJong --- cmd/lambda/accounts/create.go | 3 +- cmd/lambda/accounts/create_test.go | 2 +- cmd/lambda/accounts/redboxprincipal.go | 12 ++-- .../update_redbox_principal_policy/main.go | 46 +++++++++---- .../main_test.go | 55 +++++++++++---- pkg/api/response/account.go | 15 ++-- pkg/common/mocks/Storager.go | 17 +++-- pkg/common/storager.go | 34 ++++++++-- pkg/db/db.go | 68 +++++++++++++++++++ pkg/db/mocks/DBer.go | 29 +++++++- pkg/db/model.go | 15 ++-- pkg/rolemanager/rolemanager_test.go | 11 ++- tests/acceptance/api_test.go | 13 ++-- 13 files changed, 250 insertions(+), 70 deletions(-) diff --git a/cmd/lambda/accounts/create.go b/cmd/lambda/accounts/create.go index 01a081451..d1a0ddd83 100755 --- a/cmd/lambda/accounts/create.go +++ b/cmd/lambda/accounts/create.go @@ -93,12 +93,13 @@ func (c createController) Call(ctx context.Context, req *events.APIGatewayProxyR } // Create an IAM Role for the Redbox principal (end-user) to login to - createRolRes, err := c.createPrincipalRole(account) + createRolRes, policyHash, err := c.createPrincipalRole(account) if err != nil { log.Printf("failed to create principal role for %s: %s", request.ID, err) return response.ServerError(), nil } account.PrincipalRoleArn = createRolRes.RoleArn + account.PrincipalPolicyHash = policyHash // Write the Account to the DB err = c.Dao.PutAccount(account) diff --git a/cmd/lambda/accounts/create_test.go b/cmd/lambda/accounts/create_test.go index caad3cd85..2432e6dd0 100755 --- a/cmd/lambda/accounts/create_test.go +++ b/cmd/lambda/accounts/create_test.go @@ -500,7 +500,7 @@ func StoragerMock() common.Storager { storagerMock := &commonMocks.Storager{} storagerMock.On("GetTemplateObject", mock.Anything, mock.Anything, mock.Anything). - Return("", nil) + Return("", "", nil) return storagerMock } diff --git a/cmd/lambda/accounts/redboxprincipal.go b/cmd/lambda/accounts/redboxprincipal.go index a066b713a..a66c25bad 100644 --- a/cmd/lambda/accounts/redboxprincipal.go +++ b/cmd/lambda/accounts/redboxprincipal.go @@ -10,7 +10,7 @@ import ( "github.com/aws/aws-sdk-go/service/iam" ) -func (c createController) createPrincipalRole(account db.RedboxAccount) (*rolemanager.CreateRoleWithPolicyOutput, error) { +func (c createController) createPrincipalRole(account db.RedboxAccount) (*rolemanager.CreateRoleWithPolicyOutput, string, error) { // Create an assume role policy, // to let principals from the same account assume the role. // @@ -35,7 +35,7 @@ func (c createController) createPrincipalRole(account db.RedboxAccount) (*rolema // Render the default policy for the Redbox principal policyName := c.PrincipalPolicyName - policy, err := c.StoragerService.GetTemplateObject(c.ArtifactsBucket, c.PrincipalPolicyS3Key, + policy, policyHash, err := c.StoragerService.GetTemplateObject(c.ArtifactsBucket, c.PrincipalPolicyS3Key, redboxPrincipalPolicyInput{ PrincipalPolicyArn: fmt.Sprintf("arn:aws:iam::%s:policy/%s", account.ID, policyName), PrincipalRoleArn: fmt.Sprintf("arn:aws:iam::%s:role/%s", account.ID, c.PrincipalRoleName), @@ -43,19 +43,20 @@ func (c createController) createPrincipalRole(account db.RedboxAccount) (*rolema AdminRoleArn: account.AdminRoleArn, }) if err != nil { - return nil, err + return nil, "", err } // Assume role into the new Redbox account accountSession, err := c.TokenService.NewSession(&c.AWSSession, account.AdminRoleArn) if err != nil { - return nil, err + return nil, "", err } iamClient := iam.New(accountSession) // Create the Role + Policy c.RoleManager.SetIAMClient(iamClient) - return c.RoleManager.CreateRoleWithPolicy(&rolemanager.CreateRoleWithPolicyInput{ + createRoleOutput := &rolemanager.CreateRoleWithPolicyOutput{} + createRoleOutput, err = c.RoleManager.CreateRoleWithPolicy(&rolemanager.CreateRoleWithPolicyInput{ RoleName: c.PrincipalRoleName, RoleDescription: "Role to be assumed by principal users of Redbox", AssumeRolePolicyDocument: assumeRolePolicy, @@ -68,6 +69,7 @@ func (c createController) createPrincipalRole(account db.RedboxAccount) (*rolema ), IgnoreAlreadyExistsErrors: true, }) + return createRoleOutput, policyHash, err } type redboxPrincipalPolicyInput struct { diff --git a/cmd/lambda/update_redbox_principal_policy/main.go b/cmd/lambda/update_redbox_principal_policy/main.go index 27b60dacf..a5df8e470 100644 --- a/cmd/lambda/update_redbox_principal_policy/main.go +++ b/cmd/lambda/update_redbox_principal_policy/main.go @@ -69,17 +69,18 @@ func handler(ctx context.Context, snsEvent events.SNSEvent) error { } type processRecordInput struct { - AccountID string - DbSvc db.DBer - StoragerSvc common.Storager - TokenSvc common.TokenService - AwsSession awsiface.AwsSession - RoleManager rolemanager.PolicyManager - PrincipalRoleName string - PrincipalPolicyName string - PrincipalIAMDenyTags []string - PolicyBucket string - PolicyBucketKey string + AccountID string + DbSvc db.DBer + StoragerSvc common.Storager + TokenSvc common.TokenService + AwsSession awsiface.AwsSession + RoleManager rolemanager.PolicyManager + PrincipalRoleName string + PrincipalPolicyName string + PrincipalIAMDenyTags []string + PrevPrincipalPolicyHash string + PolicyBucket string + PolicyBucketKey string } func processRecord(input processRecordInput) error { @@ -99,13 +100,17 @@ func processRecord(input processRecordInput) error { return err } - policy, err := input.StoragerSvc.GetTemplateObject(input.PolicyBucket, input.PolicyBucketKey, getPolicyInput{ + policy, policyHash, err := input.StoragerSvc.GetTemplateObject(input.PolicyBucket, input.PolicyBucketKey, getPolicyInput{ PrincipalPolicyArn: principalPolicyArn.String(), PrincipalRoleArn: fmt.Sprintf("arn:aws:iam::%s:role/%s", input.AccountID, input.PrincipalRoleName), PrincipalIAMDenyTags: input.PrincipalIAMDenyTags, AdminRoleArn: accountRes.AdminRoleArn, }) + if policyHash == accountRes.PrincipalPolicyHash { + log.Printf("Policy already matches. Not updating '%s'", principalPolicyArn.String()) + return nil + } // Assume role into the new Redbox account accountSession, err := input.TokenSvc.NewSession(input.AwsSession, accountRes.AdminRoleArn) if err != nil { @@ -114,13 +119,26 @@ func processRecord(input processRecordInput) error { } iamSvc := iam.New(accountSession) - // Create the Role + Policy + // Update the Policy input.RoleManager.SetIAMClient(iamSvc) - return input.RoleManager.MergePolicy(&rolemanager.MergePolicyInput{ + log.Printf("Update policy '%s' to hash '%s' from '%s'.", principalPolicyArn.String(), accountRes.PrincipalPolicyHash, policyHash) + err = input.RoleManager.MergePolicy(&rolemanager.MergePolicyInput{ PolicyName: input.PrincipalPolicyName, PolicyArn: principalPolicyArn, PolicyDocument: policy, }) + if err != nil { + log.Printf("Failed updating the policy '%s': %s", principalPolicyArn.String(), err) + return err + } + + log.Printf("Update account '%s' resource record. Policy Hash from '%s' to '%s'", input.AccountID, accountRes.PrincipalPolicyHash, policyHash) + _, err = input.DbSvc.UpdateAccountPrincipalPolicyHash(input.AccountID, input.PrevPrincipalPolicyHash, policyHash) + if err != nil { + log.Printf("Failed to update account '%s' resource record. Policy Hash from '%s' to '%s': %s", + input.AccountID, accountRes.PrincipalPolicyHash, policyHash, err) + } + return err } type getPolicyInput struct { diff --git a/cmd/lambda/update_redbox_principal_policy/main_test.go b/cmd/lambda/update_redbox_principal_policy/main_test.go index ad9ada985..55bae02d1 100644 --- a/cmd/lambda/update_redbox_principal_policy/main_test.go +++ b/cmd/lambda/update_redbox_principal_policy/main_test.go @@ -27,6 +27,7 @@ type testUpdateRedboxPrincipalPolicy struct { TransitionLeaseStatusError error PrincipalPolicyName string PrincipalRoleName string + PrincipalPolicyHash string PrincipalIAMDenyTags []string StoragerPolicy string StoragerError error @@ -35,7 +36,7 @@ type testUpdateRedboxPrincipalPolicy struct { func TestUpdateRedboxPrincipalPolicy(t *testing.T) { tests := []testUpdateRedboxPrincipalPolicy{ - // Happy Path FinanceLock + // Happy Path Update Principal Policy { GetAccountResult: &db.RedboxAccount{ ID: "123456789012", @@ -43,6 +44,20 @@ func TestUpdateRedboxPrincipalPolicy(t *testing.T) { }, PrincipalPolicyName: "RedboxPrincipalPolicy", PrincipalRoleName: "RedboxPrincipalRole", + PrincipalPolicyHash: "aHash", + PrincipalIAMDenyTags: []string{"Redbox"}, + StoragerPolicy: "{\"Test\" : \"Policy\"}", + }, + // Same hash exists don't update. + { + GetAccountResult: &db.RedboxAccount{ + ID: "123456789012", + AdminRoleArn: "arn:aws:iam::123456789012:role/RedBoxAdminRole", + PrincipalPolicyHash: "aHash", + }, + PrincipalPolicyName: "RedboxPrincipalPolicy", + PrincipalRoleName: "RedboxPrincipalRole", + PrincipalPolicyHash: "aHash", PrincipalIAMDenyTags: []string{"Redbox"}, StoragerPolicy: "{\"Test\" : \"Policy\"}", }, @@ -55,6 +70,11 @@ func TestUpdateRedboxPrincipalPolicy(t *testing.T) { mockDB.On("GetAccount", mock.Anything).Return( test.GetAccountResult, test.GetAccountError) + mockDB.On("UpdateAccountPrincipalPolicyHash", + test.GetAccountResult.ID, + test.GetAccountResult.PrincipalPolicyHash, + test.PrincipalPolicyHash, + ).Return(nil, nil) mockS3 := &commonmock.Storager{} mockS3.On("GetTemplateObject", mock.Anything, mock.Anything, getPolicyInput{ PrincipalPolicyArn: fmt.Sprintf("arn:aws:iam::%s:policy/%s", test.GetAccountResult.ID, test.PrincipalPolicyName), @@ -63,26 +83,31 @@ func TestUpdateRedboxPrincipalPolicy(t *testing.T) { AdminRoleArn: test.GetAccountResult.AdminRoleArn, }).Return( test.StoragerPolicy, + test.PrincipalPolicyHash, test.StoragerError, ) + mockAdminRoleSession := &awsMocks.AwsSession{} - mockAdminRoleSession.On("ClientConfig", mock.Anything).Return(client.Config{ - Config: &aws.Config{}, - }) mockToken := &commonmock.TokenService{} - mockToken.On("NewSession", mock.Anything, test.GetAccountResult.AdminRoleArn). - Return(mockAdminRoleSession, nil) - mockToken.On("AssumeRole", mock.Anything).Return(nil, nil) + mockRoleManager := &roleMock.PolicyManager{} mockSession := &awsMocks.AwsSession{} + if test.PrincipalPolicyHash != test.GetAccountResult.PrincipalPolicyHash { + mockAdminRoleSession.On("ClientConfig", mock.Anything).Return(client.Config{ + Config: &aws.Config{}, + }) + mockToken.On("NewSession", mock.Anything, test.GetAccountResult.AdminRoleArn). + Return(mockAdminRoleSession, nil) + mockToken.On("AssumeRole", mock.Anything).Return(nil, nil) - mockRoleManager := &roleMock.PolicyManager{} - mockRoleManager.On("SetIAMClient", mock.Anything).Return() - policyArn, _ := arn.Parse(fmt.Sprintf("arn:aws:iam::%s:policy/%s", test.GetAccountResult.ID, test.PrincipalPolicyName)) - mockRoleManager.On("MergePolicy", &rolemanager.MergePolicyInput{ - PolicyArn: policyArn, - PolicyName: test.PrincipalPolicyName, - PolicyDocument: test.StoragerPolicy, - }).Return(nil) + mockRoleManager.On("SetIAMClient", mock.Anything).Return() + policyArn, _ := arn.Parse(fmt.Sprintf("arn:aws:iam::%s:policy/%s", test.GetAccountResult.ID, test.PrincipalPolicyName)) + mockRoleManager.On("MergePolicy", &rolemanager.MergePolicyInput{ + PolicyArn: policyArn, + PolicyName: test.PrincipalPolicyName, + PolicyDocument: test.StoragerPolicy, + }).Return(nil) + + } // Call transitionFinanceLock err := processRecord(processRecordInput{ diff --git a/pkg/api/response/account.go b/pkg/api/response/account.go index 643767883..9e9ed349f 100755 --- a/pkg/api/response/account.go +++ b/pkg/api/response/account.go @@ -18,11 +18,12 @@ import ( // dbAccount := db.RedboxAccount{...} // accountRes := response.AccountResponse(dbAccount) type AccountResponse struct { - ID string `json:"id"` - AccountStatus db.AccountStatus `json:"accountStatus"` - LastModifiedOn int64 `json:"lastModifiedOn"` - CreatedOn int64 `json:"createdOn"` - AdminRoleArn string `json:"adminRoleArn"` // Assumed by the Redbox master account, to manage this user account - PrincipalRoleArn string `json:"principalRoleArn"` // Assumed by principal users of Redbox - Metadata map[string]interface{} `json:"metadata"` + ID string `json:"id"` + AccountStatus db.AccountStatus `json:"accountStatus"` + LastModifiedOn int64 `json:"lastModifiedOn"` + CreatedOn int64 `json:"createdOn"` + AdminRoleArn string `json:"adminRoleArn"` // Assumed by the Redbox master account, to manage this user account + PrincipalRoleArn string `json:"principalRoleArn"` // Assumed by principal users of Redbox + PrincipalPolicyHash string `json:"principalPolicyHash"` // The policy used by the PrincipalRoleArn + Metadata map[string]interface{} `json:"metadata"` } diff --git a/pkg/common/mocks/Storager.go b/pkg/common/mocks/Storager.go index a9e5b3967..ea4536063 100755 --- a/pkg/common/mocks/Storager.go +++ b/pkg/common/mocks/Storager.go @@ -45,7 +45,7 @@ func (_m *Storager) GetObject(bucket string, key string) (string, error) { } // GetTemplateObject provides a mock function with given fields: bucket, key, input -func (_m *Storager) GetTemplateObject(bucket string, key string, input interface{}) (string, error) { +func (_m *Storager) GetTemplateObject(bucket string, key string, input interface{}) (string, string, error) { ret := _m.Called(bucket, key, input) var r0 string @@ -55,14 +55,21 @@ func (_m *Storager) GetTemplateObject(bucket string, key string, input interface r0 = ret.Get(0).(string) } - var r1 error - if rf, ok := ret.Get(1).(func(string, string, interface{}) error); ok { + var r1 string + if rf, ok := ret.Get(1).(func(string, string, interface{}) string); ok { r1 = rf(bucket, key, input) } else { - r1 = ret.Error(1) + r1 = ret.Get(1).(string) } - return r0, r1 + var r2 error + if rf, ok := ret.Get(2).(func(string, string, interface{}) error); ok { + r2 = rf(bucket, key, input) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } // Upload provides a mock function with given fields: bucket, key, filepath diff --git a/pkg/common/storager.go b/pkg/common/storager.go index 6f4aa6efc..3d622520d 100755 --- a/pkg/common/storager.go +++ b/pkg/common/storager.go @@ -16,7 +16,7 @@ import ( // based on the provided S3 Object Input type Storager interface { GetObject(bucket string, key string) (string, error) - GetTemplateObject(bucket string, key string, input interface{}) (string, error) + GetTemplateObject(bucket string, key string, input interface{}) (string, string, error) Upload(bucket string, key string, filepath string) error Download(bukcet string, key string, filepath string) error } @@ -51,11 +51,35 @@ func (stor S3) GetObject(bucket string, key string) (string, error) { return object, nil } +// GetObjectWithETag returns a string output based on the results of the retrieval +// of an existing object from S3 +func (stor S3) GetObjectWithETag(bucket string, key string) (string, string, error) { + // Retrieve the S3 Object + getInput := s3.GetObjectInput{ + Bucket: &bucket, + Key: &key, + } + getOutput, err := stor.Client.GetObject(&getInput) + if err != nil { + return "", "", err + } + + // Convert to string + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(getOutput.Body) + if err != nil { + return "", "", err + } + object := buf.String() + + return object, *getOutput.ETag, nil +} + // GetTemplateObject returns a string output based on the results of the retrieval // of an existing object from S3 -func (stor S3) GetTemplateObject(bucket string, key string, input interface{}) (string, error) { +func (stor S3) GetTemplateObject(bucket string, key string, input interface{}) (string, string, error) { // Retrieve the S3 Object - templateString, err := stor.GetObject(bucket, key) + templateString, templateETag, err := stor.GetObjectWithETag(bucket, key) tmpl := template.New(key) @@ -65,14 +89,14 @@ func (stor S3) GetTemplateObject(bucket string, key string, input interface{}) ( templParsed, err := tmpl.Parse(templateString) if err != nil { - return "", err + return "", "", err } // Render template buf := &bytes.Buffer{} err1 := templParsed.Execute(buf, input) - return strings.TrimSpace(buf.String()), err1 + return strings.TrimSpace(buf.String()), templateETag, err1 } // Upload puts an object to the provided S3 bucket based on the body provided diff --git a/pkg/db/db.go b/pkg/db/db.go index ccdd156ca..895b01133 100755 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" + "github.com/aws/aws-sdk-go/service/dynamodb/expression" ) /* @@ -48,6 +49,7 @@ type DBer interface { FindLeasesByPrincipal(principalID string) ([]*RedboxLease, error) FindLeasesByStatus(status LeaseStatus) ([]*RedboxLease, error) UpdateMetadata(accountID string, metadata map[string]interface{}) error + UpdateAccountPrincipalPolicyHash(accountID string, prevHash string, nextHash string) (*RedboxAccount, error) } // GetAccount returns a Redbox account record corresponding to an accountID @@ -496,6 +498,72 @@ func (db *DB) TransitionAccountStatus(accountID string, prevStatus AccountStatus return unmarshalAccount(result.Attributes) } +// UpdateAccountPrincipalPolicyHash updates hash representing the +// current version of the Principal IAM Policy applied to the acount +func (db *DB) UpdateAccountPrincipalPolicyHash(accountID string, prevHash string, nextHash string) (*RedboxAccount, error) { + + conditionExpression := expression.ConditionBuilder{} + if prevHash != "" { + log.Printf("Using Condition where PrincipalPolicyHash equals '%s'", prevHash) + conditionExpression = expression.Name("PrincipalPolicyHash").Equal(expression.Value(prevHash)) + } else { + log.Printf("Using Condition where PrincipalPolicyHash does not exists") + conditionExpression = expression.AttributeNotExists(expression.Name("PrincipalPolicyHash")) + } + updateExpression, _ := expression.NewBuilder().WithCondition( + conditionExpression, + ).WithUpdate( + expression.Set( + expression.Name("PrincipalPolicyHash"), + expression.Value(nextHash), + ).Set( + expression.Name("LastModifiedOn"), + expression.Value(strconv.FormatInt(time.Now().Unix(), 10)), + ), + ).Build() + + result, err := db.Client.UpdateItem( + &dynamodb.UpdateItemInput{ + // Query in Lease Table + TableName: aws.String(db.AccountTableName), + // Find Account for the requested accountId + Key: map[string]*dynamodb.AttributeValue{ + "Id": { + S: aws.String(accountID), + }, + }, + ExpressionAttributeNames: updateExpression.Names(), + ExpressionAttributeValues: updateExpression.Values(), + // Set PrincipalPolicyHash=nextHash + UpdateExpression: updateExpression.Update(), + // Only update records where the previousHash matches + ConditionExpression: updateExpression.Condition(), + // Return the updated record + ReturnValues: aws.String("ALL_NEW"), + }, + ) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if aerr.Code() == "ConditionalCheckFailedException" { + return nil, &StatusTransitionError{ + fmt.Sprintf( + "unable to update Principal Policy hash from \"%v\" to \"%v\" "+ + "for account %v: no account exists with PrincipalPolicyHash=\"%v\"", + prevHash, + nextHash, + accountID, + prevHash, + ), + } + } + return nil, err + } + return nil, err + } + + return unmarshalAccount(result.Attributes) +} + // DeleteAccount finds a given account and deletes it if it is not of status `Leased`. Returns the account. func (db *DB) DeleteAccount(accountID string) (*RedboxAccount, error) { account, err := db.GetAccount(accountID) diff --git a/pkg/db/mocks/DBer.go b/pkg/db/mocks/DBer.go index 5d2485632..610e5f7b6 100755 --- a/pkg/db/mocks/DBer.go +++ b/pkg/db/mocks/DBer.go @@ -2,8 +2,10 @@ package mocks -import db "github.com/Optum/Redbox/pkg/db" -import mock "github.com/stretchr/testify/mock" +import ( + db "github.com/Optum/Redbox/pkg/db" + mock "github.com/stretchr/testify/mock" +) // DBer is an autogenerated mock type for the DBer type type DBer struct { @@ -323,6 +325,29 @@ func (_m *DBer) TransitionLeaseStatus(accountID string, principalID string, prev return r0, r1 } +// UpdateAccountPrincipalPolicyHash provides a mock function with given fields: accountID, prevHash, nextHash +func (_m *DBer) UpdateAccountPrincipalPolicyHash(accountID string, prevHash string, nextHash string) (*db.RedboxAccount, error) { + ret := _m.Called(accountID, prevHash, nextHash) + + var r0 *db.RedboxAccount + if rf, ok := ret.Get(0).(func(string, string, string) *db.RedboxAccount); ok { + r0 = rf(accountID, prevHash, nextHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*db.RedboxAccount) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, string, string) error); ok { + r1 = rf(accountID, prevHash, nextHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // UpdateMetadata provides a mock function with given fields: accountID, metadata func (_m *DBer) UpdateMetadata(accountID string, metadata map[string]interface{}) error { ret := _m.Called(accountID, metadata) diff --git a/pkg/db/model.go b/pkg/db/model.go index e6013fa52..f96c5e499 100755 --- a/pkg/db/model.go +++ b/pkg/db/model.go @@ -2,13 +2,14 @@ package db // RedboxAccount is a type corresponding to a RedboxAccount table record type RedboxAccount struct { - ID string `json:"Id"` // AWS Account ID - AccountStatus AccountStatus `json:"AccountStatus"` // Status of the AWS Account - LastModifiedOn int64 `json:"LastModifiedOn"` // Last Modified Epoch Timestamp - CreatedOn int64 `json:"CreatedOn"` - AdminRoleArn string `json:"AdminRoleArn"` // Assumed by the Redbox master account, to manage this user account - PrincipalRoleArn string `json:"PrincipalRoleArn"` // Assumed by principal users of Redbox - Metadata map[string]interface{} `json:"Metadata"` // Any org specific metadata pertaining to the account + ID string `json:"Id"` // AWS Account ID + AccountStatus AccountStatus `json:"AccountStatus"` // Status of the AWS Account + LastModifiedOn int64 `json:"LastModifiedOn"` // Last Modified Epoch Timestamp + CreatedOn int64 `json:"CreatedOn"` + AdminRoleArn string `json:"AdminRoleArn"` // Assumed by the Redbox master account, to manage this user account + PrincipalRoleArn string `json:"PrincipalRoleArn"` // Assumed by principal users of Redbox + PrincipalPolicyHash string `json:"PrincipalPolicyHash"` // The the hash of the policy version deployed + Metadata map[string]interface{} `json:"Metadata"` // Any org specific metadata pertaining to the account } // RedboxLease is a type corresponding to a RedboxLease diff --git a/pkg/rolemanager/rolemanager_test.go b/pkg/rolemanager/rolemanager_test.go index d8bf4c39f..ef66b0a2b 100755 --- a/pkg/rolemanager/rolemanager_test.go +++ b/pkg/rolemanager/rolemanager_test.go @@ -2,13 +2,14 @@ package rolemanager import ( "errors" + "testing" + "github.com/Optum/Redbox/pkg/awsiface/mocks" errors2 "github.com/Optum/Redbox/pkg/errors" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "testing" ) func TestCreateRoleWithPolicy(t *testing.T) { @@ -175,7 +176,13 @@ func TestCreateRoleWithPolicy(t *testing.T) { // Mock iam.CreatePolicy() to return error mockIAM.On("CreatePolicy", mock.Anything). - Return(nil, AwsAlreadyExistsError{}) + Return(&iam.CreatePolicyOutput{ + Policy: &iam.Policy{ + Arn: aws.String("arn:aws:iam::123456789012:policy/TestPolicy"), + }, + }, + nil, + ) // Mock iam.AttachRolePolicy() to return an error mockIAM.On("AttachRolePolicy", &iam.AttachRolePolicyInput{ diff --git a/tests/acceptance/api_test.go b/tests/acceptance/api_test.go index 3d2225fde..f87e9c6a7 100755 --- a/tests/acceptance/api_test.go +++ b/tests/acceptance/api_test.go @@ -544,12 +544,13 @@ func TestApi(t *testing.T) { dbAccount, err := dbSvc.GetAccount(accountID) require.Nil(t, err) require.Equal(t, &db.RedboxAccount{ - ID: accountID, - AccountStatus: "NotReady", - LastModifiedOn: int64(postResJSON["lastModifiedOn"].(float64)), - CreatedOn: int64(postResJSON["createdOn"].(float64)), - AdminRoleArn: adminRoleArn, - PrincipalRoleArn: expectedPrincipalRoleArn, + ID: accountID, + AccountStatus: "NotReady", + LastModifiedOn: int64(postResJSON["lastModifiedOn"].(float64)), + CreatedOn: int64(postResJSON["createdOn"].(float64)), + AdminRoleArn: adminRoleArn, + PrincipalRoleArn: expectedPrincipalRoleArn, + PrincipalPolicyHash: "\"76807b34385a7bc4cf758c71071e2697\"", }, dbAccount) // Check that the IAM Principal Role was created