From b69412d23f81d4023e3391e087a39a7061c8836d Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Thu, 19 Jun 2025 20:18:46 +0200 Subject: [PATCH] [MM-64347] Enable System Console UI for AuditSettings by default (#31118) Co-authored-by: Doug Lauder --- server/i18n/en.json | 24 ++ server/public/model/config.go | 45 +++ server/public/model/config_test.go | 111 ++++++++ server/public/model/feature_flags.go | 2 +- .../admin_console/admin_definition.tsx | 263 ++++++++++-------- .../__snapshots__/admin_sidebar.test.tsx.snap | 44 +-- webapp/channels/src/i18n/en.json | 8 +- 7 files changed, 350 insertions(+), 147 deletions(-) diff --git a/server/i18n/en.json b/server/i18n/en.json index 789ae0a4b8..ba8723e139 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -9296,6 +9296,30 @@ "id": "model.config.is_valid.encrypt_sql.app_error", "translation": "Invalid at rest encrypt key for SQL settings. Must be 32 chars or more." }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_max_age_invalid", + "translation": "Max File Age of audit logs config must not be negative." + }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_max_backups_invalid", + "translation": "Maximum File Backups of audit logs config must not be negative." + }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_max_queue_size_invalid", + "translation": "Maximum File Queue of audit logs config must be greater than zero." + }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_max_size_invalid", + "translation": "Maximum File Size of audit logs config must be greater than zero." + }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_name_empty", + "translation": "When audit file logging is enabled, a file name must be specified." + }, + { + "id": "model.config.is_valid.experimental_audit_settings.file_name_is_directory", + "translation": "The file name must not be a directory." + }, { "id": "model.config.is_valid.export.directory.app_error", "translation": "Value for Directory should not be empty." diff --git a/server/public/model/config.go b/server/public/model/config.go index 420273bdc1..96b54c2a23 100644 --- a/server/public/model/config.go +++ b/server/public/model/config.go @@ -1568,6 +1568,47 @@ type ExperimentalAuditSettings struct { Certificate *string `access:"experimental_features"` // telemetry: none } +func (s *ExperimentalAuditSettings) isValid() *AppError { + if *s.FileEnabled { + if *s.FileName == "" { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_name_empty", nil, "", http.StatusBadRequest) + } + + if strings.HasSuffix(*s.FileName, `\`) { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_name_is_directory", nil, "", http.StatusBadRequest) + } + + if *s.FileMaxSizeMB <= 0 { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_max_size_invalid", nil, "", http.StatusBadRequest) + } + + if *s.FileMaxAgeDays < 0 { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_max_age_invalid", nil, "", http.StatusBadRequest) + } + + if *s.FileMaxBackups < 0 { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_max_backups_invalid", nil, "", http.StatusBadRequest) + } + + if *s.FileMaxQueueSize <= 0 { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.experimental_audit_settings.file_max_queue_size_invalid", nil, "", http.StatusBadRequest) + } + } + + cfg := make(mlog.LoggerConfiguration) + err := json.Unmarshal(s.AdvancedLoggingJSON, &cfg) + if err != nil { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.log.advanced_logging.json", map[string]any{"Error": err}, "", http.StatusBadRequest).Wrap(err) + } + + err = cfg.IsValid() + if err != nil { + return NewAppError("ExperimentalAuditSettings.isValid", "model.config.is_valid.log.advanced_logging.parse", map[string]any{"Error": err}, "", http.StatusBadRequest).Wrap(err) + } + + return nil +} + func (s *ExperimentalAuditSettings) SetDefaults() { if s.FileEnabled == nil { s.FileEnabled = NewPointer(false) @@ -4040,6 +4081,10 @@ func (o *Config) IsValid() *AppError { return appErr } + if appErr := o.ExperimentalAuditSettings.isValid(); appErr != nil { + return appErr + } + if appErr := o.LocalizationSettings.isValid(); appErr != nil { return appErr } diff --git a/server/public/model/config_test.go b/server/public/model/config_test.go index b85fea15bc..809e1eb1c2 100644 --- a/server/public/model/config_test.go +++ b/server/public/model/config_test.go @@ -1402,6 +1402,8 @@ func TestLdapSettingsIsValid(t *testing.T) { } func TestLogSettingsIsValid(t *testing.T) { + t.Parallel() + for name, test := range map[string]struct { LogSettings LogSettings ExpectError bool @@ -2163,6 +2165,115 @@ func TestConfigDefaultConnectedWorkspacesSettings(t *testing.T) { }) } +func TestExperimentalAuditSettingsIsValid(t *testing.T) { + t.Parallel() + + for name, test := range map[string]struct { + ExperimentalAuditSettings ExperimentalAuditSettings + ExpectError bool + }{ + "empty settings": { + ExperimentalAuditSettings: ExperimentalAuditSettings{}, + ExpectError: false, + }, + "file enabled with empty filename": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer(""), + }, + ExpectError: true, + }, + "file enabled with valid filename": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(100), + FileMaxAgeDays: NewPointer(5), + FileMaxBackups: NewPointer(10), + FileMaxQueueSize: NewPointer(1000), + }, + ExpectError: false, + }, + "invalid file max size": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(0), + }, + ExpectError: true, + }, + "negative file max size": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(-10), + }, + ExpectError: true, + }, + "negative file max age": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(100), + FileMaxAgeDays: NewPointer(-5), + }, + ExpectError: true, + }, + "negative file max backups": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(100), + FileMaxAgeDays: NewPointer(5), + FileMaxBackups: NewPointer(-10), + }, + ExpectError: true, + }, + "zero file max queue size": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(100), + FileMaxAgeDays: NewPointer(5), + FileMaxBackups: NewPointer(10), + FileMaxQueueSize: NewPointer(0), + }, + ExpectError: true, + }, + "negative file max queue size": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + FileEnabled: NewPointer(true), + FileName: NewPointer("audit.log"), + FileMaxSizeMB: NewPointer(100), + FileMaxAgeDays: NewPointer(5), + FileMaxBackups: NewPointer(10), + FileMaxQueueSize: NewPointer(-1000), + }, + ExpectError: true, + }, + "AdvancedLoggingJSON has JSON error ": { + ExperimentalAuditSettings: ExperimentalAuditSettings{ + AdvancedLoggingJSON: json.RawMessage(` + { + "foo": "bar", + `), + }, + ExpectError: true, + }, + } { + t.Run(name, func(t *testing.T) { + test.ExperimentalAuditSettings.SetDefaults() + + appErr := test.ExperimentalAuditSettings.isValid() + if test.ExpectError { + require.NotNil(t, appErr) + } else { + require.Nil(t, appErr) + } + }) + } +} + func TestFilterConfig(t *testing.T) { t.Run("should clear default values", func(t *testing.T) { cfg := &Config{} diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go index 5f67c16d95..f8acdbed0c 100644 --- a/server/public/model/feature_flags.go +++ b/server/public/model/feature_flags.go @@ -93,7 +93,7 @@ func (f *FeatureFlags) SetDefaults() { f.ChannelBookmarks = true f.WebSocketEventScope = true f.NotificationMonitoring = true - f.ExperimentalAuditSettingsSystemConsoleUI = false + f.ExperimentalAuditSettingsSystemConsoleUI = true f.CustomProfileAttributes = true f.AttributeBasedAccessControl = true } diff --git a/webapp/channels/src/components/admin_console/admin_definition.tsx b/webapp/channels/src/components/admin_console/admin_definition.tsx index 117e0ecf4b..42b717e8d9 100644 --- a/webapp/channels/src/components/admin_console/admin_definition.tsx +++ b/webapp/channels/src/components/admin_console/admin_definition.tsx @@ -5613,6 +5613,149 @@ const AdminDefinition: AdminDefinitionType = { ], }, }, + audit_logging: { + url: 'compliance/audit_logging', + title: defineMessage({id: 'admin.sidebar.audit_logging_experimental', defaultMessage: 'Audit Logging'}), + isHidden: it.any( + it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.configIsFalse('FeatureFlags', 'ExperimentalAuditSettingsSystemConsoleUI'), + it.not(it.minLicenseTier(LicenseSkus.Enterprise)), + ), + schema: { + id: 'ExperimentalAuditSettings', + name: 'Audit logging (Beta)', + settings: [ + { + type: 'banner', + label: defineMessage({id: 'admin.rate.noteDescription', defaultMessage: 'Changing properties in this section will require a server restart before taking effect.'}), + banner_type: 'info', + }, + { + type: 'bool', + key: 'ExperimentalAuditSettings.FileEnabled', + label: defineMessage({id: 'admin.audit_logging_experimental.file_enabled.title', defaultMessage: 'File Enabled'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_enabled.help_text', defaultMessage: 'Choose whether audit logs are written locally to a file or not.'}), + isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'text', + key: 'ExperimentalAuditSettings.FileName', + label: defineMessage({id: 'admin.audit_logging_experimental.file_name.title', defaultMessage: 'File Name'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_name.help_text', defaultMessage: 'The name of the file to write to.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'number', + key: 'ExperimentalAuditSettings.FileMaxSizeMB', + label: defineMessage({id: 'admin.audit_logging_experimental.file_max_size.title', defaultMessage: 'Max File Size (MB)'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_size.help_text', defaultMessage: 'Maximum size, in megabytes (MB), the log file can grow before it gets rotated.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'number', + key: 'ExperimentalAuditSettings.FileMaxAgeDays', + label: defineMessage({id: 'admin.audit_logging_experimental.file_max_age.title', defaultMessage: 'Max File Age (Days)'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_age.help_text', defaultMessage: 'Maximum number of days to retain old log files. 0 disables the removal of old log files.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'number', + key: 'ExperimentalAuditSettings.FileMaxBackups', + label: defineMessage({id: 'admin.audit_logging_experimental.file_max_backups.title', defaultMessage: 'Maximum File Backups'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_backups.help_text', defaultMessage: 'Maximum number of old log files to retain. 0 retains all old log files. Note: Configuring Max File Age can result in old log files being deleted regardless of this configuration value.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'bool', + key: 'ExperimentalAuditSettings.FileCompress', + label: defineMessage({id: 'admin.audit_logging_experimental.file_compress.title', defaultMessage: 'File Compression'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_compress.help_text', defaultMessage: 'Choose whether enable or disable file compression.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'number', + key: 'ExperimentalAuditSettings.FileMaxQueueSize', + label: defineMessage({id: 'admin.audit_logging_experimental.file_max_queue_size.title', defaultMessage: 'Maximum File Queue'}), + help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_queue_size.help_text', defaultMessage: 'The maximum number of files to be retained in the queue.'}), + isDisabled: it.any( + it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + it.stateIsFalse('ExperimentalAuditSettings.FileEnabled'), + ), + isHidden: it.licensedForFeature('Cloud'), + }, + { + type: 'longtext', + key: 'ExperimentalAuditSettings.AdvancedLoggingJSON', + label: defineMessage({id: 'admin.log.AdvancedLoggingJSONTitle', defaultMessage: 'Advanced Logging:'}), + help_text: defineMessage({id: 'admin.log.AdvancedLoggingJSONDescription', defaultMessage: 'The JSON configuration for Advanced Audit Logging. Please see documentation to learn more about Advanced Logging and the JSON format it uses.'}), + help_text_markdown: false, + help_text_values: { + link: (msg: string) => ( + + {msg} + + ), + }, + placeholder: defineMessage({id: 'admin.log.AdvancedLoggingJSONPlaceholder', defaultMessage: 'Enter your JSON configuration'}), + isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + validate: (value) => { + const valid = new ValidationResult(true, ''); + if (!value) { + return valid; + } + try { + JSON.parse(value); + return valid; + } catch (error) { + return new ValidationResult(false, error.message); + } + }, + onConfigLoad: (configVal) => JSON.stringify(configVal, null, ' '), + onConfigSave: (displayVal) => { + // Handle case where field is empty + if (!displayVal) { + return {undefined}; + } + + return JSON.parse(displayVal); + }, + }, + { + type: 'custom', + component: AuditLoggingCertificateUploadSetting, + label: defineMessage({id: 'admin.audit_logging_experimental.certificate.title', defaultMessage: 'Certificate'}), + key: 'ExperimentalAuditSettings.Certificate', + help_text: defineMessage({id: 'admin.audit_logging_experimental.certificate.help_text', defaultMessage: 'The certificate file used for audit logging encryption.'}), + isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), + isHidden: it.not(it.licensedForFeature('Cloud')), + }, + ], + }, + }, custom_terms_of_service: { url: 'compliance/custom_terms_of_service', title: defineMessage({id: 'admin.sidebar.customTermsOfService', defaultMessage: 'Custom Terms of Service'}), @@ -6086,126 +6229,6 @@ const AdminDefinition: AdminDefinitionType = { component: BleveSettings, }, }, - audit_logging: { - url: 'experimental/audit_logging', - title: defineMessage({id: 'admin.sidebar.audit_logging_experimental', defaultMessage: 'Audit Logging'}), - isHidden: it.any( - it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - it.configIsFalse('FeatureFlags', 'ExperimentalAuditSettingsSystemConsoleUI'), - it.not(it.minLicenseTier(LicenseSkus.Enterprise)), - ), - schema: { - id: 'ExperimentalAuditSettings', - name: 'Audit Log Settings (Experimental)', - settings: [ - { - type: 'bool', - key: 'ExperimentalAuditSettings.FileEnabled', - label: defineMessage({id: 'admin.audit_logging_experimental.file_enabled.title', defaultMessage: 'File Enabled'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_enabled.help_text', defaultMessage: 'Choose whether audit logs are written locally to a file or not.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'text', - key: 'ExperimentalAuditSettings.FileName', - label: defineMessage({id: 'admin.audit_logging_experimental.file_name.title', defaultMessage: 'File Name'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_name.help_text', defaultMessage: 'The name of the file to write to. NOTE: If ExperimentalAuditSettings.FileEnabled is set to TRUE, this field is required.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'number', - key: 'ExperimentalAuditSettings.FileMaxSizeMB', - label: defineMessage({id: 'admin.audit_logging_experimental.file_max_size.title', defaultMessage: 'Max File Size (MB)'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_size.help_text', defaultMessage: 'The maximum size of a single exported file, in MB.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'number', - key: 'ExperimentalAuditSettings.FileMaxAgeDays', - label: defineMessage({id: 'admin.audit_logging_experimental.file_max_age.title', defaultMessage: 'Max File Age (Days)'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_age.help_text', defaultMessage: 'The maximum age of an exported file, in days.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'number', - key: 'ExperimentalAuditSettings.FileMaxBackups', - label: defineMessage({id: 'admin.audit_logging_experimental.file_max_backups.title', defaultMessage: 'Maximum File Backups'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_backups.help_text', defaultMessage: 'The maximum number of backup files to retain'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'bool', - key: 'ExperimentalAuditSettings.FileCompress', - label: defineMessage({id: 'admin.audit_logging_experimental.file_compress.title', defaultMessage: 'File Compression'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_compress.help_text', defaultMessage: 'Choose whether enable or disable file compression.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'number', - key: 'ExperimentalAuditSettings.FileMaxQueueSize', - label: defineMessage({id: 'admin.audit_logging_experimental.file_max_queue_size.title', defaultMessage: 'Maximum File Queue'}), - help_text: defineMessage({id: 'admin.audit_logging_experimental.file_max_queue_size.help_text', defaultMessage: 'The maximum number of files to be retained in the queue.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.licensedForFeature('Cloud'), - }, - { - type: 'longtext', - key: 'ExperimentalAuditSettings.AdvancedLoggingJSON', - label: defineMessage({id: 'admin.log.AdvancedLoggingJSONTitle', defaultMessage: 'Advanced Logging:'}), - help_text: defineMessage({id: 'admin.log.AdvancedLoggingJSONDescription', defaultMessage: 'The JSON configuration for Advanced Audit Logging. Please see documentation to learn more about Advanced Logging and the JSON format it uses.'}), - help_text_markdown: false, - help_text_values: { - link: (msg: string) => ( - - {msg} - - ), - }, - placeholder: defineMessage({id: 'admin.log.AdvancedLoggingJSONPlaceholder', defaultMessage: 'Enter your JSON configuration'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - validate: (value) => { - const valid = new ValidationResult(true, ''); - if (!value) { - return valid; - } - try { - JSON.parse(value); - return valid; - } catch (error) { - return new ValidationResult(false, error.message); - } - }, - onConfigLoad: (configVal) => JSON.stringify(configVal, null, ' '), - onConfigSave: (displayVal) => { - // Handle case where field is empty - if (!displayVal) { - return {undefined}; - } - - return JSON.parse(displayVal); - }, - }, - { - type: 'custom', - component: AuditLoggingCertificateUploadSetting, - label: defineMessage({id: 'admin.audit_logging_experimental.certificate.title', defaultMessage: 'Certificate'}), - key: 'ExperimentalAuditSettings.Certificate', - help_text: defineMessage({id: 'admin.audit_logging_experimental.certificate.help_text', defaultMessage: 'The certificate file used for audit logging encryption.'}), - isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.EXPERIMENTAL.FEATURES)), - isHidden: it.not(it.licensedForFeature('Cloud')), - }, - ], - }, - }, }, }, }; diff --git a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap index fa4489dc5a..4a3aef70e9 100644 --- a/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/admin_sidebar/__snapshots__/admin_sidebar.test.tsx.snap @@ -1871,6 +1871,17 @@ exports[`components/AdminSidebar should match snapshot with license with enterpr /> } /> + + } + /> } /> - - } - /> @@ -2948,6 +2948,17 @@ exports[`components/AdminSidebar should match snapshot with license with enterpr /> } /> + + } + /> } /> - - } - /> diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 5d2a84cfc0..40fd83b2bc 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -362,15 +362,15 @@ "admin.audit_logging_experimental.file_compress.title": "File Compression", "admin.audit_logging_experimental.file_enabled.help_text": "Choose whether audit logs are written locally to a file or not.", "admin.audit_logging_experimental.file_enabled.title": "File Enabled", - "admin.audit_logging_experimental.file_max_age.help_text": "The maximum age of an exported file, in days.", + "admin.audit_logging_experimental.file_max_age.help_text": "Maximum number of days to retain old log files. 0 disables the removal of old log files.", "admin.audit_logging_experimental.file_max_age.title": "Max File Age (Days)", - "admin.audit_logging_experimental.file_max_backups.help_text": "The maximum number of backup files to retain", + "admin.audit_logging_experimental.file_max_backups.help_text": "Maximum number of old log files to retain. 0 retains all old log files. Note: Configuring Max File Age can result in old log files being deleted regardless of this configuration value.", "admin.audit_logging_experimental.file_max_backups.title": "Maximum File Backups", "admin.audit_logging_experimental.file_max_queue_size.help_text": "The maximum number of files to be retained in the queue.", "admin.audit_logging_experimental.file_max_queue_size.title": "Maximum File Queue", - "admin.audit_logging_experimental.file_max_size.help_text": "The maximum size of a single exported file, in MB.", + "admin.audit_logging_experimental.file_max_size.help_text": "Maximum size, in megabytes (MB), the log file can grow before it gets rotated.", "admin.audit_logging_experimental.file_max_size.title": "Max File Size (MB)", - "admin.audit_logging_experimental.file_name.help_text": "The name of the file to write to. NOTE: If ExperimentalAuditSettings.FileEnabled is set to TRUE, this field is required.", + "admin.audit_logging_experimental.file_name.help_text": "The name of the file to write to.", "admin.audit_logging_experimental.file_name.title": "File Name", "admin.audits.reload": "Reload User Activity Logs", "admin.authentication.email": "Email Authentication",