mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
feat: audit git ssh key regeneration (#4544)
This commit is contained in:
@ -221,10 +221,18 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
|
||||
}
|
||||
|
||||
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||
return fmt.Sprintf("{user} %s %s {target}",
|
||||
str := fmt.Sprintf("{user} %s %s",
|
||||
codersdk.AuditAction(alog.Action).FriendlyString(),
|
||||
codersdk.ResourceType(alog.ResourceType).FriendlyString(),
|
||||
)
|
||||
|
||||
// We don't display the name for git ssh keys. It's fairly long and doesn't
|
||||
// make too much sense to display.
|
||||
if alog.ResourceType != database.ResourceTypeGitSshKey {
|
||||
str += " {target}"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// auditSearchQuery takes a query string and returns the auditLog filter.
|
||||
|
@ -2676,7 +2676,7 @@ func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (databas
|
||||
return database.GitSSHKey{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) error {
|
||||
func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) (database.GitSSHKey, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
@ -2688,9 +2688,9 @@ func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitS
|
||||
key.PrivateKey = arg.PrivateKey
|
||||
key.PublicKey = arg.PublicKey
|
||||
q.gitSSHKey[index] = key
|
||||
return nil
|
||||
return key, nil
|
||||
}
|
||||
return sql.ErrNoRows
|
||||
return database.GitSSHKey{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) InsertGroupMember(_ context.Context, arg database.InsertGroupMemberParams) error {
|
||||
|
@ -148,7 +148,7 @@ type sqlcQuerier interface {
|
||||
ParameterValue(ctx context.Context, id uuid.UUID) (ParameterValue, error)
|
||||
ParameterValues(ctx context.Context, arg ParameterValuesParams) ([]ParameterValue, error)
|
||||
UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error
|
||||
UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error
|
||||
UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error)
|
||||
UpdateGroupByID(ctx context.Context, arg UpdateGroupByIDParams) (Group, error)
|
||||
UpdateMemberRoles(ctx context.Context, arg UpdateMemberRolesParams) (OrganizationMember, error)
|
||||
UpdateProvisionerDaemonByID(ctx context.Context, arg UpdateProvisionerDaemonByIDParams) error
|
||||
|
@ -815,7 +815,7 @@ func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyPar
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateGitSSHKey = `-- name: UpdateGitSSHKey :exec
|
||||
const updateGitSSHKey = `-- name: UpdateGitSSHKey :one
|
||||
UPDATE
|
||||
gitsshkeys
|
||||
SET
|
||||
@ -824,6 +824,8 @@ SET
|
||||
public_key = $4
|
||||
WHERE
|
||||
user_id = $1
|
||||
RETURNING
|
||||
user_id, created_at, updated_at, private_key, public_key
|
||||
`
|
||||
|
||||
type UpdateGitSSHKeyParams struct {
|
||||
@ -833,14 +835,22 @@ type UpdateGitSSHKeyParams struct {
|
||||
PublicKey string `db:"public_key" json:"public_key"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateGitSSHKey,
|
||||
func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateGitSSHKey,
|
||||
arg.UserID,
|
||||
arg.UpdatedAt,
|
||||
arg.PrivateKey,
|
||||
arg.PublicKey,
|
||||
)
|
||||
return err
|
||||
var i GitSSHKey
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.PrivateKey,
|
||||
&i.PublicKey,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteGroupByID = `-- name: DeleteGroupByID :exec
|
||||
|
@ -18,7 +18,7 @@ FROM
|
||||
WHERE
|
||||
user_id = $1;
|
||||
|
||||
-- name: UpdateGitSSHKey :exec
|
||||
-- name: UpdateGitSSHKey :one
|
||||
UPDATE
|
||||
gitsshkeys
|
||||
SET
|
||||
@ -26,7 +26,9 @@ SET
|
||||
private_key = $3,
|
||||
public_key = $4
|
||||
WHERE
|
||||
user_id = $1;
|
||||
user_id = $1
|
||||
RETURNING
|
||||
*;
|
||||
|
||||
-- name: DeleteGitSSHKey :exec
|
||||
DELETE FROM
|
||||
|
@ -3,6 +3,7 @@ package coderd
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/gitsshkey"
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
@ -12,14 +13,32 @@ import (
|
||||
)
|
||||
|
||||
func (api *API) regenerateGitSSHKey(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.GitSSHKey](rw, &audit.RequestParams{
|
||||
Audit: *auditor,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
)
|
||||
defer commitAudit()
|
||||
|
||||
if !api.Authorize(r, rbac.ActionUpdate, rbac.ResourceUserData.WithOwner(user.ID.String())) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
||||
oldKey, err := api.Database.GetGitSSHKey(ctx, user.ID)
|
||||
if err != nil {
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
aReq.Old = oldKey
|
||||
|
||||
privateKey, publicKey, err := gitsshkey.Generate(api.SSHKeygenAlgorithm)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@ -29,7 +48,7 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = api.Database.UpdateGitSSHKey(ctx, database.UpdateGitSSHKeyParams{
|
||||
newKey, err := api.Database.UpdateGitSSHKey(ctx, database.UpdateGitSSHKeyParams{
|
||||
UserID: user.ID,
|
||||
UpdatedAt: database.Now(),
|
||||
PrivateKey: privateKey,
|
||||
@ -43,14 +62,7 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
newKey, err := api.Database.GetGitSSHKey(ctx, user.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching user's git SSH key.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
aReq.New = newKey
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.GitSSHKey{
|
||||
UserID: newKey.UserID,
|
||||
|
@ -5,9 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/gitsshkey"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
@ -73,8 +76,10 @@ func TestGitSSHKey(t *testing.T) {
|
||||
})
|
||||
t.Run("Regenerate", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
|
||||
Auditor: auditor,
|
||||
})
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
@ -89,6 +94,9 @@ func TestGitSSHKey(t *testing.T) {
|
||||
require.GreaterOrEqual(t, key2.UpdatedAt, key1.UpdatedAt)
|
||||
require.NotEmpty(t, key2.PublicKey)
|
||||
require.NotEqual(t, key2.PublicKey, key1.PublicKey)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 1)
|
||||
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[0].Action)
|
||||
})
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user