library-ts/node/users/UserDB.ts

211 lines
4.4 KiB
TypeScript
Raw Normal View History

2025-11-10 17:41:48 +00:00
import { Arrays } from "../../browser/tools/Arrays";
import { CryptIO } from "../crypt/CryptIO";
import { Files } from "../files/Files";
import { Permission } from "./permissions/Permission";
import { Role } from "./permissions/Role";
import { Token } from "./Token";
import { TokenDB } from "./TokenDB";
import { UserData } from "./UserData";
import { UserManagementServer } from "./UserManagementServer";
export class SerializedUserDB
{
users:UserData[];
}
export class UserDB
{
_ums:UserManagementServer;
_path:string;
users:UserData[] = [];
_pendingUsers:UserData[] = [];
_signUpTokens = new Map<string,Token>();
async hasUserWithEmail( email:string ):Promise<boolean>
{
return this.users.findIndex( u => u.email === email ) != -1;
}
async signUp( email:string, password:string, userName:string = "User" ):Promise<UserData>
{
let userData = this._pendingUsers.find( u => u.email === email );
if ( ! userData )
{
userData = new UserData();
userData.id = CryptIO.createUUID();
userData.email = email;
userData.hashedPassword = await CryptIO.hash( password );
userData.name = userName;
this._pendingUsers.push( userData );
await this.save();
}
return Promise.resolve( userData );
}
async createSignUpConfirmation( userData:UserData, ip:string ):Promise<Token>
{
let token = await this._ums.tokenDB.create( ip );
this._signUpTokens.set( userData.id, token );
return Promise.resolve( token );
}
async byID( id:string )
{
return this.users.find( u => u.id === id );
}
async byEmail( email:string )
{
return this.users.find( u => u.email === email );
}
async confirmUserSignUp( userID:string, tokenID:string, ip:string ):Promise<boolean>
{
let user = this._pendingUsers.find( u => u.id == userID );
if ( ! user )
{
return Promise.resolve( false );
}
if ( ! this._signUpTokens.has( userID ) )
{
return Promise.resolve( false );
}
let token = this._signUpTokens.get( userID );
if ( token.id != tokenID )
{
return Promise.resolve( false );
}
let isValid = await this._ums.tokenDB.validate( token, ip );
if ( isValid )
{
Arrays.remove( this._pendingUsers, user );
this.users.push( user );
await this.save();
}
return Promise.resolve( isValid );
}
async login( email:string, password:string, ip:string ):Promise<Token>
{
let user = await this.byEmail( email );
if ( ! user )
{
return Promise.resolve( null );
}
let passwordValid = await CryptIO.verifyHash( password, user.hashedPassword );
if ( ! passwordValid )
{
return Promise.resolve( null );
}
let hour = 60;
let day = hour * 24;
let week = day * 7;
let token = await this._ums.tokenDB.create( ip, week );
return Promise.resolve( token );
}
async hasPermission( ud:UserData, permissionID:string )
{
let hasOwnPermission = await this._hasPermission( permissionID, ud.permissions );
if ( hasOwnPermission )
{
return Promise.resolve( true );
}
let roleID = ud.role;
while ( roleID )
{
let role = this._ums._roles.get( roleID );
if ( ! role )
{
return Promise.resolve( false );
}
let hasRolePermission = await this._hasPermission( permissionID, role.permissions );
if ( hasRolePermission )
{
return Promise.resolve( true );
}
roleID = role.inherits;
}
return Promise.resolve( false );
}
async _hasPermission( permissionID:string, permissions:Permission[] ):Promise<boolean>
{
for ( let p of permissions )
{
if ( Permission.isMatching( permissionID, p ) )
{
return Promise.resolve( true );
}
}
return Promise.resolve( false );
}
async save()
{
let serializedDB = new SerializedUserDB();
serializedDB.users = this.users;
await Files.saveJSON( this._path, serializedDB );
return Promise.resolve();
}
static async load( ums:UserManagementServer, path:string ):Promise<UserDB>
{
let userDB = new UserDB();
userDB._ums = ums;
userDB._path = path;
let userDBExists = await Files.exists( userDB._path );
if ( userDBExists )
{
let data = await Files.loadJSON<SerializedUserDB>( userDB._path );
userDB.users = data.users;
}
return Promise.resolve( userDB );
}
}