import {Injectable} from '@angular/core'; import {CryptoService} from './crypto.service'; import {GlobalVarsService} from './global-vars.service'; import {AccessLevel, Network, PrivateUserInfo, PublicUserInfo} from '../types/identity'; import HDKey from 'hdkey'; @Injectable({ providedIn: 'root' }) export class AccountService { private static usersStorageKey = 'users'; private static levelsStorageKey = 'levels'; private static publicKeyRegex = /^[a-zA-Z0-9]{54,55}$/; constructor( private cryptoService: CryptoService, private globalVars: GlobalVarsService, ) { } // Public Getters getPublicKeys(): any { return Object.keys(this.getPrivateUsers()); } getEncryptedUsers(): {[key: string]: PublicUserInfo} { const hostname = this.globalVars.hostname; const privateUsers = this.getPrivateUsers(); const publicUsers: {[key: string]: PublicUserInfo} = {}; for (const publicKey of Object.keys(privateUsers)) { const privateUser = privateUsers[publicKey]; const accessLevel = this.getAccessLevel(publicKey, hostname); if (accessLevel === AccessLevel.None) { continue; } const encryptedSeedHex = this.cryptoService.encryptSeedHex(privateUser.seedHex, hostname); const accessLevelHmac = this.cryptoService.accessLevelHmac(accessLevel, privateUser.seedHex); publicUsers[publicKey] = { hasExtraText: privateUser.extraText?.length > 0, encryptedSeedHex, network: privateUser.network, accessLevel, accessLevelHmac, }; } return publicUsers; } getAccessLevel(publicKey: string, hostname: string): AccessLevel { const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}'); const hostMapping = levels[hostname] || {}; const accessLevel = hostMapping[publicKey]; if (Object.values(AccessLevel).includes(accessLevel)) { return accessLevel; } else { return AccessLevel.None; } } // Public Modifiers addUser(keychain: HDKey, mnemonic: string, extraText: string, network: Network): string { const seedHex = this.cryptoService.keychainToSeedHex(keychain); return this.addPrivateUser({ seedHex, mnemonic, extraText, network, }); } deleteUser(publicKey: string): void { const privateUsers = this.getPrivateUsersRaw(); delete privateUsers[publicKey]; this.setPrivateUsersRaw(privateUsers); } setAccessLevel(publicKey: string, hostname: string, accessLevel: AccessLevel): void { const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}'); levels[hostname] ||= {}; levels[hostname][publicKey] = accessLevel; localStorage.setItem(AccountService.levelsStorageKey, JSON.stringify(levels)); } // Private Getters and Modifiers // TEMP: public for import flow public addPrivateUser(userInfo: PrivateUserInfo): string { const privateUsers = this.getPrivateUsersRaw(); const privateKey = this.cryptoService.seedHexToPrivateKey(userInfo.seedHex); const publicKey = this.cryptoService.privateKeyToDeSoPublicKey(privateKey, userInfo.network); privateUsers[publicKey] = userInfo; this.setPrivateUsersRaw(privateUsers); return publicKey; } private getPrivateUsers(): {[key: string]: PrivateUserInfo} { const privateUsers = this.getPrivateUsersRaw(); const filteredPrivateUsers: {[key: string]: PrivateUserInfo} = {}; for (const publicKey of Object.keys(privateUsers)) { const privateUser = privateUsers[publicKey]; // Only include users from the current network if (privateUser.network !== this.globalVars.network) { continue; } // Get rid of some users who have invalid public keys if (!publicKey.match(AccountService.publicKeyRegex)) { this.deleteUser(publicKey); continue; } filteredPrivateUsers[publicKey] = privateUser; } return filteredPrivateUsers; } private getPrivateUsersRaw(): {[key: string]: PrivateUserInfo} { return JSON.parse(localStorage.getItem(AccountService.usersStorageKey) || '{}'); } private setPrivateUsersRaw(privateUsers: {[key: string]: PrivateUserInfo}): void { localStorage.setItem(AccountService.usersStorageKey, JSON.stringify(privateUsers)); } }