Files
upstream-mirror 240db4e2a7
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 Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

130 lines
4.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Client4} from '@mattermost/client';
const PROPERTY_GROUP = 'access_control';
const TEMPLATE_OBJECT_TYPE = 'template';
const CHANNEL_OBJECT_TYPE = 'channel';
const TARGET_TYPE = 'system';
const CLASSIFICATION_FIELD_NAME = 'classification';
const CHANNEL_LINKED_FIELD_NAME = 'classification';
export const TEST_LEVELS = [
{name: 'UNCLASSIFIED', color: '#007A33', rank: 1},
{name: 'CONFIDENTIAL', color: '#0033A0', rank: 2},
{name: 'SECRET', color: '#C8102E', rank: 3},
{name: 'TOP SECRET', color: '#FF8C00', rank: 4},
];
/**
* Sets the ClassificationMarkings feature flag via server config.
*/
export async function setClassificationMarkingsFeatureFlag(adminClient: Client4, enabled: boolean) {
const config = await adminClient.getConfig();
await adminClient.updateConfig({
...config,
FeatureFlags: {
...config.FeatureFlags,
ClassificationMarkings: enabled,
},
} as Awaited<ReturnType<Client4['getConfig']>>);
}
/**
* Deletes existing classification fields (channel linked, system linked, and template)
* to provide a clean slate.
*/
export async function deleteClassificationFieldsIfExist(adminClient: Client4) {
// Delete channel linked fields first
try {
const channelFields = await adminClient.getPropertyFields(PROPERTY_GROUP, CHANNEL_OBJECT_TYPE, TARGET_TYPE, '');
for (const f of channelFields.filter((f) => f.name === CHANNEL_LINKED_FIELD_NAME && f.delete_at === 0)) {
await adminClient.deletePropertyField(PROPERTY_GROUP, CHANNEL_OBJECT_TYPE, f.id);
}
} catch {
// May not exist
}
// Delete system linked fields
for (const objectType of ['system', 'user'] as const) {
try {
const linkedFields = await adminClient.getPropertyFields(PROPERTY_GROUP, objectType, TARGET_TYPE, '');
for (const f of linkedFields.filter(
(f) => f.name === 'classification' && f.delete_at === 0 && f.linked_field_id,
)) {
await adminClient.deletePropertyField(PROPERTY_GROUP, objectType, f.id);
}
} catch {
// May not exist
}
}
// Delete template fields
try {
const fields = await adminClient.getPropertyFields(PROPERTY_GROUP, TEMPLATE_OBJECT_TYPE, TARGET_TYPE);
for (const f of fields.filter((f) => f.name === CLASSIFICATION_FIELD_NAME && f.delete_at === 0)) {
await adminClient.deletePropertyField(PROPERTY_GROUP, TEMPLATE_OBJECT_TYPE, f.id);
}
} catch {
// May not exist
}
}
export type ClassificationLevel = {
id: string;
name: string;
color: string;
rank: number;
};
export type SetupResult = {
templateFieldId: string;
channelFieldId: string;
levels: ClassificationLevel[];
};
/**
* Creates the full classification setup: template field + channel linked field.
* Returns the created fields and the resolved levels (with server-assigned IDs).
*/
export async function setupClassificationWithChannelField(
adminClient: Client4,
levels: Array<{name: string; color: string; rank: number}> = TEST_LEVELS,
): Promise<SetupResult> {
await deleteClassificationFieldsIfExist(adminClient);
// Create template field
const templateField = await adminClient.createPropertyField(PROPERTY_GROUP, TEMPLATE_OBJECT_TYPE, {
name: CLASSIFICATION_FIELD_NAME,
type: 'select',
target_type: TARGET_TYPE,
target_id: '',
attrs: {
options: levels.map((l) => ({id: '', name: l.name, color: l.color, rank: l.rank})),
},
permission_field: 'admin',
permission_values: 'admin',
permission_options: 'admin',
} as Parameters<Client4['createPropertyField']>[2]);
// Create channel linked field. permission_values must be set (matching how
// the admin console creates it) or the server refuses to let anyone,
// including the creator, set a value on it.
const channelField = await adminClient.createPropertyField(PROPERTY_GROUP, CHANNEL_OBJECT_TYPE, {
name: CHANNEL_LINKED_FIELD_NAME,
type: 'select',
target_type: TARGET_TYPE,
target_id: '',
linked_field_id: templateField.id,
permission_field: 'admin',
permission_values: 'admin',
} as Parameters<Client4['createPropertyField']>[2]);
// Resolve levels with server-assigned IDs
const options = (templateField.attrs?.options ?? []) as ClassificationLevel[];
const resolvedLevels = options.sort((a, b) => a.rank - b.rank);
return {templateFieldId: templateField.id, channelFieldId: channelField.id, levels: resolvedLevels};
}