diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2019-03-06 11:16:13 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2019-03-07 13:48:30 +0100 |
| commit | 8657e73fcc11edb3d0e22e2008c28436fffa1cb3 (patch) | |
| tree | b8fcf283e8bdecb626bd8cf7af26c771030f0ed2 /gui | |
| parent | 07c3a6f1c07d776686fbb8842b677424bdec8fca (diff) | |
| download | mullvadvpn-8657e73fcc11edb3d0e22e2008c28436fffa1cb3.tar.xz mullvadvpn-8657e73fcc11edb3d0e22e2008c28436fffa1cb3.zip | |
Add heuristics around account expiry to predict when it becomes stale
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/src/renderer/app.tsx | 24 | ||||
| -rw-r--r-- | gui/src/renderer/components/Connect.tsx | 55 |
2 files changed, 68 insertions, 11 deletions
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index 51b2983bcd..770f148c77 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -25,6 +25,7 @@ import { IWindowShapeParameters } from '../main/window-controller'; import { loadTranslations } from '../shared/gettext'; import { IGuiSettingsState } from '../shared/gui-settings-state'; import { IpcRendererEventChannel } from '../shared/ipc-event-channel'; +import AccountExpiry from './lib/account-expiry'; import { AccountToken, @@ -59,13 +60,14 @@ export default class AppRenderer { return IpcRendererEventChannel.account.getData(accountToken); }, (accountData) => { - this.reduxActions.account.updateAccountExpiry(accountData && accountData.expiry); + this.setAccountExpiry(accountData && accountData.expiry); }, ); private tunnelState: TunnelStateTransition; private settings: ISettings; private guiSettings: IGuiSettingsState; + private accountExpiry?: AccountExpiry; private connectedToDaemon = false; private autoConnected = false; private doingLogin = false; @@ -102,6 +104,10 @@ export default class AppRenderer { IpcRendererEventChannel.tunnel.listen((newState: TunnelStateTransition) => { this.setTunnelState(newState); this.updateBlockedState(newState, this.settings.blockWhenDisconnected); + + if (this.accountExpiry) { + this.detectStaleAccountExpiry(newState, this.accountExpiry); + } }); IpcRendererEventChannel.settings.listen((newSettings: ISettings) => { @@ -563,6 +569,22 @@ export default class AppRenderer { this.reduxActions.settings.updateGuiSettings(guiSettings); } + private setAccountExpiry(expiry?: string) { + this.accountExpiry = expiry ? new AccountExpiry(expiry, remote.app.getLocale()) : undefined; + this.reduxActions.account.updateAccountExpiry(expiry); + } + + private detectStaleAccountExpiry( + tunnelState: TunnelStateTransition, + accountExpiry: AccountExpiry, + ) { + // It's likely that the account expiry is stale if the daemon managed to establish the tunnel. + if (tunnelState.state === 'connected' && accountExpiry.hasExpired()) { + log.info('Detected the stale account expiry.'); + this.accountDataCache.invalidate(); + } + } + private storeAutoStart(autoStart: boolean) { this.reduxActions.settings.updateAutoStart(autoStart); } diff --git a/gui/src/renderer/components/Connect.tsx b/gui/src/renderer/components/Connect.tsx index 657d030c6b..6d10400a30 100644 --- a/gui/src/renderer/components/Connect.tsx +++ b/gui/src/renderer/components/Connect.tsx @@ -74,7 +74,23 @@ const styles = { }), }; -export default class Connect extends Component<IProps> { +interface IState { + isAccountExpired: boolean; +} + +export default class Connect extends Component<IProps, IState> { + constructor(props: IProps) { + super(props); + + this.state = { + isAccountExpired: this.checkAccountExpired(props, false), + }; + } + + public componentDidUpdate() { + this.updateAccountExpired(); + } + public render() { return ( <Layout> @@ -83,23 +99,42 @@ export default class Connect extends Component<IProps> { <SettingsBarButton onPress={this.props.onSettings} /> </Header> <Container> - {this.shouldShowExpiredAccountView() ? this.renderExpiredAccountView() : this.renderMap()} + {this.state.isAccountExpired ? this.renderExpiredAccountView() : this.renderMap()} </Container> </Layout> ); } - private shouldShowExpiredAccountView(): boolean { - const tunnelState = this.props.connection.status; + private updateAccountExpired() { + const nextAccountExpired = this.checkAccountExpired(this.props, this.state.isAccountExpired); + + if (nextAccountExpired !== this.state.isAccountExpired) { + this.setState({ + isAccountExpired: nextAccountExpired, + }); + } + } + + private checkAccountExpired(props: IProps, prevAccountExpired: boolean): boolean { + const tunnelState = props.connection.status; + + // Blocked with auth failure / expired account + if ( + tunnelState.state === 'blocked' && + tunnelState.details.reason === 'auth_failed' && + parseAuthFailure(tunnelState.details.details).kind === AuthFailureKind.expiredAccount + ) { + return true; + } - if (tunnelState.state === 'blocked' && tunnelState.details.reason === 'auth_failed') { - const authError = new AuthFailureError(tunnelState.details.details); - if (authError.kind === AuthFailureKind.expiredAccount) { - return true; - } + // Use the account expiry to deduce the account state + if (this.props.accountExpiry) { + return this.props.accountExpiry.hasExpired(); } - return this.props.accountExpiry ? this.props.accountExpiry.hasExpired() : false; + // Do not assume that the account hasn't expired if the expiry is not available at the moment + // instead return the last known state. + return prevAccountExpired; } private renderExpiredAccountView() { |
