From 4e590feabacb8f1668aeed09951d5780d9a1efc7 Mon Sep 17 00:00:00 2001 From: srinandan Date: Mon, 14 Jun 2021 10:12:57 -0700 Subject: [PATCH] add support for member types --- apiclient/iam.go | 40 ++++++++++++++++++++++------------------ client/env/iam.go | 27 +++++++++++++++++++++++---- client/sync/sync.go | 25 +++++++++++++++++-------- cmd/env/iam.go | 2 +- cmd/env/removerole.go | 8 +++++--- cmd/env/setax.go | 14 ++++++++------ cmd/env/setcustom.go | 14 ++++++++------ cmd/env/setdeploy.go | 14 ++++++++------ cmd/env/setsync.go | 14 ++++++++------ 9 files changed, 100 insertions(+), 58 deletions(-) diff --git a/apiclient/iam.go b/apiclient/iam.go index e7d4c118..7bc27054 100644 --- a/apiclient/iam.go +++ b/apiclient/iam.go @@ -214,8 +214,8 @@ func createAllRoleBindings(name string) []roleBinding { return bindings } -//SetIAMServiceAccount create a new IAM SA with the necessary roles for an Apigee Env -func SetIAMServiceAccount(serviceAccountName string, iamRole string) (err error) { +//SetIAMPermission set permissions for a member on an Apigee Env +func SetIAMPermission(memberName string, iamRole string, memberType string) (err error) { var role string switch iamRole { @@ -254,7 +254,7 @@ func SetIAMServiceAccount(serviceAccountName string, iamRole string) (err error) for i, binding := range getIamPolicy.Bindings { if binding.Role == role { //found members with the role already, add the new SA to the role - getIamPolicy.Bindings[i].Members = append(binding.Members, "serviceAccount:"+serviceAccountName) + getIamPolicy.Bindings[i].Members = append(binding.Members, memberType+":"+memberName) foundRole = true } } @@ -263,7 +263,7 @@ func SetIAMServiceAccount(serviceAccountName string, iamRole string) (err error) if !foundRole { binding := roleBinding{} binding.Role = role - binding.Members = append(binding.Members, "serviceAccount:"+serviceAccountName) + binding.Members = append(binding.Members, memberType+":"+memberName) getIamPolicy.Bindings = append(getIamPolicy.Bindings, binding) } @@ -284,8 +284,8 @@ func SetIAMServiceAccount(serviceAccountName string, iamRole string) (err error) return err } -//RemoveIAMServiceAccount removes/unbinds IAM SA from all roles for an Apigee Env -func RemoveIAMServiceAccount(serviceAccountName string, iamRole string) (err error) { +//RemoveIAMPermission removes/unbinds IAM permission from all roles for an Apigee Env +func RemoveIAMPermission(memberName string, iamRole string) (err error) { u, _ := url.Parse(BaseURL) u.Path = path.Join(u.Path, GetApigeeOrg(), "environments", GetApigeeEnv()+":getIamPolicy") getIamPolicyBody, err := HttpClient(false, u.String()) @@ -314,12 +314,16 @@ func RemoveIAMServiceAccount(serviceAccountName string, iamRole string) (err err } else if numBindings == 1 { //there is only 1 binding clilog.Info.Printf("comparing %s and %s\n", getIamPolicy.Bindings[0].Role, iamRole) if getIamPolicy.Bindings[0].Role == iamRole { - if len(getIamPolicy.Bindings[0].Members) > 1 { //more than one memeber in the role + if len(getIamPolicy.Bindings[0].Members) > 1 { //more than one member in the role removeIamPolicy.Policy.Etag = getIamPolicy.Etag + //create a new role binding + removeIamPolicy.Policy.Bindings = append(removeIamPolicy.Policy.Bindings, roleBinding{}) + //copy the role removeIamPolicy.Policy.Bindings[0].Role = getIamPolicy.Bindings[0].Role + //copy other members for _, member := range getIamPolicy.Bindings[0].Members { - clilog.Info.Printf("comparing %s and %s\n", serviceAccountName, member) - if member == serviceAccountName { + clilog.Info.Printf("comparing %s and %s\n", memberName, member) + if member == memberName { clilog.Info.Println("found member") foundMember = true //don't include this member @@ -328,14 +332,14 @@ func RemoveIAMServiceAccount(serviceAccountName string, iamRole string) (err err } } if !foundMember { - return fmt.Errorf("member %s not set for role %s in environment %s", serviceAccountName, iamRole, GetApigeeEnv()) + return fmt.Errorf("member %s not set for role %s in environment %s", memberName, iamRole, GetApigeeEnv()) } } else { //there is one member, one role - if getIamPolicy.Bindings[0].Members[0] == serviceAccountName { - clilog.Info.Printf("comparing %s and %s\n", getIamPolicy.Bindings[0].Members[0], serviceAccountName) + if getIamPolicy.Bindings[0].Members[0] == memberName { + clilog.Info.Printf("comparing %s and %s\n", getIamPolicy.Bindings[0].Members[0], memberName) removeIamPolicy.Policy.Etag = getIamPolicy.Etag } else { - return fmt.Errorf("member %s not set for role %s in environment %s", serviceAccountName, iamRole, GetApigeeEnv()) + return fmt.Errorf("member %s not set for role %s in environment %s", memberName, iamRole, GetApigeeEnv()) } } } else { @@ -349,21 +353,21 @@ func RemoveIAMServiceAccount(serviceAccountName string, iamRole string) (err err if binding.Role == iamRole { if len(binding.Members) > 1 { //there is more than one member in the role for _, member := range binding.Members { - clilog.Info.Printf("comparing %s and %s\n", member, serviceAccountName) - if member == serviceAccountName { //remove the member + clilog.Info.Printf("comparing %s and %s\n", member, memberName) + if member == memberName { //remove the member foundMember = true } else { members = append(members, member) } } if !foundMember { - return fmt.Errorf("member %s not set for role %s in environment %s", serviceAccountName, iamRole, GetApigeeEnv()) + return fmt.Errorf("member %s not set for role %s in environment %s", memberName, iamRole, GetApigeeEnv()) } } else { //there is only one member in the role - if binding.Members[0] == serviceAccountName { + if binding.Members[0] == memberName { foundMember = true } else { - return fmt.Errorf("member %s not set for role %s in environment %s", serviceAccountName, iamRole, GetApigeeEnv()) + return fmt.Errorf("member %s not set for role %s in environment %s", memberName, iamRole, GetApigeeEnv()) } } copyRoleBinding := roleBinding{} diff --git a/client/env/iam.go b/client/env/iam.go index 9e97d359..3d83ae5b 100644 --- a/client/env/iam.go +++ b/client/env/iam.go @@ -15,12 +15,15 @@ package env import ( + "fmt" "net/url" "path" "github.com/srinandan/apigeecli/apiclient" ) +var validMemberTypes = []string{"serviceAccount", "group", "user", "domain"} + //GetIAM func GetIAM() (respBody []byte, err error) { u, _ := url.Parse(apiclient.BaseURL) @@ -30,13 +33,20 @@ func GetIAM() (respBody []byte, err error) { } //SetIAM -func SetIAM(serviceAccountName string, permission string) (err error) { - return apiclient.SetIAMServiceAccount(serviceAccountName, permission) +func SetIAM(memberName string, permission string, memberType string) (err error) { + if !isValidMemberType(memberType) { + return fmt.Errorf("Invalid memberType. Valid types are %v", validMemberTypes) + } + return apiclient.SetIAMPermission(memberName, permission, memberType) } //RemoveIAM -func RemoveIAM(serviceAccountName string, role string) (err error) { - return apiclient.RemoveIAMServiceAccount(serviceAccountName, role) +func RemoveIAM(memberName string, role string, memberType string) (err error) { + if !isValidMemberType(memberType) { + return fmt.Errorf("Invalid memberType. Valid types are %v", validMemberTypes) + } + member := memberType + ":" + memberName + return apiclient.RemoveIAMPermission(member, role) } //TestIAM @@ -48,3 +58,12 @@ func TestIAM(resource string, verb string) (respBody []byte, err error) { respBody, err = apiclient.HttpClient(apiclient.GetPrintOutput(), u.String(), payload) return respBody, err } + +func isValidMemberType(memberType string) bool { + for _, validMember := range validMemberTypes { + if memberType == validMember { + return true + } + } + return false +} diff --git a/client/sync/sync.go b/client/sync/sync.go index 6e26f0ee..22d0c07d 100644 --- a/client/sync/sync.go +++ b/client/sync/sync.go @@ -58,7 +58,7 @@ func Reset() (respBody []byte, err error) { } //Set -func Set(identity string) (respBody []byte, err error) { +func Set(identity interface{}) (respBody []byte, err error) { u, _ := url.Parse(apiclient.BaseURL) u.Path = path.Join(u.Path, apiclient.GetApigeeOrg()+":getSyncAuthorization") @@ -74,16 +74,25 @@ func Set(identity string) (respBody []byte, err error) { return respBody, err } - identity = validate(identity) - - for _, setIdentity := range response.Identities { - if identity == setIdentity { - return respBody, fmt.Errorf("identity %s already set", identity) + switch param := identity.(type) { + case []string: + var syncIdentities []string + for _, syncIdentity := range param { + syncIdentities = append(syncIdentities, validate(syncIdentity)) } + response.Identities = append(response.Identities, syncIdentities...) + case string: + param = validate(param) + for _, setIdentity := range response.Identities { + if param == setIdentity { + return respBody, fmt.Errorf("identity %s already set", param) + } + } + response.Identities = append(response.Identities, param) + default: + return nil, fmt.Errorf("unsupported identity type") } - response.Identities = append(response.Identities, identity) - identities := iAMIdentities{} identities.Identities = response.Identities payload, err := json.Marshal(&identities) diff --git a/cmd/env/iam.go b/cmd/env/iam.go index d703e11b..147e9eab 100644 --- a/cmd/env/iam.go +++ b/cmd/env/iam.go @@ -25,7 +25,7 @@ var IamCmd = &cobra.Command{ Long: "Manage IAM permissions for the environment", } -var serviceAccountName, role string +var memberName, role, memberType string func init() { diff --git a/cmd/env/removerole.go b/cmd/env/removerole.go index c1f9bea1..6e9ef323 100644 --- a/cmd/env/removerole.go +++ b/cmd/env/removerole.go @@ -32,21 +32,23 @@ var RemoveRoleCmd = &cobra.Command{ return apiclient.SetApigeeOrg(org) }, RunE: func(cmd *cobra.Command, args []string) (err error) { - err = environments.RemoveIAM(serviceAccountName, role) + err = environments.RemoveIAM(memberName, role, memberType) if err != nil { return err } - fmt.Printf("Service account %s removed access to role %s\n", serviceAccountName, role) + fmt.Printf("Member %s (type = %s) removed access to role %s\n", memberName, memberType, role) return nil }, } func init() { - RemoveRoleCmd.Flags().StringVarP(&serviceAccountName, "name", "n", + RemoveRoleCmd.Flags().StringVarP(&memberName, "name", "n", "", "Service Account Name") RemoveRoleCmd.Flags().StringVarP(&role, "role", "r", "", "IAM Role") + RemoveRoleCmd.Flags().StringVarP(&memberType, "memberType", "m", + "serviceAccount", "memberType must be serviceAccount, user or group") _ = RemoveRoleCmd.MarkFlagRequired("name") _ = RemoveRoleCmd.MarkFlagRequired("role") diff --git a/cmd/env/setax.go b/cmd/env/setax.go index 698217b9..5d44a11e 100644 --- a/cmd/env/setax.go +++ b/cmd/env/setax.go @@ -25,26 +25,28 @@ import ( //Cmd to manage tracing of apis var SetAxCmd = &cobra.Command{ Use: "setax", - Short: "Set Analytics Agent role for a SA on an environment", - Long: "Set Analytics Agent role for a SA an Environment", + Short: "Set Analytics Agent role for a member on an environment", + Long: "Set Analytics Agent role for a member an Environment", Args: func(cmd *cobra.Command, args []string) (err error) { apiclient.SetApigeeEnv(environment) return apiclient.SetApigeeOrg(org) }, RunE: func(cmd *cobra.Command, args []string) (err error) { - err = environments.SetIAM(serviceAccountName, "analytics") + err = environments.SetIAM(memberName, "analytics", memberType) if err != nil { return err } - fmt.Printf("Service account %s granted access to Apigee Analytics Viewer role\n", serviceAccountName) + fmt.Printf("Member %s granted access to Apigee Analytics Viewer role\n", memberName) return nil }, } func init() { - SetAxCmd.Flags().StringVarP(&serviceAccountName, "name", "n", - "", "Service Account Name") + SetAxCmd.Flags().StringVarP(&memberName, "name", "n", + "", "Member Name, example Service Account Name") + SetAxCmd.Flags().StringVarP(&memberType, "memberType", "m", + "serviceAccount", "memberType must be serviceAccount, user or group") _ = SetAxCmd.MarkFlagRequired("name") } diff --git a/cmd/env/setcustom.go b/cmd/env/setcustom.go index 17880bc9..0df95f16 100644 --- a/cmd/env/setcustom.go +++ b/cmd/env/setcustom.go @@ -26,8 +26,8 @@ import ( //SetCustCmd to manage custom roles for an env var SetCustCmd = &cobra.Command{ Use: "setcustom", - Short: "Set a custom role for a SA on an environment", - Long: "Set a custom role for a SA on an environment", + Short: "Set a custom role for a member on an environment", + Long: "Set a custom role for a member on an environment", Args: func(cmd *cobra.Command, args []string) (err error) { apiclient.SetApigeeEnv(environment) return apiclient.SetApigeeOrg(org) @@ -38,21 +38,23 @@ var SetCustCmd = &cobra.Command{ if result == "" { return fmt.Errorf("custom role must be of the format projects/{project-id}/roles/{role-name}") } - err = environments.SetIAM(serviceAccountName, role) + err = environments.SetIAM(memberName, role, memberType) if err != nil { return err } - fmt.Printf("Service account %s, granted access to %s\n", serviceAccountName, role) + fmt.Printf("Member %s, granted access to %s\n", memberName, role) return nil }, } func init() { - SetCustCmd.Flags().StringVarP(&serviceAccountName, "name", "n", - "", "Service Account Name") + SetCustCmd.Flags().StringVarP(&memberName, "name", "n", + "", "Member Name, example Service Account Name") SetCustCmd.Flags().StringVarP(&role, "role", "r", "", "Custom IAM role in the format projects/{project-id}/roles/{role}") + SetCustCmd.Flags().StringVarP(&memberType, "memberType", "m", + "serviceAccount", "memberType must be serviceAccount, user or group") _ = SetCustCmd.MarkFlagRequired("name") _ = SetCustCmd.MarkFlagRequired("role") diff --git a/cmd/env/setdeploy.go b/cmd/env/setdeploy.go index d5518a90..6415b87e 100644 --- a/cmd/env/setdeploy.go +++ b/cmd/env/setdeploy.go @@ -25,26 +25,28 @@ import ( //Cmd to manage tracing of apis var SetDepCmd = &cobra.Command{ Use: "setdeploy", - Short: "Set Apigee Deployer role for a SA on an environment", - Long: "Set Apigee Deployer role for a SA on an environment", + Short: "Set Apigee Deployer role for a member on an environment", + Long: "Set Apigee Deployer role for a member on an environment", Args: func(cmd *cobra.Command, args []string) (err error) { apiclient.SetApigeeEnv(environment) return apiclient.SetApigeeOrg(org) }, RunE: func(cmd *cobra.Command, args []string) (err error) { - err = environments.SetIAM(serviceAccountName, "deploy") + err = environments.SetIAM(memberName, "deploy", memberType) if err != nil { return err } - fmt.Printf("Service account %s granted access to Apigee Deployer role\n", serviceAccountName) + fmt.Printf("Member %s granted access to Apigee Deployer role\n", memberName) return nil }, } func init() { - SetDepCmd.Flags().StringVarP(&serviceAccountName, "name", "n", - "", "Service Account Name") + SetDepCmd.Flags().StringVarP(&memberName, "name", "n", + "", "Member Name, example Service Account Name") + SetDepCmd.Flags().StringVarP(&memberType, "memberType", "m", + "serviceAccount", "memberType must be serviceAccount, user or group") _ = SetDepCmd.MarkFlagRequired("name") } diff --git a/cmd/env/setsync.go b/cmd/env/setsync.go index 31a88997..e9117d89 100644 --- a/cmd/env/setsync.go +++ b/cmd/env/setsync.go @@ -25,26 +25,28 @@ import ( //Cmd to manage tracing of apis var SetSyncCmd = &cobra.Command{ Use: "setsync", - Short: "Set Synchronization Manager role for a SA on an environment", - Long: "Set Synchronization Manager role for a SA on an environment", + Short: "Set Synchronization Manager role for a member on an environment", + Long: "Set Synchronization Manager role for a member on an environment", Args: func(cmd *cobra.Command, args []string) (err error) { apiclient.SetApigeeEnv(environment) return apiclient.SetApigeeOrg(org) }, RunE: func(cmd *cobra.Command, args []string) (err error) { - err = environments.SetIAM(serviceAccountName, "sync") + err = environments.SetIAM(memberName, "sync", memberType) if err != nil { return err } - fmt.Printf("Service account %s granted access to Apigee Synchronizer Manager role\n", serviceAccountName) + fmt.Printf("Member %s granted access to Apigee Synchronizer Manager role\n", memberName) return nil }, } func init() { - SetSyncCmd.Flags().StringVarP(&serviceAccountName, "name", "n", - "", "Service Account Name") + SetSyncCmd.Flags().StringVarP(&memberName, "name", "n", + "", "Member Name, example Service Account Name") + SetSyncCmd.Flags().StringVarP(&memberType, "memberType", "m", + "serviceAccount", "memberType must be serviceAccount, user or group") _ = SetSyncCmd.MarkFlagRequired("name") }