Skip to content

Commit 7f992ba

Browse files
committed
email provider oauth support
1 parent 78d4469 commit 7f992ba

File tree

9 files changed

+505
-7
lines changed

9 files changed

+505
-7
lines changed

features/admin.core.v1/configs/app.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ export class Config {
228228
I18nConstants.POLICY_ADMINISTRATION_NAMESPACE,
229229
I18nConstants.REMOTE_USER_STORES_NAMESPACE,
230230
I18nConstants.RULES_NAMESPACE,
231-
I18nConstants.PUSH_PROVIDERS_NAMESPACE
231+
I18nConstants.PUSH_PROVIDERS_NAMESPACE,
232+
I18nConstants.EMAIL_PROVIDERS_NAMESPACE
232233
],
233234
preload: []
234235
};

features/admin.core.v1/constants/i18n-constants.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ export class I18nConstants {
197197
*/
198198
public static readonly PUSH_PROVIDERS_NAMESPACE: string = I18nModuleConstants.PUSH_PROVIDERS_NAMESPACE;
199199

200+
/**
201+
* Push Providers namespace.
202+
*/
203+
public static readonly EMAIL_PROVIDERS_NAMESPACE: string = I18nModuleConstants.EMAIL_PROVIDERS_NAMESPACE;
204+
200205
/**
201206
* SMS Templates namespace.
202207
*/
@@ -357,7 +362,8 @@ export class I18nConstants {
357362
[ I18nConstants.POLICY_ADMINISTRATION_NAMESPACE, "portals" ],
358363
[ I18nConstants.REMOTE_USER_STORES_NAMESPACE, "portals" ],
359364
[ I18nConstants.RULES_NAMESPACE, "portals" ],
360-
[ I18nConstants.PUSH_PROVIDERS_NAMESPACE, "portals" ]
365+
[ I18nConstants.PUSH_PROVIDERS_NAMESPACE, "portals" ],
366+
[ I18nConstants.EMAIL_PROVIDERS_NAMESPACE, "portals" ]
361367
]);
362368

363369
/**

features/admin.email-providers.v1/constants/email-provider-constants.ts

+30
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@
1818

1919
import { IdentityAppsError } from "@wso2is/core/errors";
2020

21+
/**
22+
* Interface for the authentication type dropdown options.
23+
*/
24+
export interface AuthenticationTypeDropdownOption {
25+
key: AuthenticationType;
26+
text: string;
27+
value: AuthenticationType;
28+
}
29+
30+
/**
31+
* Enum for the authentication types.
32+
*/
33+
export enum AuthenticationType {
34+
BASIC = "BASIC",
35+
CLIENT_CREDENTIAL = "CLIENT CREDENTIAL"
36+
}
37+
2138
export class EmailProviderConstants {
2239

2340
private constructor() { }
@@ -68,4 +85,17 @@ export class EmailProviderConstants {
6885
null
6986
)
7087
};
88+
89+
public static readonly AUTH_TYPES: AuthenticationTypeDropdownOption[] = [
90+
{
91+
key: AuthenticationType.BASIC,
92+
text: "emailProviders:fields.authentication.types.basic.name",
93+
value: AuthenticationType.BASIC
94+
},
95+
{
96+
key: AuthenticationType.CLIENT_CREDENTIAL,
97+
text: "emailProviders:fields.authentication.types.client_credential.name",
98+
value: AuthenticationType.CLIENT_CREDENTIAL
99+
},
100+
];
71101
}

features/admin.email-providers.v1/pages/email-providers.tsx

+190-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
*/
1818

