From f98325b9a41021de9a6d4d7ab3bf6b773053efb2 Mon Sep 17 00:00:00 2001 From: Garrett Date: Thu, 6 Oct 2022 21:22:11 +0000 Subject: [PATCH] fix: add back missing postAPIKey route --- coderd/apikey.go | 35 +++++++++++++++++++++++++++++++++++ coderd/apikey_test.go | 12 ++++++++++++ coderd/coderd.go | 1 + codersdk/apikey.go | 14 ++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/coderd/apikey.go b/coderd/apikey.go index ba5c7c11aa8d3..645d660adab90 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -53,6 +53,41 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value}) } +// Creates a new session key, used for logging in via the CLI. +func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + user := httpmw.UserParam(r) + + if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceAPIKey.WithOwner(user.ID.String())) { + httpapi.ResourceNotFound(rw) + return + } + + lifeTime := time.Hour * 24 * 7 + cookie, err := api.createAPIKey(ctx, createAPIKeyParams{ + UserID: user.ID, + LoginType: database.LoginTypePassword, + RemoteAddr: r.RemoteAddr, + // All api generated keys will last 1 week. Browser login tokens have + // a shorter life. + ExpiresAt: database.Now().Add(lifeTime), + LifetimeSeconds: int64(lifeTime.Seconds()), + }) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to create API key.", + Detail: err.Error(), + }) + return + } + + // We intentionally do not set the cookie on the response here. + // Setting the cookie will couple the browser sesion to the API + // key we return here, meaning logging out of the website would + // invalid your CLI key. + httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value}) +} + func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) { var ( ctx = r.Context() diff --git a/coderd/apikey_test.go b/coderd/apikey_test.go index 8fbccd783faf3..e9163e5c5917e 100644 --- a/coderd/apikey_test.go +++ b/coderd/apikey_test.go @@ -39,3 +39,15 @@ func TestTokens(t *testing.T) { require.NoError(t, err) require.Empty(t, keys) } + +func TestAPIKey(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + _ = coderdtest.CreateFirstUser(t, client) + + res, err := client.CreateAPIKey(ctx, codersdk.Me) + require.NoError(t, err) + require.Greater(t, len(res.Key), 2) +} diff --git a/coderd/coderd.go b/coderd/coderd.go index 6f568e8a1efb4..35bcfdf9e63a8 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -399,6 +399,7 @@ func New(options *Options) *API { r.Get("/roles", api.userRoles) r.Route("/keys", func(r chi.Router) { + r.Post("/", api.postAPIKey) r.Route("/tokens", func(r chi.Router) { r.Post("/", api.postToken) r.Get("/", api.tokens) diff --git a/codersdk/apikey.go b/codersdk/apikey.go index 3be9a53b5d2df..44782dde85c6a 100644 --- a/codersdk/apikey.go +++ b/codersdk/apikey.go @@ -45,6 +45,20 @@ func (c *Client) CreateToken(ctx context.Context, userID string) (*GenerateAPIKe return apiKey, json.NewDecoder(res.Body).Decode(apiKey) } +// CreateAPIKey generates an API key for the user ID provided. +func (c *Client) CreateAPIKey(ctx context.Context, user string) (*GenerateAPIKeyResponse, error) { + res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", user), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode > http.StatusCreated { + return nil, readBodyAsError(res) + } + apiKey := &GenerateAPIKeyResponse{} + return apiKey, json.NewDecoder(res.Body).Decode(apiKey) +} + // GetTokens list machine API keys. func (c *Client) GetTokens(ctx context.Context, userID string) ([]APIKey, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys/tokens", userID), nil)