diff --git a/common/config-equivalences.js b/common/config-equivalences.js deleted file mode 100644 index 0441e3f8b1..0000000000 --- a/common/config-equivalences.js +++ /dev/null @@ -1,234 +0,0 @@ -import { ASSETS_PUBLIC_URL, PLUGIN_PLATFORM_NAME } from "./constants"; - -export const configEquivalences = { - pattern: "Default index pattern to use on the app. If there's no valid index pattern, the app will automatically create one with the name indicated in this option.", - 'customization.logo.app':`Set the name of the app logo stored at ${ASSETS_PUBLIC_URL}`, - 'customization.logo.sidebar':`Set the name of the sidebar logo stored at ${ASSETS_PUBLIC_URL}`, - 'customization.logo.healthcheck':`Set the name of the health-check logo stored at ${ASSETS_PUBLIC_URL}`, - 'customization.logo.reports':`Set the name of the reports logo (.png) stored at ${ASSETS_PUBLIC_URL}`, - 'checks.pattern': - 'Enable or disable the index pattern health check when opening the app.', - 'checks.template': - 'Enable or disable the template health check when opening the app.', - 'checks.api': 'Enable or disable the API health check when opening the app.', - 'checks.setup': - 'Enable or disable the setup health check when opening the app.', - 'checks.fields': - 'Enable or disable the known fields health check when opening the app.', - 'checks.metaFields': - `Change the default value of the ${PLUGIN_PLATFORM_NAME} metaField configuration`, - 'checks.timeFilter': - `Change the default value of the ${PLUGIN_PLATFORM_NAME} timeFilter configuration`, - 'checks.maxBuckets': - `Change the default value of the ${PLUGIN_PLATFORM_NAME} max buckets configuration`, - 'extensions.pci': 'Enable or disable the PCI DSS tab on Overview and Agents.', - 'extensions.gdpr': 'Enable or disable the GDPR tab on Overview and Agents.', - 'extensions.hipaa': 'Enable or disable the HIPAA tab on Overview and Agents.', - 'extensions.nist': 'Enable or disable the NIST 800-53 tab on Overview and Agents.', - 'extensions.tsc': 'Enable or disable the TSC tab on Overview and Agents.', - 'extensions.audit': 'Enable or disable the Audit tab on Overview and Agents.', - 'extensions.oscap': - 'Enable or disable the Open SCAP tab on Overview and Agents.', - 'extensions.ciscat': - 'Enable or disable the CIS-CAT tab on Overview and Agents.', - 'extensions.aws': 'Enable or disable the Amazon (AWS) tab on Overview.', - 'extensions.gcp': 'Enable or disable the Google Cloud Platform tab on Overview.', - 'extensions.virustotal': - 'Enable or disable the VirusTotal tab on Overview and Agents.', - 'extensions.osquery': - 'Enable or disable the Osquery tab on Overview and Agents.', - 'extensions.mitre': 'Enable or disable the MITRE tab on Overview and Agents.', - 'extensions.docker': - 'Enable or disable the Docker listener tab on Overview and Agents.', - timeout: - 'Maximum time, in milliseconds, the app will wait for an API response when making requests to it. It will be ignored if the value is set under 1500 milliseconds.', - 'ip.selector': - 'Define if the user is allowed to change the selected index pattern directly from the top menu bar.', - 'ip.ignore': - 'Disable certain index pattern names from being available in index pattern selector from the Wazuh app.', - 'wazuh.monitoring.enabled': - 'Enable or disable the wazuh-monitoring index creation and/or visualization.', - 'wazuh.monitoring.frequency': - 'Frequency, in seconds, of API requests to get the state of the agents and create a new document in the wazuh-monitoring index with this data.', - 'wazuh.monitoring.shards': - 'Define the number of shards to use for the wazuh-monitoring-* indices.', - 'wazuh.monitoring.replicas': - 'Define the number of replicas to use for the wazuh-monitoring-* indices.', - 'wazuh.monitoring.creation': - 'Define the interval in which a new wazuh-monitoring index will be created.', - 'wazuh.monitoring.pattern': - 'Default index pattern to use for Wazuh monitoring.', - hideManagerAlerts: - 'Hide the alerts of the manager in every dashboard.', - 'logs.level': - 'Logging level of the App.', - 'enrollment.dns': - 'Specifies the Wazuh registration server, used for the agent enrollment.', - 'enrollment.password': - 'Specifies the password used to authenticate during the agent enrollment.', - 'cron.prefix': - 'Define the index prefix of predefined jobs.', - 'cron.statistics.status': - 'Enable or disable the statistics tasks.', - 'cron.statistics.apis': - 'Enter the ID of the hosts you want to save data from, leave this empty to run the task on every host.', - 'cron.statistics.interval': 'Define the frequency of task execution using cron schedule expressions.', - 'cron.statistics.index.name': 'Define the name of the index in which the documents will be saved.', - 'cron.statistics.index.creation': 'Define the interval in which a new index will be created.', - 'cron.statistics.index.shards': 'Define the number of shards to use for the statistics indices.', - 'cron.statistics.index.replicas': 'Define the number of replicas to use for the statistics indices.', - 'alerts.sample.prefix': 'Define the index name prefix of sample alerts. It must match the template used by the index pattern to avoid unknown fields in dashboards.', -}; - -export const nameEquivalence = { - pattern: 'Index pattern', - 'customization.logo.app': 'Logo App', - 'customization.logo.sidebar': 'Logo Sidebar', - 'customization.logo.healthcheck': 'Logo Health Check', - 'customization.logo.reports': 'Logo Reports', - 'checks.pattern': 'Index pattern', - 'checks.template': 'Index template', - 'checks.api': 'API connection', - 'checks.setup': 'API version', - 'checks.fields': 'Known fields', - 'checks.metaFields': 'Remove meta fields', - 'checks.timeFilter': 'Set time filter to 24h', - 'checks.maxBuckets': 'Set max buckets to 200000', - timeout: 'Request timeout', - 'ip.selector': 'IP selector', - 'ip.ignore': 'IP ignore', - 'xpack.rbac.enabled': 'X-Pack RBAC', - 'wazuh.monitoring.enabled': 'Status', - 'wazuh.monitoring.frequency': 'Frequency', - 'wazuh.monitoring.shards': 'Index shards', - 'wazuh.monitoring.replicas': 'Index replicas', - 'wazuh.monitoring.creation': 'Index creation', - 'wazuh.monitoring.pattern': 'Index pattern', - hideManagerAlerts: 'Hide manager alerts', - 'logs.level': 'Log level', - 'enrollment.dns': 'Enrollment DNS', - 'cron.prefix': 'Cron prefix', - 'cron.statistics.status': 'Status', - 'cron.statistics.apis': 'Includes apis', - 'cron.statistics.interval': 'Interval', - 'cron.statistics.index.name': 'Index name', - 'cron.statistics.index.creation': 'Index creation', - 'cron.statistics.index.shards': 'Index shards', - 'cron.statistics.index.replicas': 'Index replicas', - 'alerts.sample.prefix': 'Sample alerts prefix', -} - -const HEALTH_CHECK = 'Health Check'; -const GENERAL = 'General'; -const SECURITY = 'Security'; -const MONITORING = 'Monitoring'; -const STATISTICS = 'Statistics'; -const CUSTOMIZATION = 'Logo Customization'; -export const categoriesNames = [HEALTH_CHECK, GENERAL, SECURITY, MONITORING, STATISTICS, CUSTOMIZATION]; - -export const categoriesEquivalence = { - pattern: GENERAL, - 'customization.logo.app':CUSTOMIZATION, - 'customization.logo.sidebar':CUSTOMIZATION, - 'customization.logo.healthcheck':CUSTOMIZATION, - 'customization.logo.reports':CUSTOMIZATION, - 'checks.pattern': HEALTH_CHECK, - 'checks.template': HEALTH_CHECK, - 'checks.api': HEALTH_CHECK, - 'checks.setup': HEALTH_CHECK, - 'checks.fields': HEALTH_CHECK, - 'checks.metaFields': HEALTH_CHECK, - 'checks.timeFilter': HEALTH_CHECK, - 'checks.maxBuckets': HEALTH_CHECK, - timeout: GENERAL, - 'ip.selector': GENERAL, - 'ip.ignore': GENERAL, - 'wazuh.monitoring.enabled': MONITORING, - 'wazuh.monitoring.frequency': MONITORING, - 'wazuh.monitoring.shards': MONITORING, - 'wazuh.monitoring.replicas': MONITORING, - 'wazuh.monitoring.creation': MONITORING, - 'wazuh.monitoring.pattern': MONITORING, - hideManagerAlerts: GENERAL, - 'logs.level': GENERAL, - 'enrollment.dns': GENERAL, - 'cron.prefix': GENERAL, - 'cron.statistics.status': STATISTICS, - 'cron.statistics.apis': STATISTICS, - 'cron.statistics.interval': STATISTICS, - 'cron.statistics.index.name': STATISTICS, - 'cron.statistics.index.creation': STATISTICS, - 'cron.statistics.index.shards': STATISTICS, - 'cron.statistics.index.replicas': STATISTICS, - 'alerts.sample.prefix': GENERAL, -} - -const TEXT = 'text'; -const NUMBER = 'number'; -const LIST = 'list'; -const BOOLEAN = 'boolean'; -const ARRAY = 'array'; -const INTERVAL = 'interval' - -export const formEquivalence = { - pattern: { type: TEXT }, - 'customization.logo.app': { type: TEXT }, - 'customization.logo.sidebar': { type: TEXT }, - 'customization.logo.healthcheck': { type: TEXT }, - 'customization.logo.reports': { type: TEXT }, - 'checks.pattern': { type: BOOLEAN }, - 'checks.template': { type: BOOLEAN }, - 'checks.api': { type: BOOLEAN }, - 'checks.setup': { type: BOOLEAN }, - 'checks.fields': { type: BOOLEAN }, - 'checks.metaFields': { type: BOOLEAN }, - 'checks.timeFilter': { type: BOOLEAN }, - 'checks.maxBuckets': { type: BOOLEAN }, - timeout: { type: NUMBER }, - 'ip.selector': { type: BOOLEAN }, - 'ip.ignore': { type: ARRAY }, - 'xpack.rbac.enabled': { type: BOOLEAN }, - 'wazuh.monitoring.enabled': { type: BOOLEAN }, - 'wazuh.monitoring.frequency': { type: NUMBER }, - 'wazuh.monitoring.shards': { type: NUMBER }, - 'wazuh.monitoring.replicas': { type: NUMBER }, - 'wazuh.monitoring.creation': { - type: LIST, params: { - options: [ - { text: 'Hourly', value: 'h' }, - { text: 'Daily', value: 'd' }, - { text: 'Weekly', value: 'w' }, - { text: 'Monthly', value: 'm' }, - ] - } - }, - 'wazuh.monitoring.pattern': { type: TEXT }, - hideManagerAlerts: { type: BOOLEAN }, - 'logs.level': { - type: LIST, params: { - options: [ - { text: 'Info', value: 'info' }, - { text: 'Debug', value: 'debug' }, - ] - } - }, - 'enrollment.dns': { type: TEXT }, - 'cron.prefix': { type: TEXT }, - 'cron.statistics.status': { type: BOOLEAN }, - 'cron.statistics.apis': { type: ARRAY }, - 'cron.statistics.interval': { type: INTERVAL }, - 'cron.statistics.index.name': { type: TEXT }, - 'cron.statistics.index.creation': { - type: LIST, params: { - options: [ - { text: 'Hourly', value: 'h' }, - { text: 'Daily', value: 'd' }, - { text: 'Weekly', value: 'w' }, - { text: 'Monthly', value: 'm' }, - ] - } - }, - 'cron.statistics.index.shards': { type: NUMBER }, - 'cron.statistics.index.replicas': { type: NUMBER }, - 'alerts.sample.prefix': { type: TEXT }, -} diff --git a/common/constants.ts b/common/constants.ts index f323c32a4c..f0e1657030 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -14,7 +14,7 @@ import { version } from '../package.json'; // Plugin export const PLUGIN_VERSION = version; -export const PLUGIN_VERSION_SHORT = version.split('.').splice(0,2).join('.'); +export const PLUGIN_VERSION_SHORT = version.split('.').splice(0, 2).join('.'); // Index patterns - Wazuh alerts export const WAZUH_INDEX_TYPE_ALERTS = 'alerts'; @@ -100,28 +100,6 @@ export const WAZUH_SECURITY_PLUGINS = [ // App configuration export const WAZUH_CONFIGURATION_CACHE_TIME = 10000; // time in ms; -export const WAZUH_CONFIGURATION_SETTINGS_NEED_RESTART = [ - 'wazuh.monitoring.enabled', - 'wazuh.monitoring.frequency', - 'cron.statistics.interval', - 'logs.level', -]; -export const WAZUH_CONFIGURATION_SETTINGS_NEED_HEALTH_CHECK = [ - 'pattern', - 'wazuh.monitoring.replicas', - 'wazuh.monitoring.creation', - 'wazuh.monitoring.pattern', - 'alerts.sample.prefix', - 'cron.statistics.index.name', - 'cron.statistics.index.creation', - 'cron.statistics.index.shards', - 'cron.statistics.index.replicas', - 'wazuh.monitoring.shards', -]; -export const WAZUH_CONFIGURATION_SETTINGS_NEED_RELOAD = [ - 'hideManagerAlerts', - 'customization.logo.sidebar' -]; // Reserved ids for Users/Role mapping export const WAZUH_API_RESERVED_ID_LOWER_THAN = 100; @@ -176,61 +154,6 @@ export const WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH = path.join( // Queue export const WAZUH_QUEUE_CRON_FREQ = '*/15 * * * * *'; // Every 15 seconds -// Default App Config -export const WAZUH_DEFAULT_APP_CONFIG = { - pattern: WAZUH_ALERTS_PATTERN, - 'checks.pattern': true, - 'checks.template': true, - 'checks.api': true, - 'checks.setup': true, - 'checks.fields': true, - 'checks.metaFields': true, - 'checks.maxBuckets': true, - 'checks.timeFilter': true, - 'extensions.pci': true, - 'extensions.gdpr': true, - 'extensions.hipaa': true, - 'extensions.nist': true, - 'extensions.tsc': true, - 'extensions.audit': true, - 'extensions.oscap': false, - 'extensions.ciscat': false, - 'extensions.aws': false, - 'extensions.office': false, - 'extensions.github': false, - 'extensions.gcp': false, - 'extensions.virustotal': false, - 'extensions.osquery': false, - 'extensions.docker': false, - timeout: 20000, - 'ip.selector': true, - 'ip.ignore': [], - 'xpack.rbac.enabled': true, - 'wazuh.monitoring.enabled': WAZUH_MONITORING_DEFAULT_ENABLED, - 'wazuh.monitoring.frequency': WAZUH_MONITORING_DEFAULT_FREQUENCY, - 'wazuh.monitoring.shards': WAZUH_MONITORING_DEFAULT_INDICES_SHARDS, - 'wazuh.monitoring.replicas': WAZUH_MONITORING_DEFAULT_INDICES_REPLICAS, - 'wazuh.monitoring.creation': WAZUH_MONITORING_DEFAULT_CREATION, - 'wazuh.monitoring.pattern': WAZUH_MONITORING_PATTERN, - 'cron.prefix': WAZUH_STATISTICS_DEFAULT_PREFIX, - 'cron.statistics.status': WAZUH_STATISTICS_DEFAULT_STATUS, - 'cron.statistics.apis': [], - 'cron.statistics.interval': WAZUH_STATISTICS_DEFAULT_CRON_FREQ, - 'cron.statistics.index.name': WAZUH_STATISTICS_DEFAULT_NAME, - 'cron.statistics.index.creation': WAZUH_STATISTICS_DEFAULT_CREATION, - 'cron.statistics.index.shards': WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, - 'cron.statistics.index.replicas': WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS, - 'alerts.sample.prefix': WAZUH_SAMPLE_ALERT_PREFIX, - hideManagerAlerts: false, - 'logs.level': 'info', - 'enrollment.dns': '', - 'enrollment.password': '', - 'customization.logo.app': '', - 'customization.logo.sidebar': '', - 'customization.logo.healthcheck':'', - 'customization.logo.reports': '' -}; - // Wazuh errors export const WAZUH_ERROR_DAEMONS_NOT_READY = 'ERROR3099'; @@ -417,6 +340,1033 @@ export const DOCUMENTATION_WEB_BASE_URL = "https://documentation.wazuh.com"; // Default Elasticsearch user name context export const ELASTIC_NAME = 'elastic'; +// Plugin settings +export enum SettingCategory { + GENERAL, + HEALTH_CHECK, + EXTENSIONS, + MONITORING, + STATISTICS, + SECURITY, + CUSTOMIZATION, +}; + +type TPluginSettingOptionsSelect = { + select: { text: string, value: any }[] +}; + +type TPluginSettingOptionsNumber = { + number: { + min?: number + max?: number + } +}; + +type TPluginSettingOptionsEditor = { + editor: { + language: string + } +}; + +type TPluginSettingOptionsSwitch = { + switch: { + values: { + disabled: { label?: string, value: any }, + enabled: { label?: string, value: any }, + } + } +}; + +export enum EpluginSettingType { + text = 'text', + textarea = 'textarea', + switch = 'switch', + number = 'number', + editor = 'editor', + select = 'select', +}; + +export type TPluginSetting = { + // Define the text displayed in the UI. + title: string + // Description. + description: string + // Category. + category: SettingCategory + // Type. + type: EpluginSettingType + // Default value. + defaultValue: any + // Default value if it is not set. It has preference over `default`. + defaultValueIfNotSet?: any + // Configurable from the configuration file. + isConfigurableFromFile: boolean + // Configurable from the UI (Settings/Configuration). + isConfigurableFromUI: boolean + // Modify the setting requires running the plugin health check (frontend). + requiresRunningHealthCheck?: boolean + // Modify the setting requires reloading the browser tab (frontend). + requiresReloadingBrowserTab?: boolean + // Modify the setting requires restarting the plugin platform to take effect. + requiresRestartingPluginPlatform?: boolean + // Define options related to the `type`. + options?: TPluginSettingOptionsNumber | TPluginSettingOptionsEditor | TPluginSettingOptionsSelect | TPluginSettingOptionsSwitch + // Transform the input value. The result is saved in the form global state of Settings/Configuration + uiFormTransformChangedInputValue?: (value: any) => any + // Transform the configuration value or default as initial value for the input in Settings/Configuration + uiFormTransformConfigurationValueToInputValue?: (value: any) => any + // Transform the input value changed in the form of Settings/Configuration and returned in the `changed` property of the hook useForm + uiFormTransformInputValueToConfigurationValue?: (value: any) => any +}; + +export type TPluginSettingWithKey = TPluginSetting & { key: TPluginSettingKey }; +export type TPluginSettingCategory = { + title: string + description?: string + documentationLink?: string + renderOrder?: number +}; + + +export const PLUGIN_SETTINGS_CATEGORIES: { [category: number]: TPluginSettingCategory } = { + [SettingCategory.HEALTH_CHECK]: { + title: 'Health check', + description: "Checks will be executed by the app's Healthcheck.", + renderOrder: SettingCategory.HEALTH_CHECK, + }, + [SettingCategory.GENERAL]: { + title: 'General', + description: "Basic app settings related to alerts index pattern, hide the manager alerts in the dashboards, logs level and more.", + renderOrder: SettingCategory.GENERAL, + }, + [SettingCategory.EXTENSIONS]: { + title: 'Initial display state of the modules of the new API host entries.', + description: "Extensions.", + }, + [SettingCategory.SECURITY]: { + title: 'Security', + description: "Application security options such as unauthorized roles.", + renderOrder: SettingCategory.SECURITY, + }, + [SettingCategory.MONITORING]: { + title: 'Task:Monitoring', + description: "Options related to the agent status monitoring job and its storage in indexes.", + renderOrder: SettingCategory.MONITORING, + }, + [SettingCategory.STATISTICS]: { + title: 'Task:Statistics', + description: "Options related to the daemons manager monitoring job and their storage in indexes..", + renderOrder: SettingCategory.STATISTICS, + }, + [SettingCategory.CUSTOMIZATION]: { + title: 'Custom branding', + description: "If you want to use custom branding elements such as logos, you can do so by editing the settings below.", + documentationLink: 'user-manual/wazuh-dashboard/white-labeling.html', + renderOrder: SettingCategory.CUSTOMIZATION, + } +}; + +export const PLUGIN_SETTINGS: { [key: string]: TPluginSetting } = { + "alerts.sample.prefix": { + title: "Sample alerts prefix", + description: "Define the index name prefix of sample alerts. It must match the template used by the index pattern to avoid unknown fields in dashboards.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.text, + defaultValue: WAZUH_SAMPLE_ALERT_PREFIX, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "checks.api": { + title: "API connection", + description: "Enable or disable the API health check when opening the app.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.fields": { + title: "Known fields", + description: "Enable or disable the known fields health check when opening the app.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.maxBuckets": { + title: "Set max buckets to 200000", + description: "Change the default value of the plugin platform max buckets configuration.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + }, + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.metaFields": { + title: "Remove meta fields", + description: "Change the default value of the plugin platform metaField configuration.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.pattern": { + title: "Index pattern", + description: "Enable or disable the index pattern health check when opening the app.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.setup": { + title: "API version", + description: "Enable or disable the setup health check when opening the app.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.template": { + title: "Index template", + description: "Enable or disable the template health check when opening the app.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "checks.timeFilter": { + title: "Set time filter to 24h", + description: "Change the default value of the plugin platform timeFilter configuration.", + category: SettingCategory.HEALTH_CHECK, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "cron.prefix": { + title: "Cron prefix", + description: "Define the index prefix of predefined jobs.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.text, + defaultValue: WAZUH_STATISTICS_DEFAULT_PREFIX, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + }, + "cron.statistics.apis": { + title: "Includes APIs", + description: "Enter the ID of the hosts you want to save data from, leave this empty to run the task on every host.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.editor, + defaultValue: [], + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + editor: { + language: 'json' + } + }, + uiFormTransformConfigurationValueToInputValue: function (value: any): any { + return JSON.stringify(value); + }, + uiFormTransformInputValueToConfigurationValue: function (value: string): any { + try { + return JSON.parse(value); + } catch (error) { + return value; + }; + }, + }, + "cron.statistics.index.creation": { + title: "Index creation", + description: "Define the interval in which a new index will be created.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.select, + options: { + select: [ + { + text: "Hourly", + value: "h" + }, + { + text: "Daily", + value: "d" + }, + { + text: "Weekly", + value: "w" + }, + { + text: "Monthly", + value: "m" + } + ] + }, + defaultValue: WAZUH_STATISTICS_DEFAULT_CREATION, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "cron.statistics.index.name": { + title: "Index name", + description: "Define the name of the index in which the documents will be saved.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.text, + defaultValue: WAZUH_STATISTICS_DEFAULT_NAME, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "cron.statistics.index.replicas": { + title: "Index replicas", + description: "Define the number of replicas to use for the statistics indices.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.number, + defaultValue: WAZUH_STATISTICS_DEFAULT_INDICES_REPLICAS, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + options: { + number: { + min: 0 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number): string { + return String(value); + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + }, + "cron.statistics.index.shards": { + title: "Index shards", + description: "Define the number of shards to use for the statistics indices.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.number, + defaultValue: WAZUH_STATISTICS_DEFAULT_INDICES_SHARDS, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + options: { + number: { + min: 1 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number){ + return String(value) + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + }, + "cron.statistics.interval": { + title: "Interval", + description: "Define the frequency of task execution using cron schedule expressions.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.text, + defaultValue: WAZUH_STATISTICS_DEFAULT_CRON_FREQ, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRestartingPluginPlatform: true, + }, + "cron.statistics.status": { + title: "Status", + description: "Enable or disable the statistics tasks.", + category: SettingCategory.STATISTICS, + type: EpluginSettingType.switch, + defaultValue: WAZUH_STATISTICS_DEFAULT_STATUS, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "customization.logo.app": { + title: "App main logo", + description: `This logo is used in the app main menu, at the top left corner.`, + category: SettingCategory.CUSTOMIZATION, + type: EpluginSettingType.text, + defaultValue: "", + isConfigurableFromFile: true, + isConfigurableFromUI: true, + }, + "customization.logo.healthcheck": { + title: "Healthcheck logo", + description: `This logo is displayed during the Healthcheck routine of the app.`, + category: SettingCategory.CUSTOMIZATION, + type: EpluginSettingType.text, + defaultValue: "", + isConfigurableFromFile: true, + isConfigurableFromUI: true, + }, + "customization.logo.reports": { + title: "PDF reports logo", + description: `This logo is used in the PDF reports generated by the app. It's placed at the top left corner of every page of the PDF.`, + category: SettingCategory.CUSTOMIZATION, + type: EpluginSettingType.text, + defaultValue: "", + defaultValueIfNotSet: REPORTS_LOGO_IMAGE_ASSETS_RELATIVE_PATH, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + }, + "customization.logo.sidebar": { + title: "Navigation drawer logo", + description: `This is the logo for the app to display in the platform's navigation drawer, this is, the main sidebar collapsible menu.`, + category: SettingCategory.CUSTOMIZATION, + type: EpluginSettingType.text, + defaultValue: "", + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresReloadingBrowserTab: true, + }, + "disabled_roles": { + title: "Disable roles", + description: "Disabled the plugin visibility for users with the roles.", + category: SettingCategory.SECURITY, + type: EpluginSettingType.editor, + defaultValue: [], + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + editor: { + language: 'json' + } + }, + uiFormTransformConfigurationValueToInputValue: function (value: any): any { + return JSON.stringify(value); + }, + uiFormTransformInputValueToConfigurationValue: function (value: string): any { + try { + return JSON.parse(value); + } catch (error) { + return value; + }; + }, + }, + "enrollment.dns": { + title: "Enrollment DNS", + description: "Specifies the Wazuh registration server, used for the agent enrollment.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.text, + defaultValue: "", + isConfigurableFromFile: true, + isConfigurableFromUI: true, + }, + "enrollment.password": { + title: "Enrollment password", + description: "Specifies the password used to authenticate during the agent enrollment.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.text, + defaultValue: "", + isConfigurableFromFile: true, + isConfigurableFromUI: false, + }, + "extensions.audit": { + title: "System auditing", + description: "Enable or disable the Audit tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.aws": { + title: "Amazon AWS", + description: "Enable or disable the Amazon (AWS) tab on Overview.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.ciscat": { + title: "CIS-CAT", + description: "Enable or disable the CIS-CAT tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.docker": { + title: "Docker listener", + description: "Enable or disable the Docker listener tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.gcp": { + title: "Google Cloud platform", + description: "Enable or disable the Google Cloud Platform tab on Overview.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.gdpr": { + title: "GDPR", + description: "Enable or disable the GDPR tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.hipaa": { + title: "Hipaa", + description: "Enable or disable the HIPAA tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.nist": { + title: "NIST", + description: "Enable or disable the NIST 800-53 tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.oscap": { + title: "OSCAP", + description: "Enable or disable the Open SCAP tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.osquery": { + title: "Osquery", + description: "Enable or disable the Osquery tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.pci": { + title: "PCI DSS", + description: "Enable or disable the PCI DSS tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.tsc": { + title: "TSC", + description: "Enable or disable the TSC tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "extensions.virustotal": { + title: "Virustotal", + description: "Enable or disable the VirusTotal tab on Overview and Agents.", + category: SettingCategory.EXTENSIONS, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "hideManagerAlerts": { + title: "Hide manager alerts", + description: "Hide the alerts of the manager in every dashboard.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.switch, + defaultValue: false, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresReloadingBrowserTab: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "ip.ignore": { + title: "Index pattern ignore", + description: "Disable certain index pattern names from being available in index pattern selector.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.editor, + defaultValue: [], + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + editor: { + language: 'json' + } + }, + uiFormTransformConfigurationValueToInputValue: function (value: any): any { + return JSON.stringify(value); + }, + uiFormTransformInputValueToConfigurationValue: function (value: string): any { + try { + return JSON.parse(value); + } catch (error) { + return value; + }; + }, + }, + "ip.selector": { + title: "IP selector", + description: "Define if the user is allowed to change the selected index pattern directly from the top menu bar.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.switch, + defaultValue: true, + isConfigurableFromFile: true, + isConfigurableFromUI: false, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "logs.level": { + title: "Log level", + description: "Logging level of the App.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.select, + options: { + select: [ + { + text: "Info", + value: "info" + }, + { + text: "Debug", + value: "debug" + } + ] + }, + defaultValue: "info", + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRestartingPluginPlatform: true, + }, + "pattern": { + title: "Index pattern", + description: "Default index pattern to use on the app. If there's no valid index pattern, the app will automatically create one with the name indicated in this option.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.text, + defaultValue: WAZUH_ALERTS_PATTERN, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "timeout": { + title: "Request timeout", + description: "Maximum time, in milliseconds, the app will wait for an API response when making requests to it. It will be ignored if the value is set under 1500 milliseconds.", + category: SettingCategory.GENERAL, + type: EpluginSettingType.number, + defaultValue: 20000, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + options: { + number: { + min: 1500 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number){ + return String(value) + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + }, + "wazuh.monitoring.creation": { + title: "Index creation", + description: "Define the interval in which a new wazuh-monitoring index will be created.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.select, + options: { + select: [ + { + text: "Hourly", + value: "h" + }, + { + text: "Daily", + value: "d" + }, + { + text: "Weekly", + value: "w" + }, + { + text: "Monthly", + value: "m" + } + ] + }, + defaultValue: WAZUH_MONITORING_DEFAULT_CREATION, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "wazuh.monitoring.enabled": { + title: "Status", + description: "Enable or disable the wazuh-monitoring index creation and/or visualization.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.switch, + defaultValue: WAZUH_MONITORING_DEFAULT_ENABLED, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRestartingPluginPlatform: true, + options: { + switch: { + values: { + disabled: { label: 'false', value: false }, + enabled: { label: 'true', value: true }, + } + } + }, + uiFormTransformChangedInputValue: function (value: boolean | string): boolean { + return Boolean(value); + }, + }, + "wazuh.monitoring.frequency": { + title: "Frequency", + description: "Frequency, in seconds, of API requests to get the state of the agents and create a new document in the wazuh-monitoring index with this data.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.number, + defaultValue: WAZUH_MONITORING_DEFAULT_FREQUENCY, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRestartingPluginPlatform: true, + options: { + number: { + min: 60 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number){ + return String(value) + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + }, + "wazuh.monitoring.pattern": { + title: "Index pattern", + description: "Default index pattern to use for Wazuh monitoring.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.text, + defaultValue: WAZUH_MONITORING_PATTERN, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + }, + "wazuh.monitoring.replicas": { + title: "Index replicas", + description: "Define the number of replicas to use for the wazuh-monitoring-* indices.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.number, + defaultValue: WAZUH_MONITORING_DEFAULT_INDICES_REPLICAS, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + options: { + number: { + min: 0 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number){ + return String(value) + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + }, + "wazuh.monitoring.shards": { + title: "Index shards", + description: "Define the number of shards to use for the wazuh-monitoring-* indices.", + category: SettingCategory.MONITORING, + type: EpluginSettingType.number, + defaultValue: WAZUH_MONITORING_DEFAULT_INDICES_SHARDS, + isConfigurableFromFile: true, + isConfigurableFromUI: true, + requiresRunningHealthCheck: true, + options: { + number: { + min: 1 + } + }, + uiFormTransformConfigurationValueToInputValue: function(value: number){ + return String(value) + }, + uiFormTransformInputValueToConfigurationValue: function(value: string): number { + return Number(value); + }, + } +}; + +export type TPluginSettingKey = keyof typeof PLUGIN_SETTINGS; + export enum HTTP_STATUS_CODES { CONTINUE = 100, diff --git a/common/services/settings.test.ts b/common/services/settings.test.ts new file mode 100644 index 0000000000..474236e172 --- /dev/null +++ b/common/services/settings.test.ts @@ -0,0 +1,34 @@ +import { + formatLabelValuePair, + formatSettingValueToFile +} from "./settings"; + +describe('[settings] Methods', () => { + + describe('formatLabelValuePair: Format the label-value pairs used to display the allowed values', () => { + it.each` + label | value | expected + ${'TestLabel'} | ${true} | ${'true (TestLabel)'} + ${'true'} | ${true} | ${'true'} + `(`label: $label | value: $value | expected: $expected`, ({ label, expected, value }) => { + expect(formatLabelValuePair(label, value)).toBe(expected); + }); + }); + + describe('formatSettingValueToFile: Format setting values to save in the configuration file', () => { + it.each` + input | expected + ${'test'} | ${'\"test\"'} + ${'test space'} | ${'\"test space\"'} + ${'test\nnew line'} | ${'\"test\\nnew line\"'} + ${''} | ${'\"\"'} + ${1} | ${1} + ${true} | ${true} + ${false} | ${false} + ${['test1']} | ${'[\"test1\"]'} + ${['test1', 'test2']} | ${'[\"test1\",\"test2\"]'} + `(`input: $input | expected: $expected`, ({ input, expected }) => { + expect(formatSettingValueToFile(input)).toBe(expected); + }); + }); +}); diff --git a/common/services/settings.ts b/common/services/settings.ts new file mode 100644 index 0000000000..4e88c6c5fe --- /dev/null +++ b/common/services/settings.ts @@ -0,0 +1,124 @@ +import { + PLUGIN_SETTINGS, + PLUGIN_SETTINGS_CATEGORIES, + TPluginSetting, + TPluginSettingKey, + TPluginSettingWithKey +} from '../constants'; + +/** + * Look for a configuration category setting by its name + * @param categoryTitle + * @returns category settings + */ +export function getCategorySettingByTitle(categoryTitle: string): any { + return Object.entries(PLUGIN_SETTINGS_CATEGORIES).find(([key, category]) => category?.title == categoryTitle)?.[1]; +} + +/** + * Get the default value of the plugin setting. + * @param setting setting key + * @returns setting default value. It returns `defaultValueIfNotSet` or `defaultValue`. + */ +export function getSettingDefaultValue(settingKey: string): any { + return typeof PLUGIN_SETTINGS[settingKey].defaultValueIfNotSet !== 'undefined' + ? PLUGIN_SETTINGS[settingKey].defaultValueIfNotSet + : PLUGIN_SETTINGS[settingKey].defaultValue; +}; + +/** + * Get the default settings configuration. key-value pair + * @returns an object with key-value pairs whose value is the default one + */ +export function getSettingsDefault() : {[key in TPluginSettingKey]: unknown} { + return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ({ + ...accum, + [pluginSettingID]: pluginSettingConfiguration.defaultValue + }), {}); +}; + +/** + * Get the settings grouped by category + * @returns an object whose keys are the categories and its value is an array of setting of that category + */ +export function getSettingsByCategories() : {[key: string]: TPluginSetting[]} { + return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ({ + ...accum, + [pluginSettingConfiguration.category]: [...(accum[pluginSettingConfiguration.category] || []), { ...pluginSettingConfiguration, key: pluginSettingID }] + }), {}); +}; + +/** + * Get the plugin settings as an array + * @returns an array of plugin setting denifitions including the key + */ +export function getSettingsDefaultList(): TPluginSettingWithKey[] { + return Object.entries(PLUGIN_SETTINGS).reduce((accum, [pluginSettingID, pluginSettingConfiguration]) => ([ + ...accum, + { ...pluginSettingConfiguration, key: pluginSettingID } + ]), []); +}; + +/** + * Format the plugin setting value received in the backend to store in the plugin configuration file (.yml). + * @param value plugin setting value sent to the endpoint + * @returns valid value to .yml + */ +export function formatSettingValueToFile(value: any) { + const formatter = formatSettingValueToFileType[typeof value] || formatSettingValueToFileType.default; + return formatter(value); +}; + +const formatSettingValueToFileType = { + string: (value: string): string => `"${value.replace(/"/,'\\"').replace(/\n/g,'\\n')}"`, // Escape the " character and new line + object: (value: any): string => JSON.stringify(value), + default: (value: any): any => value +}; + +/** + * Group the settings by category + * @param settings + * @returns + */ +export function groupSettingsByCategory(settings: TPluginSettingWithKey[]){ + const settingsSortedByCategories = settings + .sort((settingA, settingB) => settingA.key?.localeCompare?.(settingB.key)) + .reduce((accum, pluginSettingConfiguration) => ({ + ...accum, + [pluginSettingConfiguration.category]: [ + ...(accum[pluginSettingConfiguration.category] || []), + { ...pluginSettingConfiguration } + ] + }), {}); + + return Object.entries(settingsSortedByCategories) + .map(([category, settings]) => ({ category, settings })) + .filter(categoryEntry => categoryEntry.settings.length); +}; + +/** + * Get the plugin setting description composed. + * @param options + * @returns + */ + export function getPluginSettingDescription({description, options}: TPluginSetting): string{ + return [ + description, + ...(options?.select ? [`Allowed values: ${options.select.map(({text, value}) => formatLabelValuePair(text, value)).join(', ')}.`] : []), + ...(options?.switch ? [`Allowed values: ${['enabled', 'disabled'].map(s => formatLabelValuePair(options.switch.values[s].label, options.switch.values[s].value)).join(', ')}.`] : []), + ...(options?.number && 'min' in options.number ? [`Minimum value: ${options.number.min}.`] : []), + ...(options?.number && 'max' in options.number ? [`Maximum value: ${options.number.max}.`] : []), + ].join(' '); +}; + +/** + * Format the pair value-label to display the pair. If label and the string of value are equals, only displays the value, if not, displays both. + * @param value + * @param label + * @returns + */ +export function formatLabelValuePair(label, value){ + return label !== `${value}` + ? `${value} (${label})` + : `${value}` +}; diff --git a/public/components/common/form/__snapshots__/index.test.tsx.snap b/public/components/common/form/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..88693cee2d --- /dev/null +++ b/public/components/common/form/__snapshots__/index.test.tsx.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`[component] InputForm Renders correctly to match the snapshot: Input: editor 1`] = ` +
+
+
+

+ Press Enter to start editing. +

+

+ When you're done, press Escape to stop editing. +

+
+
+