Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
/* eslint-disable no-void */
import React, { ReactElement } from 'react';
import { Checkbox, Form, PageSection, SelectOption, TextInput } from '@patternfly/react-core';
import React, { ReactElement, useState } from 'react';
import {
Alert,
AlertVariant,
Checkbox,
Form,
PageSection,
SelectOption,
TextInput,
Popover,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import * as yup from 'yup';

import { NotifierIntegrationBase } from 'services/NotifierIntegrationsService';
Expand All @@ -27,6 +37,7 @@ export type EmailIntegration = {
sender: string;
disableTLS: boolean;
startTLSAuthMethod: 'DISABLED' | 'PLAIN' | 'LOGIN';
allowUnauthenticatedSmtp: boolean;
};
type: 'email';
} & NotifierIntegrationBase;
Expand Down Expand Up @@ -69,19 +80,21 @@ export const validationSchema = yup.object().shape({
.trim()
.required('A server address is required')
.matches(validHostnameRegex, 'Must be a valid server address'),
username: yup.string().trim().required('A username is required'),
allowUnauthenticatedSmtp: yup.boolean(),
username: yup.string().when('allowUnauthenticatedSmtp', {
is: false,
then: (usernameSchema) => usernameSchema.trim().required('A username is required'),
}),
password: yup
.string()
.test(
'password-test',
'A password is required',
(value, context: yup.TestContext) => {
const requirePasswordField =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
context?.from[2]?.value?.updatePassword || false;
context?.from?.[2].value?.updatePassword || false;

if (!requirePasswordField) {
if (!requirePasswordField || context.parent.allowUnauthenticatedSmtp) {
return true;
}

Expand Down Expand Up @@ -116,6 +129,7 @@ export const defaultValues: EmailIntegrationFormValues = {
sender: '',
disableTLS: false,
startTLSAuthMethod: 'DISABLED',
allowUnauthenticatedSmtp: false,
},
labelDefault: '',
labelKey: '',
Expand All @@ -130,6 +144,7 @@ function EmailIntegrationForm({
isEditable = false,
}: IntegrationFormProps<EmailIntegration>): ReactElement {
const formInitialValues = { ...defaultValues, ...initialValues };
const [storedUsername, setStoredUsername] = useState('');
if (initialValues) {
formInitialValues.notifier = {
...formInitialValues.notifier,
Expand Down Expand Up @@ -158,6 +173,7 @@ function EmailIntegrationForm({
validationSchema,
});
const { isCreating } = usePageState();
const { allowUnauthenticatedSmtp } = values.notifier.email;

function onChange(value, event) {
return setFieldValue(event.target.id, value);
Expand All @@ -170,6 +186,17 @@ function EmailIntegrationForm({
}
}

function onUpdateUnauthenticatedChange(isChecked) {
if (isChecked) {
setStoredUsername(values.notifier.email.username);
setFieldValue('notifier.email.username', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theencee Should we be clearing out the username when switching to unauthenticated, or is a username still required in certain cases?

(More importantly, is it required for the customer who asked for this feature?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unauth has optional username @vjwilson so safe to clear out

setFieldValue('notifier.email.password', '');
} else {
setFieldValue('notifier.email.username', storedUsername);
}
setFieldValue('notifier.email.allowUnauthenticatedSmtp', isChecked);
}

function onUpdateCredentialsChange(value, event) {
setFieldValue('notifier.email.password', '');
return setFieldValue(event.target.id, value);
Expand Down Expand Up @@ -216,25 +243,71 @@ function EmailIntegrationForm({
isDisabled={!isEditable}
/>
</FormLabelGroup>
<FormLabelGroup
label=""
fieldId="notifier.email.unauthenticated"
errors={errors}
>
<>
<div className="pf-u-display-flex pf-u-align-items-flex-start">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker to merge: See if the following example supports an equivalent popover via FormLabelGroup props without the need for flex alignment.

https://www.patternfly.org/v4/components/form#form-group-with-additional-label-info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I wasn't a huge fan of having to do it this way. I originally tried adding the isInline prop to the FormLabelGroup component and using that but patternfly added a pretty large margin between the icon and text. So I'm not sure if theres a way to do what we want without a few utility classes. I'll dig a little more though

<Checkbox
label="Enable unauthenticated SMTP"
id="notifier.email.unauthenticated"
isChecked={allowUnauthenticatedSmtp}
onChange={onUpdateUnauthenticatedChange}
onBlur={handleBlur}
/>
<Popover
showClose={false}
bodyContent="Enable unauthenticated SMTP will allow you to setup an email notifier if you don’t have authenticated email services."
>
<button
type="button"
aria-label="More info on unauthenticated SMTP field"
onClick={(e) => e.preventDefault()}
className="pf-c-form__group-label-help"
>
<HelpIcon />
</button>
</Popover>
</div>
{allowUnauthenticatedSmtp && (
<Alert
className="pf-u-mt-md"
title="Security Warning"
variant={AlertVariant.warning}
isInline
>
<p>
Unauthenticated SMTP is an insecure configuration and not
generally recommended. Please proceed with caution when
enabling this setting.
</p>
</Alert>
)}
</>
</FormLabelGroup>
<FormLabelGroup
label="Username"
isRequired
isRequired={!allowUnauthenticatedSmtp}
fieldId="notifier.email.username"
touched={touched}
errors={errors}
>
<TextInput
isRequired
isRequired={!allowUnauthenticatedSmtp}
type="text"
id="notifier.email.username"
value={values.notifier.email.username}
placeholder="example, postmaster@example.com"
placeholder={
allowUnauthenticatedSmtp ? '' : 'example, postmaster@example.com'
}
onChange={onChange}
onBlur={handleBlur}
isDisabled={!isEditable}
isDisabled={!isEditable || allowUnauthenticatedSmtp}
/>
</FormLabelGroup>
{!isCreating && isEditable && (
{!isCreating && isEditable && !allowUnauthenticatedSmtp && (
<FormLabelGroup
label=""
fieldId="updatePassword"
Expand All @@ -253,21 +326,23 @@ function EmailIntegrationForm({
)}
<FormLabelGroup
label="Password"
isRequired={values.updatePassword}
isRequired={values.updatePassword && !allowUnauthenticatedSmtp}
fieldId="notifier.email.password"
touched={touched}
errors={errors}
>
<TextInput
isRequired={values.updatePassword}
isRequired={values.updatePassword && !allowUnauthenticatedSmtp}
type="password"
id="notifier.email.password"
value={values.notifier.email.password}
onChange={onChange}
onBlur={handleBlur}
isDisabled={!isEditable || !values.updatePassword}
isDisabled={
!isEditable || !values.updatePassword || allowUnauthenticatedSmtp
}
placeholder={
values.updatePassword
values.updatePassword || allowUnauthenticatedSmtp
? ''
: 'Currently-stored password will be used.'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-void */
import React, { ReactElement } from 'react';
import { Checkbox, Form, PageSection, SelectOption, TextInput } from '@patternfly/react-core';
import React, { ReactElement, useState } from 'react';
import {
Alert,
AlertVariant,
Checkbox,
Form,
PageSection,
SelectOption,
TextInput,
Popover,
} from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';
import { FormikErrors, FormikTouched } from 'formik';

import SelectSingle from 'Components/SelectSingle';
Expand Down Expand Up @@ -39,6 +49,8 @@ function EmailIntegrationForm({
errors,
touched,
}: EmailIntegrationFormProps): ReactElement {
const [storedUsername, setStoredUsername] = useState('');
const { allowUnauthenticatedSmtp } = values.notifier.email;
function onChange(value, event) {
return void setFieldValue(event.target.id, value);
}
Expand All @@ -50,6 +62,17 @@ function EmailIntegrationForm({
}
}

function onUpdateUnauthenticatedChange(isChecked) {
if (isChecked) {
setStoredUsername(values.notifier.email.username);
setFieldValue('notifier.email.username', '');
setFieldValue('notifier.email.password', '');
} else {
setFieldValue('notifier.email.username', storedUsername);
}
setFieldValue('notifier.email.allowUnauthenticatedSmtp', isChecked);
}

return (
<>
<PageSection variant="light" isFilled hasOverflowScroll>
Expand Down Expand Up @@ -88,40 +111,87 @@ function EmailIntegrationForm({
onBlur={handleBlur}
/>
</FormLabelGroup>
<FormLabelGroup
label=""
fieldId="notifier.email.unauthenticated"
errors={errors}
>
<>
<div className="pf-u-display-flex pf-u-align-items-flex-start">
<Checkbox
label="Enable unauthenticated SMTP"
id="notifier.email.unauthenticated"
isChecked={allowUnauthenticatedSmtp}
onChange={onUpdateUnauthenticatedChange}
onBlur={handleBlur}
/>
<Popover
showClose={false}
bodyContent="Enable unauthenticated SMTP will allow you to setup an email notifier if you don’t have authenticated email services."
>
<button
type="button"
aria-label="More info on unauthenticated SMTP field"
onClick={(e) => e.preventDefault()}
className="pf-c-form__group-label-help"
>
<HelpIcon />
</button>
</Popover>
</div>
{allowUnauthenticatedSmtp && (
<Alert
className="pf-u-mt-md"
title="Security Warning"
variant={AlertVariant.warning}
isInline
>
<p>
Unauthenticated SMTP is an insecure configuration and not
generally recommended. Please proceed with caution when
enabling this setting.
</p>
</Alert>
)}
</>
</FormLabelGroup>
<FormLabelGroup
label="Username"
isRequired
isRequired={!allowUnauthenticatedSmtp}
fieldId="notifier.email.username"
touched={touched}
errors={errors}
>
<TextInput
isRequired
isRequired={!allowUnauthenticatedSmtp}
type="text"
id="notifier.email.username"
value={values.notifier.email.username}
placeholder="example, postmaster@example.com"
placeholder={
allowUnauthenticatedSmtp ? '' : 'example, postmaster@example.com'
}
onChange={onChange}
onBlur={handleBlur}
isDisabled={allowUnauthenticatedSmtp}
/>
</FormLabelGroup>
<FormLabelGroup
label="Password"
isRequired={values.updatePassword}
isRequired={values.updatePassword && !allowUnauthenticatedSmtp}
fieldId="notifier.email.password"
touched={touched}
errors={errors}
>
<TextInput
isRequired={values.updatePassword}
isRequired={values.updatePassword && !allowUnauthenticatedSmtp}
type="password"
id="notifier.email.password"
value={values.notifier.email.password}
onChange={onChange}
onBlur={handleBlur}
isDisabled={!values.updatePassword}
isDisabled={!values.updatePassword || allowUnauthenticatedSmtp}
placeholder={
values.updatePassword
values.updatePassword || allowUnauthenticatedSmtp
? ''
: 'Currently-stored password will be used.'
}
Expand Down
1 change: 1 addition & 0 deletions ui/apps/platform/src/types/notifier.proto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export type Email = {
// DEPRECATED_useStartTLS deprecated
from: string;
startTLSAuthMethod: EmailAuthMethod;
allowUnauthenticatedSmtp: boolean;
};

export type EmailAuthMethod = 'DISABLED' | 'PLAIN' | 'LOGIN';
Expand Down