Skip to content

Commit 187f812

Browse files
committed
remove tokens by name
1 parent 5963708 commit 187f812

File tree

11 files changed

+219
-26
lines changed

11 files changed

+219
-26
lines changed

cli/tokens.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func listTokens() *cobra.Command {
179179

180180
func removeToken() *cobra.Command {
181181
cmd := &cobra.Command{
182-
Use: "remove [id]",
182+
Use: "remove [name]",
183183
Aliases: []string{"rm"},
184184
Short: "Delete a token",
185185
Args: cobra.ExactArgs(1),
@@ -189,7 +189,12 @@ func removeToken() *cobra.Command {
189189
return xerrors.Errorf("create codersdk client: %w", err)
190190
}
191191

192-
err = client.DeleteAPIKey(cmd.Context(), codersdk.Me, args[0])
192+
token, err := client.APIKeyByName(cmd.Context(), codersdk.Me, args[0])
193+
if err != nil {
194+
return xerrors.Errorf("delete api key: %w", err)
195+
}
196+
197+
err = client.DeleteAPIKey(cmd.Context(), codersdk.Me, token.ID)
193198
if err != nil {
194199
return xerrors.Errorf("delete api key: %w", err)
195200
}

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/apikey.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,16 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
152152
httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value})
153153
}
154154

155-
// @Summary Get API key
156-
// @ID get-api-key
155+
// @Summary Get API key by ID
156+
// @ID get-api-key-by-id
157157
// @Security CoderSessionToken
158158
// @Produce json
159159
// @Tags Users
160160
// @Param user path string true "User ID, name, or me"
161161
// @Param keyid path string true "Key ID" format(uuid)
162162
// @Success 200 {object} codersdk.APIKey
163163
// @Router /users/{user}/keys/{keyid} [get]
164-
func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) {
164+
func (api *API) apiKeyByID(rw http.ResponseWriter, r *http.Request) {
165165
ctx := r.Context()
166166

167167
keyID := chi.URLParam(r, "keyid")
@@ -186,6 +186,46 @@ func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) {
186186
httpapi.Write(ctx, rw, http.StatusOK, convertAPIKey(key))
187187
}
188188

189+
// @Summary Get API key by token name
190+
// @ID get-api-key-by-name
191+
// @Security CoderSessionToken
192+
// @Produce json
193+
// @Tags Users
194+
// @Param user path string true "User ID, name, or me"
195+
// @Param keyname path string true "Key Name" format(string)
196+
// @Success 200 {object} codersdk.APIKey
197+
// @Router /users/{user}/keys/tokens/{keyname} [get]
198+
func (api *API) apiKeyByName(rw http.ResponseWriter, r *http.Request) {
199+
var (
200+
ctx = r.Context()
201+
user = httpmw.UserParam(r)
202+
tokenName = chi.URLParam(r, "keyname")
203+
)
204+
205+
token, err := api.Database.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{
206+
TokenName: tokenName,
207+
UserID: user.ID,
208+
})
209+
if errors.Is(err, sql.ErrNoRows) {
210+
httpapi.ResourceNotFound(rw)
211+
return
212+
}
213+
if err != nil {
214+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
215+
Message: "Internal error fetching API key.",
216+
Detail: err.Error(),
217+
})
218+
return
219+
}
220+
221+
if !api.Authorize(r, rbac.ActionRead, token) {
222+
httpapi.ResourceNotFound(rw)
223+
return
224+
}
225+
226+
httpapi.Write(ctx, rw, http.StatusOK, convertAPIKey(token))
227+
}
228+
189229
// @Summary Get user tokens
190230
// @ID get-user-tokens
191231
// @Security CoderSessionToken

