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
19 changes: 18 additions & 1 deletion public/apps/account/password-reset-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { logout, updateNewPassword } from './utils';
import { PASSWORD_INSTRUCTION } from '../apps-constants';
Copy link
Collaborator

Choose a reason for hiding this comment

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

For this PASSWORD_INSTRUCTION, should we also update it so that it has the new password rules that introduced by the strong password validation feature?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just like this password_validation_error_message :

'Password must be minimum 5 characters long and must contain at least one uppercase letter, one lowercase letter, one digit, and one special character.'

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point and needs to be updated on the backend as well. I'll adjust it according to the description on the PR that introduced password strength: opensearch-project/security#2557

plugins.security.restapi.password_min_length - minimum password length, default and minimum is 8

Copy link
Member Author

Choose a reason for hiding this comment

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

This is already properly set: https://github.com/opensearch-project/security-dashboards-plugin/blob/main/public/apps/apps-constants.tsx

Note: This default is duplicated and the frontend and backend. I decided to keep the value in the frontend, but it could certainly be removed from the frontend codebase in favor of just keeping a single reference on the backend.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's do this as a follow up PR to unblock your change.

import { constructErrorMessageAndLog } from '../error-utils';
import { validateCurrentPassword } from '../../utils/login-utils';
import { getDashboardsInfo } from '../../utils/dashboards-info-utils';

interface PasswordResetPanelProps {
coreStart: CoreStart;
Expand All @@ -57,6 +58,22 @@ export function PasswordResetPanel(props: PasswordResetPanelProps) {

const [errorCallOut, setErrorCallOut] = React.useState<string>('');

const [passwordHelpText, setPasswordHelpText] = React.useState<string>(PASSWORD_INSTRUCTION);

React.useEffect(() => {
const fetchData = async () => {
try {
setPasswordHelpText(
(await getDashboardsInfo(props.coreStart.http)).password_validation_error_message
);
} catch (e) {
console.error(e);
}
};

fetchData();
}, [props.coreStart.http]);

const handleReset = async () => {
const http = props.coreStart.http;
// validate the current password
Expand Down Expand Up @@ -107,7 +124,7 @@ export function PasswordResetPanel(props: PasswordResetPanelProps) {

<FormRow
headerText="New password"
helpText={PASSWORD_INSTRUCTION}
helpText={passwordHelpText}
isInvalid={isNewPasswordInvalid}
>
<EuiFieldPassword
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,11 @@ export function InternalUserEdit(props: InternalUserEditDeps) {
setNameState={setUserName}
setIsFormValid={setIsFormValid}
/>
<PasswordEditPanel updatePassword={setPassword} updateIsInvalid={setIsPasswordInvalid} />
<PasswordEditPanel
coreStart={props.coreStart}
updatePassword={setPassword}
updateIsInvalid={setIsPasswordInvalid}
/>
</EuiForm>
</PanelWithHeader>
<EuiSpacer size="m" />
Expand Down
20 changes: 19 additions & 1 deletion public/apps/configuration/utils/password-edit-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,35 @@
*/

import React from 'react';
import { CoreStart } from 'opensearch-dashboards/public';
import { EuiFieldText, EuiIcon } from '@elastic/eui';
import { FormRow } from './form-row';
import { PASSWORD_INSTRUCTION } from '../../apps-constants';
import { getDashboardsInfo } from '../../../utils/dashboards-info-utils';

export function PasswordEditPanel(props: {
coreStart: CoreStart;
updatePassword: (p: string) => void;
updateIsInvalid: (v: boolean) => void;
}) {
const [password, setPassword] = React.useState<string>('');
const [repeatPassword, setRepeatPassword] = React.useState<string>('');
const [isRepeatPasswordInvalid, setIsRepeatPasswordInvalid] = React.useState<boolean>(false);
const [passwordHelpText, setPasswordHelpText] = React.useState<string>(PASSWORD_INSTRUCTION);

React.useEffect(() => {
const fetchData = async () => {
try {
setPasswordHelpText(
(await getDashboardsInfo(props.coreStart.http)).password_validation_error_message
);
} catch (e) {
console.error(e);
}
};

fetchData();
}, [props.coreStart.http]);

React.useEffect(() => {
props.updatePassword(password);
Expand All @@ -43,7 +61,7 @@ export function PasswordEditPanel(props: {

return (
<>
<FormRow headerText="Password" helpText={PASSWORD_INSTRUCTION}>
<FormRow headerText="Password" helpText={passwordHelpText}>
<EuiFieldText
data-test-subj="password"
prepend={<EuiIcon type="lock" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,24 @@
* permissions and limitations under the License.
*/

import { shallow } from 'enzyme';
import { mount, shallow } from 'enzyme';
import React from 'react';
import { PasswordEditPanel } from '../password-edit-panel';
import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils';

const mockDashboardsInfo = {
multitenancy_enabled: true,
private_tenant_enabled: true,
default_tenant: '',
password_validation_error_message:
'Password must be minimum 5 characters long and must contain at least one uppercase letter, one lowercase letter, one digit, and one special character.',
};

jest.mock('../../../../utils/dashboards-info-utils', () => ({
getDashboardsInfo: jest.fn().mockImplementation(() => {
return mockDashboardsInfo;
}),
}));

describe('Password edit panel', () => {
let component;
Expand All @@ -24,21 +39,43 @@ describe('Password edit panel', () => {
const updateIsInvalid = jest.fn();
const useState = jest.spyOn(React, 'useState');
const useEffect = jest.spyOn(React, 'useEffect');
const mockCoreStart = {
http: 1,
};

beforeEach(() => {
useEffect.mockImplementationOnce((f) => f());
useState.mockImplementation((initialValue) => [initialValue, setState]);
component = shallow(
<PasswordEditPanel updatePassword={updatePassword} updateIsInvalid={updateIsInvalid} />
);
});

it('renders', () => {
expect(updatePassword).toHaveBeenCalledTimes(1);
expect(updateIsInvalid).toHaveBeenCalledTimes(1);
afterEach(() => {
jest.clearAllMocks();
});

it('renders', (done) => {
mount(
<PasswordEditPanel
coreStart={mockCoreStart as any}
updatePassword={updatePassword}
updateIsInvalid={updateIsInvalid}
/>
);
process.nextTick(() => {
expect(updatePassword).toHaveBeenCalledTimes(1);
expect(updateIsInvalid).toHaveBeenCalledTimes(1);
expect(setState).toBeCalledWith(mockDashboardsInfo.password_validation_error_message);
done();
});
});

it('password field update', () => {
component = shallow(
<PasswordEditPanel
coreStart={mockCoreStart as any}
updatePassword={updatePassword}
updateIsInvalid={updateIsInvalid}
/>
);
const event = {
target: { value: 'dummy' },
} as React.ChangeEvent<HTMLInputElement>;
Expand All @@ -47,6 +84,13 @@ describe('Password edit panel', () => {
});

it('repeat password field update', () => {
component = shallow(
<PasswordEditPanel
coreStart={mockCoreStart as any}
updatePassword={updatePassword}
updateIsInvalid={updateIsInvalid}
/>
);
const event = {
target: { value: 'dummy' },
} as React.ChangeEvent<HTMLInputElement>;
Expand Down
1 change: 1 addition & 0 deletions public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface DashboardsInfo {
multitenancy_enabled?: boolean;
private_tenant_enabled?: boolean;
default_tenant: string;
password_validation_error_message: string;
}

export interface ClientConfigType {
Expand Down