Skip to content

Commit

Permalink
Move firewall controls
Browse files Browse the repository at this point in the history
  • Loading branch information
nateweller committed Oct 8, 2024
1 parent f13628c commit 6114be1
Show file tree
Hide file tree
Showing 21 changed files with 738 additions and 558 deletions.
6 changes: 6 additions & 0 deletions client/my-sites/scan/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import getSiteScanRequestStatus from 'calypso/state/selectors/get-site-scan-requ
import getSiteScanState from 'calypso/state/selectors/get-site-scan-state';
import isJetpackSiteMultiSite from 'calypso/state/sites/selectors/is-jetpack-site-multi-site';
import getSelectedSiteId from 'calypso/state/ui/selectors/get-selected-site-id';
import FirewallPage from './firewall';
import ScanHistoryPage from './history';
import ScanPage from './main';
import ScanUpsellPage from './scan-upsell';
Expand Down Expand Up @@ -102,6 +103,11 @@ export function scanHistory( context, next ) {
next();
}

export function firewall( context, next ) {
context.primary = <FirewallPage />;
next();
}

function scanUpsellSwitcher( placeholder, primary ) {
const UpsellComponent =
isJetpackCloud() || isA8CForAgencies() ? ScanUpsellPage : WPCOMScanUpsellPage;
Expand Down
158 changes: 158 additions & 0 deletions client/my-sites/scan/firewall/allow-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { Button } from '@automattic/components';
import { ToggleControl } from '@wordpress/components';
import { translate } from 'i18n-calypso';
import { includes, some } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import FormFieldset from 'calypso/components/forms/form-fieldset';
import FormSettingExplanation from 'calypso/components/forms/form-setting-explanation';
import FormTextarea from 'calypso/components/forms/form-textarea';
import { useWafMutation, useWafQuery } from './data';

export default function AllowList() {
const { data: waf, isLoading } = useWafQuery();
const { mutate: updateWaf, isPending: isUpdating } = useWafMutation();

const [ inputState, setInputState ] = useState( '' );

const toggleIpAllowList = useCallback( () => {
updateWaf( {
jetpack_waf_ip_allow_list_enabled: ! waf?.jetpack_waf_ip_allow_list_enabled,
} );
}, [ updateWaf, waf?.jetpack_waf_ip_allow_list_enabled ] );

const saveIpAllowList = useCallback( () => {
updateWaf( { jetpack_waf_ip_allow_list: inputState } );
}, [ inputState, updateWaf ] );

const togglingAllowListSupported = useMemo(
() => waf?.jetpack_waf_ip_allow_list_enabled !== undefined,
[ waf?.jetpack_waf_ip_allow_list_enabled ]
);

const showAllowList = useMemo(
() => ! togglingAllowListSupported || waf?.jetpack_waf_ip_allow_list_enabled,
[ waf?.jetpack_waf_ip_allow_list_enabled, togglingAllowListSupported ]
);

const ipAddress = useMemo( () => {
if ( window.app && window.app.clientIp ) {
return window.app.clientIp;
}

return null;
}, [] );

const isIpAllowed = useMemo( () => {
if ( ! ipAddress ) {
return false;
}

const allowedIps = inputState.split( '\n' );

return (
includes( allowedIps, ipAddress ) ||
some( allowedIps, ( entry ) => {
if ( entry.indexOf( '-' ) < 0 ) {
return false;
}

const range = entry.split( '-' ).map( ( ip ) => ip.trim() );
return includes( range, ipAddress );
} )
);
}, [ ipAddress, inputState ] );

const addCurrentIpToAllowList = useCallback( () => {
let allowedIps = inputState.trimEnd();

if ( allowedIps.length ) {
allowedIps += '\n';
}

setInputState( allowedIps + ipAddress );
}, [ ipAddress, inputState ] );

useEffect( () => {
if ( waf?.jetpack_waf_ip_allow_list ) {
setInputState( waf.jetpack_waf_ip_allow_list );
}
}, [ waf?.jetpack_waf_ip_allow_list ] );

if ( ! waf ) {
return null;
}

return (
<FormFieldset>
<ToggleControl
disabled={ isLoading || isUpdating || ! togglingAllowListSupported }
onChange={ toggleIpAllowList }
checked={ ! togglingAllowListSupported || !! waf?.jetpack_waf_ip_allow_list_enabled }
label={
<>
<div>{ translate( 'Trusted IP addresses' ) }</div>
<FormSettingExplanation>
{ translate(
'IP addresses added to this list are always allowed to access your site, regardless of any other Jetpack security settings.'
) }
</FormSettingExplanation>
{ showAllowList && (
<>
<FormTextarea
id="jetpack_waf_ip_allow_list"
value={ inputState }
onChange={ ( e ) => setInputState( e.target.value ) }
disabled={ isLoading || isUpdating }
placeholder={ translate( 'Example: 12.12.12.1-12.12.12.100' ) }
/>
<FormSettingExplanation>
{ translate(
'IPv4 and IPv6 supported. Separate IPs with commas, spaces, or new lines. To specify a range, use CIDR notation (i.e. 12.12.12.0/24) or enter the low value and high value separated by a dash (i.e. 12.12.12.0–12.12.12.255).'
) }
</FormSettingExplanation>
<div className="allow-list-controls">
<div className="current-ip">
<div>
{ translate( 'Your current IP: {{strong}}%(IP)s{{/strong}}', {
args: {
IP: ipAddress || translate( 'Unknown IP address' ),
},
components: {
strong: <strong />,
},
} ) }
</div>

{ ipAddress && (
<div>
<Button
className="site-settings__add-to-explicitly-allowed-list"
onClick={ addCurrentIpToAllowList }
disabled={ isLoading || isUpdating || isIpAllowed }
compact
>
{ isIpAllowed
? translate( 'Already in list of trusted IPs' )
: translate( 'Add to Trusted IPs' ) }
</Button>
</div>
) }
</div>
<div>
<Button
onClick={ saveIpAllowList }
disabled={ isLoading || isUpdating }
primary
>
{ translate( 'Save allow list' ) }
</Button>
</div>
</div>
</>
) }
</>
}
/>
</FormFieldset>
);
}
91 changes: 91 additions & 0 deletions client/my-sites/scan/firewall/automatic-rules.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { PRODUCT_JETPACK_SCAN, WPCOM_FEATURES_SCAN } from '@automattic/calypso-products';
import { ToggleControl } from '@wordpress/components';
import { translate } from 'i18n-calypso';
import moment from 'moment';
import React, { useCallback, useMemo } from 'react';
import UpsellNudge from 'calypso/blocks/upsell-nudge';
import FormFieldset from 'calypso/components/forms/form-fieldset';
import FormSettingExplanation from 'calypso/components/forms/form-setting-explanation';
import { useSelector } from 'calypso/state';
import { getSelectedSiteSlug } from 'calypso/state/ui/selectors';
import { useWafMutation, useWafQuery } from './data';

/**
* Automatic Firewall Rules Setting
*/
export default function AutomaticRules() {
const { data: waf, isLoading } = useWafQuery();
const { mutate: updateWaf, isPending: isUpdating } = useWafMutation();

const selectedSiteSlug = useSelector( getSelectedSiteSlug );

const toggleAutomaticRules = useCallback( () => {
updateWaf( {
jetpack_waf_automatic_rules: ! waf?.jetpack_waf_automatic_rules,
} );
}, [ updateWaf, waf?.jetpack_waf_automatic_rules ] );

const automaticRulesLastUpdated = useMemo( () => {
const timestamp = parseInt( waf?.automatic_rules_last_updated );
if ( timestamp ) {
return moment( timestamp * 1000 ).format( 'MMMM D, YYYY h:mm A' );
}
return '';
}, [ waf?.automatic_rules_last_updated ] );

// Do not render if the WAF is not supported.
if ( ! waf || ! waf?.waf_supported ) {
return null;
}

return (
<>
<FormFieldset>
<ToggleControl
disabled={ isLoading || isUpdating || ! waf?.automatic_rules_available }
onChange={ toggleAutomaticRules }
checked={ !! waf?.jetpack_waf_automatic_rules }
label={
<>
<div>{ translate( 'Automatic firewall protection' ) }</div>
{ automaticRulesLastUpdated && (
<FormSettingExplanation>
{ translate( 'Automatic security rules installed. Last updated on %(date)s.', {
args: {
date: automaticRulesLastUpdated,
},
} ) }
</FormSettingExplanation>
) }
<FormSettingExplanation>
{ translate(
'Block untrusted traffic by scanning every request made to your site. Jetpack’s security rules are always up-to-date to protect against the latest threats.'
) }
</FormSettingExplanation>
<UpsellNudge
className="site-settings__security-settings-firewall-nudge"
title={
waf?.automatic_rules_available
? translate(
'Your site is not receiving the latest updates to automatic rules'
)
: translate( 'Set up automatic rules with one click' )
}
description={
waf?.automatic_rules_available
? translate( 'Upgrade to keep your site secure with up-to-date firewall rules' )
: translate( 'Upgrade to enable automatic firewall protection.' )
}
callToAction={ translate( 'Upgrade now' ) }
primaryButton
event="calypso_scan_settings_upgrade_nudge"
feature={ WPCOM_FEATURES_SCAN }
href={ `/checkout/${ selectedSiteSlug }/${ PRODUCT_JETPACK_SCAN }?redirect_to=/scan/firewall/${ selectedSiteSlug }` }
/>
</>
}
/>
</FormFieldset>
</>
);
}
88 changes: 88 additions & 0 deletions client/my-sites/scan/firewall/block-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Button } from '@automattic/components';
import { ToggleControl } from '@wordpress/components';
import { translate } from 'i18n-calypso';
import React, { useCallback, useEffect, useState } from 'react';
import FormFieldset from 'calypso/components/forms/form-fieldset';
import FormSettingExplanation from 'calypso/components/forms/form-setting-explanation';
import FormTextarea from 'calypso/components/forms/form-textarea';
import { useWafMutation, useWafQuery } from './data';

/**
* Blocked IP List Setting
*/
export default function BlockList() {
const { data: waf, isLoading } = useWafQuery();
const { mutate: updateWaf, isPending: isUpdating } = useWafMutation();

const [ inputState, setInputState ] = useState( '' );

const toggleIpBlockList = useCallback( () => {
updateWaf( {
jetpack_waf_ip_block_list_enabled: ! waf?.jetpack_waf_ip_block_list_enabled,
} );
}, [ updateWaf, waf?.jetpack_waf_ip_block_list_enabled ] );

const saveIpBlockList = useCallback( () => {
updateWaf( { jetpack_waf_ip_block_list: inputState } );
}, [ inputState, updateWaf ] );

useEffect( () => {
if ( waf?.jetpack_waf_ip_block_list ) {
setInputState( waf.jetpack_waf_ip_block_list );
}
}, [ waf?.jetpack_waf_ip_block_list ] );

// Do not render if the WAF is not supported.
if ( ! waf || ! waf?.waf_supported ) {
return null;
}

return (
<FormFieldset>
<ToggleControl
disabled={ isLoading || isUpdating }
onChange={ toggleIpBlockList }
checked={ !! waf.jetpack_waf_ip_block_list_enabled }
label={
<>
<div>{ translate( 'Block IP addresses' ) }</div>
<FormSettingExplanation>
{ translate(
'Stop specific visitors from accessing your site by their IP address.'
) }
</FormSettingExplanation>
{ waf?.jetpack_waf_ip_block_list_enabled && (
<>
<FormTextarea
id="jetpack_waf_ip_block_list"
value={ inputState }
onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) =>
setInputState( e.target.value )
}
disabled={ isLoading || isUpdating || ! waf?.jetpack_waf_ip_block_list_enabled }
placeholder={ translate( 'Example: 12.12.12.1-12.12.12.100' ) }
/>
<FormSettingExplanation>
{ translate(
'IPv4 and IPv6 supported. Separate IPs with commas, spaces, or new lines. To specify a range, use CIDR notation (i.e. 12.12.12.0/24) or enter the low value and high value separated by a dash (i.e. 12.12.12.0–12.12.12.255).'
) }
</FormSettingExplanation>
<div className="block-list-controls">
<div>
<Button
onClick={ saveIpBlockList }
disabled={ isLoading || isUpdating }
primary
>
{ translate( 'Save block list' ) }
</Button>
</div>
</div>
</>
) }
</>
}
/>
</FormFieldset>
);
}
Loading

0 comments on commit 6114be1

Please sign in to comment.