coderd/coderd.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,12 @@ func New(options *Options) *API {
556556
r.Route("/tokens", func(r chi.Router) {
557557
r.Post("/", api.postToken)
558558
r.Get("/", api.tokens)
559+
r.Route("/{keyname}", func(r chi.Router) {
560+
r.Get("/", api.apiKeyByName)
561+
})
559562
})
560563
r.Route("/{keyid}", func(r chi.Router) {
561-
r.Get("/", api.apiKey)
564+
r.Get("/", api.apiKeyByID)
562565
r.Delete("/", api.deleteAPIKey)
563566
})
564567
})

coderd/coderdtest/authorize.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
9595
AssertObject: rbac.ResourceAPIKey,
9696
AssertAction: rbac.ActionRead,
9797
},
98+
"GET:/api/v2/users/{user}/keys/tokens/{keyname}": {
99+
AssertObject: rbac.ResourceAPIKey,
100+
AssertAction: rbac.ActionRead,
101+
},
98102
"GET:/api/v2/workspacebuilds/{workspacebuild}": {
99103
AssertAction: rbac.ActionRead,
100104
AssertObject: workspaceRBACObj,

coderd/users_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,15 @@ func TestPostLogin(t *testing.T) {
266266
defer cancel()
267267

268268
split := strings.Split(client.SessionToken(), "-")
269-
key, err := client.APIKey(ctx, admin.UserID.String(), split[0])
269+
key, err := client.APIKeyByID(ctx, admin.UserID.String(), split[0])
270270
require.NoError(t, err, "fetch login key")
271271
require.Equal(t, int64(86400), key.LifetimeSeconds, "default should be 86400")
272272

273273
// tokens have a longer life
274274
token, err := client.CreateToken(ctx, codersdk.Me, codersdk.CreateTokenRequest{})
275275
require.NoError(t, err, "make new token api key")
276276
split = strings.Split(token.Key, "-")
277-
apiKey, err := client.APIKey(ctx, admin.UserID.String(), split[0])
277+
apiKey, err := client.APIKeyByID(ctx, admin.UserID.String(), split[0])
278278
require.NoError(t, err, "fetch api key")
279279

280280
require.True(t, apiKey.ExpiresAt.After(time.Now().Add(time.Hour*24*29)), "default tokens lasts more than 29 days")
@@ -356,7 +356,7 @@ func TestPostLogout(t *testing.T) {
356356
defer cancel()
357357

358358
keyID := strings.Split(client.SessionToken(), "-")[0]
359-
apiKey, err := client.APIKey(ctx, admin.UserID.String(), keyID)
359+
apiKey, err := client.APIKeyByID(ctx, admin.UserID.String(), keyID)
360360
require.NoError(t, err)
361361
require.Equal(t, keyID, apiKey.ID, "API key should exist in the database")
362362

@@ -385,7 +385,7 @@ func TestPostLogout(t *testing.T) {
385385
}
386386
require.True(t, found, "auth cookie should be returned")
387387

388-
_, err = client.APIKey(ctx, admin.UserID.String(), keyID)
388+
_, err = client.APIKeyByID(ctx, admin.UserID.String(), keyID)
389389
sdkErr := &codersdk.Error{}
390390
require.ErrorAs(t, err, &sdkErr)
391391
require.Equal(t, http.StatusUnauthorized, sdkErr.StatusCode(), "Expecting 401")
@@ -723,7 +723,7 @@ func TestUpdateUserPassword(t *testing.T) {
723723

724724
// Trying to get an API key should fail since our client's token
725725
// has been deleted.
726-
_, err = client.APIKey(ctx, user.UserID.String(), apikey1.Key)
726+
_, err = client.APIKeyByID(ctx, user.UserID.String(), apikey1.Key)
727727
require.Error(t, err)
728728
cerr := coderdtest.SDKError(t, err)
729729
require.Equal(t, http.StatusUnauthorized, cerr.StatusCode())
@@ -738,12 +738,12 @@ func TestUpdateUserPassword(t *testing.T) {
738738

739739
// Trying to get an API key should fail since all keys are deleted
740740
// on password change.
741-
_, err = client.APIKey(ctx, user.UserID.String(), apikey1.Key)
741+
_, err = client.APIKeyByID(ctx, user.UserID.String(), apikey1.Key)
742742
require.Error(t, err)
743743
cerr = coderdtest.SDKError(t, err)
744744
require.Equal(t, http.StatusNotFound, cerr.StatusCode())
745745

746-
_, err = client.APIKey(ctx, user.UserID.String(), apikey2.Key)
746+
_, err = client.APIKeyByID(ctx, user.UserID.String(), apikey2.Key)
747747
require.Error(t, err)
748748
cerr = coderdtest.SDKError(t, err)
749749
require.Equal(t, http.StatusNotFound, cerr.StatusCode())

coderd/workspaceapps_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ func TestWorkspaceApplicationAuth(t *testing.T) {
438438
// Get the current user and API key.
439439
user, err := client.User(ctx, codersdk.Me)
440440
require.NoError(t, err)
441-
currentAPIKey, err := client.APIKey(ctx, firstUser.UserID.String(), strings.Split(client.SessionToken(), "-")[0])
441+
currentAPIKey, err := client.APIKeyByID(ctx, firstUser.UserID.String(), strings.Split(client.SessionToken(), "-")[0])
442442
require.NoError(t, err)
443443

444444
// Try to load the application without authentication.
@@ -500,7 +500,7 @@ func TestWorkspaceApplicationAuth(t *testing.T) {
500500
apiKey := cookies[0].Value
501501

502502
// Fetch the API key.
503-
apiKeyInfo, err := client.APIKey(ctx, firstUser.UserID.String(), strings.Split(apiKey, "-")[0])
503+
apiKeyInfo, err := client.APIKeyByID(ctx, firstUser.UserID.String(), strings.Split(apiKey, "-")[0])
504504
require.NoError(t, err)
505505
require.Equal(t, user.ID, apiKeyInfo.UserID)
506506
require.Equal(t, codersdk.LoginTypePassword, apiKeyInfo.LoginType)

codersdk/apikey.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ func (c *Client) Tokens(ctx context.Context, userID string, filter TokensFilter)
121121
return apiKey, json.NewDecoder(res.Body).Decode(&apiKey)
122122
}
123123

124-
// APIKey returns the api key by id.
125-
func (c *Client) APIKey(ctx context.Context, userID string, id string) (*APIKey, error) {
124+
// APIKeyByID returns the api key by id.
125+
func (c *Client) APIKeyByID(ctx context.Context, userID string, id string) (*APIKey, error) {
126126
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys/%s", userID, id), nil)
127127
if err != nil {
128128
return nil, err
@@ -135,9 +135,23 @@ func (c *Client) APIKey(ctx context.Context, userID string, id string) (*APIKey,
135135
return apiKey, json.NewDecoder(res.Body).Decode(apiKey)
136136
}
137137

138-
// DeleteAPIKey deletes API key by name.
139-
func (c *Client) DeleteAPIKey(ctx context.Context, userID string, name string) error {
140-
res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/users/%s/keys/%s", userID, name), nil)
138+
// APIKeyByName returns the api key by name.
139+
func (c *Client) APIKeyByName(ctx context.Context, userID string, name string) (*APIKey, error) {
140+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys/tokens/%s", userID, name), nil)
141+
if err != nil {
142+
return nil, err
143+
}
144+
defer res.Body.Close()
145+
if res.StatusCode > http.StatusCreated {
146+
return nil, ReadBodyAsError(res)
147+
}
148+
apiKey := &APIKey{}
149+
return apiKey, json.NewDecoder(res.Body).Decode(apiKey)
150+
}
151+
152+
// DeleteAPIKey deletes API key by id.
153+
func (c *Client) DeleteAPIKey(ctx context.Context, userID string, id string) error {
154+
res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/users/%s/keys/%s", userID, id), nil)
141155
if err != nil {
142156
return err
143157
}

0 commit comments

Comments
 (0)