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(); _passwordChangeTokens = new Map(); async hasUserWithEmail( email:string ):Promise { return this.users.findIndex( u => u.email === email ) != -1; } async hasUserWithID( id:string ):Promise { return this.users.findIndex( u => u.id === id ) != -1; } async signUp( email:string, password:string, userName:string = "User" ):Promise { 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; userData.role = Role.User; userData.permissions = []; this._pendingUsers.push( userData ); await this.save(); } return Promise.resolve( userData ); } async createSignUpConfirmation( userData:UserData, ip:string ):Promise { let token = await this._ums.tokenDB.create( ip ); this._signUpTokens.set( userData.id, token ); return Promise.resolve( token ); } async createPasswordChange( userData:UserData, ip:string ):Promise { let token = await this._ums.tokenDB.create( ip ); this._passwordChangeTokens.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 { 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._signUpTokens.delete( userID ); this._ums.tokenDB._tokens.delete( token.id ); this.users.push( user ); await this.save(); } return Promise.resolve( isValid ); } async changePassword( userID:string, tokenID:string, password:string, ip:string ):Promise { if ( ! this._passwordChangeTokens.has( userID ) ) { return Promise.resolve( false ); } let token = this._passwordChangeTokens.get( userID ); if ( token.id != tokenID ) { return Promise.resolve( false ); } let isValid = await this._ums.tokenDB.validate( token, ip ); if ( isValid ) { this._passwordChangeTokens.delete( userID ); this._ums.tokenDB._tokens.delete( token.id ); let userData = await this._ums.userDB.byID( userID ); userData.hashedPassword = await CryptIO.hash( password ); await this.save(); } return Promise.resolve( isValid ); } async renew( oldToken:string, renewal:string, ip:string ):Promise { let session = this._ums._sessions.get( oldToken ); let tokenData = this._ums._tokenDB._tokens.get( session.token ); if ( tokenData.renewalToken !== renewal ) { 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 login( email:string, password:string, ip:string ):Promise { 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 ); } protected async _hasPermission( permissionID:string, permissions:Permission[] ):Promise { permissions = permissions || []; 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 { let userDB = new UserDB(); userDB._ums = ums; userDB._path = path; let userDBExists = await Files.exists( userDB._path ); if ( userDBExists ) { let data = await Files.loadJSON( userDB._path ); userDB.users = data.users; } return Promise.resolve( userDB ); } }