1
0
mirror of https://github.com/flarum/core.git synced 2025-07-31 13:40:20 +02:00

feat: global logout to clear all sessions, access tokens, email tokens and password tokens (#3605)

* chore: re-organize security locale keys alphabetically
* test: can globally logout
* feat: add global logout controller
* feat: add global logout UI to user security page
* test: re-adapt tests to changes
* feat: add boolean to indicate if logout even is global
* chore(review): split loading property
* chore: follow-up branch update

Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
This commit is contained in:
Sami Mazouz
2023-02-21 15:28:55 +01:00
committed by GitHub
parent d35bb873a8
commit bbf873442a
8 changed files with 239 additions and 20 deletions

View File

@@ -57,12 +57,30 @@ export default class UserSecurityPage<CustomAttrs extends IUserPageAttrs = IUser
items.add(
section,
<FieldSet className={`Security-${section}`} label={app.translator.trans(`core.forum.security.${sectionLocale}_heading`)}>
<FieldSet className={`UserSecurityPage-${section}`} label={app.translator.trans(`core.forum.security.${sectionLocale}_heading`)}>
{this[sectionName]().toArray()}
</FieldSet>
);
});
if (this.user!.id() === app.session.user!.id()) {
items.add(
'globalLogout',
<FieldSet className="UserSecurityPage-globalLogout" label={app.translator.trans('core.forum.security.global_logout.heading')}>
<span className="helpText">{app.translator.trans('core.forum.security.global_logout.help_text')}</span>
<Button
className="Button"
icon="fas fa-sign-out-alt"
onclick={this.globalLogout.bind(this)}
loading={this.state.loadingGlobalLogout}
disabled={this.state.loadingTerminateSessions}
>
{app.translator.trans('core.forum.security.global_logout.log_out_button')}
</Button>
</FieldSet>
);
}
return items;
}
@@ -141,7 +159,12 @@ export default class UserSecurityPage<CustomAttrs extends IUserPageAttrs = IUser
const isDisabled = !this.state.hasOtherActiveSessions();
let terminateAllOthersButton = (
<Button className="Button" onclick={this.terminateAllOtherSessions.bind(this)} loading={this.state.isLoading()} disabled={isDisabled}>
<Button
className="Button"
onclick={this.terminateAllOtherSessions.bind(this)}
loading={this.state.loadingTerminateSessions}
disabled={this.state.loadingGlobalLogout || isDisabled}
>
{app.translator.trans('core.forum.security.terminate_all_other_sessions')}
</Button>
);
@@ -174,7 +197,7 @@ export default class UserSecurityPage<CustomAttrs extends IUserPageAttrs = IUser
terminateAllOtherSessions() {
if (!confirm(extractText(app.translator.trans('core.forum.security.terminate_all_other_sessions_confirmation')))) return;
this.state.setLoading(true);
this.state.loadingTerminateSessions = true;
return app
.request({
@@ -188,12 +211,28 @@ export default class UserSecurityPage<CustomAttrs extends IUserPageAttrs = IUser
this.state.removeOtherSessionTokens();
app.alerts.show({ type: 'success' }, app.translator.trans('core.forum.security.session_terminated', { count }));
m.redraw();
})
.catch(() => {
app.alerts.show({ type: 'error' }, app.translator.trans('core.forum.security.session_termination_failed'));
})
.finally(() => this.state.setLoading(false));
.finally(() => {
this.state.loadingTerminateSessions = false;
m.redraw();
});
}
globalLogout() {
this.state.loadingGlobalLogout = true;
return app
.request({
method: 'POST',
url: app.forum.attribute<string>('baseUrl') + '/global-logout',
})
.then(() => window.location.reload())
.finally(() => {
this.state.loadingGlobalLogout = false;
m.redraw();
});
}
}

View File

@@ -2,20 +2,13 @@ import AccessToken from '../../common/models/AccessToken';
export default class UserSecurityPageState {
protected tokens: AccessToken[] | null = null;
protected loading: boolean = false;
public isLoading(): boolean {
return this.loading;
}
public loadingTerminateSessions: boolean = false;
public loadingGlobalLogout: boolean = false;
public hasLoadedTokens(): boolean {
return this.tokens !== null;
}
public setLoading(loading: boolean): void {
this.loading = loading;
}
public getTokens(): AccessToken[] | null {
return this.tokens;
}