Skip to content

Commit ba90bb0

Browse files
authored
feat: implement feature to create a token on behalf of another user in the cli (coder#14813)
This PR addresses coder#13160
1 parent e70ad2b commit ba90bb0

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

cli/testdata/coder_tokens_create_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,9 @@ OPTIONS:
1212
-n, --name string, $CODER_TOKEN_NAME
1313
Specify a human-readable name.
1414

15+
-u, --user string, $CODER_TOKEN_USER
16+
Specify the user to create the token for (Only works if logged in user
17+
is admin).
18+
1519
———
1620
Run `coder --help` for a list of global options.

cli/tokens.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func (r *RootCmd) createToken() *serpent.Command {
4848
var (
4949
tokenLifetime time.Duration
5050
name string
51+
user string
5152
)
5253
client := new(codersdk.Client)
5354
cmd := &serpent.Command{
@@ -58,7 +59,11 @@ func (r *RootCmd) createToken() *serpent.Command {
5859
r.InitClient(client),
5960
),
6061
Handler: func(inv *serpent.Invocation) error {
61-
res, err := client.CreateToken(inv.Context(), codersdk.Me, codersdk.CreateTokenRequest{
62+
userID := codersdk.Me
63+
if user != "" {
64+
userID = user
65+
}
66+
res, err := client.CreateToken(inv.Context(), userID, codersdk.CreateTokenRequest{
6267
Lifetime: tokenLifetime,
6368
TokenName: name,
6469
})
@@ -87,6 +92,13 @@ func (r *RootCmd) createToken() *serpent.Command {
8792
Description: "Specify a human-readable name.",
8893
Value: serpent.StringOf(&name),
8994
},
95+
{
96+
Flag: "user",
97+
FlagShorthand: "u",
98+
Env: "CODER_TOKEN_USER",
99+
Description: "Specify the user to create the token for (Only works if logged in user is admin).",
100+
Value: serpent.StringOf(&user),
101+
},
90102
}
91103

92104
return cmd

cli/tokens_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ import (
1717
func TestTokens(t *testing.T) {
1818
t.Parallel()
1919
client := coderdtest.New(t, nil)
20-
_ = coderdtest.CreateFirstUser(t, client)
20+
adminUser := coderdtest.CreateFirstUser(t, client)
21+
22+
secondUserClient, secondUser := coderdtest.CreateAnotherUser(t, client, adminUser.OrganizationID)
23+
_, thirdUser := coderdtest.CreateAnotherUser(t, client, adminUser.OrganizationID)
2124

2225
ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitLong)
2326
defer cancelFunc()
2427

2528
// helpful empty response
2629
inv, root := clitest.New(t, "tokens", "ls")
30+
//nolint:gocritic // This should be run as the owner user.
2731
clitest.SetupConfig(t, client, root)
2832
buf := new(bytes.Buffer)
2933
inv.Stdout = buf
@@ -42,6 +46,19 @@ func TestTokens(t *testing.T) {
4246
require.NotEmpty(t, res)
4347
id := res[:10]
4448

49+
// Test creating a token for second user from first user's (admin) session
50+
inv, root = clitest.New(t, "tokens", "create", "--name", "token-two", "--user", secondUser.ID.String())
51+
clitest.SetupConfig(t, client, root)
52+
buf = new(bytes.Buffer)
53+
inv.Stdout = buf
54+
err = inv.WithContext(ctx).Run()
55+
// Test should succeed in creating token for second user
56+
require.NoError(t, err)
57+
res = buf.String()
58+
require.NotEmpty(t, res)
59+
secondTokenID := res[:10]
60+
61+
// Test listing tokens from the first user's (admin) session
4562
inv, root = clitest.New(t, "tokens", "ls")
4663
clitest.SetupConfig(t, client, root)
4764
buf = new(bytes.Buffer)
@@ -50,11 +67,39 @@ func TestTokens(t *testing.T) {
5067
require.NoError(t, err)
5168
res = buf.String()
5269
require.NotEmpty(t, res)
70+
// Result should only contain the token created for the admin user
5371
require.Contains(t, res, "ID")
5472
require.Contains(t, res, "EXPIRES AT")
5573
require.Contains(t, res, "CREATED AT")
5674
require.Contains(t, res, "LAST USED")
5775
require.Contains(t, res, id)
76+
// Result should not contain the token created for the second user
77+
require.NotContains(t, res, secondTokenID)
78+
79+
// Test listing tokens from the second user's session
80+
inv, root = clitest.New(t, "tokens", "ls")
81+
clitest.SetupConfig(t, secondUserClient, root)
82+
buf = new(bytes.Buffer)
83+
inv.Stdout = buf
84+
err = inv.WithContext(ctx).Run()
85+
require.NoError(t, err)
86+
res = buf.String()
87+
require.NotEmpty(t, res)
88+
require.Contains(t, res, "ID")
89+
require.Contains(t, res, "EXPIRES AT")
90+
require.Contains(t, res, "CREATED AT")
91+
require.Contains(t, res, "LAST USED")
92+
// Result should contain the token created for the second user
93+
require.Contains(t, res, secondTokenID)
94+
95+
// Test creating a token for third user from second user's (non-admin) session
96+
inv, root = clitest.New(t, "tokens", "create", "--name", "token-two", "--user", thirdUser.ID.String())
97+
clitest.SetupConfig(t, secondUserClient, root)
98+
buf = new(bytes.Buffer)
99+
inv.Stdout = buf
100+
err = inv.WithContext(ctx).Run()
101+
// User (non-admin) should not be able to create a token for another user
102+
require.Error(t, err)
58103

59104
inv, root = clitest.New(t, "tokens", "ls", "--output=json")
60105
clitest.SetupConfig(t, client, root)

docs/reference/cli/tokens_create.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)