diff --git a/CHANGELOG.md b/CHANGELOG.md index 9880f068c..ace09b31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Bug fixes +- iam: include policies in role show output (#903) - dbaas: fix empty table output for user create, role list and acl show (output routed to a nil writer) (#894) - dbaas: fix show --uri for clickhouse printing a raw host:port instead of a connection URI (#894) diff --git a/cmd/iam/iam_role_show.go b/cmd/iam/iam_role_show.go index 133e5ce62..ee34cc389 100644 --- a/cmd/iam/iam_role_show.go +++ b/cmd/iam/iam_role_show.go @@ -22,11 +22,48 @@ type iamRoleShowOutput struct { Editable bool `json:"editable"` Labels map[string]string `json:"labels"` Permissions []string `json:"permission"` + Policy *iamPolicyOutput `json:"policy" output:"-"` } -func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } -func (o *iamRoleShowOutput) ToText() { output.Text(o) } -func (o *iamRoleShowOutput) ToTable() { output.Table(o) } +func (o *iamRoleShowOutput) ToJSON() { output.JSON(o) } +func (o *iamRoleShowOutput) ToText() { output.Text(o) } +func (o *iamRoleShowOutput) ToTable() { + output.Table(o) + if o.Policy != nil { + fmt.Println("\nPolicy:") + o.Policy.ToTable() + } +} + +func iamPolicyToOutput(policy *v3.IAMPolicy) *iamPolicyOutput { + if policy == nil { + return nil + } + + out := &iamPolicyOutput{ + DefaultServiceStrategy: string(policy.DefaultServiceStrategy), + Services: map[string]iamPolicyServiceOutput{}, + } + + for name, service := range policy.Services { + rules := []iamPolicyServiceRuleOutput{} + if service.Type == "rules" { + for _, rule := range service.Rules { + rules = append(rules, iamPolicyServiceRuleOutput{ + Action: string(rule.Action), + Expression: rule.Expression, + }) + } + } + + out.Services[name] = iamPolicyServiceOutput{ + Type: string(service.Type), + Rules: rules, + } + } + + return out +} type iamRoleShowCmd struct { exocmd.CliCommandSettings `cli-cmd:"-"` @@ -76,32 +113,12 @@ func (c *iamRoleShowCmd) CmdRun(_ *cobra.Command, _ []string) error { return err } + policy := iamPolicyToOutput(role.Policy) if c.Policy { - policy := role.Policy - - out := iamPolicyOutput{ - DefaultServiceStrategy: string(policy.DefaultServiceStrategy), - Services: map[string]iamPolicyServiceOutput{}, - } - - for name, service := range policy.Services { - rules := []iamPolicyServiceRuleOutput{} - if service.Type == "rules" { - for _, rule := range service.Rules { - rules = append(rules, iamPolicyServiceRuleOutput{ - Action: string(rule.Action), - Expression: rule.Expression, - }) - } - } - - out.Services[name] = iamPolicyServiceOutput{ - Type: string(service.Type), - Rules: rules, - } + if policy == nil { + return errors.New("role policy not found") } - - return c.OutputFunc(&out, nil) + return c.OutputFunc(policy, nil) } out := iamRoleShowOutput{ @@ -111,6 +128,7 @@ func (c *iamRoleShowCmd) CmdRun(_ *cobra.Command, _ []string) error { Labels: role.Labels, Name: role.Name, Permissions: role.Permissions, + Policy: policy, } return c.OutputFunc(&out, nil) diff --git a/cmd/iam/iam_role_show_test.go b/cmd/iam/iam_role_show_test.go new file mode 100644 index 000000000..8248849e0 --- /dev/null +++ b/cmd/iam/iam_role_show_test.go @@ -0,0 +1,57 @@ +package iam + +import ( + "net/http" + "net/http/httptest" + "testing" + + exocmd "github.com/exoscale/cli/cmd" + "github.com/exoscale/cli/pkg/account" + "github.com/exoscale/cli/pkg/output" + "github.com/exoscale/cli/pkg/testutils" + v3 "github.com/exoscale/egoscale/v3" +) + +func TestIAMRoleShowIncludesPolicy(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet || r.URL.Path != "/iam-role" { + w.WriteHeader(http.StatusNotFound) + return + } + + testutils.WriteJSON(t, w, http.StatusOK, v3.ListIAMRolesResponse{IAMRoles: []v3.IAMRole{{ + ID: v3.UUID("0e324aaf-4b7e-45ff-a8c9-5830e19484cc"), + Name: "vpc-canary-iam-role", + Policy: &v3.IAMPolicy{ + DefaultServiceStrategy: v3.IAMPolicyDefaultServiceStrategyDeny, + Services: map[string]v3.IAMServicePolicy{ + "networking": {Type: v3.IAMServicePolicyTypeAllow}, + "compute": {Type: v3.IAMServicePolicyTypeAllow}, + }, + }, + }}}) + })) + defer server.Close() + + testutils.SetupV3Client(t, server.URL) + account.CurrentAccount = &account.Account{} + cmd := &iamRoleShowCmd{ + CliCommandSettings: exocmd.DefaultCLICmdSettings(), + Role: "vpc-canary-iam-role", + } + var got *iamRoleShowOutput + cmd.OutputFunc = func(o output.Outputter, err error) error { + got = o.(*iamRoleShowOutput) + return err + } + + if err := cmd.CmdRun(nil, nil); err != nil { + t.Fatal(err) + } + if got.Policy == nil || got.Policy.DefaultServiceStrategy != "deny" { + t.Fatalf("unexpected policy: %+v", got.Policy) + } + if got.Policy.Services["networking"].Type != "allow" || got.Policy.Services["compute"].Type != "allow" { + t.Fatalf("unexpected policy services: %+v", got.Policy.Services) + } +}