From 0287e472f8bf66518a470f68fcf05a32001f68f6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 12 Mar 2019 19:08:12 -0600 Subject: [PATCH] Add a button to log everyone out --- src/api/admin/AdminService.ts | 29 ++++++++++++++++++- web/app/admin/home/home.component.html | 10 +++++++ web/app/admin/home/home.component.ts | 24 ++++++++++++++- .../logout-confirmation.component.html | 20 +++++++++++++ .../logout-confirmation.component.scss | 0 .../logout-confirmation.component.ts | 15 ++++++++++ web/app/app.module.ts | 3 ++ web/app/shared/models/admin-responses.ts | 3 ++ .../services/admin/admin-api.service.ts | 4 +++ 9 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 web/app/admin/home/logout-confirmation/logout-confirmation.component.html create mode 100644 web/app/admin/home/logout-confirmation/logout-confirmation.component.scss create mode 100644 web/app/admin/home/logout-confirmation/logout-confirmation.component.ts diff --git a/src/api/admin/AdminService.ts b/src/api/admin/AdminService.ts index 5ab018b..0a4004f 100644 --- a/src/api/admin/AdminService.ts +++ b/src/api/admin/AdminService.ts @@ -1,10 +1,12 @@ -import { GET, Path, QueryParam } from "typescript-rest"; +import { GET, Path, POST, QueryParam } from "typescript-rest"; import { ScalarService } from "../scalar/ScalarService"; import config from "../../config"; import { ApiError } from "../ApiError"; import { MatrixLiteClient } from "../../matrix/MatrixLiteClient"; import { CURRENT_VERSION } from "../../version"; import { getFederationConnInfo } from "../../matrix/helpers"; +import UserScalarToken from "../../db/models/UserScalarToken"; +import { Cache, CACHE_SCALAR_ACCOUNTS } from "../../MemoryCache"; interface DimensionVersionResponse { version: string; @@ -20,6 +22,9 @@ interface DimensionConfigResponse { federationHostname: string; clientServerUrl: string; }; + sessionInfo: { + numTokens: number; + }; } /** @@ -82,6 +87,9 @@ export class AdminService { federationHostname: fedInfo.hostname, clientServerUrl: config.homeserver.clientServerUrl, }, + sessionInfo: { + numTokens: await UserScalarToken.count(), + }, }; } @@ -95,4 +103,23 @@ export class AdminService { resolvedServer: await getFederationConnInfo(serverName), }; } + + @POST + @Path("sessions/logout/all") + public async logoutAll(@QueryParam("scalar_token") scalarToken: string): Promise { + await AdminService.validateAndGetAdminTokenOwner(scalarToken); + + // Clear the cache first to hopefully invalidate a bunch of them + Cache.for(CACHE_SCALAR_ACCOUNTS).clear(); + + const tokens = await UserScalarToken.all(); + for (const token of tokens) { + await token.destroy(); + } + + // Clear it again because the delete loop can be slow + Cache.for(CACHE_SCALAR_ACCOUNTS).clear(); + + return {}; + } } \ No newline at end of file diff --git a/web/app/admin/home/home.component.html b/web/app/admin/home/home.component.html index e943a6d..a34d9ee 100644 --- a/web/app/admin/home/home.component.html +++ b/web/app/admin/home/home.component.html @@ -28,6 +28,16 @@ Utility User ID: {{ config.homeserver.userId }} + +
+
+ Sessions
+ Tokens registered: {{ config.sessionInfo.numTokens }}
+ +
+
\ No newline at end of file diff --git a/web/app/admin/home/home.component.ts b/web/app/admin/home/home.component.ts index 4e3d3aa..e47bb15 100644 --- a/web/app/admin/home/home.component.ts +++ b/web/app/admin/home/home.component.ts @@ -1,6 +1,12 @@ import { Component } from "@angular/core"; import { AdminApiService } from "../../shared/services/admin/admin-api.service"; import { FE_DimensionConfig } from "../../shared/models/admin-responses"; +import { ToasterService } from "angular2-toaster"; +import { Modal, overlayConfigFactory } from "ngx-modialog"; +import { + AdminLogoutConfirmationDialogComponent, + LogoutConfirmationDialogContext +} from "./logout-confirmation/logout-confirmation.component"; @Component({ templateUrl: "./home.component.html", @@ -11,10 +17,26 @@ export class AdminHomeComponent { public isLoading = true; public config: FE_DimensionConfig; - constructor(adminApi: AdminApiService) { + constructor(private adminApi: AdminApiService, + private toaster: ToasterService, + private modal: Modal) { adminApi.getConfig().then(config => { this.config = config; this.isLoading = false; }); } + + public logoutAll(): void { + this.modal.open(AdminLogoutConfirmationDialogComponent, overlayConfigFactory({ + isBlocking: true, + }, LogoutConfirmationDialogContext)).result.then(() => { + this.adminApi.logoutAll().then(() => { + this.toaster.pop("success", "Everyone has been logged out"); + this.config.sessionInfo.numTokens = 0; + }).catch(err => { + console.error(err); + this.toaster.pop("error", "Error logging everyone out"); + }); + }); + } } diff --git a/web/app/admin/home/logout-confirmation/logout-confirmation.component.html b/web/app/admin/home/logout-confirmation/logout-confirmation.component.html new file mode 100644 index 0000000..c11da9f --- /dev/null +++ b/web/app/admin/home/logout-confirmation/logout-confirmation.component.html @@ -0,0 +1,20 @@ +
+
+

Logout confirmation

+
+
+

+ Logging everyone out will disable all known login tokens for Dimension and upstream integration managers. + Most clients will automatically re-register for a login token behind the scenes, similar to how a login token + was first acquired. +

+
+ +
\ No newline at end of file diff --git a/web/app/admin/home/logout-confirmation/logout-confirmation.component.scss b/web/app/admin/home/logout-confirmation/logout-confirmation.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/app/admin/home/logout-confirmation/logout-confirmation.component.ts b/web/app/admin/home/logout-confirmation/logout-confirmation.component.ts new file mode 100644 index 0000000..ce71df1 --- /dev/null +++ b/web/app/admin/home/logout-confirmation/logout-confirmation.component.ts @@ -0,0 +1,15 @@ +import { Component } from "@angular/core"; +import { DialogRef, ModalComponent } from "ngx-modialog"; +import { BSModalContext } from "ngx-modialog/plugins/bootstrap"; + +export class LogoutConfirmationDialogContext extends BSModalContext { +} + +@Component({ + templateUrl: "./logout-confirmation.component.html", + styleUrls: ["./logout-confirmation.component.scss"], +}) +export class AdminLogoutConfirmationDialogComponent implements ModalComponent { + + constructor(public dialog: DialogRef) {} +} diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 9464dd9..297e682 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -109,6 +109,7 @@ import { SlackBridgeConfigComponent } from "./configs/bridge/slack/slack.bridge. import { AdminSlackBridgeManageSelfhostedComponent } from "./admin/bridges/slack/manage-selfhosted/manage-selfhosted.component"; import { AdminSlackBridgeComponent } from "./admin/bridges/slack/slack.component"; import { AdminSlackApiService } from "./shared/services/admin/admin-slack-api.service"; +import { AdminLogoutConfirmationDialogComponent } from "./admin/home/logout-confirmation/logout-confirmation.component"; import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-example/reauth-example.component"; @NgModule({ @@ -199,6 +200,7 @@ import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-ex SlackBridgeConfigComponent, AdminSlackBridgeManageSelfhostedComponent, AdminSlackBridgeComponent, + AdminLogoutConfirmationDialogComponent, ReauthExampleWidgetWrapperComponent, // Vendor @@ -252,6 +254,7 @@ import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-ex AdminGitterBridgeManageSelfhostedComponent, AdminAddCustomBotComponent, AdminSlackBridgeManageSelfhostedComponent, + AdminLogoutConfirmationDialogComponent, ] }) export class AppModule { diff --git a/web/app/shared/models/admin-responses.ts b/web/app/shared/models/admin-responses.ts index 9d007b9..647f934 100644 --- a/web/app/shared/models/admin-responses.ts +++ b/web/app/shared/models/admin-responses.ts @@ -10,6 +10,9 @@ export interface FE_DimensionConfig { federationHostname: string; clientServerUrl: string; }; + sessionInfo: { + numTokens: number; + }; } export interface FE_DimensionVersion { diff --git a/web/app/shared/services/admin/admin-api.service.ts b/web/app/shared/services/admin/admin-api.service.ts index a0f4b0b..1609ce6 100644 --- a/web/app/shared/services/admin/admin-api.service.ts +++ b/web/app/shared/services/admin/admin-api.service.ts @@ -20,4 +20,8 @@ export class AdminApiService extends AuthedApi { public getVersion(): Promise { return this.authedGet("/api/v1/dimension/admin/version").map(r => r.json()).toPromise(); } + + public logoutAll(): Promise { + return this.authedPost("/api/v1/dimension/admin/sessions/logout/all").map(r => r.json()).toPromise(); + } }