Skip to content

Commit

Permalink
feat: add UpsertOptions to support appending groups and not overwriti…
Browse files Browse the repository at this point in the history
…ng username (#45)

* add flags to keep username and append group list

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>

* update readme

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>

* refactor to reflect best practices

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>

* update conditionals to set update username default true for aws-auth library

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>

* evaluate updateusername with bool pointer

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>

* remove commented switch case

Signed-off-by: Nicholas Colbert <ncolbert@goodrx.com>
  • Loading branch information
45cali authored Jun 28, 2022
1 parent 26ae151 commit caf3194
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 13 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ Retries are configurable using the following flags
--retry-min-time duration Minimum wait interval (default 200ms)
```

Append groups to mapping instead of overwriting by using --append

```
aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test --groups test --append
```

Avoid overwriting username by using --update-username=false

```
aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test2 --groups test --update-username=false
```

## Usage as a library

```go
Expand All @@ -147,9 +159,9 @@ func someFunc(client kubernetes.Interface) error {
"system:nodes",
},
WithRetries: true,
MinRetryTime: time.Millisecond * 100,
MaxRetryTime: time.Second * 30,
MaxRetryCount: 12,
MinRetryTime: time.Millisecond * 100,
MaxRetryTime: time.Second * 30,
MaxRetryCount: 12,
}

err = awsAuth.Upsert(myUpsertRole)
Expand Down
2 changes: 2 additions & 0 deletions cmd/cli/upsert.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ func init() {
upsertCmd.Flags().DurationVar(&upsertArgs.MinRetryTime, "retry-min-time", time.Millisecond*200, "Minimum wait interval")
upsertCmd.Flags().DurationVar(&upsertArgs.MaxRetryTime, "retry-max-time", time.Second*30, "Maximum wait interval")
upsertCmd.Flags().IntVar(&upsertArgs.MaxRetryCount, "retry-max-count", 12, "Maximum number of retries before giving up")
upsertCmd.Flags().BoolVar(&upsertArgs.Append, "append", false, "append to a existing group list")
upsertCmd.Flags().BoolVar(upsertArgs.UpdateUsername, "update-username", true, "set to false to not overwite username")
}
25 changes: 25 additions & 0 deletions pkg/mapper/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func New(client kubernetes.Interface, isCommandline bool) *AuthMapper {
var (
DefaultRetryerBackoffFactor float64 = 2.0
DefaultRetryerBackoffJitter = true
UpdateUsernameArgumentTrue bool = true
)

// AwsAuthData represents the data of the aws-auth configmap
Expand Down Expand Up @@ -91,6 +92,8 @@ type MapperArguments struct {
MaxRetryTime time.Duration
MaxRetryCount int
IsGlobal bool
Append bool
UpdateUsername *bool
}

func (args *MapperArguments) Validate() {
Expand Down Expand Up @@ -121,6 +124,11 @@ func (args *MapperArguments) Validate() {
log.Fatal("error: must select --mapusers or --maproles")
}
}

if args.UpdateUsername == nil {
args.UpdateUsername = &UpdateUsernameArgumentTrue
}

}

// RolesAuthMap is the basic structure of a mapRoles authentication object
Expand Down Expand Up @@ -201,6 +209,18 @@ func (r *RolesAuthMap) SetGroups(g []string) *RolesAuthMap {
return r
}

// AppendGroups sets the Groups value
func (r *UsersAuthMap) AppendGroups(g []string) *UsersAuthMap {
r.Groups = append(r.Groups, g...)
return r
}

// AppendGroups sets the Groups value
func (r *RolesAuthMap) AppendGroups(g []string) *RolesAuthMap {
r.Groups = append(r.Groups, g...)
return r
}

