snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
This commit is contained in:
151
server/channels/app/cpa_value_audit_test.go
Normal file
151
server/channels/app/cpa_value_audit_test.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/app/properties"
|
||||
)
|
||||
|
||||
// The CPA value sink writes through AddMeta rather than event parameters, so
|
||||
// auditParam does not apply here.
|
||||
func auditMeta(t *testing.T, rec map[string]any, key string) any {
|
||||
t.Helper()
|
||||
meta, ok := rec[model.AuditKeyMeta].(map[string]any)
|
||||
require.True(t, ok, "audit record has no meta")
|
||||
return meta[key]
|
||||
}
|
||||
|
||||
func auditMetaHasKey(t *testing.T, rec map[string]any, key string) bool {
|
||||
t.Helper()
|
||||
meta, ok := rec[model.AuditKeyMeta].(map[string]any)
|
||||
require.True(t, ok, "audit record has no meta")
|
||||
_, present := meta[key]
|
||||
return present
|
||||
}
|
||||
|
||||
// Pins the audit trail governance relies on to answer "who marked this channel,
|
||||
// and as what". Asserts new_value is captured verbatim: masking hides some values
|
||||
// from some administrators, so raw capture in the audit stream is a decision, and
|
||||
// narrowing it must break this test rather than pass unnoticed.
|
||||
func TestCPAValueChangeAuditForChannelValues(t *testing.T) {
|
||||
th := Setup(t).InitBasic(t)
|
||||
// LicenseCheckHook gates access_control writes on an Enterprise license.
|
||||
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise))
|
||||
capture := startPluginAuditCapture(t, th)
|
||||
|
||||
rctx := request.TestContext(t)
|
||||
cpaGroup, groupErr := th.App.GetPropertyGroup(rctx, model.AccessControlPropertyGroupName)
|
||||
require.Nil(t, groupErr)
|
||||
|
||||
newChannelField := func(t *testing.T) *model.PropertyField {
|
||||
t.Helper()
|
||||
field, fieldErr := th.App.CreatePropertyField(rctx, &model.PropertyField{
|
||||
GroupID: cpaGroup.ID,
|
||||
Name: "chan_attr_" + model.NewId(),
|
||||
Type: model.PropertyFieldTypeText,
|
||||
ObjectType: model.PropertyFieldObjectTypeChannel,
|
||||
TargetType: string(model.PropertyFieldTargetLevelSystem),
|
||||
}, false, "")
|
||||
require.Nil(t, fieldErr)
|
||||
return field
|
||||
}
|
||||
|
||||
writeValue := func(t *testing.T, field *model.PropertyField, raw string) *model.PropertyValue {
|
||||
t.Helper()
|
||||
values, upsertErr := th.App.UpsertPropertyValues(rctx, []*model.PropertyValue{{
|
||||
TargetID: th.BasicChannel.Id,
|
||||
TargetType: model.PropertyFieldObjectTypeChannel,
|
||||
GroupID: cpaGroup.ID,
|
||||
FieldID: field.ID,
|
||||
Value: json.RawMessage(raw),
|
||||
}}, model.PropertyFieldObjectTypeChannel, th.BasicChannel.Id, "")
|
||||
require.Nil(t, upsertErr)
|
||||
require.Len(t, values, 1)
|
||||
return values[0]
|
||||
}
|
||||
|
||||
nextRecord := func(t *testing.T, fn func()) map[string]any {
|
||||
t.Helper()
|
||||
before := len(capture.recordsFor(model.AuditEventCPAValueChange))
|
||||
fn()
|
||||
records := capture.recordsFor(model.AuditEventCPAValueChange)
|
||||
require.Len(t, records, before+1, "expected exactly one new %s record", model.AuditEventCPAValueChange)
|
||||
return records[len(records)-1]
|
||||
}
|
||||
|
||||
t.Run("upsert records the channel target, the field, and the new value verbatim", func(t *testing.T) {
|
||||
field := newChannelField(t)
|
||||
|
||||
rec := nextRecord(t, func() { writeValue(t, field, `"AURORA"`) })
|
||||
|
||||
require.Equal(t, model.AuditStatusSuccess, rec[model.AuditKeyStatus])
|
||||
require.Equal(t, model.AccessControlPropertyGroupName, auditMeta(t, rec, "group"))
|
||||
require.Equal(t, properties.ValueAuditActionUpsert, auditMeta(t, rec, "action"))
|
||||
require.Equal(t, model.PropertyFieldObjectTypeChannel, auditMeta(t, rec, "target_type"))
|
||||
require.Equal(t, th.BasicChannel.Id, auditMeta(t, rec, "target_id"))
|
||||
require.Equal(t, field.ID, auditMeta(t, rec, "field_id"))
|
||||
require.Equal(t, `"AURORA"`, auditMeta(t, rec, "new_value"))
|
||||
})
|
||||
|
||||
t.Run("editing a value records the new value but not the previous one", func(t *testing.T) {
|
||||
field := newChannelField(t)
|
||||
writeValue(t, field, `"AURORA"`)
|
||||
|
||||
rec := nextRecord(t, func() { writeValue(t, field, `"NOFORN"`) })
|
||||
|
||||
require.Equal(t, properties.ValueAuditActionUpsert, auditMeta(t, rec, "action"))
|
||||
require.Equal(t, `"NOFORN"`, auditMeta(t, rec, "new_value"))
|
||||
|
||||
// The sink supports prior_value but the upsert path never sets
|
||||
// ValueAuditEvent.Prev, so an edit has no before/after pair. Asserted so
|
||||
// the gap is visible rather than assumed absent.
|
||||
require.False(t, auditMetaHasKey(t, rec, "prior_value"))
|
||||
})
|
||||
|
||||
t.Run("deleting a single value records the prior value and the value id", func(t *testing.T) {
|
||||
field := newChannelField(t)
|
||||
value := writeValue(t, field, `"ELEVATED"`)
|
||||
|
||||
rec := nextRecord(t, func() {
|
||||
require.Nil(t, th.App.DeletePropertyValue(rctx, cpaGroup.ID, value.ID))
|
||||
})
|
||||
|
||||
require.Equal(t, properties.ValueAuditActionDelete, auditMeta(t, rec, "action"))
|
||||
require.Equal(t, th.BasicChannel.Id, auditMeta(t, rec, "target_id"))
|
||||
require.Equal(t, field.ID, auditMeta(t, rec, "field_id"))
|
||||
require.Equal(t, value.ID, auditMeta(t, rec, "value_id"))
|
||||
require.Equal(t, `"ELEVATED"`, auditMeta(t, rec, "prior_value"))
|
||||
})
|
||||
|
||||
t.Run("bulk deletes record the scope but carry no value payload", func(t *testing.T) {
|
||||
targetField := newChannelField(t)
|
||||
writeValue(t, targetField, `"UNCLASSIFIED"`)
|
||||
|
||||
rec := nextRecord(t, func() {
|
||||
require.Nil(t, th.App.DeletePropertyValuesForTarget(rctx, cpaGroup.ID, model.PropertyFieldObjectTypeChannel, th.BasicChannel.Id))
|
||||
})
|
||||
require.Equal(t, properties.ValueAuditActionDeleteForTarget, auditMeta(t, rec, "action"))
|
||||
require.Equal(t, th.BasicChannel.Id, auditMeta(t, rec, "target_id"))
|
||||
require.False(t, auditMetaHasKey(t, rec, "prior_value"))
|
||||
require.False(t, auditMetaHasKey(t, rec, "new_value"))
|
||||
|
||||
fieldScoped := newChannelField(t)
|
||||
writeValue(t, fieldScoped, `"SECRET"`)
|
||||
|
||||
rec = nextRecord(t, func() {
|
||||
require.Nil(t, th.App.DeletePropertyValuesForField(rctx, cpaGroup.ID, fieldScoped.ID))
|
||||
})
|
||||
require.Equal(t, properties.ValueAuditActionDeleteForField, auditMeta(t, rec, "action"))
|
||||
require.Equal(t, fieldScoped.ID, auditMeta(t, rec, "field_id"))
|
||||
require.False(t, auditMetaHasKey(t, rec, "prior_value"))
|
||||
require.False(t, auditMetaHasKey(t, rec, "new_value"))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user