[MM-51521] Prevent adjusting password stength fields when set in environment (#24151)
Co-authored-by: Nathan Geist <ngeist@spiria.com>
Этот коммит содержится в:
@@ -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>
|
||||||
|
`;
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import AdminSettings from './admin_settings';
|
|||||||
import type {BaseProps, BaseState} from './admin_settings';
|
import type {BaseProps, BaseState} from './admin_settings';
|
||||||
import BlockableLink from './blockable_link';
|
import BlockableLink from './blockable_link';
|
||||||
import BooleanSetting from './boolean_setting';
|
import BooleanSetting from './boolean_setting';
|
||||||
|
import CheckboxSetting from './checkbox_setting';
|
||||||
import Setting from './setting';
|
import Setting from './setting';
|
||||||
import SettingsGroup from './settings_group';
|
import SettingsGroup from './settings_group';
|
||||||
import TextSetting from './text_setting';
|
import TextSetting from './text_setting';
|
||||||
@@ -182,9 +183,9 @@ export default class PasswordSettings extends AdminSettings<Props, State> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCheckboxChange = (id: string) => {
|
handleBooleanChange = (id: string) => {
|
||||||
return (event: React.ChangeEvent<HTMLInputElement>) => {
|
return (_: string, value: boolean) => {
|
||||||
this.handleChange(id, event.target.checked);
|
this.handleChange(id, value);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -220,52 +221,52 @@ export default class PasswordSettings extends AdminSettings<Props, State> {
|
|||||||
label={<FormattedMessage {...messages.passwordRequirements}/>}
|
label={<FormattedMessage {...messages.passwordRequirements}/>}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label className='checkbox-inline'>
|
<CheckboxSetting
|
||||||
<input
|
id='admin.password.lowercase'
|
||||||
type='checkbox'
|
label={
|
||||||
defaultChecked={this.state.passwordLowercase}
|
<FormattedMessage {...messages.lowercase}/>
|
||||||
name='admin.password.lowercase'
|
}
|
||||||
disabled={this.props.isDisabled}
|
defaultChecked={this.state.passwordLowercase}
|
||||||
onChange={this.handleCheckboxChange('passwordLowercase')}
|
onChange={this.handleBooleanChange('passwordLowercase')}
|
||||||
/>
|
setByEnv={this.isSetByEnv('PasswordSettings.Lowercase')}
|
||||||
<FormattedMessage {...messages.lowercase}/>
|
disabled={this.props.isDisabled}
|
||||||
</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='checkbox-inline'>
|
<CheckboxSetting
|
||||||
<input
|
id='admin.password.uppercase'
|
||||||
type='checkbox'
|
label={
|
||||||
defaultChecked={this.state.passwordUppercase}
|
<FormattedMessage {...messages.uppercase}/>
|
||||||
name='admin.password.uppercase'
|
}
|
||||||
disabled={this.props.isDisabled}
|
defaultChecked={this.state.passwordUppercase}
|
||||||
onChange={this.handleCheckboxChange('passwordUppercase')}
|
onChange={this.handleBooleanChange('passwordUppercase')}
|
||||||
/>
|
setByEnv={this.isSetByEnv('PasswordSettings.Uppercase')}
|
||||||
<FormattedMessage {...messages.uppercase}/>
|
disabled={this.props.isDisabled}
|
||||||
</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='checkbox-inline'>
|
<CheckboxSetting
|
||||||
<input
|
id='admin.password.number'
|
||||||
type='checkbox'
|
label={
|
||||||
defaultChecked={this.state.passwordNumber}
|
<FormattedMessage {...messages.number}/>
|
||||||
name='admin.password.number'
|
}
|
||||||
disabled={this.props.isDisabled}
|
defaultChecked={this.state.passwordNumber}
|
||||||
onChange={this.handleCheckboxChange('passwordNumber')}
|
onChange={this.handleBooleanChange('passwordNumber')}
|
||||||
/>
|
setByEnv={this.isSetByEnv('PasswordSettings.Number')}
|
||||||
<FormattedMessage {...messages.number}/>
|
disabled={this.props.isDisabled}
|
||||||
</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='checkbox-inline'>
|
<CheckboxSetting
|
||||||
<input
|
id='admin.password.symbol'
|
||||||
type='checkbox'
|
label={
|
||||||
defaultChecked={this.state.passwordSymbol}
|
<FormattedMessage {...messages.symbol}/>
|
||||||
name='admin.password.symbol'
|
}
|
||||||
disabled={this.props.isDisabled}
|
defaultChecked={this.state.passwordSymbol}
|
||||||
onChange={this.handleCheckboxChange('passwordSymbol')}
|
onChange={this.handleBooleanChange('passwordSymbol')}
|
||||||
/>
|
setByEnv={this.isSetByEnv('PasswordSettings.Symbol')}
|
||||||
<FormattedMessage {...messages.symbol}/>
|
disabled={this.props.isDisabled}
|
||||||
</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import SetByEnv from './set_by_env';
|
import SetByEnv from './set_by_env';
|
||||||
@@ -11,21 +12,29 @@ export type Props = {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
helpText?: React.ReactNode;
|
helpText?: React.ReactNode;
|
||||||
setByEnv?: boolean;
|
setByEnv?: boolean;
|
||||||
|
nested?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Settings = ({children, setByEnv, helpText, inputId, label}: Props) => {
|
const Settings = ({children, setByEnv, helpText, inputId, label, nested = false}: Props) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid={inputId}
|
data-testid={inputId}
|
||||||
className='form-group'
|
className='form-group'
|
||||||
>
|
>
|
||||||
<label
|
{!nested && (
|
||||||
className='control-label col-sm-4'
|
<label
|
||||||
htmlFor={inputId}
|
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}
|
{children}
|
||||||
<div
|
<div
|
||||||
data-testid={inputId + 'help-text'}
|
data-testid={inputId + 'help-text'}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user