From b830c05e3e10c82a1e90c59d340690d911613855 Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Wed, 27 Nov 2024 20:01:08 +0100 Subject: [PATCH] chore: track usage of built-in example templates (#15671) Addresses https://github.com/coder/nexus/issues/99. Changes: - Save the id of the built-in example template used to create a template version in the database - Include the example id in telemetry --- coderd/database/dbgen/dbgen.go | 21 ++++---- coderd/database/dbmem/dbmem.go | 21 ++++---- coderd/database/dump.sql | 4 +- ...0277_template_version_example_ids.down.sql | 28 +++++++++++ ...000277_template_version_example_ids.up.sql | 30 +++++++++++ coderd/database/models.go | 6 ++- coderd/database/queries.sql.go | 50 +++++++++++-------- coderd/database/queries/templateversions.sql | 5 +- coderd/telemetry/telemetry.go | 14 ++++-- coderd/telemetry/telemetry_test.go | 18 ++++++- coderd/templateversions.go | 4 ++ coderd/templateversions_test.go | 17 ++++++- docs/admin/security/audit-logs.md | 2 +- enterprise/audit/table.go | 1 + 14 files changed, 167 insertions(+), 54 deletions(-) create mode 100644 coderd/database/migrations/000277_template_version_example_ids.down.sql create mode 100644 coderd/database/migrations/000277_template_version_example_ids.up.sql diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index ae898d4f1f..9c8696112d 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -788,16 +788,17 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers err := db.InTx(func(db database.Store) error { versionID := takeFirst(orig.ID, uuid.New()) err := db.InsertTemplateVersion(genCtx, database.InsertTemplateVersionParams{ - ID: versionID, - TemplateID: takeFirst(orig.TemplateID, uuid.NullUUID{}), - OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), - CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), - UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), - Name: takeFirst(orig.Name, testutil.GetRandomName(t)), - Message: orig.Message, - Readme: takeFirst(orig.Readme, testutil.GetRandomName(t)), - JobID: takeFirst(orig.JobID, uuid.New()), - CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), + ID: versionID, + TemplateID: takeFirst(orig.TemplateID, uuid.NullUUID{}), + OrganizationID: takeFirst(orig.OrganizationID, uuid.New()), + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), + UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), + Name: takeFirst(orig.Name, testutil.GetRandomName(t)), + Message: orig.Message, + Readme: takeFirst(orig.Readme, testutil.GetRandomName(t)), + JobID: takeFirst(orig.JobID, uuid.New()), + CreatedBy: takeFirst(orig.CreatedBy, uuid.New()), + SourceExampleID: takeFirst(orig.SourceExampleID, sql.NullString{}), }) if err != nil { return err diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 5583fff111..765573b311 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -7699,16 +7699,17 @@ func (q *FakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse //nolint:gosimple version := database.TemplateVersionTable{ - ID: arg.ID, - TemplateID: arg.TemplateID, - OrganizationID: arg.OrganizationID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - Name: arg.Name, - Message: arg.Message, - Readme: arg.Readme, - JobID: arg.JobID, - CreatedBy: arg.CreatedBy, + ID: arg.ID, + TemplateID: arg.TemplateID, + OrganizationID: arg.OrganizationID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + Name: arg.Name, + Message: arg.Message, + Readme: arg.Readme, + JobID: arg.JobID, + CreatedBy: arg.CreatedBy, + SourceExampleID: arg.SourceExampleID, } q.templateVersions = append(q.templateVersions, version) return nil diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 9919011579..eba9b7cf10 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1217,7 +1217,8 @@ CREATE TABLE template_versions ( created_by uuid NOT NULL, external_auth_providers jsonb DEFAULT '[]'::jsonb NOT NULL, message character varying(1048576) DEFAULT ''::character varying NOT NULL, - archived boolean DEFAULT false NOT NULL + archived boolean DEFAULT false NOT NULL, + source_example_id text ); COMMENT ON COLUMN template_versions.external_auth_providers IS 'IDs of External auth providers for a specific template version'; @@ -1245,6 +1246,7 @@ CREATE VIEW template_version_with_user AS template_versions.external_auth_providers, template_versions.message, template_versions.archived, + template_versions.source_example_id, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, COALESCE(visible_users.username, ''::text) AS created_by_username FROM (template_versions diff --git a/coderd/database/migrations/000277_template_version_example_ids.down.sql b/coderd/database/migrations/000277_template_version_example_ids.down.sql new file mode 100644 index 0000000000..ad961e9f63 --- /dev/null +++ b/coderd/database/migrations/000277_template_version_example_ids.down.sql @@ -0,0 +1,28 @@ +-- We cannot alter the column type while a view depends on it, so we drop it and recreate it. +DROP VIEW template_version_with_user; + +ALTER TABLE + template_versions +DROP COLUMN source_example_id; + +-- Recreate `template_version_with_user` as described in dump.sql +CREATE VIEW template_version_with_user AS +SELECT + template_versions.id, + template_versions.template_id, + template_versions.organization_id, + template_versions.created_at, + template_versions.updated_at, + template_versions.name, + template_versions.readme, + template_versions.job_id, + template_versions.created_by, + template_versions.external_auth_providers, + template_versions.message, + template_versions.archived, + COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, + COALESCE(visible_users.username, ''::text) AS created_by_username +FROM (template_versions + LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id)); + +COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.'; diff --git a/coderd/database/migrations/000277_template_version_example_ids.up.sql b/coderd/database/migrations/000277_template_version_example_ids.up.sql new file mode 100644 index 0000000000..aca34b31de --- /dev/null +++ b/coderd/database/migrations/000277_template_version_example_ids.up.sql @@ -0,0 +1,30 @@ +-- We cannot alter the column type while a view depends on it, so we drop it and recreate it. +DROP VIEW template_version_with_user; + +ALTER TABLE + template_versions +ADD + COLUMN source_example_id TEXT; + +-- Recreate `template_version_with_user` as described in dump.sql +CREATE VIEW template_version_with_user AS +SELECT + template_versions.id, + template_versions.template_id, + template_versions.organization_id, + template_versions.created_at, + template_versions.updated_at, + template_versions.name, + template_versions.readme, + template_versions.job_id, + template_versions.created_by, + template_versions.external_auth_providers, + template_versions.message, + template_versions.archived, + template_versions.source_example_id, + COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, + COALESCE(visible_users.username, ''::text) AS created_by_username +FROM (template_versions + LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id)); + +COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.'; diff --git a/coderd/database/models.go b/coderd/database/models.go index af0a3122f7..6b99245079 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -2773,6 +2773,7 @@ type TemplateVersion struct { ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"` Message string `db:"message" json:"message"` Archived bool `db:"archived" json:"archived"` + SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"` CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` CreatedByUsername string `db:"created_by_username" json:"created_by_username"` } @@ -2826,8 +2827,9 @@ type TemplateVersionTable struct { // IDs of External auth providers for a specific template version ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"` // Message describing the changes in this version of the template, similar to a Git commit message. Like a commit message, this should be a short, high-level description of the changes in this version of the template. This message is immutable and should not be updated after the fact. - Message string `db:"message" json:"message"` - Archived bool `db:"archived" json:"archived"` + Message string `db:"message" json:"message"` + Archived bool `db:"archived" json:"archived"` + SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"` } type TemplateVersionVariable struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 4eec78cf97..33a3ce12a4 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -8996,7 +8996,7 @@ FROM -- Scope an archive to a single template and ignore already archived template versions ( SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id FROM template_versions WHERE @@ -9097,7 +9097,7 @@ func (q *sqlQuerier) ArchiveUnusedTemplateVersions(ctx context.Context, arg Arch const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9134,6 +9134,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ) @@ -9142,7 +9143,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9165,6 +9166,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) ( &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ) @@ -9173,7 +9175,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) ( const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9196,6 +9198,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ) @@ -9204,7 +9207,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9233,6 +9236,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ) @@ -9241,7 +9245,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9270,6 +9274,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ); err != nil { @@ -9288,7 +9293,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE @@ -9364,6 +9369,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ); err != nil { @@ -9381,7 +9387,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge } const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many -SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE created_at > $1 +SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id, created_by_avatar_url, created_by_username FROM template_version_with_user AS template_versions WHERE created_at > $1 ` func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) { @@ -9406,6 +9412,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create &i.ExternalAuthProviders, &i.Message, &i.Archived, + &i.SourceExampleID, &i.CreatedByAvatarURL, &i.CreatedByUsername, ); err != nil { @@ -9434,23 +9441,25 @@ INSERT INTO message, readme, job_id, - created_by + created_by, + source_example_id ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ` type InsertTemplateVersionParams struct { - ID uuid.UUID `db:"id" json:"id"` - TemplateID uuid.NullUUID `db:"template_id" json:"template_id"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - Message string `db:"message" json:"message"` - Readme string `db:"readme" json:"readme"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + ID uuid.UUID `db:"id" json:"id"` + TemplateID uuid.NullUUID `db:"template_id" json:"template_id"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + Message string `db:"message" json:"message"` + Readme string `db:"readme" json:"readme"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + SourceExampleID sql.NullString `db:"source_example_id" json:"source_example_id"` } func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTemplateVersionParams) error { @@ -9465,6 +9474,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla arg.Readme, arg.JobID, arg.CreatedBy, + arg.SourceExampleID, ) return err } diff --git a/coderd/database/queries/templateversions.sql b/coderd/database/queries/templateversions.sql index 094c1b6014..0436a7f9ba 100644 --- a/coderd/database/queries/templateversions.sql +++ b/coderd/database/queries/templateversions.sql @@ -87,10 +87,11 @@ INSERT INTO message, readme, job_id, - created_by + created_by, + source_example_id ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10); + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11); -- name: UpdateTemplateVersionByID :exec UPDATE diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 8ad85b0b39..233450c43d 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -868,6 +868,9 @@ func ConvertTemplateVersion(version database.TemplateVersion) TemplateVersion { if version.TemplateID.Valid { snapVersion.TemplateID = &version.TemplateID.UUID } + if version.SourceExampleID.Valid { + snapVersion.SourceExampleID = &version.SourceExampleID.String + } return snapVersion } @@ -1116,11 +1119,12 @@ type Template struct { } type TemplateVersion struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - TemplateID *uuid.UUID `json:"template_id,omitempty"` - OrganizationID uuid.UUID `json:"organization_id"` - JobID uuid.UUID `json:"job_id"` + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + TemplateID *uuid.UUID `json:"template_id,omitempty"` + OrganizationID uuid.UUID `json:"organization_id"` + JobID uuid.UUID `json:"job_id"` + SourceExampleID *string `json:"source_example_id,omitempty"` } type ProvisionerJob struct { diff --git a/coderd/telemetry/telemetry_test.go b/coderd/telemetry/telemetry_test.go index 214d111a17..2b70cd2a6d 100644 --- a/coderd/telemetry/telemetry_test.go +++ b/coderd/telemetry/telemetry_test.go @@ -1,6 +1,7 @@ package telemetry_test import ( + "database/sql" "encoding/json" "net/http" "net/http/httptest" @@ -48,6 +49,10 @@ func TestTelemetry(t *testing.T) { _ = dbgen.Template(t, db, database.Template{ Provisioner: database.ProvisionerTypeTerraform, }) + sourceExampleID := uuid.NewString() + _ = dbgen.TemplateVersion(t, db, database.TemplateVersion{ + SourceExampleID: sql.NullString{String: sourceExampleID, Valid: true}, + }) _ = dbgen.TemplateVersion(t, db, database.TemplateVersion{}) user := dbgen.User(t, db, database.User{}) _ = dbgen.Workspace(t, db, database.WorkspaceTable{}) @@ -93,7 +98,7 @@ func TestTelemetry(t *testing.T) { require.Len(t, snapshot.ProvisionerJobs, 1) require.Len(t, snapshot.Licenses, 1) require.Len(t, snapshot.Templates, 1) - require.Len(t, snapshot.TemplateVersions, 1) + require.Len(t, snapshot.TemplateVersions, 2) require.Len(t, snapshot.Users, 1) require.Len(t, snapshot.Groups, 2) // 1 member in the everyone group + 1 member in the custom group @@ -111,6 +116,17 @@ func TestTelemetry(t *testing.T) { require.Len(t, wsa.Subsystems, 2) require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystems[0]) require.Equal(t, string(database.WorkspaceAgentSubsystemExectrace), wsa.Subsystems[1]) + + tvs := snapshot.TemplateVersions + sort.Slice(tvs, func(i, j int) bool { + // Sort by SourceExampleID presence (non-nil comes before nil) + if (tvs[i].SourceExampleID != nil) != (tvs[j].SourceExampleID != nil) { + return tvs[i].SourceExampleID != nil + } + return false + }) + require.Equal(t, tvs[0].SourceExampleID, &sourceExampleID) + require.Nil(t, tvs[1].SourceExampleID) }) t.Run("HashedEmail", func(t *testing.T) { t.Parallel() diff --git a/coderd/templateversions.go b/coderd/templateversions.go index a0609c42c3..12def3e5d6 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1582,6 +1582,10 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht Readme: "", JobID: provisionerJob.ID, CreatedBy: apiKey.UserID, + SourceExampleID: sql.NullString{ + String: req.ExampleID, + Valid: req.ExampleID != "", + }, }) if err != nil { if database.IsUniqueViolation(err, database.UniqueTemplateVersionsTemplateIDNameKey) { diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 5ebbd0f418..5e96de10d5 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/rbac" @@ -134,7 +135,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { t.Run("WithParameters", func(t *testing.T) { t.Parallel() auditor := audit.NewMock() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor}) + client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor}) user := coderdtest.CreateFirstUser(t, client) data, err := echo.Tar(&echo.Responses{ Parse: echo.ParseComplete, @@ -160,11 +161,17 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { require.Len(t, auditor.AuditLogs(), 2) assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[1].Action) + + admin, err := client.User(ctx, user.UserID.String()) + require.NoError(t, err) + tvDB, err := db.GetTemplateVersionByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(admin, user.OrganizationID)), version.ID) + require.NoError(t, err) + require.False(t, tvDB.SourceExampleID.Valid) }) t.Run("Example", func(t *testing.T) { t.Parallel() - client := coderdtest.New(t, nil) + client, db := coderdtest.NewWithDatabase(t, nil) user := coderdtest.CreateFirstUser(t, client) ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) @@ -205,6 +212,12 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { require.NoError(t, err) require.Equal(t, "my-example", tv.Name) + admin, err := client.User(ctx, user.UserID.String()) + require.NoError(t, err) + tvDB, err := db.GetTemplateVersionByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(admin, user.OrganizationID)), tv.ID) + require.NoError(t, err) + require.Equal(t, ls[0].ID, tvDB.SourceExampleID.String) + // ensure the template tar was uploaded correctly fl, ct, err := client.Download(ctx, tv.Job.FileID) require.NoError(t, err) diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md index 3ea4e145d1..db214b0e14 100644 --- a/docs/admin/security/audit-logs.md +++ b/docs/admin/security/audit-logs.md @@ -24,7 +24,7 @@ We track the following resources: | OAuth2ProviderAppSecret
|
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| | Organization
|
FieldTracked
created_atfalse
descriptiontrue
display_nametrue
icontrue
idfalse
is_defaulttrue
nametrue
updated_attrue
| | Template
write, delete |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| TemplateVersion
create, write |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| | User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
theme_preferencefalse
updated_atfalse
usernametrue
| | WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| | WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index f9e74959f2..24f7dfa4b4 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -127,6 +127,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "created_by_avatar_url": ActionIgnore, "created_by_username": ActionIgnore, "archived": ActionTrack, + "source_example_id": ActionIgnore, // Never changes. }, &database.User{}: { "id": ActionTrack,