feat: authorize notes for listed (#1823)

This commit is contained in:
Mo
2022-10-18 10:49:30 -05:00
committed by GitHub
parent e4de9c1be7
commit 9954bdc29f
14 changed files with 104 additions and 46 deletions

View File

@@ -167,6 +167,7 @@ export const ChallengeStrings = {
SelectProtectedNote: 'Authentication is required to select a protected note',
DisableMfa: 'Authentication is required to disable two-factor authentication',
DeleteAccount: 'Authentication is required to delete your account',
ListedAuthorization: 'Authentication is required to approve this note for Listed',
}
export const ErrorAlertStrings = {

View File

@@ -79,6 +79,8 @@ export class Challenge implements ChallengeInterface {
return ChallengeStrings.DisableMfa
case ChallengeReason.DeleteAccount:
return ChallengeStrings.DeleteAccount
case ChallengeReason.AuthorizeNoteForListed:
return ChallengeStrings.ListedAuthorization
case ChallengeReason.Custom:
return ''
default:

View File

@@ -1,3 +1,4 @@
import { SNNote } from '@standardnotes/models'
import { Uuid } from '@standardnotes/common'
import { ListedAccount, ListedAccountInfo } from '@standardnotes/responses'
@@ -6,4 +7,6 @@ export interface ListedClientInterface {
requestNewListedAccount: () => Promise<ListedAccount | undefined>
getListedAccounts(): Promise<ListedAccount[]>
getListedAccountInfo(account: ListedAccount, inContextOfItem?: Uuid): Promise<ListedAccountInfo | undefined>
isNoteAuthorizedForListed(note: SNNote): boolean
authorizeNoteForListed(note: SNNote): Promise<boolean>
}

View File

@@ -8,8 +8,9 @@ import { SNSettingsService } from '../Settings/SNSettingsService'
import { ListedClientInterface } from './ListedClientInterface'
import { SNApiService } from '../Api/ApiService'
import { ListedAccount, ListedAccountInfo, ListedAccountInfoResponse } from '@standardnotes/responses'
import { SNActionsExtension } from '@standardnotes/models'
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
import { NoteMutator, SNActionsExtension, SNNote } from '@standardnotes/models'
import { AbstractService, InternalEventBusInterface, MutatorClientInterface } from '@standardnotes/services'
import { SNProtectionService } from '../Protection'
export class ListedService extends AbstractService implements ListedClientInterface {
constructor(
@@ -17,6 +18,8 @@ export class ListedService extends AbstractService implements ListedClientInterf
private itemManager: ItemManager,
private settingsService: SNSettingsService,
private httpSerivce: SNHttpService,
private protectionService: SNProtectionService,
private mutatorService: MutatorClientInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
@@ -27,6 +30,8 @@ export class ListedService extends AbstractService implements ListedClientInterf
;(this.settingsService as unknown) = undefined
;(this.apiService as unknown) = undefined
;(this.httpSerivce as unknown) = undefined
;(this.protectionService as unknown) = undefined
;(this.mutatorService as unknown) = undefined
super.deinit()
}
@@ -34,6 +39,23 @@ export class ListedService extends AbstractService implements ListedClientInterf
return this.apiService.user != undefined
}
public isNoteAuthorizedForListed(note: SNNote): boolean {
return note.authorizedForListed
}
public async authorizeNoteForListed(note: SNNote): Promise<boolean> {
const result = await this.protectionService.authorizeListedPublishing()
if (result === false) {
return false
}
await this.mutatorService.changeAndSaveItem<NoteMutator>(note, (mutator) => {
mutator.authorizedForListed = true
})
return true
}
/**
* Account creation is asyncronous on the backend due to message-based nature of architecture.
* In order to get the newly created account, we poll the server to check for new accounts.
@@ -73,8 +95,11 @@ export class ListedService extends AbstractService implements ListedClientInterf
if (inContextOfItem) {
url += `&item_uuid=${inContextOfItem}`
}
const response = (await this.httpSerivce.getAbsolute(url)) as ListedAccountInfoResponse
if (response.error || !response.data || isString(response.data)) {
const response = (await this.httpSerivce.getAbsolute(url).catch((error) => {
console.error(error)
})) as ListedAccountInfoResponse
if (!response || response.error || !response.data || isString(response.data)) {
return undefined
}

View File

@@ -219,13 +219,18 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
return this.authorizeAction(ChallengeReason.RevokeSession)
}
async authorizeListedPublishing(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.AuthorizeNoteForListed, { forcePrompt: true })
}
async authorizeAction(
reason: ChallengeReason,
{ fallBackToAccountPassword = true, requireAccountPassword = false } = {},
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
): Promise<boolean> {
return this.validateOrRenewSession(reason, {
requireAccountPassword,
fallBackToAccountPassword,
forcePrompt,
})
}
@@ -295,9 +300,9 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
private async validateOrRenewSession(
reason: ChallengeReason,
{ fallBackToAccountPassword = true, requireAccountPassword = false } = {},
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
): Promise<boolean> {
if (this.getSessionExpiryDate() > new Date()) {
if (this.getSessionExpiryDate() > new Date() && !forcePrompt) {
return true
}