extract stale policy and new policy

This commit is contained in:
kkb0318
2024-07-03 22:44:09 +09:00
parent 2d86bba2a9
commit fff9016a06
3 changed files with 146 additions and 3 deletions
+1
View File
@@ -27,6 +27,7 @@ type AwsIamAPI interface {
DeleteOpenIDConnectProvider(ctx context.Context, params *iam.DeleteOpenIDConnectProviderInput, optFns ...func(*iam.Options)) (*iam.DeleteOpenIDConnectProviderOutput, error)
CreateRole(ctx context.Context, params *iam.CreateRoleInput, optFns ...func(*iam.Options)) (*iam.CreateRoleOutput, error)
UpdateAssumeRolePolicy(ctx context.Context, params *iam.UpdateAssumeRolePolicyInput, optFns ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error)
ListAttachedRolePolicies(ctx context.Context, params *iam.ListAttachedRolePoliciesInput, optFns ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error)
AttachRolePolicy(ctx context.Context, params *iam.AttachRolePolicyInput, optFns ...func(*iam.Options)) (*iam.AttachRolePolicyOutput, error)
DeleteRole(ctx context.Context, params *iam.DeleteRoleInput, optFns ...func(*iam.Options)) (*iam.DeleteRoleOutput, error)
DetachRolePolicy(ctx context.Context, params *iam.DetachRolePolicyInput, optFns ...func(*iam.Options)) (*iam.DetachRolePolicyOutput, error)
+37 -3
View File
@@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/aws/smithy-go"
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
"github.com/kkb0318/irsa-manager/internal/issuer"
@@ -29,6 +30,7 @@ type RoleManager struct {
AccountId string
}
// PolicyArn returns the full ARN of a given policy name. If the policy name already has the full ARN, it returns it as is.
func (r *RoleManager) PolicyArn(policy string) *string {
prefix := "arn:aws:iam::"
if strings.HasPrefix(policy, prefix) {
@@ -37,6 +39,33 @@ func (r *RoleManager) PolicyArn(policy string) *string {
return aws.String(fmt.Sprintf("%saws:policy/%s", prefix, policy))
}
// ExtractNewPolicies returns the names of the policies that are in the current settings (r.Policies) but are not yet attached to the role.
func (r *RoleManager) ExtractNewPolicies(l *iam.ListAttachedRolePoliciesOutput) []string {
result := []string{}
for _, p := range r.Policies {
if slices.ContainsFunc(l.AttachedPolicies, func(a types.AttachedPolicy) bool {
return *r.PolicyArn(p) != *a.PolicyArn
}) {
result = append(result, p)
}
}
return result
}
// ExtractStalePolicies returns the ARNs of the policies that are attached to the role but are not in the current settings (r.Policies).
func (r *RoleManager) ExtractStalePolicies(l *iam.ListAttachedRolePoliciesOutput) []string {
result := []string{}
for _, ap := range l.AttachedPolicies {
if slices.ContainsFunc(r.Policies, func(p string) bool {
return *r.PolicyArn(p) != *ap.PolicyArn
}) {
result = append(result, *ap.PolicyArn)
}
}
return result
}
// DeleteIRSARole detaches specified policies from the IAM role and deletes the IAM role
func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error {
for _, policy := range r.Policies {
@@ -93,7 +122,7 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
AssumeRolePolicyDocument: aws.String(string(trustPolicyJSON)),
}
_, err = a.Client.CreateRole(context.TODO(), createRoleInput)
_, err = a.Client.CreateRole(ctx, createRoleInput)
if errorHandler(err, []string{"EntityAlreadyExists"}) != nil {
return err
}
@@ -104,11 +133,16 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
PolicyDocument: aws.String(string(trustPolicyJSON)),
}
_, err = a.Client.UpdateAssumeRolePolicy(context.TODO(), updateRoleInput)
_, err = a.Client.UpdateAssumeRolePolicy(ctx, updateRoleInput)
if err != nil {
return fmt.Errorf("failed to update assume role policy for role %s: %w", r.RoleName, err)
}
log.Printf("Assume role policy for %s updated successfully", r.RoleName)
// TODO:
// listPoliciesOutput, err := a.Client.ListAttachedRolePolicies(ctx, &iam.ListAttachedRolePoliciesInput{RoleName: aws.String(r.RoleName)})
// if err != nil {
// return fmt.Errorf("failed to list attached role policies with %s: %w", r.RoleName, err)
// }
for _, policy := range r.Policies {
attachRolePolicyInput := &iam.AttachRolePolicyInput{
@@ -116,7 +150,7 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
PolicyArn: r.PolicyArn(policy),
}
_, err = a.Client.AttachRolePolicy(context.TODO(), attachRolePolicyInput)
_, err = a.Client.AttachRolePolicy(ctx, attachRolePolicyInput)
if err != nil {
return err
}
+108
View File
@@ -0,0 +1,108 @@
package aws
import (
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/stretchr/testify/assert"
)
func TestExtractNewPolicies(t *testing.T) {
tests := []struct {
name string
policies []string
attachedPolicies *iam.ListAttachedRolePoliciesOutput
expected []string
}{
{
"PolicyAlreadyAttached",
[]string{"ReadOnlyAccess", "AdministratorAccess"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
},
},
[]string{"AdministratorAccess"},
},
{
"NoPolicyAttached",
[]string{"PowerUserAccess", "SecurityAudit"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
},
},
[]string{"PowerUserAccess", "SecurityAudit"},
},
{
"AllPoliciesAlreadyAttached",
[]string{"ReadOnlyAccess"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
},
},
[]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &RoleManager{Policies: tt.policies}
result := r.ExtractNewPolicies(tt.attachedPolicies)
assert.Equal(t, tt.expected, result)
})
}
}
func TestExtractStalePolicies(t *testing.T) {
tests := []struct {
name string
policies []string
attachedPolicies *iam.ListAttachedRolePoliciesOutput
expected []string
}{
{
"StalePolicyExists",
[]string{"ReadOnlyAccess"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
{PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess")},
},
},
[]string{"arn:aws:iam::aws:policy/AdministratorAccess"},
},
{
"MultipleStalePoliciesExist",
[]string{"ReadOnlyAccess", "SecurityAudit"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/PowerUserAccess")},
{PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess")},
},
},
[]string{"arn:aws:iam::aws:policy/PowerUserAccess", "arn:aws:iam::aws:policy/AdministratorAccess"},
},
{
"NoStalePolicies",
[]string{"ReadOnlyAccess"},
&iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
},
},
[]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &RoleManager{Policies: tt.policies}
result := r.ExtractStalePolicies(tt.attachedPolicies)
assert.Equal(t, tt.expected, result)
})
}
}