Skip to content

Commit

Permalink
feat(iam): improve rules handling (#3753)
Browse files Browse the repository at this point in the history
  • Loading branch information
Codelax authored Apr 10, 2024
1 parent d54c911 commit e2e9309
Show file tree
Hide file tree
Showing 16 changed files with 1,173 additions and 20 deletions.
21 changes: 21 additions & 0 deletions cmd/scw/testdata/test-all-usage-iam-rule-create-usage.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
Create a rule for a specific IAM policy

USAGE:
scw iam rule create <policy-id ...> [arg=value ...]

ARGS:
policy-id Id of policy to update
[permission-set-names.{index}] Names of permission sets bound to the rule
[project-ids.{index}] List of Project IDs the rule is scoped to
[organization-id] ID of Organization the rule is scoped to

FLAGS:
-h, --help help for create

GLOBAL FLAGS:
-c, --config string The path to the config file
-D, --debug Enable debug mode
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
-p, --profile string The config profile to use
19 changes: 19 additions & 0 deletions cmd/scw/testdata/test-all-usage-iam-rule-delete-usage.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
Delete a rule for a specific IAM policy

USAGE:
scw iam rule delete <policy-id ...> [arg=value ...]

ARGS:
policy-id Id of policy to update
[rule-id] Id of rule to delete

FLAGS:
-h, --help help for delete

GLOBAL FLAGS:
-c, --config string The path to the config file
-D, --debug Enable debug mode
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
-p, --profile string The config profile to use
4 changes: 4 additions & 0 deletions cmd/scw/testdata/test-all-usage-iam-rule-usage.golden
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ AVAILABLE COMMANDS:
list List rules of a given policy
update Set rules of a given policy

UTILITY COMMANDS:
create Create a rule for a specific IAM policy
delete Delete a rule for a specific IAM policy

FLAGS:
-h, --help help for rule

Expand Down
44 changes: 44 additions & 0 deletions docs/commands/iam.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ IAM API.
- [List policies of an Organization](#list-policies-of-an-organization)
- [Update an existing policy](#update-an-existing-policy)
- [Rules management commands](#rules-management-commands)
- [Create a rule for a specific IAM policy](#create-a-rule-for-a-specific-iam-policy)
- [Delete a rule for a specific IAM policy](#delete-a-rule-for-a-specific-iam-policy)
- [List rules of a given policy](#list-rules-of-a-given-policy)
- [Set rules of a given policy](#set-rules-of-a-given-policy)
- [SSH keys management commands](#ssh-keys-management-commands)
Expand Down Expand Up @@ -809,6 +811,48 @@ scw iam policy update <policy-id ...> [arg=value ...]
Rules management commands.


### Create a rule for a specific IAM policy



**Usage:**

```
scw iam rule create <policy-id ...> [arg=value ...]
```


**Args:**

| Name | | Description |
|------|---|-------------|
| policy-id | | Id of policy to update |
| permission-set-names.{index} | | Names of permission sets bound to the rule |
| project-ids.{index} | | List of Project IDs the rule is scoped to |
| organization-id | | ID of Organization the rule is scoped to |



### Delete a rule for a specific IAM policy



**Usage:**

```
scw iam rule delete <policy-id ...> [arg=value ...]
```


**Args:**

| Name | | Description |
|------|---|-------------|
| policy-id | | Id of policy to update |
| rule-id | | Id of rule to delete |



### List rules of a given policy

List the rules of a given policy. By default, the rules listed are ordered by creation date in ascending order. This can be modified via the `order_by` field. You must define the `policy_id` in the query path of your request.
Expand Down
2 changes: 1 addition & 1 deletion internal/core/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ func TestCheckCombine(checks ...TestCheck) TestCheck {
// TestCheckExitCode assert exitCode
func TestCheckExitCode(expectedCode int) TestCheck {
return func(t *testing.T, ctx *CheckFuncCtx) {
assert.Equal(t, expectedCode, ctx.ExitCode, "Invalid exit code")
assert.Equal(t, expectedCode, ctx.ExitCode, "Invalid exit code\n%s", string(ctx.Stderr))
}
}

Expand Down
23 changes: 4 additions & 19 deletions internal/namespaces/iam/v1alpha1/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ func GetCommands() *core.Commands {

cmds.Merge(core.NewCommands(
initWithSSHCommand(),
iamRuleCreateCommand(),
iamRuleDeleteCommand(),
))

// These commands have an "optional" organization-id that is required for now.
Expand All @@ -47,25 +49,8 @@ func GetCommands() *core.Commands {
}

// Autocomplete permission set names using IAM API.
cmds.MustFind("iam", "policy", "create").Override(func(c *core.Command) *core.Command {
c.ArgSpecs.GetByName("rules.{index}.permission-set-names.{index}").AutoCompleteFunc = func(ctx context.Context, _ string, _ any) core.AutocompleteSuggestions {
client := core.ExtractClient(ctx)
api := iam.NewAPI(client)
// TODO: store result in a CLI cache
resp, err := api.ListPermissionSets(&iam.ListPermissionSetsRequest{
PageSize: scw.Uint32Ptr(100),
}, scw.WithAllPages())
if err != nil {
return nil
}
suggestions := core.AutocompleteSuggestions{}
for _, ps := range resp.PermissionSets {
suggestions = append(suggestions, ps.Name)
}
return suggestions
}
return c
})
cmds.MustFind("iam", "policy", "create").Override(iamPolicyCreateBuilder)
cmds.MustFind("iam", "policy", "get").Override(iamPolicyGetBuilder)

return cmds
}
Expand Down
71 changes: 71 additions & 0 deletions internal/namespaces/iam/v1alpha1/custom_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package iam

import (
"context"
"fmt"

"github.com/scaleway/scaleway-cli/v2/internal/core"
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
"github.com/scaleway/scaleway-sdk-go/scw"
)

func iamPolicyCreateBuilder(c *core.Command) *core.Command {
c.ArgSpecs.GetByName("rules.{index}.permission-set-names.{index}").AutoCompleteFunc = func(ctx context.Context, _ string, _ any) core.AutocompleteSuggestions {
client := core.ExtractClient(ctx)
api := iam.NewAPI(client)
// TODO: store result in a CLI cache
resp, err := api.ListPermissionSets(&iam.ListPermissionSetsRequest{
PageSize: scw.Uint32Ptr(100),
}, scw.WithAllPages())
if err != nil {
return nil
}
suggestions := core.AutocompleteSuggestions{}
for _, ps := range resp.PermissionSets {
suggestions = append(suggestions, ps.Name)
}
return suggestions
}
return c
}

type PolicyGetInterceptorResponse struct {
*iam.Policy
Rules []*iam.Rule
}

func iamPolicyGetBuilder(c *core.Command) *core.Command {
c.View = &core.View{
Title: "Policy",
Sections: []*core.ViewSection{
{
Title: "Rules",
FieldName: "Rules",
},
},
}
c.AddInterceptors(func(ctx context.Context, argsI interface{}, runner core.CommandRunner) (interface{}, error) {
args := argsI.(*iam.GetPolicyRequest)
api := iam.NewAPI(core.ExtractClient(ctx))

respI, err := runner(ctx, argsI)
if err != nil {
return respI, err
}
resp := &PolicyGetInterceptorResponse{
Policy: respI.(*iam.Policy),
}

rules, err := api.ListRules(&iam.ListRulesRequest{
PolicyID: args.PolicyID,
}, scw.WithContext(ctx), scw.WithAllPages())
if err != nil {
return nil, fmt.Errorf("failed to list rules for given policy: %w", err)
}
resp.Rules = rules.Rules

return resp, nil
})

return c
}
35 changes: 35 additions & 0 deletions internal/namespaces/iam/v1alpha1/custom_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package iam_test

import (
"testing"

"github.com/alecthomas/assert"
"github.com/scaleway/scaleway-cli/v2/internal/core"
"github.com/scaleway/scaleway-cli/v2/internal/namespaces/account/v3"
iam "github.com/scaleway/scaleway-cli/v2/internal/namespaces/iam/v1alpha1"
)

func Test_getPolicyWithRules(t *testing.T) {
t.Run("simple", core.Test(&core.TestConfig{
Commands: core.NewCommandsMerge(
iam.GetCommands(),
account.GetCommands(),
),
BeforeFunc: core.BeforeFuncCombine(
core.ExecStoreBeforeCmd("Project", "scw account project create name=test-cli-get-policy"),
core.ExecStoreBeforeCmd("Policy", "scw iam policy create name=test-cli-get-policy no-principal=true rules.0.permission-set-names.0=IPAMReadOnly rules.0.project-ids.0={{ .Project.ID }}"),
),
Cmd: `scw iam policy get {{ .Policy.ID }}`,
Check: core.TestCheckCombine(
func(t *testing.T, ctx *core.CheckFuncCtx) {
assert.Contains(t, string(ctx.Stdout), "IPAMReadOnly")
},
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
AfterFunc: core.AfterFuncCombine(
core.ExecAfterCmd("scw iam policy delete {{ .Policy.ID }}"),
core.ExecAfterCmd("scw account project delete project-id={{ .Project.ID }}"),
),
}))
}
Loading

0 comments on commit e2e9309

Please sign in to comment.