Skip to content

Commit 32bb1e7

Browse files
authored
fix: add back missing postAPIKey route (#4406)
1 parent a89d690 commit 32bb1e7

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

coderd/apikey.go

+35
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,41 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
5353
httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value})
5454
}
5555

56+
// Creates a new session key, used for logging in via the CLI.
57+
func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
58+
ctx := r.Context()
59+
user := httpmw.UserParam(r)
60+
61+
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceAPIKey.WithOwner(user.ID.String())) {
62+
httpapi.ResourceNotFound(rw)
63+
return
64+
}
65+
66+
lifeTime := time.Hour * 24 * 7
67+
cookie, err := api.createAPIKey(ctx, createAPIKeyParams{
68+
UserID: user.ID,
69+
LoginType: database.LoginTypePassword,
70+
RemoteAddr: r.RemoteAddr,
71+
// All api generated keys will last 1 week. Browser login tokens have
72+
// a shorter life.
73+
ExpiresAt: database.Now().Add(lifeTime),
74+
LifetimeSeconds: int64(lifeTime.Seconds()),
75+
})
76+
if err != nil {
77+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
78+
Message: "Failed to create API key.",
79+
Detail: err.Error(),
80+
})
81+
return
82+
}
83+
84+
// We intentionally do not set the cookie on the response here.
85+
// Setting the cookie will couple the browser sesion to the API
86+
// key we return here, meaning logging out of the website would
87+
// invalid your CLI key.
88+
httpapi.Write(ctx, rw, http.StatusCreated, codersdk.GenerateAPIKeyResponse{Key: cookie.Value})
89+
}
90+
5691
func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) {
5792
var (
5893
ctx = r.Context()

coderd/apikey_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,15 @@ func TestTokens(t *testing.T) {
3939
require.NoError(t, err)
4040
require.Empty(t, keys)
4141
}
42+
43+
func TestAPIKey(t *testing.T) {
44+
t.Parallel()
45+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
46+
defer cancel()
47+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
48+
_ = coderdtest.CreateFirstUser(t, client)
49+
50+
res, err := client.CreateAPIKey(ctx, codersdk.Me)
51+
require.NoError(t, err)
52+
require.Greater(t, len(res.Key), 2)
53+
}

coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ func New(options *Options) *API {
399399
r.Get("/roles", api.userRoles)
400400

401401
r.Route("/keys", func(r chi.Router) {
402+
r.Post("/", api.postAPIKey)
402403
r.Route("/tokens", func(r chi.Router) {
403404
r.Post("/", api.postToken)
404405
r.Get("/", api.tokens)

codersdk/apikey.go

+14
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ func (c *Client) CreateToken(ctx context.Context, userID string) (*GenerateAPIKe
4545
return apiKey, json.NewDecoder(res.Body).Decode(apiKey)
4646
}
4747

48+
// CreateAPIKey generates an API key for the user ID provided.
49+
func (c *Client) CreateAPIKey(ctx context.Context, user string) (*GenerateAPIKeyResponse, error) {
50+
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/keys", user), nil)
51+
if err != nil {
52+
return nil, err
53+
}
54+
defer res.Body.Close()
55+
if res.StatusCode > http.StatusCreated {
56+
return nil, readBodyAsError(res)
57+
}
58+
apiKey := &GenerateAPIKeyResponse{}
59+
return apiKey, json.NewDecoder(res.Body).Decode(apiKey)
60+
}
61+
4862
// GetTokens list machine API keys.
4963
func (c *Client) GetTokens(ctx context.Context, userID string) ([]APIKey, error) {
5064
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys/tokens", userID), nil)

0 commit comments

Comments
 (0)