diff --git a/cli/testdata/coder_tokens_create_--help.golden b/cli/testdata/coder_tokens_create_--help.golden index f36d80f229..f887c38acd 100644 --- a/cli/testdata/coder_tokens_create_--help.golden +++ b/cli/testdata/coder_tokens_create_--help.golden @@ -12,5 +12,9 @@ OPTIONS: -n, --name string, $CODER_TOKEN_NAME Specify a human-readable name. + -u, --user string, $CODER_TOKEN_USER + Specify the user to create the token for (Only works if logged in user + is admin). + ——— Run `coder --help` for a list of global options. diff --git a/cli/tokens.go b/cli/tokens.go index 4961ac7e3e..30eda97e17 100644 --- a/cli/tokens.go +++ b/cli/tokens.go @@ -48,6 +48,7 @@ func (r *RootCmd) createToken() *serpent.Command { var ( tokenLifetime time.Duration name string + user string ) client := new(codersdk.Client) cmd := &serpent.Command{ @@ -58,7 +59,11 @@ func (r *RootCmd) createToken() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { - res, err := client.CreateToken(inv.Context(), codersdk.Me, codersdk.CreateTokenRequest{ + userID := codersdk.Me + if user != "" { + userID = user + } + res, err := client.CreateToken(inv.Context(), userID, codersdk.CreateTokenRequest{ Lifetime: tokenLifetime, TokenName: name, }) @@ -87,6 +92,13 @@ func (r *RootCmd) createToken() *serpent.Command { Description: "Specify a human-readable name.", Value: serpent.StringOf(&name), }, + { + Flag: "user", + FlagShorthand: "u", + Env: "CODER_TOKEN_USER", + Description: "Specify the user to create the token for (Only works if logged in user is admin).", + Value: serpent.StringOf(&user), + }, } return cmd diff --git a/cli/tokens_test.go b/cli/tokens_test.go index fdb062b959..7c024f3ad1 100644 --- a/cli/tokens_test.go +++ b/cli/tokens_test.go @@ -17,13 +17,17 @@ import ( func TestTokens(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) - _ = coderdtest.CreateFirstUser(t, client) + adminUser := coderdtest.CreateFirstUser(t, client) + + secondUserClient, secondUser := coderdtest.CreateAnotherUser(t, client, adminUser.OrganizationID) + _, thirdUser := coderdtest.CreateAnotherUser(t, client, adminUser.OrganizationID) ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancelFunc() // helpful empty response inv, root := clitest.New(t, "tokens", "ls") + //nolint:gocritic // This should be run as the owner user. clitest.SetupConfig(t, client, root) buf := new(bytes.Buffer) inv.Stdout = buf @@ -42,6 +46,19 @@ func TestTokens(t *testing.T) { require.NotEmpty(t, res) id := res[:10] + // Test creating a token for second user from first user's (admin) session + inv, root = clitest.New(t, "tokens", "create", "--name", "token-two", "--user", secondUser.ID.String()) + clitest.SetupConfig(t, client, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + // Test should succeed in creating token for second user + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) + secondTokenID := res[:10] + + // Test listing tokens from the first user's (admin) session inv, root = clitest.New(t, "tokens", "ls") clitest.SetupConfig(t, client, root) buf = new(bytes.Buffer) @@ -50,11 +67,39 @@ func TestTokens(t *testing.T) { require.NoError(t, err) res = buf.String() require.NotEmpty(t, res) + // Result should only contain the token created for the admin user + require.Contains(t, res, "ID") + require.Contains(t, res, "EXPIRES AT") + require.Contains(t, res, "CREATED AT") + require.Contains(t, res, "LAST USED") + require.Contains(t, res, id) + // Result should not contain the token created for the second user + require.NotContains(t, res, secondTokenID) + + // Test listing tokens from the second user's session + inv, root = clitest.New(t, "tokens", "ls") + clitest.SetupConfig(t, secondUserClient, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) require.Contains(t, res, "ID") require.Contains(t, res, "EXPIRES AT") require.Contains(t, res, "CREATED AT") require.Contains(t, res, "LAST USED") - require.Contains(t, res, id) + // Result should contain the token created for the second user + require.Contains(t, res, secondTokenID) + + // Test creating a token for third user from second user's (non-admin) session + inv, root = clitest.New(t, "tokens", "create", "--name", "token-two", "--user", thirdUser.ID.String()) + clitest.SetupConfig(t, secondUserClient, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + // User (non-admin) should not be able to create a token for another user + require.Error(t, err) inv, root = clitest.New(t, "tokens", "ls", "--output=json") clitest.SetupConfig(t, client, root) diff --git a/docs/reference/cli/tokens_create.md b/docs/reference/cli/tokens_create.md index e6b613fa00..09a4a5d200 100644 --- a/docs/reference/cli/tokens_create.md +++ b/docs/reference/cli/tokens_create.md @@ -30,3 +30,12 @@ Specify a duration for the lifetime of the token. | Environment | $CODER_TOKEN_NAME | Specify a human-readable name. + +### -u, --user + +| | | +| ----------- | ------------------------------ | +| Type | string | +| Environment | $CODER_TOKEN_USER | + +Specify the user to create the token for (Only works if logged in user is admin).