diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 0ce42d4..f788315 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -1,70 +1,96 @@ import {Injectable} from '@angular/core'; -import {CryptoService} from './crypto.service'; import {GlobalVarsService} from './global-vars.service'; -import {AccessLevel, PrivateAccountInfo, PublicChannelInfo} from '../types/identity'; +import {HubService} from './hub.service'; +import {AccessLevel, ActionType, PrivateAccountInfo, PublicChannelInfo} from '../types/identity'; @Injectable({ providedIn: 'root' }) export class AccountService { - private static levelsStorageKey = 'levels'; + private static walletStorageKey = 'wallet'; + private static channelsStorageKey = 'channels'; + private static accessStorageKey = 'access'; constructor( - private cryptoService: CryptoService, private globalVars: GlobalVarsService, + private hubService: HubService, ) { } - // Public Getters + /* - // TODO - As of this writing, we want to share channel claim ids with the - // account on login, and spending addresses on request (probably with - // explicit permission) - getChannels(): {[key: string]: PublicChannelInfo} { - // TODO - will want for accessLevel stuff - // const hostname = this.globalVars.hostname; + What we're keeping in local storage. TODO - make these into data structs. - const privateAccounts = this.getWalletAccounts(); - const channels: {[key: string]: PublicChannelInfo} = {}; + // The wallet, taken from wallet Sync. Perhaps even with local changes to be + // pushed back to sync. + localStorage["wallet"] = { + // We may later want to add signature, sync metadata, etc. Or maybe we want + // the strict string representation that came out of sync. + walletStoreVersion: 0, - for (const name of Object.keys(privateAccounts)) { - const privateAccount = privateAccounts[name]; - for (const channelPubKeyAddress of Object.keys(privateAccount.certificates)) { - // TODO - For LBRY's purposes, not only will we want per-channel access - // levels, we'll want per channel per hostname per action access levels. - - // TODO - finish when we have accessLevel stuff - /* - const accessLevel = this.getAccessLevel(name, hostname); - if (accessLevel === AccessLevel.None) { - continue; - } - - // TODO - Implement the hmac properly - // TODO - why do we even have hmac if everything's in local storage anyway? - const accessLevelHmac = this.cryptoService.accessLevelHmac(accessLevel, privateAccount.seed); - */ - - channels[channelPubKeyAddress] = { - pubKeyAddress: channelPubKeyAddress, - network: privateAccount.ledger, - - // TODO - fill in when we have accessLevel stuff - accessLevel: 0, - accessLevelHmac: "", - }; - } - } - - return channels; + wallet: "...", } - // TODO - Need to confirm that this works I think - public getWalletAccounts(): {[key: string]: PrivateAccountInfo} { - const wallet = this.cryptoService.getWallet(this.globalVars.hostname) - if (wallet === null) { - return {} + // Access information. For each hostname, what channel is logged in, and what + // level of permission for various actions did the user give? + localStorage["access"] = { + // There can be multiple hostnames + "": { // TODO - type Hostname + "currentChannel": "", // TODO - type ChannelClaimID + "levels": { + // There can be multiple channels + "": { + // There can be multiple action types + "": "", + } + } } - const filteredAccounts: {[key: string]: PrivateAccountInfo} = {}; + } + + localStorage["channels"] = { + // There can be multiple channels + "": { + "claimId": "", + "handle": "", + "pubKeyAddress": "", + } + } + */ + + private hasWallet(): boolean { + return !!localStorage.getItem(AccountService.walletStorageKey); + } + + // TODO define a wallet type, and/or use a type defined by json-schema + public getWallet(): {accounts: [PrivateAccountInfo]} | null { + const walletStoreStr = localStorage.getItem(AccountService.walletStorageKey); + const walletStore = JSON.parse(walletStoreStr || 'null') + if (walletStore !== null) { + if (walletStore.walletStoreVersion === 0) { + return walletStore.wallet + } + } + return null + } + + private putWallet(wallet: object | null) { + const walletStore = { + // We may later want to add signature, sync metadata, etc. Or maybe we + // want the strict string representation that came out of sync. This is + // not the version of the wallet's internal structure; that has its own + // version key + walletStoreVersion: 0, + + wallet + } + localStorage.setItem(AccountService.walletStorageKey, JSON.stringify(walletStore)); + } + + public getAccounts(): PrivateAccountInfo[] { + const wallet = this.getWallet() + if (wallet === null) { + return [] + } + const filteredAccounts: PrivateAccountInfo[] = []; for (const account of wallet.accounts) { // Only include accounts from the current network @@ -72,15 +98,74 @@ export class AccountService { continue; } - filteredAccounts[account.name] = account; + filteredAccounts.push(account); } return filteredAccounts } - getAccessLevel(accountName: string, hostname: string): AccessLevel { - const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}'); - const hostMapping = levels[hostname] || {}; - const accessLevel = hostMapping[accountName]; + private clearChannels() { + localStorage.setItem(AccountService.channelsStorageKey, JSON.stringify(null)); + } + + public updateChannels() { + let xPubs: string[] = this.getAccounts().map(account => account.public_key) + let channels: {[key: string]: PublicChannelInfo} = {}; + for (const hubChannel of this.hubService.getChannels(xPubs)) { + channels[hubChannel.claimId] = { + claimId: hubChannel.claimId, + handle: hubChannel.handle, + pubKeyAddress: hubChannel.pubKeyAddress, + // TODO -- more fields? + } + } + localStorage.setItem(AccountService.channelsStorageKey, JSON.stringify(channels)); + } + + public hasChannels() { + return !!localStorage.getItem(AccountService.channelsStorageKey); + } + + public getChannels() { + return JSON.parse(localStorage.getItem(AccountService.channelsStorageKey) || '{}'); + } + + private clearAccess() { + localStorage.setItem(AccountService.channelsStorageKey, JSON.stringify(null)); + } + + private initAccess() { + // no currentChannel or access level for any action on any hostname + localStorage.setItem(AccountService.accessStorageKey, '{}'); + } + + public hasAccess() { + return !!localStorage.getItem(AccountService.accessStorageKey); + } + + public getActiveChannel(hostname: string): PublicChannelInfo | null { + // TODO - and actually, this maybe only needs to happen on startup. could save in a local variable. + const channels = this.getChannels() + const access = JSON.parse(localStorage.getItem(AccountService.accessStorageKey) || '{}'); + + if (access[hostname]) { + const activeChannelClaimId = access[hostname].currentChannel + return channels[activeChannelClaimId] + } + return null + } + + public getActiveChannelAccessLevel(hostname: string, action: ActionType): AccessLevel { + const access = JSON.parse(localStorage.getItem(AccountService.accessStorageKey) || '{}'); + + if (!access[hostname]) { + return AccessLevel.None + } + const activeChannelClaimId = access[hostname].currentChannel + if (!access[hostname].levels[activeChannelClaimId]) { + return AccessLevel.None + } + + const accessLevel = access[hostname].levels[activeChannelClaimId][action] if (Object.values(AccessLevel).includes(accessLevel)) { return accessLevel; @@ -89,27 +174,63 @@ export class AccountService { } } - // Public Modifiers - - setAccessLevel(accountName: string, hostname: string, accessLevel: AccessLevel): void { - const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}'); - - levels[hostname] ||= {}; - levels[hostname][accountName] = accessLevel; - - localStorage.setItem(AccountService.levelsStorageKey, JSON.stringify(levels)); + public setAccessLevel(hostname: string, channelClaimId: string, action: ActionType, level: AccessLevel) { + const access = JSON.parse(localStorage.getItem(AccountService.accessStorageKey) || '{}'); + if (!(hostname in access)) { + access[hostname] = {levels: {}} + } + if (!(channelClaimId in access[hostname].levels)) { + access[hostname].levels[channelClaimId] = {} + } + access[hostname].levels[channelClaimId][action] = level + localStorage.setItem(AccountService.accessStorageKey, JSON.stringify(access)); } - // log out of hostname entirely by setting accesslevel to None - resetAccessLevels(hostname: string): void { - const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}'); - - levels[hostname] ||= {}; - for (const accountName in levels[hostname]) { - levels[hostname][accountName] = AccessLevel.None; + public setAccessCurrentChannel(hostname: string, channelClaimId: string) { + const access = JSON.parse(localStorage.getItem(AccountService.accessStorageKey) || '{}'); + if (!(hostname in access)) { + access[hostname] = {levels: {}} } + access[hostname].currentChannel = channelClaimId + localStorage.setItem(AccountService.accessStorageKey, JSON.stringify(access)); + } - localStorage.setItem(AccountService.levelsStorageKey, JSON.stringify(levels)); + public walletLogout() { + this.putWallet(null) + this.clearAccess() + this.clearChannels() + } + + public walletLogin(wallet: object | null) { + // no ambiguity from half-completed actions + // make sure we're fully logged out + this.walletLogout() + + this.putWallet(wallet) + this.initAccess() + this.updateChannels() + } + + // TODO - delete this if I don't end up using it + public walletIsLoggedIn(): boolean { + return ( + // All three should be set. It means the last login was complete, and no + // logout was started since. + this.hasAccess() && this.hasChannels() && this.hasWallet() + ) + } + + // TODO: Do we want to save per-hostname-per-channel-per-action access levels + // between logins? Counterargument: users probably won't log in and out too + // often. + private appClearAccess(hostname: string): void { + const access = JSON.parse(localStorage.getItem(AccountService.accessStorageKey) || '{}'); + delete access[hostname] + localStorage.setItem(AccountService.accessStorageKey, JSON.stringify(access)); + } + + public appLogout(hostname: string): void { + this.appClearAccess(hostname) } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2f5763d..78648b1 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,7 +9,6 @@ import { EmbedComponent } from './embed/embed.component'; import { HomeComponent } from './home/home.component'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {IdentityService} from './identity.service'; -import {CookieModule} from 'ngx-cookie'; import { LogoutComponent } from './logout/logout.component'; import { BannerComponent } from './banner/banner.component'; import { SignUpComponent } from './sign-up/sign-up.component'; @@ -50,7 +49,6 @@ import { LogInWalletComponent } from './log-in-wallet/log-in-wallet.component' NgxIntlTelInputModule, MatFormFieldModule, MatTooltipModule, - CookieModule.forRoot() ], providers: [ IdentityService, diff --git a/src/app/approve/approve.component.ts b/src/app/approve/approve.component.ts index e0f0a58..e9911c9 100644 --- a/src/app/approve/approve.component.ts +++ b/src/app/approve/approve.component.ts @@ -67,6 +67,9 @@ export class ApproveComponent implements OnInit { this.publicKey = this.base58KeyCheck(this.transaction.publicKey); this.generateTransactionDescription(); + /* + TODO this.accountService.getActiveChannelAccessLevel + */ }); } @@ -82,6 +85,10 @@ export class ApproveComponent implements OnInit { const signedTransactionHex = this.signingService.signTransaction(seedHex, this.transactionHex); this.finishFlow(signedTransactionHex); */ + + /* + TODO this.accountService.setAccessLevel if people want to keep allowing the action + */ } finishFlow(signedTransactionHex?: string): void { diff --git a/src/app/backend-api.service.ts b/src/app/backend-api.service.ts index aead80e..6486292 100644 --- a/src/app/backend-api.service.ts +++ b/src/app/backend-api.service.ts @@ -3,8 +3,7 @@ import {HttpClient} from '@angular/common/http'; import {Observable, of} from 'rxjs'; import {catchError, map} from 'rxjs/operators'; import {environment} from '../environments/environment'; -import {CryptoService} from './crypto.service'; -import {GlobalVarsService} from './global-vars.service'; +import {AccountService} from './account.service'; export class ProfileEntryResponse { Username: string | null = null; @@ -25,8 +24,7 @@ export class BackendAPIService { constructor( private httpClient: HttpClient, - private cryptoService: CryptoService, - private globalVars: GlobalVarsService, + private accountService: AccountService, ) { } post(path: string, body: any): Observable { @@ -80,7 +78,7 @@ export class BackendAPIService { ): Observable<{bodyJson: string, signature: string} | null> { // A stub for now - const wallet : object | null = this.cryptoService.getWallet(this.globalVars.hostname); + const wallet : object | null = this.accountService.getWallet(); if (wallet === null) { return of(null) } diff --git a/src/app/crypto.service.ts b/src/app/crypto.service.ts index a0e498f..3171b10 100644 --- a/src/app/crypto.service.ts +++ b/src/app/crypto.service.ts @@ -1,101 +1,32 @@ import { Injectable } from '@angular/core'; import {ec as EC} from 'elliptic'; import bs58check from 'bs58check'; -import {CookieService} from 'ngx-cookie'; import {createHmac, createCipher, createDecipher, randomBytes} from 'crypto'; -import {AccessLevel, PrivateAccountInfo} from '../types/identity'; -import { GlobalVarsService } from './global-vars.service'; +import {AccessLevel} from '../types/identity'; @Injectable({ providedIn: 'root' }) export class CryptoService { - constructor( - private cookieService: CookieService, - private globalVars: GlobalVarsService - ) {} - - // Safari only lets us store things in cookies - mustUseStorageAccess(): boolean { - // Webviews have full control over storage access - if (this.globalVars.webview) { - return false; - } - - const supportsStorageAccess = typeof document.hasStorageAccess === 'function'; - const isChrome = navigator.userAgent.indexOf('Chrome') > -1; - const isSafari = !isChrome && navigator.userAgent.indexOf('Safari') > -1; - - // Firefox and Edge support the storage access API but do not enforce it. - // For now, only use cookies if we support storage access and use Safari. - const mustUseStorageAccess = supportsStorageAccess && isSafari; - - return mustUseStorageAccess; - } + constructor() {} // 32 bytes = 256 bits is plenty of entropy for encryption newEncryptionKey(): string { return randomBytes(32).toString('hex'); } + // TODO we won't need this soon right? seedHexEncryptionStorageKey(hostname: string): string { return `seed-hex-key-${hostname}`; } - // Alternate plan is to use this same system, but instead of encrypting the - // seed and sending it back and forth, we do the wallet. It may be superior - // because at most times the decrypted wallet is not accessible anywhere - // without sending a message between the app and identity service. But we'd - // have to trust that we'll never accidentally send the wallet to the app - // unencrypted. - walletStorageKey(hostname: string): string { - return `wallet-key-${hostname}`; - } - - hasWallet(hostname: string): boolean { - const storageKey = this.walletStorageKey(hostname); - - if (this.mustUseStorageAccess()) { - return !!this.cookieService.get(storageKey); - } else { - return !!localStorage.getItem(storageKey); - } - } - hasSeedHexEncryptionKey(hostname: string): boolean { const storageKey = this.seedHexEncryptionStorageKey(hostname); - if (this.mustUseStorageAccess()) { - return !!this.cookieService.get(storageKey); - } else { - return !!localStorage.getItem(storageKey); - } + return !!localStorage.getItem(storageKey); } - // TODO define a wallet type, and/or use a type defined by json-schema - getWallet(hostname: string): {accounts: [PrivateAccountInfo]} | null { - const storageKey = this.walletStorageKey(hostname); - let walletStr - if (this.mustUseStorageAccess()) { - walletStr = this.cookieService.get(storageKey); - } else { - walletStr = localStorage.getItem(storageKey); - } - return JSON.parse(walletStr || 'null') - } - - putWallet(hostname: string, wallet: object | null) { - const storageKey = this.walletStorageKey(hostname); - - if (this.mustUseStorageAccess()) { - this.cookieService.put(storageKey, JSON.stringify(wallet), { - expires: new Date('2100/01/01 00:00:00'), - }); - } else { - localStorage.setItem(storageKey, JSON.stringify(wallet)); - } - } // Place a seed encryption key in storage. If reset is set to true, the // previous key is overwritten, which is useful in logging out users. @@ -103,20 +34,10 @@ export class CryptoService { const storageKey = this.seedHexEncryptionStorageKey(hostname); let encryptionKey; - if (this.mustUseStorageAccess()) { - encryptionKey = this.cookieService.get(storageKey); - if (!encryptionKey || reset) { - encryptionKey = this.newEncryptionKey(); - this.cookieService.put(storageKey, encryptionKey, { - expires: new Date('2100/01/01 00:00:00'), - }); - } - } else { - encryptionKey = localStorage.getItem(storageKey) || ''; - if (!encryptionKey || reset) { - encryptionKey = this.newEncryptionKey(); - localStorage.setItem(storageKey, encryptionKey); - } + encryptionKey = localStorage.getItem(storageKey) || ''; + if (!encryptionKey || reset) { + encryptionKey = this.newEncryptionKey(); + localStorage.setItem(storageKey, encryptionKey); } // If the encryption key is unset or malformed we need to stop diff --git a/src/app/hub.service.spec.ts b/src/app/hub.service.spec.ts new file mode 100644 index 0000000..7943b09 --- /dev/null +++ b/src/app/hub.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { HubService } from './account.service'; + +describe('HubService', () => { + let service: HubService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(HubService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/hub.service.ts b/src/app/hub.service.ts new file mode 100644 index 0000000..44c297d --- /dev/null +++ b/src/app/hub.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class HubService { + + constructor( + ) { } + + // Just for stubbing until we get the real thing + private rndStr() { + return (Math.random() + 1).toString(16).substring(2); + } + + // Obviously just a stub for the actual API + public getChannels(xPubs: string[]): any { + return [{ + claimId: this.rndStr(), + handle: '@test-' + this.rndStr(), + pubKeyAddress: this.rndStr(), + }, { + claimId: this.rndStr(), + handle: '@test-' + this.rndStr(), + pubKeyAddress: this.rndStr(), + }] + } + +} diff --git a/src/app/identity.service.ts b/src/app/identity.service.ts index 46bd959..d886c03 100644 --- a/src/app/identity.service.ts +++ b/src/app/identity.service.ts @@ -4,7 +4,6 @@ import {v4 as uuid} from 'uuid'; import {AccessLevel, PublicChannelInfo} from '../types/identity'; import {CryptoService} from './crypto.service'; import {GlobalVarsService} from './global-vars.service'; -import {CookieService} from 'ngx-cookie'; import {SigningService} from './signing.service'; import {HttpParams} from '@angular/common/http'; @@ -24,12 +23,30 @@ export class IdentityService { constructor( private cryptoService: CryptoService, private globalVars: GlobalVarsService, - private cookieService: CookieService, private signingService: SigningService, ) { window.addEventListener('message', (event) => this.handleMessage(event)); } + // Safari only lets us store things in cookies + mustUseStorageAccess(): boolean { + // Webviews have full control over storage access + // TODO why do we trust the app to send this properly + if (this.globalVars.webview) { + return false; + } + + const supportsStorageAccess = typeof document.hasStorageAccess === 'function'; + const isChrome = navigator.userAgent.indexOf('Chrome') > -1; + const isSafari = !isChrome && navigator.userAgent.indexOf('Safari') > -1; + + // Firefox and Edge support the storage access API but do not enforce it. + // For now, only use cookies if we support storage access and use Safari. + const mustUseStorageAccess = supportsStorageAccess && isSafari; + + return mustUseStorageAccess; + } + // Outgoing Messages initialize(): Observable { @@ -41,8 +58,7 @@ export class IdentityService { } login(payload: { - channels: {[key: string]: PublicChannelInfo}, - accountNameAdded?: string, // Which channel id was just authorized. the app may allow the user to switch between them, but this determines which is on now. + channel: PublicChannelInfo | null, signedUp?: boolean signedTransactionHex?: string, addresses?: string[], @@ -118,7 +134,7 @@ export class IdentityService { private async handleInfo(event: MessageEvent): Promise { // check storage access API let hasStorageAccess = true; - if (this.cryptoService.mustUseStorageAccess()) { + if (this.mustUseStorageAccess()) { hasStorageAccess = await document.hasStorageAccess(); } @@ -130,15 +146,37 @@ export class IdentityService { hasLocalStorageAccess = false; } - // check for cookie access - this.cookieService.put('deso-test-access', 'true'); - const hasCookieAccess = !!this.cookieService.get('deso-test-access'); + // TODO - Sort out the storage access issue: + // + // There was a part of the code that was defaulting to cookies if + // this.mustUseStorageAccess() was true (only applies to Safari). I don't + // want to do that because I'm afraid the data we'll be storing won't fit. + // So, I'm going to consider browsers with this.mustUseStorageAccess() as + // unsupported until we can get back and figure out why DeSo chose to go + // with cookies for those cases. + // + // It looks like the issue is that Safari only supports saving in cookies + // (per the comment above this.mustUseStorageAccess(), which I moved from + // DeSo's crypto.service). If that's the case, I don't understand how it + // was able to save users and levels in the identity service. Someone who + // understands should look into that. + // + // PERHAPS it's that DeSo only strictly need certain things to be in + // localStorage, and everything else (like the encryption key) could fit + // in the cookie. Maybe we could store the wallet sync password in the + // cookie and reconstruct the wallet and channels on start. The problem + // would be resetting the access levels each time. Maybe that will just + // be the cost of using Safari. + // + // See: + // https://github.com/deso-protocol/identity/blob/0543c40cb4e7e39cc9098554f99c27649e3d1d03/src/app/crypto.service.ts#L36 + // https://github.com/deso-protocol/identity/blob/0543c40cb4e7e39cc9098554f99c27649e3d1d03/src/app/identity.service.ts#L261 + // https://github.com/deso-protocol/identity/pull/50/ // store if browser is supported or not - this.browserSupported = hasCookieAccess || hasLocalStorageAccess; + this.browserSupported = hasLocalStorageAccess && !this.mustUseStorageAccess(); this.respond(event.data.id, { - hasCookieAccess, hasStorageAccess, hasLocalStorageAccess, browserSupported: this.browserSupported, diff --git a/src/app/log-in-app/log-in-app.component.html b/src/app/log-in-app/log-in-app.component.html index de055f1..b336f94 100644 --- a/src/app/log-in-app/log-in-app.component.html +++ b/src/app/log-in-app/log-in-app.component.html @@ -13,15 +13,14 @@ TODO - Unused for now. Revamp this page to be a channel picker for logging in Wallet Sync Login redirects to here, like load-seed did. Now we choose which account to log in with. --> -
+
    Select an account
    -
  • +
  • -
    {{ item.key }}…
    -
    {{ item.value }}
    +
    {{ item.value.handle }}
  • diff --git a/src/app/log-in-app/log-in-app.component.ts b/src/app/log-in-app/log-in-app.component.ts index 4c244bb..de01c2d 100644 --- a/src/app/log-in-app/log-in-app.component.ts +++ b/src/app/log-in-app/log-in-app.component.ts @@ -2,6 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {AccountService} from '../account.service'; import {IdentityService} from '../identity.service'; import {GlobalVarsService} from '../global-vars.service'; +import {PublicChannelInfo} from '../../types/identity'; @Component({ selector: 'app-log-in-app', @@ -9,7 +10,8 @@ import {GlobalVarsService} from '../global-vars.service'; styleUrls: ['./log-in-app.component.scss'] }) export class LogInAppComponent implements OnInit { - allAccountNames: {[key: string]: string} = {}; + allChannels: {[key: string]: PublicChannelInfo} = {}; + hasChannels: boolean = false; constructor( private accountService: AccountService, @@ -18,15 +20,15 @@ export class LogInAppComponent implements OnInit { ) { } ngOnInit(): void { - // TODO - Will hopefully be channel claim IDs but I don't know what will be available in reality - // this.allAccountNames = ... + this.allChannels = this.accountService.getChannels() + this.hasChannels = Object.keys(this.allChannels).length > 0 } - selectAccount(accountName: string): void { - this.accountService.setAccessLevel(accountName, this.globalVars.hostname, this.globalVars.accessLevelRequest); + selectAccount(channelClaimId: string): void { + this.accountService.setAccessCurrentChannel(this.globalVars.hostname, channelClaimId) + this.identityService.login({ - channels: this.accountService.getChannels(), - accountNameAdded: accountName, + channel: this.accountService.getActiveChannel(this.globalVars.hostname), signedUp: false }); } diff --git a/src/app/log-in-wallet/log-in-wallet.component.ts b/src/app/log-in-wallet/log-in-wallet.component.ts index e75b9d8..868f26e 100644 --- a/src/app/log-in-wallet/log-in-wallet.component.ts +++ b/src/app/log-in-wallet/log-in-wallet.component.ts @@ -1,5 +1,6 @@ import {BackendAPIService} from '../backend-api.service'; import {Component, OnInit} from '@angular/core'; +import {AccountService} from '../account.service'; import {CryptoService} from '../crypto.service'; import {GlobalVarsService} from '../global-vars.service'; import {Router} from '@angular/router'; @@ -25,6 +26,7 @@ export class LogInWalletComponent implements OnInit { constructor( private backendApi: BackendAPIService, private cryptoService: CryptoService, + private accountService: AccountService, public globalVars: GlobalVarsService, private router: Router, ) { } @@ -73,7 +75,7 @@ export class LogInWalletComponent implements OnInit { return } - this.cryptoService.putWallet(this.globalVars.hostname, wallet); + this.accountService.walletLogin(wallet); // Clear the form this.loginUsername = ''; @@ -90,18 +92,16 @@ export class LogInWalletComponent implements OnInit { // Paste Wallet (temporary measure for initial version) getWalletDumpInitial() { - const wallet : object | null = this.cryptoService.getWallet(this.globalVars.hostname); + const wallet : object | null = this.accountService.getWallet(); return JSON.stringify(wallet, null, 2) || "" } loginWithWalletDump(): void { const walletStr = (document.getElementById("wallet-dump")).value; const wallet = JSON.parse(walletStr) - this.cryptoService.putWallet(this.globalVars.hostname, wallet); + this.accountService.walletLogin(wallet); - throw "fix me" - // For now, we'll just pick the first channel in the wallet and log right in - // this.router.navigate(['/', RouteNames.LOG_IN_APP], {queryParamsHandling: 'merge'}); + this.router.navigate(['/', RouteNames.LOG_IN_APP], {queryParamsHandling: 'merge'}); } } diff --git a/src/app/logout/logout.component.ts b/src/app/logout/logout.component.ts index 68b614b..f326314 100644 --- a/src/app/logout/logout.component.ts +++ b/src/app/logout/logout.component.ts @@ -33,7 +33,7 @@ export class LogoutComponent implements OnInit { onSubmit(): void { // We set the accessLevel for the logged out user to None. - this.accountService.resetAccessLevels(this.globalVars.hostname); + this.accountService.appLogout(this.globalVars.hostname); // We reset the seed encryption key so that all existing accounts, except // the logged out user, will regenerate their encryptedSeedHex. Without this, // someone could have reused the encryptedSeedHex of an already logged out user. @@ -43,7 +43,7 @@ export class LogoutComponent implements OnInit { finishFlow(): void { this.identityService.login({ - channels: this.accountService.getChannels(), + channel: this.accountService.getActiveChannel(this.globalVars.hostname), }); } diff --git a/src/app/sign-up/sign-up.component.ts b/src/app/sign-up/sign-up.component.ts index da22524..361f58d 100644 --- a/src/app/sign-up/sign-up.component.ts +++ b/src/app/sign-up/sign-up.component.ts @@ -18,7 +18,6 @@ export class SignUpComponent implements OnInit, OnDestroy { seedCopied = false; mnemonicCheck = ''; extraTextCheck = ''; - accountNameAdded = ''; // Advanced tab showMnemonicError = false; @@ -89,10 +88,10 @@ export class SignUpComponent implements OnInit, OnDestroy { const extraText = this.extraTextCheck; const keychain = this.cryptoService.mnemonicToKeychain(mnemonic, extraText); - this.accountNameAdded = this.accountService.addUser(keychain, mnemonic, extraText, network); + const accountNameAdded = this.accountService.addUser(keychain, mnemonic, extraText, network); this.accountService.setAccessLevel( - this.accountNameAdded, this.globalVars.hostname, this.globalVars.accessLevelRequest); + accountNameAdded, this.globalVars.hostname, this.globalVars.accessLevelRequest); this.login(); */ @@ -106,8 +105,7 @@ export class SignUpComponent implements OnInit, OnDestroy { login(): void { this.identityService.login({ - channels: this.accountService.getChannels(), - accountNameAdded: this.accountNameAdded, + channel: this.accountService.getActiveChannel(this.globalVars.hostname), signedUp: true, }); } diff --git a/src/app/test-sign-transaction/test-sign-transaction.component.ts b/src/app/test-sign-transaction/test-sign-transaction.component.ts index 97d5804..eb85643 100644 --- a/src/app/test-sign-transaction/test-sign-transaction.component.ts +++ b/src/app/test-sign-transaction/test-sign-transaction.component.ts @@ -2,8 +2,7 @@ import {AccountService} from '../account.service'; import {Component, OnInit} from '@angular/core'; import {SigningService} from '../signing.service'; import {IdentityService} from '../identity.service'; -import {CryptoService} from '../crypto.service'; -import { GlobalVarsService } from '../global-vars.service'; +import {GlobalVarsService} from '../global-vars.service'; @Component({ selector: 'app-test-sign-transaction', @@ -18,7 +17,6 @@ export class TestSignTransactionComponent implements OnInit { private accountService: AccountService, private signingService: SigningService, private identityService: IdentityService, - private cryptoService: CryptoService, private globalVars: GlobalVarsService, ) { } @@ -28,7 +26,7 @@ export class TestSignTransactionComponent implements OnInit { const fromAddress = params.get("fromAddress") || "" const nonWitnessUtxoHexes = params.get("nonWitnessUtxoHexes") || null - const wallet : object | null = this.cryptoService.getWallet(this.globalVars.hostname); + const wallet : object | null = this.accountService.getWallet(); const signingKey = this.signingService.getSigningKey(wallet, fromAddress) // TODO what if error? etc etc. @@ -41,7 +39,7 @@ export class TestSignTransactionComponent implements OnInit { finishFlow(signedTransactionHex?: string): void { this.identityService.login({ - channels: this.accountService.getChannels(), + channel: this.accountService.getActiveChannel(this.globalVars.hostname), signedTransactionHex, }); } diff --git a/src/app/test-sign/test-sign.component.ts b/src/app/test-sign/test-sign.component.ts index 3adc6af..14462f9 100644 --- a/src/app/test-sign/test-sign.component.ts +++ b/src/app/test-sign/test-sign.component.ts @@ -1,5 +1,6 @@ import {AccountService} from '../account.service'; import {Component, OnInit} from '@angular/core'; +import {GlobalVarsService} from '../global-vars.service'; import {SigningService} from '../signing.service'; import {IdentityService} from '../identity.service'; @@ -20,6 +21,7 @@ export class TestSignComponent implements OnInit { constructor( private accountService: AccountService, + private globalVars: GlobalVarsService, private signingService: SigningService, private identityService: IdentityService, ) { } @@ -36,7 +38,7 @@ export class TestSignComponent implements OnInit { finishFlow(signatureHex?: string): void { this.identityService.login({ - channels: this.accountService.getChannels(), + channel: this.accountService.getActiveChannel(this.globalVars.hostname), signatureHex, }); } diff --git a/src/types/identity.ts b/src/types/identity.ts index 9d5582b..7c9ba14 100644 --- a/src/types/identity.ts +++ b/src/types/identity.ts @@ -38,10 +38,19 @@ export interface PrivateAccountInfo { // can be sent to the app export interface PublicChannelInfo { // TODO - add more useful stuff + claimId: string; + handle: string; pubKeyAddress: string; - network: Network; - accessLevel: AccessLevel; - accessLevelHmac: string; + + // Don't care about sending the hmac-verifiable accessLevel to the app for it + // to send back, as DeSo did. I don't get it, it's overly complicated. We can + // just check the permissions based on what's in localStorage. + // + // Though, maybe this was for the sake of Safari where localStorage doesn't + // work? We'll see I guess. + // + // accessLevel: AccessLevel; + // accessLevelHmac: string; } export enum AccessLevel { @@ -60,3 +69,9 @@ export enum AccessLevel { // Node can sign all transactions without approval Full = 4, } + +export enum ActionType { + Action = 0, + Transaction = 1, + // TODO - probably gets a lot more detailed than this +}