Refactor code

This commit is contained in:
Pierre HUBERT 2021-05-14 18:44:17 +02:00
parent bf46cc7043
commit 5334fd9430
5 changed files with 167 additions and 163 deletions

View File

@ -4,17 +4,9 @@
* @author Pierre Hubert * @author Pierre Hubert
*/ */
import { import { AuthKey } from "./AdminKeyHelper";
ArrayBufferToBase64,
base64NoPaddingToUint8Array,
} from "../utils/Base64Utils";
import { serverRequest } from "./APIHelper"; import { serverRequest } from "./APIHelper";
export interface AuthKey {
name: string;
id: number;
}
export interface AuthOptions { export interface AuthOptions {
reset_token: string; reset_token: string;
keys: AuthKey[]; keys: AuthKey[];
@ -33,18 +25,12 @@ export interface NewAdminGeneralSettings {
email: string; email: string;
} }
export interface AdminAccountKey {
id: number;
name: string;
time_add: number;
}
export interface AdminResetToken { export interface AdminResetToken {
token: string; token: string;
expire: number; expire: number;
} }
const SESSION_STORAGE_TOKEN = "auth_token"; export const SESSION_STORAGE_TOKEN = "auth_token";
let currentAccount: AdminAccount | null = null; let currentAccount: AdminAccount | null = null;
@ -165,133 +151,4 @@ export class AccountHelper {
id: adminID, id: adminID,
}); });
} }
/**
* First step of access key enrollment
*/
static async GetKeyRegistrationChallenge(): Promise<any> {
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<void> {
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<any> {
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<any> {
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<AdminAccountKey[]> {
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,
});
}
} }

View File

@ -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<any> {
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<void> {
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<any> {
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<any> {
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<AdminAccountKey[]> {
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,
});
}
}

View File

@ -21,11 +21,8 @@ import {
import { Delete } from "@material-ui/icons"; import { Delete } from "@material-ui/icons";
import React from "react"; import React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { import { AccountHelper, AdminAccount } from "../../helpers/AccountHelper";
AccountHelper, import { AdminAccountKey, AdminKeyHelper } from "../../helpers/AdminKeyHelper";
AdminAccount,
AdminAccountKey,
} from "../../helpers/AccountHelper";
import { CopyToClipboard } from "../../utils/ClipboardUtils"; import { CopyToClipboard } from "../../utils/ClipboardUtils";
import { AsyncWidget } from "../widgets/AsyncWidget"; import { AsyncWidget } from "../widgets/AsyncWidget";
import { import {
@ -199,7 +196,7 @@ export class KeySettingsSection extends React.Component<
} }
async load() { async load() {
const keys = await AccountHelper.GetAdminKeys(this.props.admin.id); const keys = await AdminKeyHelper.GetAdminKeys(this.props.admin.id);
this.setState({ keys: keys }); this.setState({ keys: keys });
} }
@ -227,7 +224,8 @@ export class KeySettingsSection extends React.Component<
async registerNewKey() { async registerNewKey() {
try { try {
const challenge = await AccountHelper.GetKeyRegistrationChallenge(); const challenge =
await AdminKeyHelper.GetKeyRegistrationChallenge();
const credential = await navigator.credentials.create(challenge); const credential = await navigator.credentials.create(challenge);
if (credential == null) throw new Error("Operation aborted!"); if (credential == null) throw new Error("Operation aborted!");
@ -238,7 +236,7 @@ export class KeySettingsSection extends React.Component<
minLength: 2, minLength: 2,
}); });
await AccountHelper.RegisterKey(name, credential); await AdminKeyHelper.RegisterKey(name, credential);
snackbar("Successfully enrolled a new key!"); snackbar("Successfully enrolled a new key!");
@ -258,7 +256,7 @@ export class KeySettingsSection extends React.Component<
) )
return; 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!"); snackbar("The key was successfully deleted!");
this.setState({ counter: this.state.counter + 1 }); this.setState({ counter: this.state.counter + 1 });

View File

@ -21,11 +21,8 @@ import {
import { ErrorOutline, Lock, VpnKey } from "@material-ui/icons"; import { ErrorOutline, Lock, VpnKey } from "@material-ui/icons";
import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import React from "react"; import React from "react";
import { import { AccountHelper, AuthOptions } from "../../helpers/AccountHelper";
AccountHelper, import { AdminKeyHelper, AuthKey } from "../../helpers/AdminKeyHelper";
AuthKey,
AuthOptions,
} from "../../helpers/AccountHelper";
import { input, matAlert } from "../widgets/DialogsProvider"; import { input, matAlert } from "../widgets/DialogsProvider";
function ErrorGettingOptions(p: { message: string }) { function ErrorGettingOptions(p: { message: string }) {
@ -244,14 +241,14 @@ class AuthOptionsWidget extends React.Component<
async loginWithSecurityKey(key: AuthKey) { async loginWithSecurityKey(key: AuthKey) {
try { try {
const challenge = await AccountHelper.GetAuthenticationChallenge( const challenge = await AdminKeyHelper.GetAuthenticationChallenge(
this.props.email, this.props.email,
key key
); );
const result = await navigator.credentials.get(challenge); const result = await navigator.credentials.get(challenge);
await AccountHelper.AuthenticateWithKey( await AdminKeyHelper.AuthenticateWithKey(
this.props.email, this.props.email,
key, key,
result result
@ -267,7 +264,7 @@ class AuthOptionsWidget extends React.Component<
render() { render() {
// Check if no option is available // Check if no option is available
if ( if (
this.props.options.keys.length == 0 && this.props.options.keys.length === 0 &&
!this.props.options.reset_token !this.props.options.reset_token
) )
return ( return (

View File

@ -12,7 +12,6 @@ import {
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
makeStyles, makeStyles,
MenuItem,
Paper, Paper,
Toolbar, Toolbar,
Typography, Typography,