Skip to content

Commit

Permalink
[7.8] [Uptime] Settings threshold validation (#65454) (#66178)
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzad31 authored May 12, 2020
1 parent 3ed215e commit 46856c8
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 29 deletions.
14 changes: 14 additions & 0 deletions x-pack/plugins/uptime/common/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';

export const VALUE_MUST_BE_GREATER_THEN_ZEO = i18n.translate(
'xpack.uptime.settings.invalid.error',
{
defaultMessage: 'Value must be greater than 0.',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
import { DynamicSettings } from '../../../common/runtime_types';
import { SettingsFormProps } from '../../pages/settings';
import { certificateFormTranslations } from './translations';

export type OnFieldChangeType = (changedValues: Partial<DynamicSettings>) => void;

Expand Down Expand Up @@ -58,7 +59,7 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
>
<EuiFormRow
describedByIds={['errorState']}
error={fieldErrors?.certificatesThresholds?.expirationThresholdError}
error={fieldErrors?.expirationThresholdError}
fullWidth
helpText={
<FormattedMessage
Expand All @@ -69,7 +70,7 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
}}
/>
}
isInvalid={!!fieldErrors?.certificatesThresholds?.expirationThresholdError}
isInvalid={!!fieldErrors?.expirationThresholdError}
label={
<FormattedMessage
id="xpack.uptime.sourceConfiguration.errorStateLabel"
Expand All @@ -80,6 +81,8 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
<EuiFlexGroup>
<EuiFlexItem grow={2}>
<EuiFieldNumber
min={1}
aria-label={certificateFormTranslations.expirationInputAriaLabel}
data-test-subj={`expiration-threshold-input-${loading ? 'loading' : 'loaded'}`}
fullWidth
disabled={isDisabled}
Expand All @@ -104,7 +107,7 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
</EuiFormRow>
<EuiFormRow
describedByIds={['warningState']}
error={fieldErrors?.certificatesThresholds?.ageThresholdError}
error={fieldErrors?.ageThresholdError}
fullWidth
helpText={
<FormattedMessage
Expand All @@ -115,7 +118,7 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
}}
/>
}
isInvalid={!!fieldErrors?.certificatesThresholds?.ageThresholdError}
isInvalid={!!fieldErrors?.ageThresholdError}
label={
<FormattedMessage
id="xpack.uptime.sourceConfiguration.warningStateLabel"
Expand All @@ -126,14 +129,16 @@ export const CertificateExpirationForm: React.FC<SettingsFormProps> = ({
<EuiFlexGroup>
<EuiFlexItem grow={2}>
<EuiFieldNumber
min={1}
aria-label={certificateFormTranslations.ageInputAriaLabel}
data-test-subj={`age-threshold-input-${loading ? 'loading' : 'loaded'}`}
fullWidth
disabled={isDisabled}
isLoading={loading}
value={formFields?.certAgeThreshold ?? ''}
onChange={e =>
onChange={({ currentTarget: { value } }) =>
onChange({
certAgeThreshold: Number(e.currentTarget.value),
certAgeThreshold: Number(value),
})
}
/>
Expand Down
24 changes: 24 additions & 0 deletions x-pack/plugins/uptime/public/components/settings/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';

export const certificateFormTranslations = {
ageInputAriaLabel: i18n.translate(
'xpack.uptime.sourceConfiguration.ageLimitThresholdInput.ariaLabel',
{
defaultMessage:
'An input that controls the maximum number of days for which a TLS certificate may be valid before Kibana will show a warning.',
}
),
expirationInputAriaLabel: i18n.translate(
'xpack.uptime.sourceConfiguration.certificateExpirationThresholdInput.ariaLabel',
{
defaultMessage:
'An input that controls the minimum number of days remaining for TLS certificate expiration before Kibana will show a warning.',
}
),
};
39 changes: 22 additions & 17 deletions x-pack/plugins/uptime/public/pages/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ import {
OnFieldChangeType,
} from '../components/settings/certificate_form';
import * as Translations from './translations';
import { VALUE_MUST_BE_GREATER_THEN_ZEO } from '../../common/translations';

interface SettingsPageFieldErrors {
heartbeatIndices: 'May not be blank' | '';
certificatesThresholds: {
expirationThresholdError: string | null;
ageThresholdError: string | null;
} | null;
heartbeatIndices: string | '';
expirationThresholdError?: string;
ageThresholdError?: string;
}

export interface SettingsFormProps {
Expand All @@ -49,22 +48,28 @@ export interface SettingsFormProps {
isDisabled: boolean;
}

const isValidCertVal = (val: string | number) => {
if (val === '') {
return Translations.BLANK_STR;
}
if (val === 0) {
return VALUE_MUST_BE_GREATER_THEN_ZEO;
}
};

const getFieldErrors = (formFields: DynamicSettings | null): SettingsPageFieldErrors | null => {
if (formFields) {
const blankStr = 'May not be blank';
const { certAgeThreshold, certExpirationThreshold, heartbeatIndices } = formFields;
const heartbeatIndErr = heartbeatIndices.match(/^\S+$/) ? '' : blankStr;
const expirationThresholdError = certExpirationThreshold ? null : blankStr;
const ageThresholdError = certAgeThreshold ? null : blankStr;

const indError = heartbeatIndices.match(/^\S+$/) ? '' : Translations.BLANK_STR;

const expError = isValidCertVal(certExpirationThreshold);
const ageError = isValidCertVal(certAgeThreshold);

return {
heartbeatIndices: heartbeatIndErr,
certificatesThresholds:
expirationThresholdError || ageThresholdError
? {
expirationThresholdError,
ageThresholdError,
}
: null,
heartbeatIndices: indError,
expirationThresholdError: expError,
ageThresholdError: ageError,
};
}
return null;
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/uptime/public/pages/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ export const settings = {
defaultMessage: 'Return to overview',
}),
};

export const BLANK_STR = i18n.translate('xpack.uptime.settings.blank.error', {
defaultMessage: 'May not be blank.',
});
29 changes: 23 additions & 6 deletions x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { UMServerLibs } from '../lib/lib';
import { DynamicSettings, DynamicSettingsType } from '../../common/runtime_types';
import { UMRestApiRouteFactory } from '.';
import { savedObjectsAdapter } from '../lib/saved_objects';
import { VALUE_MUST_BE_GREATER_THEN_ZEO } from '../../common/translations';

export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
method: 'GET',
Expand All @@ -23,19 +24,35 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSer
},
});

const validateCertsValues = (settings: DynamicSettings) => {
const errors: any = {};
if (settings.certAgeThreshold <= 0) {
errors.certAgeThreshold = VALUE_MUST_BE_GREATER_THEN_ZEO;
}
if (settings.certExpirationThreshold <= 0) {
errors.certExpirationThreshold = VALUE_MUST_BE_GREATER_THEN_ZEO;
}
if (errors.certAgeThreshold || errors.certExpirationThreshold) {
return errors;
}
};

export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
method: 'POST',
path: '/api/uptime/dynamic_settings',
validate: {
body: schema.object({}, { unknowns: 'allow' }),
body: schema.object({
heartbeatIndices: schema.string(),
certAgeThreshold: schema.number(),
certExpirationThreshold: schema.number(),
}),
},
writeAccess: true,
options: {
tags: ['access:uptime-write'],
},
handler: async ({ savedObjectsClient }, _context, request, response): Promise<any> => {
const decoded = DynamicSettingsType.decode(request.body);
if (isRight(decoded)) {
const certThresholdErrors = validateCertsValues(request.body as DynamicSettings);

if (isRight(decoded) && !certThresholdErrors) {
const newSettings: DynamicSettings = decoded.right;
await savedObjectsAdapter.setUptimeDynamicSettings(savedObjectsClient, newSettings);

Expand All @@ -47,7 +64,7 @@ export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSe
} else {
const error = PathReporter.report(decoded).join(', ');
return response.badRequest({
body: error,
body: JSON.stringify(certThresholdErrors) || error,
});
}
},
Expand Down

0 comments on commit 46856c8

Please sign in to comment.