84 lines
2 KiB
TypeScript
84 lines
2 KiB
TypeScript
import { createHmac, randomBytes } from "node:crypto";
|
|
|
|
function hashForPassword(salt: string, password: string) {
|
|
return createHmac("sha256", salt).update(password).digest("hex");
|
|
}
|
|
function createPasswordHashAndSalt(password: string): { salt: string; hash: string } {
|
|
const secret = randomBytes(32).toString("hex");
|
|
return {
|
|
salt: secret,
|
|
hash: hashForPassword(secret, password),
|
|
};
|
|
}
|
|
|
|
export class Password {
|
|
private _salt: string;
|
|
private _hash: string;
|
|
constructor(pw: string | { salt: string; hash: string }) {
|
|
const { salt, hash } = typeof pw === "string" ? createPasswordHashAndSalt(pw) : pw;
|
|
this._hash = hash;
|
|
this._salt = salt;
|
|
}
|
|
set_password(password: string) {
|
|
const { salt, hash } = createPasswordHashAndSalt(password);
|
|
this._hash = hash;
|
|
this._salt = salt;
|
|
}
|
|
check_password(password: string): boolean {
|
|
return this._hash === hashForPassword(this._salt, password);
|
|
}
|
|
get salt() {
|
|
return this._salt;
|
|
}
|
|
get hash() {
|
|
return this._hash;
|
|
}
|
|
}
|
|
|
|
export interface UserCreateInput {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface IUser {
|
|
readonly username: string;
|
|
readonly password: Password;
|
|
/**
|
|
* return user's permission list.
|
|
*/
|
|
get_permissions(): Promise<string[]>;
|
|
/**
|
|
* add permission
|
|
* @param name permission name to add
|
|
* @returns if `name` doesn't exist, return true
|
|
*/
|
|
add(name: string): Promise<boolean>;
|
|
/**
|
|
* remove permission
|
|
* @param name permission name to remove
|
|
* @returns if `name` exist, return true
|
|
*/
|
|
remove(name: string): Promise<boolean>;
|
|
/**
|
|
* reset password.
|
|
* @param password password to set
|
|
*/
|
|
reset_password(password: string): Promise<void>;
|
|
}
|
|
|
|
export interface UserAccessor {
|
|
/**
|
|
* create user
|
|
* @returns if user exist, return undefined
|
|
*/
|
|
createUser: (input: UserCreateInput) => Promise<IUser | undefined>;
|
|
/**
|
|
* find user
|
|
*/
|
|
findUser: (username: string) => Promise<IUser | undefined>;
|
|
/**
|
|
* remove user
|
|
* @returns if user exist, true
|
|
*/
|
|
delUser: (username: string) => Promise<boolean>;
|
|
}
|