Fleet UI: Add advanced setting to set expiry window for activity log (#17989)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Add advanced setting to set expiry window for activity log
|
||||
@@ -46,6 +46,10 @@ const DEFAULT_CONFIG_MOCK: IConfig = {
|
||||
host_expiry_enabled: false,
|
||||
host_expiry_window: 0,
|
||||
},
|
||||
activity_expiry_settings: {
|
||||
activity_expiry_enabled: true,
|
||||
activity_expiry_window: 90,
|
||||
},
|
||||
agent_options: "",
|
||||
license: {
|
||||
tier: "free",
|
||||
|
||||
@@ -65,41 +65,6 @@ export interface IFleetDesktopSettings {
|
||||
transparency_url: string;
|
||||
}
|
||||
|
||||
export interface IConfigFormData {
|
||||
smtpAuthenticationMethod: string;
|
||||
smtpAuthenticationType: string;
|
||||
domain: string;
|
||||
smtpEnableSslTls: boolean;
|
||||
enableStartTls: boolean;
|
||||
serverUrl: string;
|
||||
orgLogoUrl: string;
|
||||
orgName: string;
|
||||
smtpPassword: string;
|
||||
smtpPort?: number;
|
||||
smtpSenderAddress: string;
|
||||
smtpServer: string;
|
||||
smtpUsername: string;
|
||||
verifySslCerts: boolean;
|
||||
entityId: string;
|
||||
idpImageUrl: string;
|
||||
metadata: string;
|
||||
metadataUrl: string;
|
||||
idpName: string;
|
||||
enableSso: boolean;
|
||||
enableSsoIdpLogin: boolean;
|
||||
enableSmtp: boolean;
|
||||
enableHostExpiry: boolean;
|
||||
hostExpiryWindow: number;
|
||||
disableLiveQuery: boolean;
|
||||
agentOptions: any;
|
||||
enableHostStatusWebhook: boolean;
|
||||
hostStatusWebhookDestinationUrl?: string;
|
||||
hostStatusWebhookHostPercentage?: number;
|
||||
hostStatusWebhookDaysCount?: number;
|
||||
enableUsageStatistics: boolean;
|
||||
transparencyUrl: string;
|
||||
}
|
||||
|
||||
export interface IConfigFeatures {
|
||||
enable_host_users: boolean;
|
||||
enable_software_inventory: boolean;
|
||||
@@ -125,7 +90,7 @@ export interface IConfig {
|
||||
server_settings: IConfigServerSettings;
|
||||
smtp_settings?: {
|
||||
enable_smtp: boolean;
|
||||
configured: boolean;
|
||||
configured?: boolean;
|
||||
sender_address: string;
|
||||
server: string;
|
||||
port?: number;
|
||||
@@ -152,10 +117,14 @@ export interface IConfig {
|
||||
};
|
||||
host_expiry_settings: {
|
||||
host_expiry_enabled: boolean;
|
||||
host_expiry_window: number;
|
||||
host_expiry_window?: number;
|
||||
};
|
||||
activity_expiry_settings: {
|
||||
activity_expiry_enabled: boolean;
|
||||
activity_expiry_window?: number;
|
||||
};
|
||||
features: IConfigFeatures;
|
||||
agent_options: string;
|
||||
agent_options: unknown; // Can pass empty object
|
||||
update_interval: {
|
||||
osquery_detail: number;
|
||||
osquery_policy: number;
|
||||
|
||||
@@ -1,31 +1,53 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Checkbox from "components/forms/fields/Checkbox";
|
||||
// @ts-ignore
|
||||
import InputField from "components/forms/fields/InputField";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
// @ts-ignore
|
||||
import Dropdown from "components/forms/fields/Dropdown";
|
||||
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
import { ACTIVITY_EXPIRY_WINDOW_DROPDOWN_OPTIONS } from "utilities/constants";
|
||||
import { getCustomDropdownOptions } from "utilities/helpers";
|
||||
|
||||
import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
interface IAdvancedConfigFormData {
|
||||
domain: string;
|
||||
verifySSLCerts: boolean;
|
||||
enableStartTLS?: boolean;
|
||||
enableHostExpiry: boolean;
|
||||
hostExpiryWindow: number;
|
||||
deleteActivities: boolean;
|
||||
activityExpiryWindow: number;
|
||||
disableLiveQuery: boolean;
|
||||
disableScripts: boolean;
|
||||
disableQueryReports: boolean;
|
||||
}
|
||||
|
||||
interface IAdvancedConfigFormErrors {
|
||||
host_expiry_window?: string | null;
|
||||
}
|
||||
|
||||
const Advanced = ({
|
||||
appConfig,
|
||||
handleSubmit,
|
||||
isUpdatingSettings,
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const [formData, setFormData] = useState({
|
||||
const [formData, setFormData] = useState<IAdvancedConfigFormData>({
|
||||
domain: appConfig.smtp_settings?.domain || "",
|
||||
verifySSLCerts: appConfig.smtp_settings?.verify_ssl_certs || false,
|
||||
enableStartTLS: appConfig.smtp_settings?.enable_start_tls,
|
||||
enableHostExpiry:
|
||||
appConfig.host_expiry_settings.host_expiry_enabled || false,
|
||||
hostExpiryWindow: appConfig.host_expiry_settings.host_expiry_window || 0,
|
||||
deleteActivities:
|
||||
appConfig.activity_expiry_settings?.activity_expiry_enabled || false,
|
||||
activityExpiryWindow:
|
||||
appConfig.activity_expiry_settings?.activity_expiry_window || 30,
|
||||
disableLiveQuery: appConfig.server_settings.live_query_disabled || false,
|
||||
disableQueryReports:
|
||||
appConfig.server_settings.query_reports_disabled || false,
|
||||
@@ -38,20 +60,35 @@ const Advanced = ({
|
||||
enableStartTLS,
|
||||
enableHostExpiry,
|
||||
hostExpiryWindow,
|
||||
deleteActivities,
|
||||
activityExpiryWindow,
|
||||
disableLiveQuery,
|
||||
disableScripts,
|
||||
disableQueryReports,
|
||||
} = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<IAdvancedConfigFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const activityExpiryWindowOptions = useMemo(
|
||||
() =>
|
||||
getCustomDropdownOptions(
|
||||
ACTIVITY_EXPIRY_WINDOW_DROPDOWN_OPTIONS,
|
||||
activityExpiryWindow,
|
||||
// it's safe to assume that frequency is a number
|
||||
(frequency: number | string) => `${frequency as number} days`
|
||||
),
|
||||
// intentionally leave activityExpiryWindow out of the dependencies, so that the custom
|
||||
// options are maintained even if the user changes the frequency in the UI
|
||||
[deleteActivities]
|
||||
);
|
||||
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// validate desired form fields
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IAdvancedConfigFormErrors = {};
|
||||
|
||||
if (enableHostExpiry && (!hostExpiryWindow || hostExpiryWindow <= 0)) {
|
||||
errors.host_expiry_window =
|
||||
@@ -72,12 +109,13 @@ const Advanced = ({
|
||||
enable_analytics: appConfig.server_settings.enable_analytics,
|
||||
query_reports_disabled: disableQueryReports,
|
||||
scripts_disabled: disableScripts,
|
||||
deferred_save_host: appConfig.server_settings.deferred_save_host,
|
||||
},
|
||||
smtp_settings: {
|
||||
enable_smtp: appConfig.smtp_settings?.enable_smtp || false,
|
||||
sender_address: appConfig.smtp_settings?.sender_address || "",
|
||||
server: appConfig.smtp_settings?.server || "",
|
||||
port: Number(appConfig.smtp_settings?.port),
|
||||
port: appConfig.smtp_settings?.port || undefined,
|
||||
authentication_type: appConfig.smtp_settings?.authentication_type || "",
|
||||
user_name: appConfig.smtp_settings?.user_name || "",
|
||||
password: appConfig.smtp_settings?.password || "",
|
||||
@@ -86,11 +124,15 @@ const Advanced = ({
|
||||
appConfig.smtp_settings?.authentication_method || "",
|
||||
domain,
|
||||
verify_ssl_certs: verifySSLCerts,
|
||||
enable_start_tls: enableStartTLS,
|
||||
enable_start_tls: enableStartTLS || false,
|
||||
},
|
||||
host_expiry_settings: {
|
||||
host_expiry_enabled: enableHostExpiry,
|
||||
host_expiry_window: Number(hostExpiryWindow),
|
||||
host_expiry_window: hostExpiryWindow || undefined,
|
||||
},
|
||||
activity_expiry_settings: {
|
||||
activity_expiry_enabled: deleteActivities,
|
||||
activity_expiry_window: activityExpiryWindow || undefined,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -107,7 +149,7 @@ const Advanced = ({
|
||||
</p>
|
||||
<InputField
|
||||
label="Domain"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="domain"
|
||||
value={domain}
|
||||
parseTarget
|
||||
@@ -122,7 +164,7 @@ const Advanced = ({
|
||||
}
|
||||
/>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="verifySSLCerts"
|
||||
value={verifySSLCerts}
|
||||
parseTarget
|
||||
@@ -140,7 +182,7 @@ const Advanced = ({
|
||||
Verify SSL certs
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableStartTLS"
|
||||
value={enableStartTLS}
|
||||
parseTarget
|
||||
@@ -158,7 +200,7 @@ const Advanced = ({
|
||||
Enable STARTTLS
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableHostExpiry"
|
||||
value={enableHostExpiry}
|
||||
parseTarget
|
||||
@@ -187,7 +229,7 @@ const Advanced = ({
|
||||
<InputField
|
||||
label="Host expiry window"
|
||||
type="number"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="hostExpiryWindow"
|
||||
value={hostExpiryWindow}
|
||||
parseTarget
|
||||
@@ -195,7 +237,37 @@ const Advanced = ({
|
||||
/>
|
||||
)}
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="deleteActivities"
|
||||
value={deleteActivities}
|
||||
parseTarget
|
||||
tooltipContent={
|
||||
<>
|
||||
When enabled, allows automatic cleanup of audit logs older than
|
||||
the number of days specified in the{" "}
|
||||
<em>Audit log retention window</em> setting.
|
||||
<em>
|
||||
(Default: <strong>Off</strong>)
|
||||
</em>
|
||||
</>
|
||||
}
|
||||
>
|
||||
Delete activities
|
||||
</Checkbox>
|
||||
{deleteActivities && (
|
||||
<Dropdown
|
||||
searchable={false}
|
||||
options={activityExpiryWindowOptions}
|
||||
onChange={onInputChange}
|
||||
placeholder="Select"
|
||||
value={activityExpiryWindow}
|
||||
label="Max activity age"
|
||||
name="Max activity age"
|
||||
parseTarget
|
||||
/>
|
||||
)}
|
||||
<Checkbox
|
||||
onChange={onInputChange}
|
||||
name="disableLiveQuery"
|
||||
value={disableLiveQuery}
|
||||
parseTarget
|
||||
@@ -212,7 +284,7 @@ const Advanced = ({
|
||||
Disable live queries
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="disableScripts"
|
||||
value={disableScripts}
|
||||
parseTarget
|
||||
@@ -229,7 +301,7 @@ const Advanced = ({
|
||||
Disable scripts
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="disableQueryReports"
|
||||
value={disableQueryReports}
|
||||
parseTarget
|
||||
|
||||
@@ -13,10 +13,18 @@ import YamlAce from "components/YamlAce";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
|
||||
import { IAppConfigFormProps, IAppConfigFormErrors } from "../constants";
|
||||
import { IAppConfigFormProps } from "../constants";
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
interface IAgentOptionsFormData {
|
||||
agentOptions?: string;
|
||||
}
|
||||
|
||||
interface IAgentOptionsFormErrors {
|
||||
agent_options?: string | null;
|
||||
}
|
||||
|
||||
const Agents = ({
|
||||
appConfig,
|
||||
handleSubmit,
|
||||
@@ -25,10 +33,10 @@ const Agents = ({
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const { ADMIN_TEAMS } = paths;
|
||||
|
||||
const [formData, setFormData] = useState<any>({
|
||||
const [formData, setFormData] = useState<IAgentOptionsFormData>({
|
||||
agentOptions: agentOptionsToYaml(appConfig.agent_options),
|
||||
});
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<IAgentOptionsFormErrors>({});
|
||||
|
||||
const { agentOptions } = formData;
|
||||
|
||||
@@ -37,7 +45,7 @@ const Agents = ({
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IAgentOptionsFormErrors = {};
|
||||
|
||||
if (agentOptions) {
|
||||
const { error: yamlError, valid: yamlValid } = validateYaml(agentOptions);
|
||||
@@ -58,7 +66,7 @@ const Agents = ({
|
||||
evt.preventDefault();
|
||||
|
||||
// Formatting of API not UI and allows empty agent options
|
||||
const formDataToSubmit = agentOptions
|
||||
const formDataToSubmit: any = agentOptions
|
||||
? {
|
||||
agent_options: yaml.load(agentOptions),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { IConfig, IConfigFormData } from "interfaces/config";
|
||||
import { IConfig } from "interfaces/config";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
// @ts-ignore
|
||||
@@ -13,9 +13,14 @@ import {
|
||||
DEFAULT_TRANSPARENCY_URL,
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
|
||||
interface IFleetDesktopFormData {
|
||||
transparencyUrl: string;
|
||||
}
|
||||
interface IFleetDesktopFormErrors {
|
||||
transparency_url?: string | null;
|
||||
}
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
const FleetDesktop = ({
|
||||
@@ -24,16 +29,14 @@ const FleetDesktop = ({
|
||||
isPremiumTier,
|
||||
isUpdatingSettings,
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const [formData, setFormData] = useState<
|
||||
Pick<IConfigFormData, "transparencyUrl">
|
||||
>({
|
||||
const [formData, setFormData] = useState<IFleetDesktopFormData>({
|
||||
transparencyUrl:
|
||||
appConfig.fleet_desktop?.transparency_url || DEFAULT_TRANSPARENCY_URL,
|
||||
});
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<IFleetDesktopFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ value }: IFormField) => {
|
||||
const onInputChange = ({ value }: IFormField) => {
|
||||
setFormData({ transparencyUrl: value.toString() });
|
||||
setFormErrors({});
|
||||
};
|
||||
@@ -41,7 +44,7 @@ const FleetDesktop = ({
|
||||
const validateForm = () => {
|
||||
const { transparencyUrl } = formData;
|
||||
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IFleetDesktopFormErrors = {};
|
||||
if (transparencyUrl && !validUrl({ url: transparencyUrl })) {
|
||||
errors.transparency_url = `${transparencyUrl} is not a valid URL`;
|
||||
}
|
||||
@@ -72,7 +75,7 @@ const FleetDesktop = ({
|
||||
<form onSubmit={onFormSubmit} autoComplete="off">
|
||||
<InputField
|
||||
label="Custom transparency URL"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="transparency_url"
|
||||
value={formData.transparencyUrl}
|
||||
parseTarget
|
||||
|
||||
+24
-17
@@ -18,20 +18,20 @@ import InputField from "components/forms/fields/InputField";
|
||||
import validUrl from "components/forms/validators/valid_url";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
export type IGlobalHostStatusWebhookFormData = {
|
||||
interface IGlobalHostStatusWebhookFormData {
|
||||
enableHostStatusWebhook: boolean;
|
||||
destination_url: string;
|
||||
destination_url?: string;
|
||||
hostStatusWebhookHostPercentage: number;
|
||||
hostStatusWebhookWindow: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface IGlobalHostStatusWebhookFormErrors {
|
||||
destination_url?: string;
|
||||
}
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
const GlobalHostStatusWebhook = ({
|
||||
appConfig,
|
||||
@@ -61,15 +61,18 @@ const GlobalHostStatusWebhook = ({
|
||||
hostStatusWebhookWindow,
|
||||
} = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [
|
||||
formErrors,
|
||||
setFormErrors,
|
||||
] = useState<IGlobalHostStatusWebhookFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
setFormErrors({});
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IGlobalHostStatusWebhookFormErrors = {};
|
||||
|
||||
if (enableHostStatusWebhook) {
|
||||
if (!destination_url) {
|
||||
@@ -103,6 +106,10 @@ const GlobalHostStatusWebhook = ({
|
||||
host_percentage: hostStatusWebhookHostPercentage,
|
||||
days_count: hostStatusWebhookWindow,
|
||||
},
|
||||
failing_policies_webhook:
|
||||
appConfig.webhook_settings.failing_policies_webhook,
|
||||
vulnerabilities_webhook:
|
||||
appConfig.webhook_settings.vulnerabilities_webhook,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -139,7 +146,7 @@ const GlobalHostStatusWebhook = ({
|
||||
Send an alert if a portion of your hosts go offline.
|
||||
</p>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableHostStatusWebhook"
|
||||
value={enableHostStatusWebhook}
|
||||
parseTarget
|
||||
@@ -165,7 +172,7 @@ const GlobalHostStatusWebhook = ({
|
||||
<InputField
|
||||
placeholder="https://server.com/example"
|
||||
label="Destination URL"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="destination_url"
|
||||
value={destination_url}
|
||||
parseTarget
|
||||
@@ -181,7 +188,7 @@ const GlobalHostStatusWebhook = ({
|
||||
<Dropdown
|
||||
label="Percentage of hosts"
|
||||
options={percentageHostsOptions}
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="hostStatusWebhookHostPercentage"
|
||||
value={hostStatusWebhookHostPercentage}
|
||||
parseTarget
|
||||
@@ -200,7 +207,7 @@ const GlobalHostStatusWebhook = ({
|
||||
<Dropdown
|
||||
label="Number of days"
|
||||
options={windowOptions}
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="hostStatusWebhookWindow"
|
||||
value={hostStatusWebhookWindow}
|
||||
parseTarget
|
||||
|
||||
@@ -8,19 +8,22 @@ import OrgLogoIcon from "components/icons/OrgLogoIcon";
|
||||
import validUrl from "components/forms/validators/valid_url";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
interface IOrgInfoFormData {
|
||||
orgName: string;
|
||||
orgLogoURL: string;
|
||||
orgName: string;
|
||||
orgLogoURLLightBackground: string;
|
||||
orgSupportURL: string;
|
||||
}
|
||||
|
||||
interface IOrgInfoFormErrors {
|
||||
org_name?: string | null;
|
||||
org_logo_url?: string | null;
|
||||
org_logo_url_light_background?: string | null;
|
||||
org_support_url?: string | null;
|
||||
}
|
||||
|
||||
// TODO: change base classes to these cards to follow the same pattern as the
|
||||
// other components in the app.
|
||||
const baseClass = "app-config-form";
|
||||
@@ -47,15 +50,15 @@ const Info = ({
|
||||
orgSupportURL,
|
||||
} = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<IOrgInfoFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
setFormErrors({});
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IOrgInfoFormErrors = {};
|
||||
|
||||
if (!orgName) {
|
||||
errors.org_name = "Organization name must be present";
|
||||
@@ -96,7 +99,7 @@ const Info = ({
|
||||
<form onSubmit={onFormSubmit} autoComplete="off">
|
||||
<InputField
|
||||
label="Organization name"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="orgName"
|
||||
value={orgName}
|
||||
parseTarget
|
||||
@@ -105,7 +108,7 @@ const Info = ({
|
||||
/>
|
||||
<InputField
|
||||
label="Organization support URL"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="orgSupportURL"
|
||||
value={orgSupportURL}
|
||||
parseTarget
|
||||
@@ -115,7 +118,7 @@ const Info = ({
|
||||
<div className={`${cardClass}__logo-field-set`}>
|
||||
<InputField
|
||||
label="Organization avatar URL (for dark backgrounds)"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="orgLogoURL"
|
||||
value={orgLogoURL}
|
||||
parseTarget
|
||||
@@ -137,7 +140,7 @@ const Info = ({
|
||||
<div className={`${cardClass}__logo-field-set`}>
|
||||
<InputField
|
||||
label="Organization avatar URL (for light backgrounds)"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="orgLogoURLLightBackground"
|
||||
value={orgLogoURLLightBackground}
|
||||
parseTarget
|
||||
|
||||
@@ -16,11 +16,30 @@ import SectionHeader from "components/SectionHeader";
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
authMethodOptions,
|
||||
authTypeOptions,
|
||||
} from "../constants";
|
||||
|
||||
interface ISmtpConfigFormData {
|
||||
enableSMTP: boolean;
|
||||
smtpSenderAddress: string;
|
||||
smtpServer: string;
|
||||
smtpPort?: number;
|
||||
smtpEnableSSLTLS: boolean;
|
||||
smtpAuthenticationType: string;
|
||||
smtpUsername: string;
|
||||
smtpPassword: string;
|
||||
smtpAuthenticationMethod: string;
|
||||
}
|
||||
|
||||
interface ISmtpConfigFormErrors {
|
||||
sender_address?: string | null;
|
||||
server?: string | null;
|
||||
server_port?: string | null;
|
||||
user_name?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
const Smtp = ({
|
||||
@@ -30,7 +49,7 @@ const Smtp = ({
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const { isPremiumTier } = useContext(AppContext);
|
||||
|
||||
const [formData, setFormData] = useState<any>({
|
||||
const [formData, setFormData] = useState<ISmtpConfigFormData>({
|
||||
enableSMTP: appConfig.smtp_settings?.enable_smtp || false,
|
||||
smtpSenderAddress: appConfig.smtp_settings?.sender_address || "",
|
||||
smtpServer: appConfig.smtp_settings?.server || "",
|
||||
@@ -55,16 +74,16 @@ const Smtp = ({
|
||||
smtpAuthenticationMethod,
|
||||
} = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<ISmtpConfigFormErrors>({});
|
||||
|
||||
const sesConfigured = appConfig.email?.backend === "ses" || false;
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: ISmtpConfigFormErrors = {};
|
||||
|
||||
if (enableSMTP) {
|
||||
if (!smtpSenderAddress) {
|
||||
@@ -118,7 +137,8 @@ const Smtp = ({
|
||||
authentication_method: smtpAuthenticationMethod,
|
||||
domain: appConfig.smtp_settings?.domain || "",
|
||||
verify_ssl_certs: appConfig.smtp_settings?.verify_ssl_certs || false,
|
||||
enable_start_tls: appConfig.smtp_settings?.enable_start_tls,
|
||||
enable_start_tls: appConfig.smtp_settings?.enable_start_tls || false,
|
||||
configured: appConfig.smtp_settings?.configured || false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -134,7 +154,7 @@ const Smtp = ({
|
||||
<>
|
||||
<InputField
|
||||
label="SMTP username"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpUsername"
|
||||
value={smtpUsername}
|
||||
parseTarget
|
||||
@@ -145,7 +165,7 @@ const Smtp = ({
|
||||
<InputField
|
||||
label="SMTP password"
|
||||
type="password"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpPassword"
|
||||
value={smtpPassword}
|
||||
parseTarget
|
||||
@@ -157,7 +177,7 @@ const Smtp = ({
|
||||
label="Auth method"
|
||||
options={authMethodOptions}
|
||||
placeholder=""
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpAuthenticationMethod"
|
||||
value={smtpAuthenticationMethod}
|
||||
parseTarget
|
||||
@@ -189,7 +209,7 @@ const Smtp = ({
|
||||
return (
|
||||
<form onSubmit={onFormSubmit} autoComplete="off">
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableSMTP"
|
||||
value={enableSMTP}
|
||||
parseTarget
|
||||
@@ -198,7 +218,7 @@ const Smtp = ({
|
||||
</Checkbox>
|
||||
<InputField
|
||||
label="Sender address"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpSenderAddress"
|
||||
value={smtpSenderAddress}
|
||||
parseTarget
|
||||
@@ -209,7 +229,7 @@ const Smtp = ({
|
||||
<div className="smtp-server-inputs">
|
||||
<InputField
|
||||
label="SMTP server"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpServer"
|
||||
value={smtpServer}
|
||||
parseTarget
|
||||
@@ -220,7 +240,7 @@ const Smtp = ({
|
||||
<InputField
|
||||
label=" "
|
||||
type="number"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpPort"
|
||||
value={smtpPort}
|
||||
parseTarget
|
||||
@@ -229,7 +249,7 @@ const Smtp = ({
|
||||
/>
|
||||
</div>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpEnableSSLTLS"
|
||||
value={smtpEnableSSLTLS}
|
||||
parseTarget
|
||||
@@ -239,7 +259,7 @@ const Smtp = ({
|
||||
<Dropdown
|
||||
label="Authentication type"
|
||||
options={authTypeOptions}
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="smtpAuthenticationType"
|
||||
value={smtpAuthenticationType}
|
||||
parseTarget
|
||||
|
||||
@@ -8,23 +8,27 @@ import InputField from "components/forms/fields/InputField";
|
||||
import validUrl from "components/forms/validators/valid_url";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
interface ISsoFormData {
|
||||
enableSso?: boolean;
|
||||
idpName?: string;
|
||||
entityId?: string;
|
||||
idpImageUrl?: string;
|
||||
metadata?: string;
|
||||
metadataUrl?: string;
|
||||
enableSsoIdpLogin?: boolean;
|
||||
enableJitProvisioning?: boolean;
|
||||
idpName: string;
|
||||
enableSso: boolean;
|
||||
entityId: string;
|
||||
idpImageUrl: string;
|
||||
metadata: string;
|
||||
metadataUrl: string;
|
||||
enableSsoIdpLogin: boolean;
|
||||
enableJitProvisioning: boolean;
|
||||
}
|
||||
|
||||
interface ISsoFormErrors {
|
||||
idp_image_url?: string | null;
|
||||
metadata?: string | null;
|
||||
metadata_url?: string | null;
|
||||
entity_id?: string | null;
|
||||
idp_name?: string | null;
|
||||
}
|
||||
|
||||
const Sso = ({
|
||||
@@ -56,14 +60,14 @@ const Sso = ({
|
||||
enableJitProvisioning,
|
||||
} = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<ISsoFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: ISsoFormErrors = {};
|
||||
|
||||
if (enableSso) {
|
||||
if (idpImageUrl && !validUrl({ url: idpImageUrl })) {
|
||||
@@ -113,6 +117,8 @@ const Sso = ({
|
||||
enable_sso: enableSso,
|
||||
enable_sso_idp_login: enableSsoIdpLogin,
|
||||
enable_jit_provisioning: enableJitProvisioning,
|
||||
issuer_uri: appConfig.sso_settings.issuer_uri,
|
||||
enable_jit_role_sync: appConfig.sso_settings.enable_jit_role_sync,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -125,7 +131,7 @@ const Sso = ({
|
||||
<SectionHeader title="Single sign-on options" />
|
||||
<form onSubmit={onFormSubmit} autoComplete="off">
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableSso"
|
||||
value={enableSso}
|
||||
parseTarget
|
||||
@@ -134,7 +140,7 @@ const Sso = ({
|
||||
</Checkbox>
|
||||
<InputField
|
||||
label="Identity provider name"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="idpName"
|
||||
value={idpName}
|
||||
parseTarget
|
||||
@@ -145,7 +151,7 @@ const Sso = ({
|
||||
<InputField
|
||||
label="Entity ID"
|
||||
helpText="The URI you provide here must exactly match the Entity ID field used in identity provider configuration."
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="entityId"
|
||||
value={entityId}
|
||||
parseTarget
|
||||
@@ -155,7 +161,7 @@ const Sso = ({
|
||||
/>
|
||||
<InputField
|
||||
label="IDP image URL"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="idpImageUrl"
|
||||
value={idpImageUrl}
|
||||
parseTarget
|
||||
@@ -167,7 +173,7 @@ const Sso = ({
|
||||
<InputField
|
||||
label="Metadata"
|
||||
type="textarea"
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="metadata"
|
||||
value={metadata}
|
||||
parseTarget
|
||||
@@ -179,7 +185,7 @@ const Sso = ({
|
||||
<InputField
|
||||
label="Metadata URL"
|
||||
helpText="If available from the identity provider, this is the preferred means of providing metadata."
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="metadataUrl"
|
||||
value={metadataUrl}
|
||||
parseTarget
|
||||
@@ -188,7 +194,7 @@ const Sso = ({
|
||||
tooltip="A URL that references the identity provider metadata."
|
||||
/>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableSsoIdpLogin"
|
||||
value={enableSsoIdpLogin}
|
||||
parseTarget
|
||||
@@ -197,7 +203,7 @@ const Sso = ({
|
||||
</Checkbox>
|
||||
{isPremiumTier && (
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableJitProvisioning"
|
||||
value={enableJitProvisioning}
|
||||
parseTarget
|
||||
|
||||
@@ -9,19 +9,23 @@ import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
interface IStatisticsFormData {
|
||||
enableUsageStatistics: boolean;
|
||||
}
|
||||
|
||||
const Statistics = ({
|
||||
appConfig,
|
||||
handleSubmit,
|
||||
isPremiumTier,
|
||||
isUpdatingSettings,
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const [formData, setFormData] = useState<any>({
|
||||
const [formData, setFormData] = useState<IStatisticsFormData>({
|
||||
enableUsageStatistics: appConfig.server_settings.enable_analytics,
|
||||
});
|
||||
|
||||
const { enableUsageStatistics } = formData;
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
};
|
||||
|
||||
@@ -35,6 +39,10 @@ const Statistics = ({
|
||||
live_query_disabled:
|
||||
appConfig.server_settings.live_query_disabled || false,
|
||||
enable_analytics: enableUsageStatistics,
|
||||
deferred_save_host: appConfig.server_settings.deferred_save_host,
|
||||
query_reports_disabled:
|
||||
appConfig.server_settings.query_reports_disabled,
|
||||
scripts_disabled: appConfig.server_settings.scripts_disabled,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -63,7 +71,7 @@ const Statistics = ({
|
||||
/>
|
||||
</p>
|
||||
<Checkbox
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="enableUsageStatistics"
|
||||
value={isPremiumTier ? true : enableUsageStatistics} // Set to true for all premium customers
|
||||
parseTarget
|
||||
|
||||
@@ -6,11 +6,15 @@ import InputField from "components/forms/fields/InputField";
|
||||
import validUrl from "components/forms/validators/valid_url";
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
|
||||
import {
|
||||
IAppConfigFormProps,
|
||||
IFormField,
|
||||
IAppConfigFormErrors,
|
||||
} from "../constants";
|
||||
import { IAppConfigFormProps, IFormField } from "../constants";
|
||||
|
||||
interface IWebAddressFormData {
|
||||
serverURL: string;
|
||||
}
|
||||
|
||||
interface IWebAddressFormErrors {
|
||||
server_url?: string | null;
|
||||
}
|
||||
|
||||
const baseClass = "app-config-form";
|
||||
|
||||
@@ -19,21 +23,21 @@ const WebAddress = ({
|
||||
handleSubmit,
|
||||
isUpdatingSettings,
|
||||
}: IAppConfigFormProps): JSX.Element => {
|
||||
const [formData, setFormData] = useState<any>({
|
||||
const [formData, setFormData] = useState<IWebAddressFormData>({
|
||||
serverURL: appConfig.server_settings.server_url || "",
|
||||
});
|
||||
|
||||
const { serverURL } = formData;
|
||||
|
||||
const [formErrors, setFormErrors] = useState<IAppConfigFormErrors>({});
|
||||
const [formErrors, setFormErrors] = useState<IWebAddressFormErrors>({});
|
||||
|
||||
const handleInputChange = ({ name, value }: IFormField) => {
|
||||
const onInputChange = ({ name, value }: IFormField) => {
|
||||
setFormData({ ...formData, [name]: value });
|
||||
setFormErrors({});
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors: IAppConfigFormErrors = {};
|
||||
const errors: IWebAddressFormErrors = {};
|
||||
if (!serverURL) {
|
||||
errors.server_url = "Fleet server URL must be present";
|
||||
} else if (!validUrl({ url: serverURL, protocol: "http" })) {
|
||||
@@ -52,6 +56,10 @@ const WebAddress = ({
|
||||
server_url: serverURL,
|
||||
live_query_disabled: appConfig.server_settings.live_query_disabled,
|
||||
enable_analytics: appConfig.server_settings.enable_analytics,
|
||||
deferred_save_host: appConfig.server_settings.deferred_save_host,
|
||||
query_reports_disabled:
|
||||
appConfig.server_settings.query_reports_disabled,
|
||||
scripts_disabled: appConfig.server_settings.scripts_disabled,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -70,7 +78,7 @@ const WebAddress = ({
|
||||
Include base path only (eg. no <code>/latest</code>)
|
||||
</>
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
onChange={onInputChange}
|
||||
name="serverURL"
|
||||
value={serverURL}
|
||||
parseTarget
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface IAppConfigFormProps {
|
||||
appConfig: IConfig;
|
||||
isPremiumTier?: boolean;
|
||||
isUpdatingSettings?: boolean;
|
||||
handleSubmit: any;
|
||||
handleSubmit: (formUpdates: Partial<IConfig>) => false | undefined;
|
||||
}
|
||||
|
||||
export interface IFormField {
|
||||
@@ -14,30 +14,6 @@ export interface IFormField {
|
||||
value: string | boolean | number;
|
||||
}
|
||||
|
||||
export interface IAppConfigFormErrors {
|
||||
metadata?: string | null;
|
||||
metadata_url?: string | null;
|
||||
entity_id?: string | null;
|
||||
idp_name?: string | null;
|
||||
server_url?: string | null;
|
||||
org_name?: string | null;
|
||||
org_logo_url?: string | null;
|
||||
org_logo_url_light_background?: string | null;
|
||||
org_support_url?: string | null;
|
||||
idp_image_url?: string | null;
|
||||
sender_address?: string | null;
|
||||
server?: string | null;
|
||||
server_port?: string | null;
|
||||
user_name?: string | null;
|
||||
password?: string | null;
|
||||
destination_url?: string | null;
|
||||
days_count?: string | null;
|
||||
host_percentage?: string | null;
|
||||
host_expiry_window?: string | null;
|
||||
agent_options?: string | null;
|
||||
transparency_url?: string | null;
|
||||
}
|
||||
|
||||
export const authMethodOptions = [
|
||||
{ label: "Plain", value: "authmethod_plain" },
|
||||
{ label: "Cram MD5", value: "authmethod_cram_md5" },
|
||||
|
||||
@@ -26,6 +26,12 @@ export const DEFAULT_GRAVATAR_LINK_FALLBACK =
|
||||
export const DEFAULT_GRAVATAR_LINK_DARK_FALLBACK =
|
||||
"/assets/images/icon-avatar-default-dark-24x24%402x.png";
|
||||
|
||||
export const ACTIVITY_EXPIRY_WINDOW_DROPDOWN_OPTIONS: IDropdownOption[] = [
|
||||
{ value: 30, label: "30 days" },
|
||||
{ value: 60, label: "60 days" },
|
||||
{ value: 90, label: "90 days" },
|
||||
];
|
||||
|
||||
export const FREQUENCY_DROPDOWN_OPTIONS: IDropdownOption[] = [
|
||||
{ value: 0, label: "Never" },
|
||||
{ value: 300, label: "Every 5 minutes" },
|
||||
|
||||
Reference in New Issue
Block a user