1919
import { useRequiredScopes } from "@wso2is/access-control";
20+
import Box from "@oxygen-ui/react/Box";
21+
import InputAdornment from "@oxygen-ui/react/InputAdornment";
2022
import { AppConstants } from "@wso2is/admin.core.v1/constants/app-constants";
2123
import { history } from "@wso2is/admin.core.v1/helpers/history";
2224
import { FeatureConfigInterface } from "@wso2is/admin.core.v1/models/config";
@@ -32,6 +34,7 @@ import {
3234
DangerZoneGroup,
3335
DocumentationLink,
3436
EmphasizedSegment,
37+
Heading,
3538
PageLayout,
3639
PrimaryButton,
3740
SecondaryButton,
@@ -49,9 +52,13 @@ import React, {
4952
import { Trans, useTranslation } from "react-i18next";
5053
import { useDispatch, useSelector } from "react-redux";
5154
import { Dispatch } from "redux";
52-
import { Divider, Grid, Placeholder, Ref } from "semantic-ui-react";
55+
import { Divider, DropdownProps, Grid, Placeholder, Ref, Icon } from "semantic-ui-react";
5356
import { deleteEmailProviderConfigurations, updateEmailProviderConfigurations, useEmailProviderConfig } from "../api";
54-
import { EmailProviderConstants } from "../constants";
57+
import {
58+
AuthenticationTypeDropdownOption,
59+
AuthenticationType,
60+
EmailProviderConstants
61+
} from "../constants";
5562
import {
5663
EmailProviderConfigAPIResponseInterface,
5764
EmailProviderConfigFormErrorValidationsInterface,
@@ -101,6 +108,10 @@ const EmailProvidersPage: FunctionComponent<EmailProvidersPageInterface> = (
101108
error: emailProviderConfigFetchRequestError
102109
} = useEmailProviderConfig();
103110

111+
const [ endpointAuthType, setEndpointAuthType ] = useState<AuthenticationType>(null);
112+
const [ showPrimarySecret, setShowPrimarySecret ] = useState<boolean>(false);
113+
const [ showSecondarySecret, setShowSecondarySecret ] = useState<boolean>(false);
114+
104115
useEffect(() => {
105116
if (originalEmailProviderConfig instanceof IdentityAppsApiException || emailProviderConfigFetchRequestError) {
106117
handleRetrieveError();
@@ -138,6 +149,148 @@ const EmailProvidersPage: FunctionComponent<EmailProvidersPageInterface> = (
138149
}
139150
}, [ originalEmailProviderConfig ]);
140151

152+
153+
const renderInputAdornmentOfSecret = (showSecret: boolean, onClick: () => void): ReactElement => (
154+
<InputAdornment position="end">
155+
<Icon
156+
link={ true }
157+
className="list-icon reset-field-to-default-adornment"
158+
size="small"
159+
color="grey"
160+
name={ !showSecret ? "eye" : "eye slash" }
161+
data-componentid={ `${componentId}-endpoint-authentication-property-secret-view-button` }
162+
onClick={ onClick }
163+
/>
164+
</InputAdornment>
165+
);
166+
167+
/**
168+
* This method renders property fields of each endpoint authentication type.
169+
*
170+
* @returns property fields of the selected authentication type.
171+
*/
172+
const renderEndpointAuthPropertyFields = (): ReactElement => {
173+
switch (endpointAuthType) {
174+
case AuthenticationType.BASIC:
175+
return (
176+
<>
177+
<Field.Input
178+
ariaLabel="username"
179+
className="addon-field-wrapper"
180+
name="usernameAuthProperty"
181+
label={ t(
182+
"customAuthenticator:fields.createWizard.configurationsStep." +
183+
"authenticationTypeDropdown.authProperties.username.label"
184+
) }
185+
placeholder={ t(
186+
"customAuthenticator:fields.createWizard.configurationsStep." +
187+
"authenticationTypeDropdown.authProperties.username.placeholder"
188+
) }
189+
inputType="password"
190+
type={ showPrimarySecret ? "text" : "password" }
191+
InputProps={ {
192+
endAdornment: renderInputAdornmentOfSecret(showPrimarySecret, () =>
193+
setShowPrimarySecret(!showPrimarySecret)
194+
)
195+
} }
196+
required={ true }
197+
maxLength={ 100 }
198+
minLength={ 0 }
199+
data-componentid={ `${componentId}-endpoint-authentication-property-username` }
200+
width={ 15 }
201+
/>
202+
<Field.Input
203+
ariaLabel="password"
204+
className="addon-field-wrapper"
205+
label={ t(
206+
"customAuthenticator:fields.createWizard.configurationsStep." +
207+
"authenticationTypeDropdown.authProperties.password.label"
208+
) }
209+
placeholder={ t(
210+
"customAuthenticator:fields.createWizard.configurationsStep." +
211+
"authenticationTypeDropdown.authProperties.password.placeholder"
212+
) }
213+
name="passwordAuthProperty"
214+
inputType="password"
215+
type={ showSecondarySecret ? "text" : "password" }
216+
InputProps={ {
217+
endAdornment: renderInputAdornmentOfSecret(showSecondarySecret, () =>
218+
setShowSecondarySecret(!showSecondarySecret)
219+
)
220+
} }
221+
required={ true }
222+
maxLength={ 100 }
223+
minLength={ 0 }
224+
data-componentid={ `${componentId}-endpoint-authentication-property-password` }
225+
width={ 15 }
226+
/>
227+
</>
228+
);
229+
case AuthenticationType.CLIENT_CREDENTIAL:
230+
return (
231+
<>
232+
<Field.Input
233+
ariaLabel="clientID"
234+
className="addon-field-wrapper"
235+
name="clientIDAuthProperty"
236+
inputType="text"
237+
type={ "text" }
238+
label={ t(
239+
"customAuthenticator:fields.createWizard.configurationsStep." +
240+
"authenticationTypeDropdown.authProperties.header.label"
241+
) }
242+
placeholder={ t(
243+
"customAuthenticator:fields.createWizard.configurationsStep." +
244+
"authenticationTypeDropdown.authProperties.header.placeholder"
245+
) }
246+
required={ true }
247+
maxLength={ 100 }
248+
minLength={ 0 }
249+
data-componentid={ `${componentId}-endpoint-authentication-property-header` }
250+
width={ 15 }
251+
/>
252+
<Field.Input
253+
ariaLabel="value"
254+
className="addon-field-wrapper"
255+
name="valueAuthProperty"
256+
inputType="password"
257+
type={ showSecondarySecret ? "text" : "password" }
258+
InputProps={ {
259+
endAdornment: renderInputAdornmentOfSecret(showSecondarySecret, () =>
260+
setShowSecondarySecret(!showSecondarySecret)
261+
)
262+
} }
263+
label={ t(
264+
"customAuthenticator:fields.createWizard.configurationsStep." +
265+
"authenticationTypeDropdown.authProperties.value.label"
266+
) }
267+
placeholder={ t(
268+
"customAuthenticator:fields.createWizard.configurationsStep." +
269+
"authenticationTypeDropdown.authProperties.value.placeholder"
270+
) }
271+
required={ true }
272+
maxLength={ 100 }
273+
minLength={ 0 }
274+
data-componentid={ `${componentId}-endpoint-authentication-property-value` }
275+
width={ 15 }
276+
/>
277+
</>
278+
);
279+
default:
280+
break;
281+
}
282+
};
283+
284+
/**
285+
* This method handles authentication type dropdown changes.
286+
*
287+
* @param event - event associated with the dropdown change.
288+
* @param data - data changed by the event
289+
*/
290+
const handleDropdownChange = (event: React.MouseEvent<HTMLAnchorElement>, data: DropdownProps) => {
291+
setEndpointAuthType(data.value as AuthenticationType);
292+
};
293+
141294
/**
142295
* Displays the error banner when unable to fetch email provider configuration.
143296
*/
@@ -635,7 +788,7 @@ const EmailProvidersPage: FunctionComponent<EmailProvidersPageInterface> = (
635788
/>
636789
</Grid.Column>
637790
</Grid.Row>
638-
<Grid.Row columns={ 2 } key={ 3 }>
791+
{/* <Grid.Row columns={ 2 } key={ 3 }>
639792
<Grid.Column key="userName">
640793
<Field.Input
641794
ariaLabel="Username Field"
@@ -687,7 +840,7 @@ const EmailProvidersPage: FunctionComponent<EmailProvidersPageInterface> = (
687840
autoComplete="new-password"
688841
/>
689842
</Grid.Column>
690-
</Grid.Row>
843+
</Grid.Row> */}
691844
<Grid.Row columns={ 2 } key={ 3 }>
692845
<Grid.Column key="displayName">
693846
<Field.Input
@@ -716,6 +869,39 @@ const EmailProvidersPage: FunctionComponent<EmailProvidersPageInterface> = (
716869
</Grid.Column>
717870
</Grid.Row>
718871
</Grid>
872+
873+
<Divider className="divider-container" />
874+
<Heading className="heading-container" as="h5">
875+
{ t("emailProviders:fields.authenticationTypeDropdown.title") }
876+
</Heading>
877+
<Box className="box-container">
878+
<Field.Dropdown
879+
ariaLabel="authenticationType"
880+
name="authenticationType"
881+
label={ t(
882+
"emailProviders:fields.authenticationTypeDropdown.label"
883+
) }
884+
placeholder={ t(
885+
"emailProviders:fields.authenticationTypeDropdown.placeholder"
886+
) }
887+
hint={ t(
888+
"emailProviders:fields.authenticationTypeDropdown.hint"
889+
) }
890+
required={ true }
891+
value={ endpointAuthType }
892+
options={ [
893+
...EmailProviderConstants.AUTH_TYPES.map((option: AuthenticationTypeDropdownOption) => ({
894+
text: t(option.text),
895+
value: option.value.toString()
896+
}))
897+
] }
898+
onChange={ handleDropdownChange }
899+
enableReinitialize={ true }
900+
data-componentid={ `${componentId}-create-wizard-endpoint-authentication-dropdown` }
901+
width={ 15 }
902+
/>
903+
<div className="box-field">{ renderEndpointAuthPropertyFields() }</div>
904+
</Box>
719905
</Form>
720906
{
721907
hasEmailProviderUpdatePermissions && (

modules/i18n/src/constants.ts

+5
Original file line numberDiff line numberDiff line change
@@ -369,4 +369,9 @@ export class I18nModuleConstants {
369369
* This key is used to store the user's language preference in cookies or local storage.
370370
*/
371371
public static readonly PREFERENCE_STORAGE_KEY: string = "ui_lang";
372+
373+
/**
374+
* Email Provider namespace.
375+
*/
376+
public static readonly EMAIL_PROVIDERS_NAMESPACE: string = "emailProviders";
372377
}

0 commit comments

Comments
 (0)