Skip to content

Commit 40d9658

Browse files
feat(designer): Adding Password Masking to Authentication Editors (#6826)
feat(designer): Adding Password Masking to Authentication Editors (#6687) * temp commit * temp commit * a checkpoint? amazing... * temp commit before i ruin everything * I may have ruined everything 😅 * something is broken * This should probably be rewritten, but it somehow works * adding the property to other auth passwords * test and clean up Co-authored-by: Eric Wu <[email protected]>
1 parent 0ef2921 commit 40d9658

23 files changed

+494
-167
lines changed

Localize/lang/strings.json

+4
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@
541541
"H1wnHr": "Required. The string to slice.",
542542
"H2WdiZ": "Enter the valid minute values (from 0 to 59) separated by comma, e.g., 15,30",
543543
"H5VikC": "Invalid connection, mapping must not form a closed loop.",
544+
"H6IC6L": "Show Password",
544545
"H8bEUn": "Required. The number that Subtrahend is removed from.",
545546
"H9CZTr": "The expression is invalid. Make sure to use single quotes.",
546547
"HDqP2g": "Required. The key name of the form data value to return.",
@@ -1615,6 +1616,7 @@
16151616
"_H1wnHr.comment": "Required string parameter to slice",
16161617
"_H2WdiZ.comment": "Placeholder for schedule minutes",
16171618
"_H5VikC.comment": "Error message for circular logic connection validation",
1619+
"_H6IC6L.comment": "Label to show password",
16181620
"_H8bEUn.comment": "Required number parameter to be minused in sub function",
16191621
"_H9CZTr.comment": "Invalid expression due to misused double quotes",
16201622
"_HDqP2g.comment": "Required string parameter to be used as key for triggerFormDataValue function",
@@ -2687,6 +2689,7 @@
26872689
"_sfTqHY.comment": "Aria label for the close button in the node search panel",
26882690
"_shF9tZ.comment": "Header text for key-value pair keys",
26892691
"_snJFUi.comment": "\"Copy\" keyboard command text for Windows",
2692+
"_snzCiK.comment": "Label to hide password",
26902693
"_soqP+Z.comment": "Label for description of custom or Function",
26912694
"_srMbm9.comment": "Chatbot undo operation cancel button text",
26922695
"_srpZD2.comment": "Client ID Label Display Name",
@@ -3471,6 +3474,7 @@
34713474
"sfTqHY": "Close panel",
34723475
"shF9tZ": "Key",
34733476
"snJFUi": "Ctrl+C",
3477+
"snzCiK": "Hide Password",
34743478
"soqP+Z": "Returns true if either parameter is true",
34753479
"srMbm9": "Cancel",
34763480
"srpZD2": "Client ID",

e2e/designer/editors/password.spec.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { test, expect } from '@playwright/test';
2+
import { GoToMockWorkflow } from '../utils/GoToWorkflow';
3+
4+
test(
5+
'password mask',
6+
{
7+
tag: '@mock',
8+
},
9+
async ({ page }) => {
10+
await page.goto('/');
11+
12+
await GoToMockWorkflow(page, 'Panel');
13+
14+
await page.getByTestId('card-http').click();
15+
await page.getByText('Showing 0 of').click();
16+
await page.getByText('Authentication').click();
17+
await page.getByLabel('Operation details panel').getByText('Authentication').click();
18+
19+
await page.getByText('None').click();
20+
await page.getByRole('option', { name: 'Basic' }).click();
21+
22+
await page.getByTestId('msla-authentication-editor-basic-username').click();
23+
await page.keyboard.type('user');
24+
25+
await page.getByTestId('msla-authentication-editor-basic-password').click();
26+
await page.keyboard.type('password');
27+
28+
await page.getByRole('tab', { name: 'Code view' }).click();
29+
await expect(page.getByRole('code')).toContainText(
30+
'{ "type": "Http", "inputs": { "uri": "http://test.com", "method": "GET", "body": "@variables(\'ArrayVariable\')", "authentication": { "type": "Basic", "username": "user", "password": "password" } }, "runAfter": { "Filter_array": [ "SUCCEEDED" ] }, "runtimeConfiguration": { "contentTransfer": { "transferMode": "Chunked" } }}'
31+
);
32+
33+
await page.getByRole('tab', { name: 'Parameters' }).click();
34+
35+
await page.getByTestId('msla-authentication-editor-basic-username').click();
36+
await page.getByTestId('msla-authentication-editor-basic-username').press('ControlOrMeta+a');
37+
await page.getByTestId('msla-authentication-editor-basic-username').press('ControlOrMeta+c');
38+
39+
// Verify Show password butotn works
40+
await page.getByTestId('msla-authentication-editor-basic-password').click();
41+
await page.keyboard.type('test');
42+
await page.getByLabel('Show Password').click();
43+
44+
await page.getByTestId('msla-authentication-editor-basic-password').click();
45+
await page.keyboard.type('2');
46+
47+
await page.getByRole('tab', { name: 'Code view' }).click();
48+
await expect(page.getByRole('code')).toContainText(
49+
'{ "type": "Http", "inputs": { "uri": "http://test.com", "method": "GET", "body": "@variables(\'ArrayVariable\')", "authentication": { "type": "Basic", "username": "user", "password": "passwordtest2" } }, "runAfter": { "Filter_array": [ "SUCCEEDED" ] }, "runtimeConfiguration": { "contentTransfer": { "transferMode": "Chunked" } }}'
50+
);
51+
52+
await page.getByRole('tab', { name: 'Parameters' }).click();
53+
await page.getByTestId('msla-authentication-editor-basic-password').click();
54+
55+
await page.getByTestId('msla-authentication-editor-basic-password').press('ArrowLeft');
56+
await page.getByTestId('msla-authentication-editor-basic-password').press('ArrowLeft');
57+
await page.getByTestId('msla-authentication-editor-basic-password').press('Shift+ArrowLeft');
58+
await page.getByTestId('msla-authentication-editor-basic-password').press('Shift+ArrowLeft');
59+
await page.getByTestId('msla-authentication-editor-basic-password').press('Shift+ArrowLeft');
60+
await page.getByTestId('msla-authentication-editor-basic-password').press('Shift+ArrowLeft');
61+
await page.getByTestId('msla-authentication-editor-basic-username').press('ControlOrMeta+v');
62+
await page.keyboard.type('text');
63+
64+
await page.getByRole('tab', { name: 'Code view' }).click();
65+
await expect(page.getByRole('code')).toContainText(
66+
'{ "type": "Http", "inputs": { "uri": "http://test.com", "method": "GET", "body": "@variables(\'ArrayVariable\')", "authentication": { "type": "Basic", "username": "user", "password": "passworusertextt2" } }, "runAfter": { "Filter_array": [ "SUCCEEDED" ] }, "runtimeConfiguration": { "contentTransfer": { "transferMode": "Chunked" } }}'
67+
);
68+
}
69+
);

libs/designer-ui/src/lib/authentication/AADOAuth/AADOAuth.tsx

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { AuthProps, OAuthProps } from '..';
2-
import type { ValueSegment } from '../../editor';
3-
import type { ChangeState, GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../../editor/base';
4-
import type { TokenPickerButtonEditorProps } from '../../editor/base/plugins/tokenpickerbutton';
2+
import type { BaseEditorProps, ChangeState } from '../../editor/base';
53
import { AuthenticationDropdown } from '../AuthenticationDropdown';
64
import { AuthenticationProperty } from '../AuthenticationProperty';
75
import { AUTHENTICATION_PROPERTIES } from '../util';
@@ -17,14 +15,9 @@ export const AuthenticationOAuthType = {
1715
} as const;
1816
export type AuthenticationOAuthType = (typeof AuthenticationOAuthType)[keyof typeof AuthenticationOAuthType];
1917

20-
interface ActiveDirectoryAuthenticationProps {
18+
interface ActiveDirectoryAuthenticationProps extends Partial<BaseEditorProps> {
2119
OauthProps: OAuthProps;
22-
readonly?: boolean;
23-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
2420
setCurrentProps: Dispatch<SetStateAction<AuthProps>>;
25-
getTokenPicker: GetTokenPickerHandler;
26-
tokenMapping?: Record<string, ValueSegment>;
27-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
2821
}
2922

3023
export const ActiveDirectoryAuthentication = ({
@@ -113,25 +106,25 @@ export const ActiveDirectoryAuthentication = ({
113106
{...props}
114107
initialValue={oauthAuthority}
115108
AuthProperty={AUTHENTICATION_PROPERTIES.AAD_OAUTH_AUTHORITY}
116-
onBlur={updateOAuthAuthority}
109+
handleBlur={updateOAuthAuthority}
117110
/>
118111
<AuthenticationProperty
119112
{...props}
120113
initialValue={oauthTenant}
121114
AuthProperty={AUTHENTICATION_PROPERTIES.AAD_OAUTH_TENANT}
122-
onBlur={updateOAuthTenant}
115+
handleBlur={updateOAuthTenant}
123116
/>
124117
<AuthenticationProperty
125118
{...props}
126119
initialValue={oauthAudience}
127120
AuthProperty={AUTHENTICATION_PROPERTIES.AAD_OAUTH_AUDIENCE}
128-
onBlur={updateOAuthAudience}
121+
handleBlur={updateOAuthAudience}
129122
/>
130123
<AuthenticationProperty
131124
{...props}
132125
initialValue={oauthClientId}
133126
AuthProperty={AUTHENTICATION_PROPERTIES.AAD_OAUTH_CLIENT_ID}
134-
onBlur={updateOAuthClientId}
127+
handleBlur={updateOAuthClientId}
135128
/>
136129
<AuthenticationDropdown
137130
readonly={props.readonly}

libs/designer-ui/src/lib/authentication/AADOAuth/AADOAuthCredentials.tsx

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
import type { AuthProps, ClientCertificateProps } from '..';
22
import type { ValueSegment } from '../../editor';
3-
import type { ChangeState, GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../../editor/base';
4-
import type { TokenPickerButtonEditorProps } from '../../editor/base/plugins/tokenpickerbutton';
3+
import type { BaseEditorProps, ChangeState } from '../../editor/base';
54
import { AuthenticationProperty } from '../AuthenticationProperty';
65
import { CertificateAuthentication } from '../CertificateAuth';
76
import { AUTHENTICATION_PROPERTIES } from '../util';
87
import { AuthenticationOAuthType } from './AADOAuth';
98
import type { Dispatch, SetStateAction } from 'react';
109

11-
interface AadOAuthCredentialsProps {
10+
interface AadOAuthCredentialsProps extends Partial<BaseEditorProps> {
1211
selectedCredTypeKey: string;
1312
secret?: ValueSegment[];
1413
clientCertificateProps: ClientCertificateProps;
15-
readonly?: boolean;
16-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
1714
setCurrentProps: Dispatch<SetStateAction<AuthProps>>;
18-
getTokenPicker: GetTokenPickerHandler;
19-
tokenMapping?: Record<string, ValueSegment>;
20-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
2115
}
2216

2317
export const AadOAuthCredentials = ({
@@ -39,9 +33,11 @@ export const AadOAuthCredentials = ({
3933
{selectedCredTypeKey === AuthenticationOAuthType.SECRET ? (
4034
<AuthenticationProperty
4135
{...props}
36+
dataAutomationId={'msla-authentication-editor-aad-oauth-secret'}
37+
passwordMask={true}
4238
initialValue={secret}
4339
AuthProperty={AUTHENTICATION_PROPERTIES.AAD_OAUTH_SECRET}
44-
onBlur={updateOAuthTypeSecret}
40+
handleBlur={updateOAuthTypeSecret}
4541
/>
4642
) : selectedCredTypeKey === AuthenticationOAuthType.CERTIFICATE ? (
4743
<CertificateAuthentication

libs/designer-ui/src/lib/authentication/AuthenticationProperty.tsx

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
import type { ValueSegment } from '../editor';
2-
import type { ChangeHandler, GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../editor/base';
3-
import type { TokenPickerButtonEditorProps } from '../editor/base/plugins/tokenpickerbutton';
1+
import type { BaseEditorProps, ChangeHandler } from '../editor/base';
42
import { StringEditor } from '../editor/string';
53
import type { AuthProperty } from './util';
64
import { Label } from '../label';
5+
import { css } from '@fluentui/utilities';
76

8-
interface AuthenticationPropertyProps {
7+
interface AuthenticationPropertyProps extends Partial<BaseEditorProps> {
98
AuthProperty: AuthProperty;
10-
initialValue?: ValueSegment[];
11-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
12-
readonly?: boolean;
13-
tokenMapping?: Record<string, ValueSegment>;
14-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
15-
getTokenPicker: GetTokenPickerHandler;
16-
onBlur?: ChangeHandler;
9+
handleBlur?: ChangeHandler;
10+
passwordMask?: boolean;
1711
}
1812

19-
export const AuthenticationProperty = ({ initialValue = [], AuthProperty, onBlur, ...props }: AuthenticationPropertyProps): JSX.Element => {
13+
export const AuthenticationProperty = ({
14+
initialValue = [],
15+
AuthProperty,
16+
handleBlur,
17+
...props
18+
}: AuthenticationPropertyProps): JSX.Element => {
2019
return (
2120
<div className="msla-authentication-editor-expanded-item">
2221
<div className="msla-input-parameter-label">
@@ -26,11 +25,11 @@ export const AuthenticationProperty = ({ initialValue = [], AuthProperty, onBlur
2625
<StringEditor
2726
{...props}
2827
valueType={AuthProperty.type}
29-
className="msla-authentication-editor-expanded-editor"
28+
className={css('msla-authentication-editor-expanded-editor', props.passwordMask && 'hasIcon')}
3029
initialValue={initialValue}
3130
placeholder={AuthProperty.placeHolder}
32-
basePlugins={{ tokens: true }}
33-
editorBlur={onBlur}
31+
basePlugins={{ tokens: true, passwordMask: props.passwordMask }}
32+
editorBlur={handleBlur}
3433
/>
3534
</div>
3635
</div>

libs/designer-ui/src/lib/authentication/BasicAuth.tsx

+7-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
import type { AuthProps, BasicProps } from '.';
2-
import type { ValueSegment } from '../editor';
3-
import type { ChangeState, GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../editor/base';
4-
import type { TokenPickerButtonEditorProps } from '../editor/base/plugins/tokenpickerbutton';
2+
import type { BaseEditorProps, ChangeState } from '../editor/base';
53
import { AuthenticationProperty } from './AuthenticationProperty';
64
import { AUTHENTICATION_PROPERTIES } from './util';
75
import type { Dispatch, SetStateAction } from 'react';
86

9-
interface BasicAuthenticationProps {
7+
interface BasicAuthenticationProps extends Partial<BaseEditorProps> {
108
basicProps: BasicProps;
11-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
12-
readonly?: boolean;
139
setCurrentProps: Dispatch<SetStateAction<AuthProps>>;
14-
getTokenPicker: GetTokenPickerHandler;
15-
tokenMapping?: Record<string, ValueSegment>;
16-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
1710
}
1811

1912
export const BasicAuthentication = ({ basicProps, setCurrentProps, ...props }: BasicAuthenticationProps): JSX.Element => {
@@ -37,15 +30,18 @@ export const BasicAuthentication = ({ basicProps, setCurrentProps, ...props }: B
3730
<div className="msla-authentication-editor-basic-container">
3831
<AuthenticationProperty
3932
{...props}
33+
dataAutomationId={'msla-authentication-editor-basic-username'}
4034
initialValue={basicUsername}
4135
AuthProperty={AUTHENTICATION_PROPERTIES.BASIC_USERNAME}
42-
onBlur={updateBasicUserName}
36+
handleBlur={updateBasicUserName}
4337
/>
4438
<AuthenticationProperty
4539
{...props}
40+
dataAutomationId={'msla-authentication-editor-basic-password'}
4641
initialValue={basicPassword}
4742
AuthProperty={AUTHENTICATION_PROPERTIES.BASIC_PASSWORD}
48-
onBlur={updateBasicPassword}
43+
handleBlur={updateBasicPassword}
44+
passwordMask={true}
4945
/>
5046
</div>
5147
);

libs/designer-ui/src/lib/authentication/CertificateAuth.tsx

+7-11
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
import type { AuthProps, ClientCertificateProps } from '.';
2-
import type { ValueSegment } from '../editor';
3-
import type { ChangeState, GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../editor/base';
4-
import type { TokenPickerButtonEditorProps } from '../editor/base/plugins/tokenpickerbutton';
2+
import type { BaseEditorProps, ChangeState } from '../editor/base';
53
import { AuthenticationProperty } from './AuthenticationProperty';
64
import { AUTHENTICATION_PROPERTIES } from './util';
75
import type { Dispatch, SetStateAction } from 'react';
86

9-
interface CertificateAuthenticationProps {
7+
interface CertificateAuthenticationProps extends Partial<BaseEditorProps> {
108
clientCertificateProps: ClientCertificateProps;
119
isOAuth?: boolean;
12-
readonly?: boolean;
13-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
1410
setCurrentProps: Dispatch<SetStateAction<AuthProps>>;
15-
getTokenPicker: GetTokenPickerHandler;
16-
tokenMapping?: Record<string, ValueSegment>;
17-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
1811
}
1912

2013
export const CertificateAuthentication = ({
@@ -56,15 +49,18 @@ export const CertificateAuthentication = ({
5649
<div className="msla-authentication-editor-certificate-container">
5750
<AuthenticationProperty
5851
{...props}
52+
dataAutomationId={'msla-authentication-editor-client-certificate-pfx'}
5953
initialValue={clientCertificatePfx}
6054
AuthProperty={AUTHENTICATION_PROPERTIES.CLIENT_CERTIFICATE_PFX}
61-
onBlur={isOAuth ? updateOAuthTypeCertificatePfx : updateClientCertificatePfx}
55+
handleBlur={isOAuth ? updateOAuthTypeCertificatePfx : updateClientCertificatePfx}
6256
/>
6357
<AuthenticationProperty
6458
{...props}
59+
dataAutomationId={'msla-authentication-editor-client-certificate-password'}
6560
initialValue={clientCertificatePassword}
6661
AuthProperty={AUTHENTICATION_PROPERTIES.CLIENT_CERTIFICATE_PASSWORD}
67-
onBlur={isOAuth ? updateOAuthTypeCertificatePassword : updateClientCertificatePassword}
62+
passwordMask={true}
63+
handleBlur={isOAuth ? updateOAuthTypeCertificatePassword : updateClientCertificatePassword}
6864
/>
6965
</div>
7066
);

libs/designer-ui/src/lib/authentication/CollapsedAuthentication.tsx

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import type { AuthProps } from '.';
22
import { AuthenticationType } from '.';
33
import type { ValueSegment } from '../editor';
4-
import type { GetTokenPickerHandler, loadParameterValueFromStringHandler } from '../editor/base';
4+
import type { BaseEditorProps } from '../editor/base';
55
import { EditorWrapper } from '../editor/base/EditorWrapper';
6-
import type { TokenPickerButtonEditorProps } from '../editor/base/plugins/tokenpickerbutton';
76
import { serializeEditorState } from '../editor/base/utils/editorToSegment';
87
import { getChildrenNodes, isTokenValueSegment } from '../editor/base/utils/helper';
98
import { serializeAuthentication, validateAuthenticationString } from './util';
@@ -12,17 +11,12 @@ import { $getRoot, type EditorState } from 'lexical';
1211
import type { Dispatch, SetStateAction } from 'react';
1312
import { useIntl } from 'react-intl';
1413

15-
interface CollapsedAuthenticationProps {
14+
interface CollapsedAuthenticationProps extends Partial<BaseEditorProps> {
1615
collapsedValue: ValueSegment[];
1716
setErrorMessage: (s: string) => void;
1817
setCurrentProps: Dispatch<SetStateAction<AuthProps>>;
19-
readonly?: boolean;
20-
tokenPickerButtonProps?: TokenPickerButtonEditorProps;
2118
setOption: (s: AuthenticationType) => void;
2219
serializeValue: (value: ValueSegment[]) => void;
23-
getTokenPicker: GetTokenPickerHandler;
24-
tokenMapping?: Record<string, ValueSegment>;
25-
loadParameterValueFromString?: loadParameterValueFromStringHandler;
2620
}
2721

2822
export const CollapsedAuthentication = ({
@@ -62,7 +56,7 @@ export const CollapsedAuthentication = ({
6256
);
6357
}
6458
}
65-
} catch (e) {
59+
} catch (_e) {
6660
// if it is a template expression, we'll assume that it is valid
6761
if (isTokenValueSegment(newCollapsedValue)) {
6862
setErrorMessage('');
@@ -81,7 +75,12 @@ export const CollapsedAuthentication = ({
8175
};
8276
return (
8377
<div className="msla-authentication-editor-collapsed-container">
84-
<EditorWrapper {...props} initialValue={collapsedValue} basePlugins={{ tabbable: true }}>
78+
<EditorWrapper
79+
{...props}
80+
dataAutomationId={'msla-authentication-editor-collapsed-editor'}
81+
initialValue={collapsedValue}
82+
basePlugins={{ tabbable: true }}
83+
>
8584
<OnChangePlugin ignoreSelectionChange onChange={onChange} />
8685
</EditorWrapper>
8786
</div>

0 commit comments

Comments
 (0)