mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-09 00:09:43 +00:00
extract stale policy and new policy
This commit is contained in:
@@ -27,6 +27,7 @@ type AwsIamAPI interface {
|
|||||||
DeleteOpenIDConnectProvider(ctx context.Context, params *iam.DeleteOpenIDConnectProviderInput, optFns ...func(*iam.Options)) (*iam.DeleteOpenIDConnectProviderOutput, error)
|
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)
|
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)
|
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)
|
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)
|
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)
|
DetachRolePolicy(ctx context.Context, params *iam.DetachRolePolicyInput, optFns ...func(*iam.Options)) (*iam.DetachRolePolicyOutput, error)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"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"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||||
"github.com/aws/smithy-go"
|
"github.com/aws/smithy-go"
|
||||||
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
|
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
|
||||||
"github.com/kkb0318/irsa-manager/internal/issuer"
|
"github.com/kkb0318/irsa-manager/internal/issuer"
|
||||||
@@ -29,6 +30,7 @@ type RoleManager struct {
|
|||||||
AccountId string
|
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 {
|
func (r *RoleManager) PolicyArn(policy string) *string {
|
||||||
prefix := "arn:aws:iam::"
|
prefix := "arn:aws:iam::"
|
||||||
if strings.HasPrefix(policy, prefix) {
|
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))
|
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
|
// DeleteIRSARole detaches specified policies from the IAM role and deletes the IAM role
|
||||||
func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error {
|
func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error {
|
||||||
for _, policy := range r.Policies {
|
for _, policy := range r.Policies {
|
||||||
@@ -93,7 +122,7 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
|
|||||||
AssumeRolePolicyDocument: aws.String(string(trustPolicyJSON)),
|
AssumeRolePolicyDocument: aws.String(string(trustPolicyJSON)),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = a.Client.CreateRole(context.TODO(), createRoleInput)
|
_, err = a.Client.CreateRole(ctx, createRoleInput)
|
||||||
if errorHandler(err, []string{"EntityAlreadyExists"}) != nil {
|
if errorHandler(err, []string{"EntityAlreadyExists"}) != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -104,11 +133,16 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
|
|||||||
PolicyDocument: aws.String(string(trustPolicyJSON)),
|
PolicyDocument: aws.String(string(trustPolicyJSON)),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = a.Client.UpdateAssumeRolePolicy(context.TODO(), updateRoleInput)
|
_, err = a.Client.UpdateAssumeRolePolicy(ctx, updateRoleInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update assume role policy for role %s: %w", r.RoleName, err)
|
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)
|
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 {
|
for _, policy := range r.Policies {
|
||||||
attachRolePolicyInput := &iam.AttachRolePolicyInput{
|
attachRolePolicyInput := &iam.AttachRolePolicyInput{
|
||||||
@@ -116,7 +150,7 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
|
|||||||
PolicyArn: r.PolicyArn(policy),
|
PolicyArn: r.PolicyArn(policy),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = a.Client.AttachRolePolicy(context.TODO(), attachRolePolicyInput)
|
_, err = a.Client.AttachRolePolicy(ctx, attachRolePolicyInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user