func WithRetry(fn func(*MapperArguments) error, args *MapperArguments) error {
// Update the config map and return an AuthMap
var (
Expand Down Expand Up @@ -230,3 +250,8 @@ func WithRetry(fn func(*MapperArguments) error, args *MapperArguments) error {
}
return errors.Wrap(err, "waiter timed out")
}

type UpsertOptions struct {
Append bool
UpdateUsername bool
}
37 changes: 27 additions & 10 deletions pkg/mapper/upsert.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,15 @@ func (b *AuthMapper) upsertAuth(args *MapperArguments) error {
return err
}

opts := &UpsertOptions{
Append: args.Append,
UpdateUsername: *args.UpdateUsername,
}

if args.MapRoles {
var roleResource = NewRolesAuthMap(args.RoleARN, args.Username, args.Groups)

newMap, ok := upsertRole(authData.MapRoles, roleResource)
newMap, ok := upsertRole(authData.MapRoles, roleResource, opts)
if ok {
log.Printf("role %v has been updated\n", roleResource.RoleARN)
} else {
Expand All @@ -165,7 +170,7 @@ func (b *AuthMapper) upsertAuth(args *MapperArguments) error {
if args.MapUsers {
var userResource = NewUsersAuthMap(args.UserARN, args.Username, args.Groups)

newMap, ok := upsertUser(authData.MapUsers, userResource)
newMap, ok := upsertUser(authData.MapUsers, userResource, opts)
if ok {
log.Printf("role %v has been updated\n", userResource.UserARN)
} else {
Expand All @@ -177,20 +182,26 @@ func (b *AuthMapper) upsertAuth(args *MapperArguments) error {
return UpdateAuthMap(b.KubernetesClient, authData, configMap)
}

func upsertRole(authMaps []*RolesAuthMap, resource *RolesAuthMap) ([]*RolesAuthMap, bool) {
func upsertRole(authMaps []*RolesAuthMap, resource *RolesAuthMap, opts *UpsertOptions) ([]*RolesAuthMap, bool) {
var match bool
var updated bool
for _, existing := range authMaps {
// Update
if existing.RoleARN == resource.RoleARN {
match = true
if !reflect.DeepEqual(existing.Groups, resource.Groups) {
existing.SetGroups(resource.Groups)
if opts.Append {
existing.AppendGroups(resource.Groups)
} else {
existing.SetGroups(resource.Groups)
}
updated = true
}
if existing.Username != resource.Username {
existing.SetUsername(resource.Username)
updated = true
if opts.UpdateUsername {
existing.SetUsername(resource.Username)
updated = true
}
}
}
}
Expand All @@ -203,20 +214,26 @@ func upsertRole(authMaps []*RolesAuthMap, resource *RolesAuthMap) ([]*RolesAuthM
return authMaps, updated
}

func upsertUser(authMaps []*UsersAuthMap, resource *UsersAuthMap) ([]*UsersAuthMap, bool) {
func upsertUser(authMaps []*UsersAuthMap, resource *UsersAuthMap, opts *UpsertOptions) ([]*UsersAuthMap, bool) {
var match bool
var updated bool
for _, existing := range authMaps {
// Update
if existing.UserARN == resource.UserARN {
match = true
if !reflect.DeepEqual(existing.Groups, resource.Groups) {
existing.SetGroups(resource.Groups)
if opts.Append {
existing.AppendGroups(resource.Groups)
} else {
existing.SetGroups(resource.Groups)
}
updated = true
}
if existing.Username != resource.Username {
existing.SetUsername(resource.Username)
updated = true
if opts.UpdateUsername {
existing.SetUsername(resource.Username)
updated = true
}
}
}
}
Expand Down
76 changes: 76 additions & 0 deletions pkg/mapper/upsert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,79 @@ func TestUpsertWithRetries(t *testing.T) {
g.Expect(auth.MapUsers[0].Username).To(gomega.Equal("this:is:a:test"))
g.Expect(auth.MapUsers[0].Groups).To(gomega.Equal([]string{"system:some-role"}))
}

func TestMapper_UpsertUpdateAppendGroups(t *testing.T) {
g := gomega.NewWithT(t)
gomega.RegisterTestingT(t)
client := fake.NewSimpleClientset()
mapper := New(client, true)
create_MockConfigMap(client)

err := mapper.Upsert(&MapperArguments{
MapRoles: true,
RoleARN: "arn:aws:iam::00000000000:role/node-1",
Username: "this:is:a:test",
Groups: []string{"appendedGroup"},
Append: true,
})
g.Expect(err).NotTo(gomega.HaveOccurred())

err = mapper.Upsert(&MapperArguments{
MapUsers: true,
UserARN: "arn:aws:iam::00000000000:user/user-1",
Username: "this:is:a:test",
Groups: []string{"appendedGroup"},
Append: true,
})
g.Expect(err).NotTo(gomega.HaveOccurred())

auth, _, err := ReadAuthMap(client)
g.Expect(err).NotTo(gomega.HaveOccurred())
g.Expect(len(auth.MapRoles)).To(gomega.Equal(1))
g.Expect(len(auth.MapUsers)).To(gomega.Equal(1))
g.Expect(auth.MapRoles[0].RoleARN).To(gomega.Equal("arn:aws:iam::00000000000:role/node-1"))
g.Expect(auth.MapRoles[0].Username).To(gomega.Equal("this:is:a:test"))
g.Expect(auth.MapRoles[0].Groups).To(gomega.Equal([]string{"system:bootstrappers", "system:nodes", "appendedGroup"}))
g.Expect(auth.MapUsers[0].UserARN).To(gomega.Equal("arn:aws:iam::00000000000:user/user-1"))
g.Expect(auth.MapUsers[0].Username).To(gomega.Equal("this:is:a:test"))
g.Expect(auth.MapUsers[0].Groups).To(gomega.Equal([]string{"system:masters", "appendedGroup"}))
}

func TestMapper_UpdateUsername(t *testing.T) {
g := gomega.NewWithT(t)
gomega.RegisterTestingT(t)
client := fake.NewSimpleClientset()
mapper := New(client, true)
create_MockConfigMap(client)

updateUsername := false

err := mapper.Upsert(&MapperArguments{
MapRoles: true,
RoleARN: "arn:aws:iam::00000000000:role/node-1",
Username: "this:is:a:test",
Groups: []string{"system:bootstrappers", "system:nodes"},
UpdateUsername: &updateUsername,
})
g.Expect(err).NotTo(gomega.HaveOccurred())

err = mapper.Upsert(&MapperArguments{
MapUsers: true,
UserARN: "arn:aws:iam::00000000000:user/user-1",
Username: "this:is:a:test",
Groups: []string{"system:masters"},
UpdateUsername: &updateUsername,
})
g.Expect(err).NotTo(gomega.HaveOccurred())

auth, _, err := ReadAuthMap(client)
g.Expect(err).NotTo(gomega.HaveOccurred())
g.Expect(len(auth.MapRoles)).To(gomega.Equal(1))
g.Expect(len(auth.MapUsers)).To(gomega.Equal(1))
g.Expect(auth.MapRoles[0].RoleARN).To(gomega.Equal("arn:aws:iam::00000000000:role/node-1"))
g.Expect(auth.MapRoles[0].Username).To(gomega.Equal("system:node:{{EC2PrivateDNSName}}"))
g.Expect(auth.MapRoles[0].Groups).To(gomega.Equal([]string{"system:bootstrappers", "system:nodes"}))
g.Expect(auth.MapUsers[0].UserARN).To(gomega.Equal("arn:aws:iam::00000000000:user/user-1"))
g.Expect(auth.MapUsers[0].Username).To(gomega.Equal("admin"))
g.Expect(auth.MapUsers[0].Groups).To(gomega.Equal([]string{"system:masters"}))
}

0 comments on commit caf3194

Please sign in to comment.