From 5334fd94302755063fcd7bb45d5bb86378afee86 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 14 May 2021 18:44:17 +0200 Subject: [PATCH] Refactor code --- src/helpers/AccountHelper.ts | 147 +----------------------- src/helpers/AdminKeyHelper.ts | 153 +++++++++++++++++++++++++ src/ui/routes/AccountSettingsRoute.tsx | 16 ++- src/ui/routes/LoginRoute.tsx | 13 +-- src/ui/routes/MainRoute.tsx | 1 - 5 files changed, 167 insertions(+), 163 deletions(-) create mode 100644 src/helpers/AdminKeyHelper.ts diff --git a/src/helpers/AccountHelper.ts b/src/helpers/AccountHelper.ts index 765ad69..a1f83a2 100644 --- a/src/helpers/AccountHelper.ts +++ b/src/helpers/AccountHelper.ts @@ -4,17 +4,9 @@ * @author Pierre Hubert */ -import { - ArrayBufferToBase64, - base64NoPaddingToUint8Array, -} from "../utils/Base64Utils"; +import { AuthKey } from "./AdminKeyHelper"; import { serverRequest } from "./APIHelper"; -export interface AuthKey { - name: string; - id: number; -} - export interface AuthOptions { reset_token: string; keys: AuthKey[]; @@ -33,18 +25,12 @@ export interface NewAdminGeneralSettings { email: string; } -export interface AdminAccountKey { - id: number; - name: string; - time_add: number; -} - export interface AdminResetToken { token: string; expire: number; } -const SESSION_STORAGE_TOKEN = "auth_token"; +export const SESSION_STORAGE_TOKEN = "auth_token"; let currentAccount: AdminAccount | null = null; @@ -165,133 +151,4 @@ export class AccountHelper { id: adminID, }); } - - /** - * First step of access key enrollment - */ - static async GetKeyRegistrationChallenge(): Promise { - const res = await serverRequest("accounts/challenge_register_key"); - res.publicKey.challenge = base64NoPaddingToUint8Array( - res.publicKey.challenge - ); - res.publicKey.user.id = base64NoPaddingToUint8Array( - res.publicKey.user.id - ); - - return res; - } - - /** - * Register key - * - * @param name The name of the key to create - * @param cred The credentials to register - */ - static async RegisterKey(name: string, cred: any): Promise { - const res = { - id: cred.id, - rawId: ArrayBufferToBase64(cred.rawId), - type: cred.type, - response: { - attestationObject: ArrayBufferToBase64( - cred.response.attestationObject - ), - clientDataJSON: ArrayBufferToBase64( - cred.response.clientDataJSON - ), - }, - }; - - await serverRequest("accounts/register_key", { - name: name, - key: JSON.stringify(res), - }); - } - - /** - * First step of security key authentication - * - * @param mail Target admin account email address - * @param key The key to use to authentifcate - */ - static async GetAuthenticationChallenge( - mail: string, - key: AuthKey - ): Promise { - const res = await serverRequest("accounts/challenge_auth_with_key", { - mail: mail, - key_id: key.id, - }); - - res.publicKey.challenge = base64NoPaddingToUint8Array( - res.publicKey.challenge - ); - - for (let cred of res.publicKey.allowCredentials) { - cred.id = base64NoPaddingToUint8Array(cred.id); - } - - return res; - } - - /** - * Attempt to sign in using security key - * - * @param mail Target admin account email address - * @param key Key used to authenticate - * @param cred Response to authentication - */ - static async AuthenticateWithKey( - mail: string, - key: AuthKey, - cred: any - ): Promise { - const creds = { - id: cred.id, - rawId: ArrayBufferToBase64(cred.rawId), - type: cred.type, - response: { - authenticatorData: ArrayBufferToBase64( - cred.response.authenticatorData - ), - clientDataJSON: ArrayBufferToBase64( - cred.response.clientDataJSON - ), - signature: ArrayBufferToBase64(cred.response.signature), - userHandle: cred.response.userHandle, - }, - }; - - const res = await serverRequest("accounts/auth_with_key", { - mail: mail, - key_id: key.id, - credential: JSON.stringify(creds), - }); - - sessionStorage.setItem(SESSION_STORAGE_TOKEN, res.token); - } - - /** - * Get the list of keys of an admin - * - * @param adminID The id of the target administrator - */ - static async GetAdminKeys(adminID: number): Promise { - return await serverRequest("accounts/keys", { - id: adminID, - }); - } - - /** - * Delete an admin auth key - * - * @param adminID The id of the target admin - * @param keyID The id of the key to delete - */ - static async DeleteAuthKey(adminID: number, keyID: number) { - return await serverRequest("accounts/delete_auth_key", { - adminID: adminID, - keyID: keyID, - }); - } } diff --git a/src/helpers/AdminKeyHelper.ts b/src/helpers/AdminKeyHelper.ts new file mode 100644 index 0000000..36e00f9 --- /dev/null +++ b/src/helpers/AdminKeyHelper.ts @@ -0,0 +1,153 @@ +/** + * Admin security keys management helper + * + * @author Pierre Hubert + */ + +import { + ArrayBufferToBase64, + base64NoPaddingToUint8Array, +} from "../utils/Base64Utils"; +import { SESSION_STORAGE_TOKEN } from "./AccountHelper"; +import { serverRequest } from "./APIHelper"; + +export interface AdminAccountKey { + id: number; + name: string; + time_add: number; +} +export interface AuthKey { + name: string; + id: number; +} + +export class AdminKeyHelper { + /** + * First step of access key enrollment + */ + static async GetKeyRegistrationChallenge(): Promise { + const res = await serverRequest("keys/challenge_register_key"); + res.publicKey.challenge = base64NoPaddingToUint8Array( + res.publicKey.challenge + ); + res.publicKey.user.id = base64NoPaddingToUint8Array( + res.publicKey.user.id + ); + + return res; + } + + /** + * Register key + * + * @param name The name of the key to create + * @param cred The credentials to register + */ + static async RegisterKey(name: string, cred: any): Promise { + const res = { + id: cred.id, + rawId: ArrayBufferToBase64(cred.rawId), + type: cred.type, + response: { + attestationObject: ArrayBufferToBase64( + cred.response.attestationObject + ), + clientDataJSON: ArrayBufferToBase64( + cred.response.clientDataJSON + ), + }, + }; + + await serverRequest("keys/register_key", { + name: name, + key: JSON.stringify(res), + }); + } + + /** + * First step of security key authentication + * + * @param mail Target admin account email address + * @param key The key to use to authentifcate + */ + static async GetAuthenticationChallenge( + mail: string, + key: AuthKey + ): Promise { + const res = await serverRequest("keys/challenge_auth_with_key", { + mail: mail, + key_id: key.id, + }); + + res.publicKey.challenge = base64NoPaddingToUint8Array( + res.publicKey.challenge + ); + + for (let cred of res.publicKey.allowCredentials) { + cred.id = base64NoPaddingToUint8Array(cred.id); + } + + return res; + } + + /** + * Attempt to sign in using security key + * + * @param mail Target admin account email address + * @param key Key used to authenticate + * @param cred Response to authentication + */ + static async AuthenticateWithKey( + mail: string, + key: AuthKey, + cred: any + ): Promise { + const creds = { + id: cred.id, + rawId: ArrayBufferToBase64(cred.rawId), + type: cred.type, + response: { + authenticatorData: ArrayBufferToBase64( + cred.response.authenticatorData + ), + clientDataJSON: ArrayBufferToBase64( + cred.response.clientDataJSON + ), + signature: ArrayBufferToBase64(cred.response.signature), + userHandle: cred.response.userHandle, + }, + }; + + const res = await serverRequest("keys/auth_with_key", { + mail: mail, + key_id: key.id, + credential: JSON.stringify(creds), + }); + + sessionStorage.setItem(SESSION_STORAGE_TOKEN, res.token); + } + + /** + * Get the list of keys of an admin + * + * @param adminID The id of the target administrator + */ + static async GetAdminKeys(adminID: number): Promise { + return await serverRequest("keys/keys", { + id: adminID, + }); + } + + /** + * Delete an admin auth key + * + * @param adminID The id of the target admin + * @param keyID The id of the key to delete + */ + static async DeleteAuthKey(adminID: number, keyID: number) { + return await serverRequest("keys/delete_auth_key", { + adminID: adminID, + keyID: keyID, + }); + } +} diff --git a/src/ui/routes/AccountSettingsRoute.tsx b/src/ui/routes/AccountSettingsRoute.tsx index 60988a5..6ac2a81 100644 --- a/src/ui/routes/AccountSettingsRoute.tsx +++ b/src/ui/routes/AccountSettingsRoute.tsx @@ -21,11 +21,8 @@ import { import { Delete } from "@material-ui/icons"; import React from "react"; import { useParams } from "react-router-dom"; -import { - AccountHelper, - AdminAccount, - AdminAccountKey, -} from "../../helpers/AccountHelper"; +import { AccountHelper, AdminAccount } from "../../helpers/AccountHelper"; +import { AdminAccountKey, AdminKeyHelper } from "../../helpers/AdminKeyHelper"; import { CopyToClipboard } from "../../utils/ClipboardUtils"; import { AsyncWidget } from "../widgets/AsyncWidget"; import { @@ -199,7 +196,7 @@ export class KeySettingsSection extends React.Component< } async load() { - const keys = await AccountHelper.GetAdminKeys(this.props.admin.id); + const keys = await AdminKeyHelper.GetAdminKeys(this.props.admin.id); this.setState({ keys: keys }); } @@ -227,7 +224,8 @@ export class KeySettingsSection extends React.Component< async registerNewKey() { try { - const challenge = await AccountHelper.GetKeyRegistrationChallenge(); + const challenge = + await AdminKeyHelper.GetKeyRegistrationChallenge(); const credential = await navigator.credentials.create(challenge); if (credential == null) throw new Error("Operation aborted!"); @@ -238,7 +236,7 @@ export class KeySettingsSection extends React.Component< minLength: 2, }); - await AccountHelper.RegisterKey(name, credential); + await AdminKeyHelper.RegisterKey(name, credential); snackbar("Successfully enrolled a new key!"); @@ -258,7 +256,7 @@ export class KeySettingsSection extends React.Component< ) return; - await AccountHelper.DeleteAuthKey(this.props.admin.id, key.id); + await AdminKeyHelper.DeleteAuthKey(this.props.admin.id, key.id); snackbar("The key was successfully deleted!"); this.setState({ counter: this.state.counter + 1 }); diff --git a/src/ui/routes/LoginRoute.tsx b/src/ui/routes/LoginRoute.tsx index 9848aa3..84d1684 100644 --- a/src/ui/routes/LoginRoute.tsx +++ b/src/ui/routes/LoginRoute.tsx @@ -21,11 +21,8 @@ import { import { ErrorOutline, Lock, VpnKey } from "@material-ui/icons"; import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; import React from "react"; -import { - AccountHelper, - AuthKey, - AuthOptions, -} from "../../helpers/AccountHelper"; +import { AccountHelper, AuthOptions } from "../../helpers/AccountHelper"; +import { AdminKeyHelper, AuthKey } from "../../helpers/AdminKeyHelper"; import { input, matAlert } from "../widgets/DialogsProvider"; function ErrorGettingOptions(p: { message: string }) { @@ -244,14 +241,14 @@ class AuthOptionsWidget extends React.Component< async loginWithSecurityKey(key: AuthKey) { try { - const challenge = await AccountHelper.GetAuthenticationChallenge( + const challenge = await AdminKeyHelper.GetAuthenticationChallenge( this.props.email, key ); const result = await navigator.credentials.get(challenge); - await AccountHelper.AuthenticateWithKey( + await AdminKeyHelper.AuthenticateWithKey( this.props.email, key, result @@ -267,7 +264,7 @@ class AuthOptionsWidget extends React.Component< render() { // Check if no option is available if ( - this.props.options.keys.length == 0 && + this.props.options.keys.length === 0 && !this.props.options.reset_token ) return ( diff --git a/src/ui/routes/MainRoute.tsx b/src/ui/routes/MainRoute.tsx index 825ce38..c950570 100644 --- a/src/ui/routes/MainRoute.tsx +++ b/src/ui/routes/MainRoute.tsx @@ -12,7 +12,6 @@ import { ListItemIcon, ListItemText, makeStyles, - MenuItem, Paper, Toolbar, Typography,