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