[MM-51521] Prevent adjusting password stength fields when set in environment (#24151)

Co-authored-by: Nathan Geist <ngeist@spiria.com>
This commit is contained in:
Nathan 2024-01-23 08:52:22 -07:00 committed by GitHub
parent 7d43712daa
commit 5985a0c531
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 210 additions and 50 deletions

View File

@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/admin_console/CheckboxSetting should match snapshot 1`] = `
<div>
<div
class="form-group"
data-testid="string.id"
>
<div
class="col-sm-12"
>
<a
id="string.id"
/>
<label
class="checkbox-inline"
>
<input
data-testid="string.id"
id="string.id"
name="string.id"
type="checkbox"
/>
some label
</label>
<div
class="help-text"
data-testid="string.idhelp-text"
/>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,50 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils';
import CheckboxSetting from './checkbox_setting';
describe('components/admin_console/CheckboxSetting', () => {
test('should match snapshot', () => {
const onChange = jest.fn();
const {container} = renderWithContext(
<CheckboxSetting
id='string.id'
label='some label'
defaultChecked={false}
onChange={onChange}
setByEnv={false}
disabled={false}
/>,
);
const checkbox: HTMLInputElement = screen.getByRole('checkbox');
expect(checkbox).toBeVisible();
expect(checkbox).toHaveProperty('type', 'checkbox');
expect(container).toMatchSnapshot();
});
test('onChange', () => {
const onChange = jest.fn();
renderWithContext(
<CheckboxSetting
id='string.id'
label='some label'
defaultChecked={false}
onChange={onChange}
setByEnv={false}
disabled={false}
/>,
);
const checkbox: HTMLInputElement = screen.getByRole('checkbox');
expect(checkbox).not.toBeChecked();
fireEvent.click(checkbox);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('string.id', true);
expect(checkbox).toBeChecked();
});
});

View File

@ -0,0 +1,67 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Setting from './setting';
type Props = {
id: string;
label: React.ReactNode;
defaultChecked?: boolean;
onChange: (id: string, foo: boolean) => void;
disabled: boolean;
setByEnv: boolean;
disabledText?: React.ReactNode;
helpText?: React.ReactNode;
}
export default class CheckboxSetting extends React.PureComponent<Props> {
public static defaultProps = {
disabled: false,
};
private handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.onChange(this.props.id, e.target.checked);
};
public render() {
let helpText;
if (this.props.disabled && this.props.disabledText) {
helpText = (
<div>
<span className='admin-console__disabled-text'>
{this.props.disabledText}
</span>
{this.props.helpText}
</div>
);
} else {
helpText = this.props.helpText;
}
return (
<Setting
inputId={this.props.id}
label={this.props.label}
helpText={helpText}
setByEnv={this.props.setByEnv}
nested={true}
>
<a id={this.props.id}/>
<label className='checkbox-inline'>
<input
data-testid={this.props.id}
type='checkbox'
id={this.props.id}
name={this.props.id}
defaultChecked={this.props.defaultChecked}
onChange={this.handleChange}
disabled={this.props.disabled || this.props.setByEnv}
/>
{this.props.label}
</label>
</Setting>
);
}
}

View File

@ -14,6 +14,7 @@ import AdminSettings from './admin_settings';
import type {BaseProps, BaseState} from './admin_settings';
import BlockableLink from './blockable_link';
import BooleanSetting from './boolean_setting';
import CheckboxSetting from './checkbox_setting';
import Setting from './setting';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@ -182,9 +183,9 @@ export default class PasswordSettings extends AdminSettings<Props, State> {
);
};
handleCheckboxChange = (id: string) => {
return (event: React.ChangeEvent<HTMLInputElement>) => {
this.handleChange(id, event.target.checked);
handleBooleanChange = (id: string) => {
return (_: string, value: boolean) => {
this.handleChange(id, value);
};
};
@ -220,52 +221,52 @@ export default class PasswordSettings extends AdminSettings<Props, State> {
label={<FormattedMessage {...messages.passwordRequirements}/>}
>
<div>
<label className='checkbox-inline'>
<input
type='checkbox'
defaultChecked={this.state.passwordLowercase}
name='admin.password.lowercase'
disabled={this.props.isDisabled}
onChange={this.handleCheckboxChange('passwordLowercase')}
/>
<FormattedMessage {...messages.lowercase}/>
</label>
<CheckboxSetting
id='admin.password.lowercase'
label={
<FormattedMessage {...messages.lowercase}/>
}
defaultChecked={this.state.passwordLowercase}
onChange={this.handleBooleanChange('passwordLowercase')}
setByEnv={this.isSetByEnv('PasswordSettings.Lowercase')}
disabled={this.props.isDisabled}
/>
</div>
<div>
<label className='checkbox-inline'>
<input
type='checkbox'
defaultChecked={this.state.passwordUppercase}
name='admin.password.uppercase'
disabled={this.props.isDisabled}
onChange={this.handleCheckboxChange('passwordUppercase')}
/>
<FormattedMessage {...messages.uppercase}/>
</label>
<CheckboxSetting
id='admin.password.uppercase'
label={
<FormattedMessage {...messages.uppercase}/>
}
defaultChecked={this.state.passwordUppercase}
onChange={this.handleBooleanChange('passwordUppercase')}
setByEnv={this.isSetByEnv('PasswordSettings.Uppercase')}
disabled={this.props.isDisabled}
/>
</div>
<div>
<label className='checkbox-inline'>
<input
type='checkbox'
defaultChecked={this.state.passwordNumber}
name='admin.password.number'
disabled={this.props.isDisabled}
onChange={this.handleCheckboxChange('passwordNumber')}
/>
<FormattedMessage {...messages.number}/>
</label>
<CheckboxSetting
id='admin.password.number'
label={
<FormattedMessage {...messages.number}/>
}
defaultChecked={this.state.passwordNumber}
onChange={this.handleBooleanChange('passwordNumber')}
setByEnv={this.isSetByEnv('PasswordSettings.Number')}
disabled={this.props.isDisabled}
/>
</div>
<div>
<label className='checkbox-inline'>
<input
type='checkbox'
defaultChecked={this.state.passwordSymbol}
name='admin.password.symbol'
disabled={this.props.isDisabled}
onChange={this.handleCheckboxChange('passwordSymbol')}
/>
<FormattedMessage {...messages.symbol}/>
</label>
<CheckboxSetting
id='admin.password.symbol'
label={
<FormattedMessage {...messages.symbol}/>
}
defaultChecked={this.state.passwordSymbol}
onChange={this.handleBooleanChange('passwordSymbol')}
setByEnv={this.isSetByEnv('PasswordSettings.Symbol')}
disabled={this.props.isDisabled}
/>
</div>
<div>
<br/>

View File

@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React from 'react';
import SetByEnv from './set_by_env';
@ -11,21 +12,29 @@ export type Props = {
children?: React.ReactNode;
helpText?: React.ReactNode;
setByEnv?: boolean;
nested?: boolean;
}
const Settings = ({children, setByEnv, helpText, inputId, label}: Props) => {
const Settings = ({children, setByEnv, helpText, inputId, label, nested = false}: Props) => {
return (
<div
data-testid={inputId}
className='form-group'
>
<label
className='control-label col-sm-4'
htmlFor={inputId}
{!nested && (
<label
className='control-label col-sm-4'
htmlFor={inputId}
>
{label}
</label>
)}
<div
className={classNames({
'col-sm-8': nested === false,
'col-sm-12': nested === true,
})}
>
{label}
</label>
<div className='col-sm-8'>
{children}
<div
data-testid={inputId + 'help-text'}