Skip to content

Commit e2e9309

Browse files
authored
feat(iam): improve rules handling (#3753)
1 parent d54c911 commit e2e9309

16 files changed

+1173
-20
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Create a rule for a specific IAM policy
4+
5+
USAGE:
6+
scw iam rule create <policy-id ...> [arg=value ...]
7+
8+
ARGS:
9+
policy-id Id of policy to update
10+
[permission-set-names.{index}] Names of permission sets bound to the rule
11+
[project-ids.{index}] List of Project IDs the rule is scoped to
12+
[organization-id] ID of Organization the rule is scoped to
13+
14+
FLAGS:
15+
-h, --help help for create
16+
17+
GLOBAL FLAGS:
18+
-c, --config string The path to the config file
19+
-D, --debug Enable debug mode
20+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
21+
-p, --profile string The config profile to use
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Delete a rule for a specific IAM policy
4+
5+
USAGE:
6+
scw iam rule delete <policy-id ...> [arg=value ...]
7+
8+
ARGS:
9+
policy-id Id of policy to update
10+
[rule-id] Id of rule to delete
11+
12+
FLAGS:
13+
-h, --help help for delete
14+
15+
GLOBAL FLAGS:
16+
-c, --config string The path to the config file
17+
-D, --debug Enable debug mode
18+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
19+
-p, --profile string The config profile to use

cmd/scw/testdata/test-all-usage-iam-rule-usage.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ AVAILABLE COMMANDS:
99
list List rules of a given policy
1010
update Set rules of a given policy
1111

12+
UTILITY COMMANDS:
13+
create Create a rule for a specific IAM policy
14+
delete Delete a rule for a specific IAM policy
15+
1216
FLAGS:
1317
-h, --help help for rule
1418

docs/commands/iam.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ IAM API.
4141
- [List policies of an Organization](#list-policies-of-an-organization)
4242
- [Update an existing policy](#update-an-existing-policy)
4343
- [Rules management commands](#rules-management-commands)
44+
- [Create a rule for a specific IAM policy](#create-a-rule-for-a-specific-iam-policy)
45+
- [Delete a rule for a specific IAM policy](#delete-a-rule-for-a-specific-iam-policy)
4446
- [List rules of a given policy](#list-rules-of-a-given-policy)
4547
- [Set rules of a given policy](#set-rules-of-a-given-policy)
4648
- [SSH keys management commands](#ssh-keys-management-commands)
@@ -809,6 +811,48 @@ scw iam policy update <policy-id ...> [arg=value ...]
809811
Rules management commands.
810812

811813

814+
### Create a rule for a specific IAM policy
815+
816+
817+
818+
**Usage:**
819+
820+
```
821+
scw iam rule create <policy-id ...> [arg=value ...]
822+
```
823+
824+
825+
**Args:**
826+
827+
| Name | | Description |
828+
|------|---|-------------|
829+
| policy-id | | Id of policy to update |
830+
| permission-set-names.{index} | | Names of permission sets bound to the rule |
831+
| project-ids.{index} | | List of Project IDs the rule is scoped to |
832+
| organization-id | | ID of Organization the rule is scoped to |
833+
834+
835+
836+
### Delete a rule for a specific IAM policy
837+
838+
839+
840+
**Usage:**
841+
842+
```
843+
scw iam rule delete <policy-id ...> [arg=value ...]
844+
```
845+
846+
847+
**Args:**
848+
849+
| Name | | Description |
850+
|------|---|-------------|
851+
| policy-id | | Id of policy to update |
852+
| rule-id | | Id of rule to delete |
853+
854+
855+
812856
### List rules of a given policy
813857

814858
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.

internal/core/testing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ func TestCheckCombine(checks ...TestCheck) TestCheck {
596596
// TestCheckExitCode assert exitCode
597597
func TestCheckExitCode(expectedCode int) TestCheck {
598598
return func(t *testing.T, ctx *CheckFuncCtx) {
599-
assert.Equal(t, expectedCode, ctx.ExitCode, "Invalid exit code")
599+
assert.Equal(t, expectedCode, ctx.ExitCode, "Invalid exit code\n%s", string(ctx.Stderr))
600600
}
601601
}
602602

internal/namespaces/iam/v1alpha1/custom.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ func GetCommands() *core.Commands {
3232

3333
cmds.Merge(core.NewCommands(
3434
initWithSSHCommand(),
35+
iamRuleCreateCommand(),
36+
iamRuleDeleteCommand(),
3537
))
3638

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

4951
// Autocomplete permission set names using IAM API.
50-
cmds.MustFind("iam", "policy", "create").Override(func(c *core.Command) *core.Command {
51-
c.ArgSpecs.GetByName("rules.{index}.permission-set-names.{index}").AutoCompleteFunc = func(ctx context.Context, _ string, _ any) core.AutocompleteSuggestions {
52-
client := core.ExtractClient(ctx)
53-
api := iam.NewAPI(client)
54-
// TODO: store result in a CLI cache
55-
resp, err := api.ListPermissionSets(&iam.ListPermissionSetsRequest{
56-
PageSize: scw.Uint32Ptr(100),
57-
}, scw.WithAllPages())
58-
if err != nil {
59-
return nil
60-
}
61-
suggestions := core.AutocompleteSuggestions{}
62-
for _, ps := range resp.PermissionSets {
63-
suggestions = append(suggestions, ps.Name)
64-
}
65-
return suggestions
66-
}
67-
return c
68-
})
52+
cmds.MustFind("iam", "policy", "create").Override(iamPolicyCreateBuilder)
53+
cmds.MustFind("iam", "policy", "get").Override(iamPolicyGetBuilder)
6954

7055
return cmds
7156
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package iam
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/scaleway/scaleway-cli/v2/internal/core"
8+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
9+
"github.com/scaleway/scaleway-sdk-go/scw"
10+
)
11+
12+
func iamPolicyCreateBuilder(c *core.Command) *core.Command {
13+
c.ArgSpecs.GetByName("rules.{index}.permission-set-names.{index}").AutoCompleteFunc = func(ctx context.Context, _ string, _ any) core.AutocompleteSuggestions {
14+
client := core.ExtractClient(ctx)
15+
api := iam.NewAPI(client)
16+
// TODO: store result in a CLI cache
17+
resp, err := api.ListPermissionSets(&iam.ListPermissionSetsRequest{
18+
PageSize: scw.Uint32Ptr(100),
19+
}, scw.WithAllPages())
20+
if err != nil {
21+
return nil
22+
}
23+
suggestions := core.AutocompleteSuggestions{}
24+
for _, ps := range resp.PermissionSets {
25+
suggestions = append(suggestions, ps.Name)
26+
}
27+
return suggestions
28+
}
29+
return c
30+
}
31+
32+
type PolicyGetInterceptorResponse struct {
33+
*iam.Policy
34+
Rules []*iam.Rule
35+
}
36+
37+
func iamPolicyGetBuilder(c *core.Command) *core.Command {
38+
c.View = &core.View{
39+
Title: "Policy",
40+
Sections: []*core.ViewSection{
41+
{
42+
Title: "Rules",
43+
FieldName: "Rules",
44+
},
45+
},
46+
}
47+
c.AddInterceptors(func(ctx context.Context, argsI interface{}, runner core.CommandRunner) (interface{}, error) {
48+
args := argsI.(*iam.GetPolicyRequest)
49+
api := iam.NewAPI(core.ExtractClient(ctx))
50+
51+
respI, err := runner(ctx, argsI)
52+
if err != nil {
53+
return respI, err
54+
}
55+
resp := &PolicyGetInterceptorResponse{
56+
Policy: respI.(*iam.Policy),
57+
}
58+
59+
rules, err := api.ListRules(&iam.ListRulesRequest{
60+
PolicyID: args.PolicyID,
61+
}, scw.WithContext(ctx), scw.WithAllPages())
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to list rules for given policy: %w", err)
64+
}
65+
resp.Rules = rules.Rules
66+
67+
return resp, nil
68+
})
69+
70+
return c
71+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package iam_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/alecthomas/assert"
7+
"github.com/scaleway/scaleway-cli/v2/internal/core"
8+
"github.com/scaleway/scaleway-cli/v2/internal/namespaces/account/v3"
9+
iam "github.com/scaleway/scaleway-cli/v2/internal/namespaces/iam/v1alpha1"
10+
)
11+
12+
func Test_getPolicyWithRules(t *testing.T) {
13+
t.Run("simple", core.Test(&core.TestConfig{
14+
Commands: core.NewCommandsMerge(
15+
iam.GetCommands(),
16+
account.GetCommands(),
17+
),
18+
BeforeFunc: core.BeforeFuncCombine(
19+
core.ExecStoreBeforeCmd("Project", "scw account project create name=test-cli-get-policy"),
20+
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 }}"),
21+
),
22+
Cmd: `scw iam policy get {{ .Policy.ID }}`,
23+
Check: core.TestCheckCombine(
24+
func(t *testing.T, ctx *core.CheckFuncCtx) {
25+
assert.Contains(t, string(ctx.Stdout), "IPAMReadOnly")
26+
},
27+
core.TestCheckGolden(),
28+
core.TestCheckExitCode(0),
29+
),
30+
AfterFunc: core.AfterFuncCombine(
31+
core.ExecAfterCmd("scw iam policy delete {{ .Policy.ID }}"),
32+
core.ExecAfterCmd("scw account project delete project-id={{ .Project.ID }}"),
33+
),
34+
}))
35+
}

0 commit comments

Comments
 (0)