From 6dc1e16c8847fa23dca7beca0e2f9bb8ac92c6b8 Mon Sep 17 00:00:00 2001 From: Philip-21 Date: Fri, 31 May 2024 09:38:14 +0100 Subject: [PATCH 1/4] first commits Signed-off-by: Philip-21 --- cli/tokens.go | 30 +++++++++++++++++++++--------- codersdk/apikey.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/cli/tokens.go b/cli/tokens.go index 4961ac7e3e9b5..5269fafec7d6c 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,16 +59,27 @@ 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{ - Lifetime: tokenLifetime, - TokenName: name, - }) - if err != nil { - return xerrors.Errorf("create tokens: %w", err) + if user != "" { + adminID := codersdk.Me + res, err := client.CreateTokenForUser(inv.Context(), adminID, user, codersdk.CreateTokenRequest{ + Lifetime: tokenLifetime, + TokenName: name, + }) + if err != nil { + return xerrors.Errorf("create tokens for user %s: %w", user, err) + } + _, _ = fmt.Fprintln(inv.Stdout, res.Key) // Print the token to stdout + } else { + // Otherwise, create a token for the current user + res, err := client.CreateToken(inv.Context(), codersdk.Me, codersdk.CreateTokenRequest{ + Lifetime: tokenLifetime, + TokenName: name, + }) + if err != nil { + return xerrors.Errorf("create tokens: %w", err) + } + _, _ = fmt.Fprintln(inv.Stdout, res.Key) // Print the token to stdout } - - _, _ = fmt.Fprintln(inv.Stdout, res.Key) - return nil }, } diff --git a/codersdk/apikey.go b/codersdk/apikey.go index 32c97cf538417..2838e5eb4e0ab 100644 --- a/codersdk/apikey.go +++ b/codersdk/apikey.go @@ -78,6 +78,42 @@ func (c *Client) CreateToken(ctx context.Context, userID string, req CreateToken return apiKey, json.NewDecoder(res.Body).Decode(&apiKey) } + +// CreateTokenForUser allows an admin to create a token on behalf of another user. +func (c *Client) CreateTokenForUser(ctx context.Context, adminID, targetUserID string, req CreateTokenRequest) (GenerateAPIKeyResponse, error) { + isAdmin, err := c.isAdmin(ctx, adminID) + if err != nil { + return GenerateAPIKeyResponse{}, fmt.Errorf("admin check failed: %w", err) + } + if !isAdmin { + return GenerateAPIKeyResponse{}, fmt.Errorf("user %s is not an admin", adminID) + } + return c.CreateToken(ctx, targetUserID, req) +} + +// isAdmin is a placeholder function to check if a user is an admin. +func (c *Client) isAdmin(ctx context.Context, userID string) (bool, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys", userID), nil) + if err != nil { + return false, fmt.Errorf("unable to get user : %s", userID) + } + defer res.Body.Close() + if res.StatusCode > http.StatusCreated { + return false, ReadBodyAsError(res) + } + //extract roles, when the API returns roles from the response, ensuring system admin privileges + var roles []string + if err := json.NewDecoder(res.Body).Decode(&roles); err != nil { + return false, fmt.Errorf("failed to decode roles: %w", err) + } + for _, role := range roles { + if role == "admin" { + return true, nil + } + } + return false, nil +} + // CreateAPIKey generates an API key for the user ID provided. // CreateToken should be used over CreateAPIKey. CreateToken allows better // tracking of the token's usage and allows for custom expiration. From b846135f22b60043c8d6d17932010053a8ccc08e Mon Sep 17 00:00:00 2001 From: Philip-21 Date: Fri, 31 May 2024 23:48:46 +0100 Subject: [PATCH 2/4] adding more conditional statement, adding necessary userID strings Signed-off-by: Philip-21 --- cli/tokens.go | 6 ++++-- codersdk/apikey.go | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cli/tokens.go b/cli/tokens.go index 5269fafec7d6c..16c90ebed84bc 100644 --- a/cli/tokens.go +++ b/cli/tokens.go @@ -59,9 +59,11 @@ func (r *RootCmd) createToken() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { + userID := codersdk.Me if user != "" { + userID = user adminID := codersdk.Me - res, err := client.CreateTokenForUser(inv.Context(), adminID, user, codersdk.CreateTokenRequest{ + res, err := client.CreateTokenForUser(inv.Context(), adminID, userID, codersdk.CreateTokenRequest{ Lifetime: tokenLifetime, TokenName: name, }) @@ -71,7 +73,7 @@ func (r *RootCmd) createToken() *serpent.Command { _, _ = fmt.Fprintln(inv.Stdout, res.Key) // Print the token to stdout } else { // Otherwise, create a token for the current user - res, err := client.CreateToken(inv.Context(), codersdk.Me, codersdk.CreateTokenRequest{ + res, err := client.CreateToken(inv.Context(), userID, codersdk.CreateTokenRequest{ Lifetime: tokenLifetime, TokenName: name, }) diff --git a/codersdk/apikey.go b/codersdk/apikey.go index 2838e5eb4e0ab..3ad7d1dc42976 100644 --- a/codersdk/apikey.go +++ b/codersdk/apikey.go @@ -88,6 +88,9 @@ func (c *Client) CreateTokenForUser(ctx context.Context, adminID, targetUserID s if !isAdmin { return GenerateAPIKeyResponse{}, fmt.Errorf("user %s is not an admin", adminID) } + if adminID == targetUserID { + return GenerateAPIKeyResponse{}, fmt.Errorf("admin cannot create a token for themselves") + } return c.CreateToken(ctx, targetUserID, req) } From 778c11690be014ae38250a7f347408b3b76200d9 Mon Sep 17 00:00:00 2001 From: Philip-21 Date: Thu, 6 Jun 2024 07:33:09 +0100 Subject: [PATCH 3/4] adding new flag options Signed-off-by: Philip-21 --- cli/tokens.go | 7 +++++++ pnpm-lock.yaml | 32 +++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/cli/tokens.go b/cli/tokens.go index 16c90ebed84bc..e61461720196f 100644 --- a/cli/tokens.go +++ b/cli/tokens.go @@ -101,6 +101,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: "create a token on behalf of another user", + Value: serpent.StringOf(&name), + }, } return cmd diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5e4d2584e40f..d04d1cf7a21e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,29 +1,35 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - exec: - specifier: ^0.2.1 - version: 0.2.1 +importers: -devDependencies: - prettier: - specifier: 3.0.0 - version: 3.0.0 + .: + dependencies: + exec: + specifier: ^0.2.1 + version: 0.2.1 + devDependencies: + prettier: + specifier: 3.0.0 + version: 3.0.0 packages: - /exec@0.2.1: + exec@0.2.1: resolution: {integrity: sha512-lE5ZlJgRYh+rmwidatL2AqRA/U9IBoCpKlLriBmnfUIrV/Rj4oLjb63qZ57iBCHWi5j9IjLt5wOWkFYPiTfYAg==} engines: {node: '>= v0.9.1'} deprecated: deprecated in favor of builtin child_process.execFile - dev: false - /prettier@3.0.0: + prettier@3.0.0: resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} engines: {node: '>=14'} hasBin: true - dev: true + +snapshots: + + exec@0.2.1: {} + + prettier@3.0.0: {} From e576227e4138d3d50a3000f54461b3b9409df890 Mon Sep 17 00:00:00 2001 From: Philip-21 Date: Thu, 6 Jun 2024 07:49:11 +0100 Subject: [PATCH 4/4] updating tests Signed-off-by: Philip-21 --- cli/tokens_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cli/tokens_test.go b/cli/tokens_test.go index fdb062b959a3b..bbb45d9ccda35 100644 --- a/cli/tokens_test.go +++ b/cli/tokens_test.go @@ -42,6 +42,16 @@ func TestTokens(t *testing.T) { require.NotEmpty(t, res) id := res[:10] + inv, root = clitest.New(t, "tokens", "create", "-u", "user-one") + clitest.SetupConfig(t, client, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) + + inv, root = clitest.New(t, "tokens", "ls") clitest.SetupConfig(t, client, root) buf = new(bytes.Buffer) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy