diff options
12 files changed, 102 insertions, 13 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9406d0055c..97830d19af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ Line wrap the file at 100 chars. Th ## [Unreleased] ### Added +- Handle "block when disconnected" extra kill-switch level in the GUI, showing the disconnected + state as blocked when appropriate and also having a toggle switch for the setting in the Advanced + Settings screen. + #### Linux - Detect if the computer is offline. If so, don't sit in a reconnect loop, instead block and show an error message. diff --git a/gui/packages/desktop/src/main/daemon-rpc.js b/gui/packages/desktop/src/main/daemon-rpc.js index 5064af6ba8..26da6e14af 100644 --- a/gui/packages/desktop/src/main/daemon-rpc.js +++ b/gui/packages/desktop/src/main/daemon-rpc.js @@ -383,6 +383,7 @@ export type Settings = { accountToken: ?AccountToken, allowLan: boolean, autoConnect: boolean, + blockWhenDisconnected: boolean, relaySettings: RelaySettings, tunnelOptions: TunnelOptions, }; @@ -391,6 +392,7 @@ const SettingsSchema = partialObject({ account_token: maybe(string), allow_lan: boolean, auto_connect: boolean, + block_when_disconnected: boolean, relay_settings: RelaySettingsSchema, tunnel_options: TunnelOptionsSchema, }); @@ -404,6 +406,7 @@ export interface DaemonRpcProtocol { updateRelaySettings(RelaySettingsUpdate): Promise<void>; setAllowLan(boolean): Promise<void>; setEnableIpv6(boolean): Promise<void>; + setBlockWhenDisconnected(boolean): Promise<void>; setOpenVpnMssfix(?number): Promise<void>; setAutoConnect(boolean): Promise<void>; connectTunnel(): Promise<void>; @@ -507,6 +510,10 @@ export class DaemonRpc implements DaemonRpcProtocol { await this._transport.send('set_enable_ipv6', [enableIpv6]); } + async setBlockWhenDisconnected(blockWhenDisconnected: boolean): Promise<void> { + await this._transport.send('set_block_when_disconnected', [blockWhenDisconnected]); + } + async setOpenVpnMssfix(mssfix: ?number): Promise<void> { await this._transport.send('set_openvpn_mssfix', [mssfix]); } diff --git a/gui/packages/desktop/src/main/index.js b/gui/packages/desktop/src/main/index.js index 92803129bd..a8785554fe 100644 --- a/gui/packages/desktop/src/main/index.js +++ b/gui/packages/desktop/src/main/index.js @@ -66,6 +66,7 @@ const ApplicationMain = { accountToken: null, allowLan: false, autoConnect: false, + blockWhenDisconnected: false, relaySettings: { normal: { location: 'any', @@ -454,7 +455,7 @@ const ApplicationMain = { _setTunnelState(newState: TunnelStateTransition) { this._tunnelState = newState; - this._updateTrayIcon(newState); + this._updateTrayIcon(newState, this._settings.blockWhenDisconnected); this._updateLocation(); if (!this._shouldSuppressNotifications()) { @@ -468,6 +469,7 @@ const ApplicationMain = { _setSettings(newSettings: Settings) { this._settings = newSettings; + this._updateTrayIcon(this._tunnelState, newSettings.blockWhenDisconnected); if (this._ipcEventChannel) { this._ipcEventChannel.settings.notify(newSettings); @@ -648,7 +650,7 @@ const ApplicationMain = { } }, - _trayIconType(tunnelState: TunnelStateTransition): TrayIconType { + _trayIconType(tunnelState: TunnelStateTransition, blockWhenDisconnected: boolean): TrayIconType { switch (tunnelState.state) { case 'connected': return 'secured'; @@ -668,26 +670,29 @@ const ApplicationMain = { return 'securing'; case 'nothing': - return 'unsecured'; + // handle the same way as disconnected + break; default: - log.error(`Invalid after disconnect action: ${(tunnelState.details: empty)}`); + throw new Error(`Invalid after disconnect state: ${(tunnelState.details: empty)}`); } - break; + // fallthrough case 'disconnected': - return 'unsecured'; + if (blockWhenDisconnected) { + return 'securing'; + } else { + return 'unsecured'; + } default: - log.error(`Invalid tunnel state: ${(tunnelState.state: empty)}`); + // unreachable, but can't prove it to flow because of the fallthrough + throw new Error(`Invalid tunnel state: ${tunnelState.state}`); } - - // Unreachable, but flow doesn't agree - return 'unsecured'; }, - _updateTrayIcon(tunnelState: TunnelStateTransition) { - const type = this._trayIconType(tunnelState); + _updateTrayIcon(tunnelState: TunnelStateTransition, blockWhenDisconnected: boolean) { + const type = this._trayIconType(tunnelState, blockWhenDisconnected); if (this._trayIconController) { this._trayIconController.animateToIcon(type); diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index 6a806f0e6b..0f281ab42c 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -325,6 +325,12 @@ export default class AppRenderer { actions.settings.updateEnableIpv6(enableIpv6); } + async setBlockWhenDisconnected(blockWhenDisconnected: boolean) { + const actions = this._reduxActions; + await this._daemonRpc.setBlockWhenDisconnected(blockWhenDisconnected); + actions.settings.updateBlockWhenDisconnected(blockWhenDisconnected); + } + async setOpenVpnMssfix(mssfix: ?number) { const actions = this._reduxActions; actions.settings.updateOpenVpnMssfix(mssfix); @@ -474,6 +480,7 @@ export default class AppRenderer { reduxSettings.updateAllowLan(newSettings.allowLan); reduxSettings.updateAutoConnect(newSettings.autoConnect); reduxSettings.updateEnableIpv6(newSettings.tunnelOptions.enableIpv6); + reduxSettings.updateBlockWhenDisconnected(newSettings.blockWhenDisconnected); reduxSettings.updateOpenVpnMssfix(newSettings.tunnelOptions.openvpn.mssfix); this._setRelaySettings(newSettings.relaySettings); diff --git a/gui/packages/desktop/src/renderer/components/AdvancedSettings.js b/gui/packages/desktop/src/renderer/components/AdvancedSettings.js index ddfcf5f2ce..a885849cac 100644 --- a/gui/packages/desktop/src/renderer/components/AdvancedSettings.js +++ b/gui/packages/desktop/src/renderer/components/AdvancedSettings.js @@ -21,10 +21,12 @@ const MAX_MSSFIX_VALUE = 1450; type Props = { enableIpv6: boolean, + blockWhenDisconnected: boolean, protocol: string, mssfix: ?number, port: string | number, setEnableIpv6: (boolean) => void, + setBlockWhenDisconnected: (boolean) => void, setOpenVpnMssfix: (?number) => void, onUpdate: (protocol: string, port: string | number) => void, onClose: () => void, @@ -98,6 +100,19 @@ export class AdvancedSettings extends Component<Props, State> { </Cell.Container> <Cell.Footer>Enable IPv6 communication through the tunnel.</Cell.Footer> + <Cell.Container> + <Cell.Label>Block when disconnected</Cell.Label> + <Switch + isOn={this.props.blockWhenDisconnected} + onChange={this.props.setBlockWhenDisconnected} + /> + </Cell.Container> + <Cell.Footer> + { + "Unless connected, always block all network traffic, even when you've disconnected or quit the app." + } + </Cell.Footer> + <View style={styles.advanced_settings__content}> <Selector title={'Network protocols'} diff --git a/gui/packages/desktop/src/renderer/components/Connect.js b/gui/packages/desktop/src/renderer/components/Connect.js index ae83f0a1f0..507171e372 100644 --- a/gui/packages/desktop/src/renderer/components/Connect.js +++ b/gui/packages/desktop/src/renderer/components/Connect.js @@ -22,6 +22,7 @@ type Props = { accountExpiry: ?string, selectedRelayName: string, connectionInfoOpen: boolean, + blockWhenDisconnected: boolean, onSettings: () => void, onSelectLocation: () => void, onConnect: () => void, @@ -199,6 +200,7 @@ export default class Connect extends Component<Props> { tunnelState={this.props.connection.status} version={this.props.version} openExternalLink={this.props.onExternalLink} + blockWhenDisconnected={this.props.blockWhenDisconnected} /> </View> </View> diff --git a/gui/packages/desktop/src/renderer/components/NotificationArea.js b/gui/packages/desktop/src/renderer/components/NotificationArea.js index 1625d63431..dac5b916f6 100644 --- a/gui/packages/desktop/src/renderer/components/NotificationArea.js +++ b/gui/packages/desktop/src/renderer/components/NotificationArea.js @@ -20,6 +20,7 @@ type Props = { tunnelState: TunnelStateTransition, version: VersionReduxState, openExternalLink: (string) => void, + blockWhenDisconnected: boolean, }; type NotificationAreaPresentation = @@ -62,7 +63,7 @@ export default class NotificationArea extends Component<Props, State> { }; static getDerivedStateFromProps(props: Props, state: State) { - const { version, tunnelState } = props; + const { version, tunnelState, blockWhenDisconnected } = props; switch (tunnelState.state) { case 'connecting': @@ -89,6 +90,16 @@ export default class NotificationArea extends Component<Props, State> { } // fallthrough + case 'disconnected': + if (blockWhenDisconnected) { + return { + visible: true, + type: 'blocking', + reason: '', + }; + } + // fallthrough + default: if (!version.consistent) { return { diff --git a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js index f19fcd2f09..5a3cad6ed5 100644 --- a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js +++ b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.js @@ -16,6 +16,7 @@ const mapStateToProps = (state: ReduxState) => { return { enableIpv6: state.settings.enableIpv6, + blockWhenDisconnected: state.settings.blockWhenDisconnected, mssfix: state.settings.openVpn.mssfix, ...protocolAndPort, }; @@ -73,6 +74,14 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) => } }, + setBlockWhenDisconnected: async (blockWhenDisconnected) => { + try { + await props.app.setBlockWhenDisconnected(blockWhenDisconnected); + } catch (e) { + log.error('Failed to update block when disconnected', e.message); + } + }, + setOpenVpnMssfix: async (mssfix) => { try { await props.app.setOpenVpnMssfix(mssfix); diff --git a/gui/packages/desktop/src/renderer/containers/ConnectPage.js b/gui/packages/desktop/src/renderer/containers/ConnectPage.js index fbd7b8aa6b..77b8109b4f 100644 --- a/gui/packages/desktop/src/renderer/containers/ConnectPage.js +++ b/gui/packages/desktop/src/renderer/containers/ConnectPage.js @@ -63,6 +63,7 @@ const mapStateToProps = (state: ReduxState) => { connection: state.connection, version: state.version, connectionInfoOpen: state.userInterface.connectionInfoOpen, + blockWhenDisconnected: state.settings.blockWhenDisconnected, }; }; diff --git a/gui/packages/desktop/src/renderer/lib/daemon-rpc-proxy.js b/gui/packages/desktop/src/renderer/lib/daemon-rpc-proxy.js index 5b928c8989..8b2e03c3dc 100644 --- a/gui/packages/desktop/src/renderer/lib/daemon-rpc-proxy.js +++ b/gui/packages/desktop/src/renderer/lib/daemon-rpc-proxy.js @@ -122,6 +122,10 @@ export default class DaemonRpcProxy implements DaemonRpcProtocol { return this._sendMessage('setEnableIpv6', enableIpv6); } + setBlockWhenDisconnected(blockWhenDisconnected: boolean): Promise<void> { + return this._sendMessage('setBlockWhenDisconnected', blockWhenDisconnected); + } + setOpenVpnMssfix(mssfix: ?number): Promise<void> { return this._sendMessage('setOpenVpnMssfix', mssfix); } diff --git a/gui/packages/desktop/src/renderer/redux/settings/actions.js b/gui/packages/desktop/src/renderer/redux/settings/actions.js index edb9f8e756..8a7de02e90 100644 --- a/gui/packages/desktop/src/renderer/redux/settings/actions.js +++ b/gui/packages/desktop/src/renderer/redux/settings/actions.js @@ -27,6 +27,11 @@ export type UpdateEnableIpv6Action = { enableIpv6: boolean, }; +export type UpdateBlockWhenDisconnectedAction = { + type: 'UPDATE_BLOCK_WHEN_DISCONNECTED', + blockWhenDisconnected: boolean, +}; + export type UpdateOpenVpnMssfixAction = { type: 'UPDATE_OPENVPN_MSSFIX', mssfix: ?number, @@ -38,6 +43,7 @@ export type SettingsAction = | UpdateAutoConnectAction | UpdateAllowLanAction | UpdateEnableIpv6Action + | UpdateBlockWhenDisconnectedAction | UpdateOpenVpnMssfixAction; function updateRelay(relay: RelaySettingsRedux): UpdateRelayAction { @@ -77,6 +83,15 @@ function updateEnableIpv6(enableIpv6: boolean): UpdateEnableIpv6Action { }; } +function updateBlockWhenDisconnected( + blockWhenDisconnected: boolean, +): UpdateBlockWhenDisconnectedAction { + return { + type: 'UPDATE_BLOCK_WHEN_DISCONNECTED', + blockWhenDisconnected, + }; +} + function updateOpenVpnMssfix(mssfix: ?number): UpdateOpenVpnMssfixAction { return { type: 'UPDATE_OPENVPN_MSSFIX', @@ -90,5 +105,6 @@ export default { updateAutoConnect, updateAllowLan, updateEnableIpv6, + updateBlockWhenDisconnected, updateOpenVpnMssfix, }; diff --git a/gui/packages/desktop/src/renderer/redux/settings/reducers.js b/gui/packages/desktop/src/renderer/redux/settings/reducers.js index eba3341e7a..e94cba5132 100644 --- a/gui/packages/desktop/src/renderer/redux/settings/reducers.js +++ b/gui/packages/desktop/src/renderer/redux/settings/reducers.js @@ -49,6 +49,7 @@ export type SettingsReduxState = { autoConnect: boolean, allowLan: boolean, enableIpv6: boolean, + blockWhenDisconnected: boolean, openVpn: { mssfix: ?number, }, @@ -66,6 +67,7 @@ const initialState: SettingsReduxState = { autoConnect: false, allowLan: false, enableIpv6: true, + blockWhenDisconnected: false, openVpn: { mssfix: null, }, @@ -106,6 +108,12 @@ export default function( enableIpv6: action.enableIpv6, }; + case 'UPDATE_BLOCK_WHEN_DISCONNECTED': + return { + ...state, + blockWhenDisconnected: action.blockWhenDisconnected, + }; + case 'UPDATE_OPENVPN_MSSFIX': return { ...state, |
