diff --git a/coderd/apikey.go b/coderd/apikey.go index 12646d627a212..91ab0ad957624 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -131,8 +131,19 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { // @Success 201 {object} codersdk.GenerateAPIKeyResponse // @Router /users/{user}/keys [post] func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { - ctx := r.Context() - user := httpmw.UserParam(r) + var ( + ctx = r.Context() + user = httpmw.UserParam(r) + auditor = api.Auditor.Load() + aReq, commitAudit = audit.InitRequest[database.APIKey](rw, &audit.RequestParams{ + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionCreate, + }) + ) + aReq.Old = database.APIKey{} + defer commitAudit() // TODO(Cian): System users technically just have the 'member' role // and we don't want to disallow all members from creating API keys. @@ -142,7 +153,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { return } - cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ + cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, DefaultLifetime: api.DeploymentValues.Sessions.DefaultTokenDuration.Value(), LoginType: database.LoginTypePassword, @@ -156,6 +167,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { return } + aReq.New = *key // We intentionally do not set the cookie on the response here. // Setting the cookie will couple the browser session to the API // key we return here, meaning logging out of the website would diff --git a/coderd/apikey_test.go b/coderd/apikey_test.go index 1509aa2e2f402..73655754a8fb3 100644 --- a/coderd/apikey_test.go +++ b/coderd/apikey_test.go @@ -2,6 +2,7 @@ package coderd_test import ( "context" + "encoding/json" "net/http" "strings" "testing" @@ -303,14 +304,32 @@ func TestSessionExpiry(t *testing.T) { func TestAPIKey_OK(t *testing.T) { t.Parallel() + + // Given: a deployment with auditing enabled ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - _ = coderdtest.CreateFirstUser(t, client) + auditor := audit.NewMock() + client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor}) + owner := coderdtest.CreateFirstUser(t, client) + auditor.ResetLogs() + // When: an API key is created res, err := client.CreateAPIKey(ctx, codersdk.Me) require.NoError(t, err) require.Greater(t, len(res.Key), 2) + + // Then: an audit log is generated + als := auditor.AuditLogs() + require.Len(t, als, 1) + al := als[0] + assert.Equal(t, owner.UserID, al.UserID) + assert.Equal(t, database.AuditActionCreate, al.Action) + assert.Equal(t, database.ResourceTypeApiKey, al.ResourceType) + + // Then: the diff MUST NOT contain the generated key. + raw, err := json.Marshal(al) + require.NoError(t, err) + require.NotContains(t, res.Key, string(raw)) } func TestAPIKey_Deleted(t *testing.T) {