Skip to content

Commit 477d138

Browse files
committed
feat(iam): support api-key get option to retrieve the ApiKey principal policies
1 parent 0757c23 commit 477d138

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

cmd/scw/testdata/test-all-usage-iam-api-key-get-usage.golden

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ USAGE:
66
scw iam api-key get <access-key ...> [arg=value ...]
77

88
ARGS:
9-
access-key Access key to search for
9+
access-key Access key to search for
10+
[with-policies=false] Display policies associated with the API key
1011

1112
FLAGS:
1213
-h, --help help for get

docs/commands/iam.md

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ scw iam api-key get <access-key ...> [arg=value ...]
135135
| Name | | Description |
136136
|------|---|-------------|
137137
| access-key | Required | Access key to search for |
138+
| with-policies | Default: `false` | Display policies associated with the API key |
138139

139140

140141

internal/namespaces/iam/v1alpha1/custom.go

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ func GetCommands() *core.Commands {
5252
cmds.MustFind("iam", "policy", "create").Override(iamPolicyCreateBuilder)
5353
cmds.MustFind("iam", "policy", "get").Override(iamPolicyGetBuilder)
5454

55+
cmds.MustFind("iam", "api-key", "get").Override(iamAPIKeyGetBuilder)
56+
5557
return cmds
5658
}
5759

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package iam
2+
3+
import (
4+
"context"
5+
"reflect"
6+
7+
"github.com/scaleway/scaleway-cli/v2/core"
8+
"github.com/scaleway/scaleway-cli/v2/core/human"
9+
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
type iamGetAPIKeyArgs struct {
14+
AccessKey string
15+
WithPolicies bool
16+
}
17+
18+
type apiKeyOptions struct {
19+
WithPolicies bool
20+
}
21+
22+
func WithPolicies(withPolicies bool) apiKeyOptions {
23+
return apiKeyOptions{
24+
WithPolicies: withPolicies,
25+
}
26+
}
27+
28+
func getApiKey(
29+
ctx context.Context,
30+
api *iam.API,
31+
accessKey string,
32+
options apiKeyOptions,
33+
) (apiKeyResponse, error) {
34+
var response apiKeyResponse
35+
apiKey, err := api.GetAPIKey(&iam.GetAPIKeyRequest{
36+
AccessKey: accessKey,
37+
}, scw.WithContext(ctx))
38+
if err != nil {
39+
return response, err
40+
}
41+
42+
user, err := api.GetUser(&iam.GetUserRequest{
43+
UserID: *apiKey.UserID,
44+
}, scw.WithContext(ctx))
45+
if err != nil {
46+
return response, err
47+
}
48+
49+
response.APIKey = apiKey
50+
response.UserType = user.Type
51+
52+
if options.WithPolicies {
53+
listPolicyRequest := &iam.ListPoliciesRequest{
54+
UserIDs: []string{*apiKey.UserID},
55+
}
56+
// if user is owner, list all policies attached to the organization
57+
// because the user has no policies attached directly
58+
if user.Type == iam.UserTypeOwner {
59+
listPolicyRequest.OrganizationID = apiKey.DefaultProjectID
60+
listPolicyRequest.UserIDs = []string{}
61+
}
62+
policies, err := api.ListPolicies(
63+
listPolicyRequest,
64+
scw.WithAllPages(),
65+
scw.WithContext(ctx),
66+
)
67+
if err != nil {
68+
return response, err
69+
}
70+
response.Policies = policies.Policies
71+
}
72+
73+
return response, nil
74+
}
75+
76+
type apiKeyResponse struct {
77+
APIKey *iam.APIKey
78+
UserType iam.UserType `json:"user_type"`
79+
Policies []*iam.Policy `json:"policies"`
80+
}
81+
82+
func apiKeyMarshalerFunc(i interface{}, opt *human.MarshalOpt) (string, error) {
83+
type tmp apiKeyResponse
84+
resp := tmp(i.(apiKeyResponse))
85+
86+
sections := []*human.MarshalSection{
87+
{
88+
FieldName: "UserType",
89+
Title: "User Type",
90+
},
91+
{
92+
FieldName: "APIKey",
93+
Title: "API Key",
94+
},
95+
}
96+
97+
if len(resp.Policies) > 0 {
98+
sections = append(sections, &human.MarshalSection{
99+
FieldName: "Policies",
100+
Title: "Policies",
101+
})
102+
}
103+
104+
opt.Sections = sections
105+
106+
return human.Marshal(resp, opt)
107+
}
108+
109+
func iamAPIKeyGetBuilder(c *core.Command) *core.Command {
110+
human.RegisterMarshalerFunc(apiKeyResponse{}, apiKeyMarshalerFunc)
111+
112+
return &core.Command{
113+
Short: `Get an API key`,
114+
Long: `Retrieve information about an API key, specified by the ` + "`" + `access_key` + "`" + ` parameter. The API key's details, including either the ` + "`" + `user_id` + "`" + ` or ` + "`" + `application_id` + "`" + ` of its bearer are returned in the response. Note that the string value for the ` + "`" + `secret_key` + "`" + ` is nullable, and therefore is not displayed in the response. The ` + "`" + `secret_key` + "`" + ` value is only displayed upon API key creation.`,
115+
Namespace: "iam",
116+
Resource: "api-key",
117+
Verb: "get",
118+
// Deprecated: false,
119+
ArgsType: reflect.TypeOf(iamGetAPIKeyArgs{}),
120+
ArgSpecs: core.ArgSpecs{
121+
{
122+
Name: "access-key",
123+
Short: `Access key to search for`,
124+
Required: true,
125+
Deprecated: false,
126+
Positional: true,
127+
},
128+
{
129+
Name: "with-policies",
130+
Short: `Display policies associated with the API key`,
131+
Default: core.DefaultValueSetter("false"),
132+
Required: false,
133+
Deprecated: false,
134+
Positional: false,
135+
},
136+
},
137+
Run: func(ctx context.Context, args interface{}) (i interface{}, e error) {
138+
arguments := args.(*iamGetAPIKeyArgs)
139+
140+
client := core.ExtractClient(ctx)
141+
api := iam.NewAPI(client)
142+
143+
return getApiKey(ctx, api, arguments.AccessKey, apiKeyOptions{
144+
WithPolicies: arguments.WithPolicies,
145+
})
146+
},
147+
}
148+
}

0 commit comments

Comments
 (0)