-
Notifications
You must be signed in to change notification settings - Fork 800
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Jetpack: Add block and allow list toggles to WAF settings (#38267)
* Add individual controls for toggling the IP block and allow lists. * Add block and allow list toggles * Add changelog entry * Reorganize * Further rearranging * Fixes and improvements * Ensure allow list toggle is off when main settings are disabled * Fix disabled logic * Remove isModuleFound import * Remove unnecessary logic * Remove missed logic --------- Co-authored-by: Nate Weller <[email protected]>
- Loading branch information
1 parent
aef6b3a
commit 187b7a0
Showing
11 changed files
with
369 additions
and
329 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,7 @@ | |
} | ||
|
||
.dops-card { | ||
padding-right: rem( 48px ); | ||
padding-right: rem( 72px ); | ||
} | ||
|
||
.jp-support-info { | ||
|
248 changes: 248 additions & 0 deletions
248
projects/plugins/jetpack/_inc/client/security/allowList.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
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, | ||
} ); | ||
} | ||
}; | ||
|
||
/** | ||
* 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.setState( { ...this.state, ipAllowList: event?.target?.value } ); | ||
}; | ||
|
||
currentIpIsSafelisted = () => { | ||
// get current IP allow list in textarea from this.state.ipAllowList; | ||
return !! includes( this.state.ipAllowList, this.props.currentIp ); | ||
}; | ||
|
||
addToSafelist = () => { | ||
const newAllowList = | ||
this.state.ipAllowList + | ||
( 0 >= this.state.ipAllowList.length ? '' : '\n' ) + | ||
this.props.currentIp; | ||
|
||
// Update the allow list | ||
this.setState( { ...this.state, ipAllowList: newAllowList } ); | ||
}; | ||
|
||
render() { | ||
const isWafActive = this.props.getOptionValue( 'waf' ); | ||
const isProtectActive = this.props.getOptionValue( 'protect' ); | ||
const wafUnavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'waf' ); | ||
const protectUnavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'protect' ); | ||
const baseInputDisabledCase = | ||
( ! isWafActive && ! isProtectActive ) || | ||
( wafUnavailableInOfflineMode && protectUnavailableInOfflineMode ) || | ||
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 } | ||
> | ||
{ ( isWafActive || isProtectActive ) && <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={ | ||
( isWafActive || isProtectActive ) && 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={ | ||
baseInputDisabledCase || | ||
this.props.isUpdatingWafSettings || | ||
! this.props.settings?.ipAllowListEnabled | ||
} | ||
name="ipAllowList" | ||
placeholder={ __( 'Example:', 'jetpack' ) + '\n12.12.12.1\n12.12.12.2' } | ||
value={ this.state.ipAllowList } | ||
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 ) ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.