mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat: add template/template version auditing (#3965)
This commit is contained in:
@ -920,7 +920,7 @@ func (q *fakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg da
|
||||
return database.Template{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) error {
|
||||
func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
@ -935,10 +935,10 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
|
||||
tpl.MaxTtl = arg.MaxTtl
|
||||
tpl.MinAutostartInterval = arg.MinAutostartInterval
|
||||
q.templates[idx] = tpl
|
||||
return nil
|
||||
return tpl, nil
|
||||
}
|
||||
|
||||
return sql.ErrNoRows
|
||||
return database.Template{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) GetTemplatesWithFilter(_ context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) {
|
||||
|
@ -134,7 +134,7 @@ type querier interface {
|
||||
UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error
|
||||
UpdateTemplateActiveVersionByID(ctx context.Context, arg UpdateTemplateActiveVersionByIDParams) error
|
||||
UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTemplateDeletedByIDParams) error
|
||||
UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error
|
||||
UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error)
|
||||
UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error
|
||||
UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error
|
||||
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
|
||||
|
@ -2361,7 +2361,7 @@ func (q *sqlQuerier) UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTe
|
||||
return err
|
||||
}
|
||||
|
||||
const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :exec
|
||||
const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :one
|
||||
UPDATE
|
||||
templates
|
||||
SET
|
||||
@ -2387,8 +2387,8 @@ type UpdateTemplateMetaByIDParams struct {
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateTemplateMetaByID,
|
||||
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateTemplateMetaByID,
|
||||
arg.ID,
|
||||
arg.UpdatedAt,
|
||||
arg.Description,
|
||||
@ -2397,7 +2397,23 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl
|
||||
arg.Name,
|
||||
arg.Icon,
|
||||
)
|
||||
return err
|
||||
var i Template
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.OrganizationID,
|
||||
&i.Deleted,
|
||||
&i.Name,
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
&i.CreatedBy,
|
||||
&i.Icon,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
|
||||
|
@ -91,7 +91,7 @@ SET
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: UpdateTemplateMetaByID :exec
|
||||
-- name: UpdateTemplateMetaByID :one
|
||||
UPDATE
|
||||
templates
|
||||
SET
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
"github.com/coder/coder/coderd/httpmw"
|
||||
@ -82,7 +83,18 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
var (
|
||||
template = httpmw.TemplateParam(r)
|
||||
aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionDelete,
|
||||
})
|
||||
)
|
||||
defer commitAudit()
|
||||
aReq.Old = template
|
||||
|
||||
if !api.Authorize(r, rbac.ActionDelete, template) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
@ -91,10 +103,7 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaces, err := api.Database.GetWorkspaces(r.Context(), database.GetWorkspacesParams{
|
||||
TemplateIds: []uuid.UUID{template.ID},
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspaces by template id.",
|
||||
Detail: err.Error(),
|
||||
@ -126,9 +135,26 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Create a new template in an organization.
|
||||
func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var createTemplate codersdk.CreateTemplateRequest
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
var (
|
||||
createTemplate codersdk.CreateTemplateRequest
|
||||
organization = httpmw.OrganizationParam(r)
|
||||
apiKey = httpmw.APIKey(r)
|
||||
templateAudit, commitTemplateAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionCreate,
|
||||
})
|
||||
templateVersionAudit, commitTemplateVersionAudit = audit.InitRequest[database.TemplateVersion](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
)
|
||||
defer commitTemplateAudit()
|
||||
defer commitTemplateVersionAudit()
|
||||
|
||||
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(organization.ID)) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
@ -175,6 +201,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
return
|
||||
}
|
||||
templateVersionAudit.Old = templateVersion
|
||||
|
||||
importJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@ -234,6 +262,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
||||
return xerrors.Errorf("insert template: %s", err)
|
||||
}
|
||||
|
||||
templateAudit.New = dbTemplate
|
||||
|
||||
err = db.UpdateTemplateVersionByID(r.Context(), database.UpdateTemplateVersionByIDParams{
|
||||
ID: templateVersion.ID,
|
||||
TemplateID: uuid.NullUUID{
|
||||
@ -245,6 +275,12 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert template version: %s", err)
|
||||
}
|
||||
newTemplateVersion := templateVersion
|
||||
newTemplateVersion.TemplateID = uuid.NullUUID{
|
||||
UUID: dbTemplate.ID,
|
||||
Valid: true,
|
||||
}
|
||||
templateVersionAudit.New = newTemplateVersion
|
||||
|
||||
for _, parameterValue := range createTemplate.ParameterValues {
|
||||
_, err = db.InsertParameterValue(r.Context(), database.InsertParameterValueParams{
|
||||
@ -397,7 +433,18 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
|
||||
}
|
||||
|
||||
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
var (
|
||||
template = httpmw.TemplateParam(r)
|
||||
aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
)
|
||||
defer commitAudit()
|
||||
aReq.Old = template
|
||||
|
||||
if !api.Authorize(r, rbac.ActionUpdate, template) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
@ -474,7 +521,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
minAutostartInterval = time.Duration(template.MinAutostartInterval)
|
||||
}
|
||||
|
||||
if err := s.UpdateTemplateMetaByID(r.Context(), database.UpdateTemplateMetaByIDParams{
|
||||
updated, err = s.UpdateTemplateMetaByID(r.Context(), database.UpdateTemplateMetaByIDParams{
|
||||
ID: template.ID,
|
||||
UpdatedAt: database.Now(),
|
||||
Name: name,
|
||||
@ -482,28 +529,24 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
Icon: icon,
|
||||
MaxTtl: int64(maxTTL),
|
||||
MinAutostartInterval: int64(minAutostartInterval),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updated, err = s.GetTemplateByID(r.Context(), template.ID)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error updating template metadata.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
httpapi.InternalServerError(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
if updated.UpdatedAt.IsZero() {
|
||||
aReq.New = template
|
||||
httpapi.Write(rw, http.StatusNotModified, nil)
|
||||
return
|
||||
}
|
||||
aReq.New = updated
|
||||
|
||||
createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{updated})
|
||||
if err != nil {
|
||||
|
@ -13,7 +13,9 @@ import (
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
|
||||
"github.com/coder/coder/agent"
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/rbac"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
"github.com/coder/coder/codersdk"
|
||||
@ -78,7 +80,8 @@ func TestPostTemplateByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
|
||||
@ -92,6 +95,11 @@ func TestPostTemplateByOrganization(t *testing.T) {
|
||||
|
||||
assert.Equal(t, expected.Name, got.Name)
|
||||
assert.Equal(t, expected.Description, got.Description)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 3)
|
||||
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs[0].Action)
|
||||
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[1].Action)
|
||||
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs[2].Action)
|
||||
})
|
||||
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
@ -291,7 +299,8 @@ func TestPatchTemplateMeta(t *testing.T) {
|
||||
t.Run("Modified", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, nil)
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
@ -332,6 +341,9 @@ func TestPatchTemplateMeta(t *testing.T) {
|
||||
assert.Equal(t, req.Icon, updated.Icon)
|
||||
assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 4)
|
||||
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[3].Action)
|
||||
})
|
||||
|
||||
t.Run("NoMaxTTL", func(t *testing.T) {
|
||||
@ -514,7 +526,8 @@ func TestDeleteTemplate(t *testing.T) {
|
||||
|
||||
t.Run("NoWorkspaces", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
@ -524,6 +537,9 @@ func TestDeleteTemplate(t *testing.T) {
|
||||
|
||||
err := client.DeleteTemplate(ctx, template.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 4)
|
||||
assert.Equal(t, database.AuditActionDelete, auditor.AuditLogs[3].Action)
|
||||
})
|
||||
|
||||
t.Run("Workspaces", func(t *testing.T) {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
"github.com/coder/coder/coderd/httpmw"
|
||||
@ -556,7 +557,18 @@ func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
var (
|
||||
template = httpmw.TemplateParam(r)
|
||||
aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionWrite,
|
||||
})
|
||||
)
|
||||
defer commitAudit()
|
||||
aReq.Old = template
|
||||
|
||||
if !api.Authorize(r, rbac.ActionUpdate, template) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
@ -581,7 +593,7 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
if version.TemplateID.UUID.String() != template.ID.String() {
|
||||
httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{
|
||||
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "The provided template version doesn't belong to the specified template.",
|
||||
})
|
||||
return
|
||||
@ -605,6 +617,10 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
return
|
||||
}
|
||||
newTemplate := template
|
||||
newTemplate.ActiveVersionID = req.ID
|
||||
aReq.New = newTemplate
|
||||
|
||||
httpapi.Write(rw, http.StatusOK, codersdk.Response{
|
||||
Message: "Updated the active template version!",
|
||||
})
|
||||
@ -612,13 +628,30 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
||||
|
||||
// Creates a new version of a template. An import job is queued to parse the storage method provided.
|
||||
func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
var req codersdk.CreateTemplateVersionRequest
|
||||
var (
|
||||
apiKey = httpmw.APIKey(r)
|
||||
organization = httpmw.OrganizationParam(r)
|
||||
aReq, commitAudit = audit.InitRequest[database.TemplateVersion](rw, &audit.RequestParams{
|
||||
Features: api.FeaturesService,
|
||||
Log: api.Logger,
|
||||
Request: r,
|
||||
Action: database.AuditActionCreate,
|
||||
})
|
||||
|
||||
req codersdk.CreateTemplateVersionRequest
|
||||
)
|
||||
defer commitAudit()
|
||||
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
// Making a new template version is the same permission as creating a new template.
|
||||
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(organization.ID)) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
||||
if req.TemplateID != uuid.Nil {
|
||||
_, err := api.Database.GetTemplateByID(r.Context(), req.TemplateID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@ -651,12 +684,6 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
// Making a new template version is the same permission as creating a new template.
|
||||
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(organization.ID)) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(r, rbac.ActionRead, file) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
@ -778,6 +805,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
||||
})
|
||||
return
|
||||
}
|
||||
aReq.New = templateVersion
|
||||
|
||||
createdByName, err := getUsernameByUserID(r.Context(), api.Database, templateVersion.CreatedBy)
|
||||
if err != nil {
|
||||
|
@ -11,7 +11,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/coder/coder/coderd/audit"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
@ -76,7 +78,8 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
|
||||
|
||||
t.Run("WithParameters", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
@ -102,6 +105,9 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 1)
|
||||
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs[0].Action)
|
||||
})
|
||||
}
|
||||
|
||||
@ -540,12 +546,13 @@ func TestPatchActiveTemplateVersion(t *testing.T) {
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
|
||||
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Found", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
auditor := audit.NewMock()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
@ -557,6 +564,9 @@ func TestPatchActiveTemplateVersion(t *testing.T) {
|
||||
ID: version.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, auditor.AuditLogs, 4)
|
||||
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[3].Action)
|
||||
})
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user