Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jetpack: Add block and allow list toggles to WAF settings #38361

Closed
wants to merge 14 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
}

.dops-card {
padding-right: rem( 48px );
padding-right: rem( 72px );
}

.jp-support-info {
Expand Down
245 changes: 245 additions & 0 deletions projects/plugins/jetpack/_inc/client/security/allowList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { ToggleControl } from '@automattic/jetpack-components';
import { __, _x, sprintf } from '@wordpress/i18n';
import Button from 'components/button';
import { FormFieldset } from 'components/forms';
import { createNotice, removeNotice } from 'components/global-notices/state/notices/actions';
import { withModuleSettingsFormHelpers } from 'components/module-settings/with-module-settings-form-helpers';
import SettingsCard from 'components/settings-card';
import SettingsGroup from 'components/settings-group';
import { includes } from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import QueryWafSettings from '../components/data/query-waf-bootstrap-path';
import Textarea from '../components/textarea';
import { updateWafSettings } from '../state/waf/actions';
import { getWafSettings, isFetchingWafSettings, isUpdatingWafSettings } from '../state/waf/reducer';

const AllowList = class extends Component {
/**
* Get options for initial state.
*
* @returns {object}
*/
state = {
ipAllowListEnabled: this.props.settings?.ipAllowListEnabled,
ipAllowList: this.props.settings?.ipAllowList,
};

/**
* Keep the form values in sync with updates to the settings prop.
*
* @param {object} prevProps - Next render props.
*/
componentDidUpdate = prevProps => {
// Sync the form values with the settings prop.
if ( this.props.settings !== prevProps.settings ) {
this.setState( {
...this.state,
ipAllowListEnabled: this.props.settings?.ipAllowListEnabled,
ipAllowList: this.props.settings?.ipAllowList,
} );
}

// Sync the allow list value with the value in redux.
if ( prevProps.allowListInputState !== this.props.allowListInputState ) {
this.setState( {
...this.state,
ipAllowList: this.props.allowListInputState,
} );
}
};

/**
* Handle settings updates.
*
* @returns {void}
*/
onSubmit = () => {
this.props.removeNotice( 'module-setting-update' );
this.props.removeNotice( 'module-setting-update-success' );

this.props.createNotice( 'is-info', __( 'Updating settings…', 'jetpack' ), {
id: 'module-setting-update',
} );

this.props
.updateWafSettings( this.state )
.then( () => {
this.props.removeNotice( 'module-setting-update' );
this.props.createNotice( 'is-success', __( 'Updated Settings.', 'jetpack' ), {
id: 'module-setting-update-success',
} );
} )
.catch( error => {
this.props.removeNotice( 'module-setting-update' );
this.props.createNotice(
'is-error',
sprintf(
/* translators: placeholder is an error code or an error message. */
__( 'Error updating settings. %s', 'jetpack' ),
error.message || error.code
),
{
id: 'module-setting-update',
}
);
} );
};

/**
* Toggle IP allow list.
*/
toggleIpAllowList = () => {
this.setState(
{ ...this.state, ipAllowListEnabled: ! this.state.ipAllowListEnabled },
this.onSubmit
);
};

/**
* Handle IP allow list change.
*
* @param {Event} event - = The event object.
*/
handleIpAllowListChange = event => {
this.props.updateWafIpAllowList( event.target.value );
};

currentIpIsSafelisted = () => {
// get current IP allow list in textarea from this.props.allowListInputState;
return !! includes( this.props.allowListInputState, this.props.currentIp );
};

addToSafelist = () => {
const newSafelist =
this.props.allowListInputState +
( 0 >= this.props.allowListInputState.length ? '' : '\n' ) +
this.props.currentIp;

// Update the allow list
this.props.updateWafIpAllowList( newSafelist );
};

render() {
const baseInputDisabledCase =
this.props.isFetchingWafSettings || this.props.isSavingAnyOption( [ 'waf' ] );

const moduleHeader = (
<div className="waf__header">
<span>{ _x( 'Always allowed IP addresses', 'Settings header', 'jetpack' ) }</span>
</div>
);

return (
<SettingsCard
{ ...this.props }
header={ moduleHeader }
module="waf"
onSubmit={ this.onSubmit }
hideButton={ true }
>
<QueryWafSettings />
<SettingsGroup
disableInOfflineMode
support={ {
text: "Adding an IP address to the allow list will prevent it from being blocked by Jetpack's firewall and brute force protection features.",
link: this.props.getModule( 'waf' ).learn_more_button,
} }
>
<FormFieldset>
<div className="waf__settings__toggle-setting">
<ToggleControl
checked={ this.props.settings?.ipAllowListEnabled }
toggling={
this.props.isUpdatingWafSettings &&
this.state.ipAllowListEnabled !== this.props.settings?.ipAllowListEnabled
}
disabled={ baseInputDisabledCase }
onChange={ this.toggleIpAllowList }
label={
<span className="jp-form-toggle-explanation">
{ __(
"Prevent Jetpack's security features from blocking specific IP addresses",
'jetpack'
) }
</span>
}
/>
<div className="waf__settings__ips">
<Textarea
disabled={
this.props.isUpdatingWafSettings || ! this.props.settings?.ipAllowListEnabled
}
name="ipAllowList"
placeholder={ __( 'Example:', 'jetpack' ) + '\n12.12.12.1\n12.12.12.2' }
value={ this.props.allowListInputState }
onChange={ this.handleIpAllowListChange }
/>
<div className="allow-list-button-container">
{ this.props.currentIp && (
<div className="current-ip">
<div className="jp-form-label-wide">
{ sprintf(
/* translators: placeholder is an IP address. */
__( 'Your current IP: %s', 'jetpack' ),
this.props.currentIp
) }
</div>
{
<Button
rna
compact
disabled={
this.props.isUpdatingWafSettings ||
! this.props.settings?.ipAllowListEnabled ||
this.currentIpIsSafelisted() ||
this.props.isSavingAnyOption( [ 'jetpack_waf_ip_allow_list' ] )
}
onClick={ this.addToSafelist }
>
{ __( 'Add to Allow List', 'jetpack' ) }
</Button>
}
</div>
) }
<Button
primary
rna
compact
type="button"
className="waf__settings__ips__save-button"
disabled={
this.state.ipAllowList === this.props.settings?.ipAllowList ||
( this.props.isUpdatingWafSettings &&
this.state.ipAllowList !== this.props.settings?.ipAllowList )
}
onClick={ this.onSubmit }
>
{ __( 'Save allow list', 'jetpack' ) }
</Button>
</div>
</div>
</div>
</FormFieldset>
</SettingsGroup>
</SettingsCard>
);
}
};

export default connect(
state => {
return {
isFetchingSettings: isFetchingWafSettings( state ),
isUpdatingWafSettings: isUpdatingWafSettings( state ),
settings: getWafSettings( state ),
};
},
dispatch => {
return {
updateWafSettings: newSettings => dispatch( updateWafSettings( newSettings ) ),
createNotice: ( type, message, props ) => dispatch( createNotice( type, message, props ) ),
removeNotice: notice => dispatch( removeNotice( notice ) ),
};
}
)( withModuleSettingsFormHelpers( AllowList ) );
2 changes: 2 additions & 0 deletions projects/plugins/jetpack/_inc/client/security/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { isModuleFound } from 'state/search';
import { getSettings } from 'state/settings';
import { siteHasFeature } from 'state/site';
import { isPluginActive, isPluginInstalled } from 'state/site/plugins';
import AllowList from './allowList';
import Antispam from './antispam';
import BackupsScan from './backups-scan';
import { JetpackBackup } from './jetpack-backup';
Expand Down Expand Up @@ -111,6 +112,7 @@ export class Security extends Component {
) }
{ foundWaf && <Waf { ...commonProps } /> }
{ foundProtect && <Protect { ...commonProps } /> }
<AllowList { ...commonProps } />
{ foundSso && <SSO { ...commonProps } /> }
</div>
);
Expand Down
Loading
Loading