web-wallet/src/app/account.service.ts

142 lines
4.2 KiB
TypeScript

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));
}
}