refactor: demo params
This commit is contained in:
@@ -2,13 +2,12 @@ import { UserRegistrationResponseBody } from '@standardnotes/api'
|
|||||||
import { ProtocolVersion } from '@standardnotes/common'
|
import { ProtocolVersion } from '@standardnotes/common'
|
||||||
import { SNRootKey } from '@standardnotes/encryption'
|
import { SNRootKey } from '@standardnotes/encryption'
|
||||||
import { RootKeyInterface } from '@standardnotes/models'
|
import { RootKeyInterface } from '@standardnotes/models'
|
||||||
import { ClientDisplayableError, SessionBody, SignInResponse, User, HttpResponse } from '@standardnotes/responses'
|
import { SessionBody, SignInResponse, User, HttpResponse } from '@standardnotes/responses'
|
||||||
import { Base64String } from '@standardnotes/sncrypto-common'
|
import { Base64String } from '@standardnotes/sncrypto-common'
|
||||||
|
|
||||||
import { SessionManagerResponse } from './SessionManagerResponse'
|
import { SessionManagerResponse } from './SessionManagerResponse'
|
||||||
|
|
||||||
export interface SessionsClientInterface {
|
export interface SessionsClientInterface {
|
||||||
createDemoShareToken(): Promise<Base64String | ClientDisplayableError>
|
|
||||||
populateSessionFromDemoShareToken(token: Base64String): Promise<void>
|
populateSessionFromDemoShareToken(token: Base64String): Promise<void>
|
||||||
getUser(): User | undefined
|
getUser(): User | undefined
|
||||||
isCurrentSessionReadOnly(): boolean | undefined
|
isCurrentSessionReadOnly(): boolean | undefined
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import {
|
|||||||
} from '@standardnotes/responses'
|
} from '@standardnotes/responses'
|
||||||
import { CopyPayloadWithContentOverride } from '@standardnotes/models'
|
import { CopyPayloadWithContentOverride } from '@standardnotes/models'
|
||||||
import { LegacySession, MapperInterface, Result, Session, SessionToken } from '@standardnotes/domain-core'
|
import { LegacySession, MapperInterface, Result, Session, SessionToken } from '@standardnotes/domain-core'
|
||||||
import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey, CreateNewRootKey } from '@standardnotes/encryption'
|
import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey } from '@standardnotes/encryption'
|
||||||
import { Subscription } from '@standardnotes/security'
|
import { Subscription } from '@standardnotes/security'
|
||||||
import * as Common from '@standardnotes/common'
|
import * as Common from '@standardnotes/common'
|
||||||
|
|
||||||
@@ -640,32 +640,6 @@ export class SNSessionManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createDemoShareToken(): Promise<Base64String | ClientDisplayableError> {
|
|
||||||
const session = this.getSession()
|
|
||||||
if (!session) {
|
|
||||||
return new ClientDisplayableError('Cannot generate share token without active session')
|
|
||||||
}
|
|
||||||
if (!(session instanceof Session)) {
|
|
||||||
return new ClientDisplayableError('Cannot generate share token with non-token session')
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyParams = (await this.protocolService.getRootKeyParams()) as SNRootKeyParams
|
|
||||||
|
|
||||||
const payload: ShareToken = {
|
|
||||||
accessToken: session.accessToken.value,
|
|
||||||
refreshToken: session.refreshToken.value,
|
|
||||||
accessExpiration: session.accessToken.expiresAt,
|
|
||||||
refreshExpiration: session.refreshToken.expiresAt,
|
|
||||||
readonlyAccess: true,
|
|
||||||
masterKey: this.protocolService.getRootKey()?.masterKey as string,
|
|
||||||
keyParams: keyParams.content,
|
|
||||||
user: this.getSureUser(),
|
|
||||||
host: this.apiService.getHost(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.protocolService.crypto.base64Encode(JSON.stringify(payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
private decodeDemoShareToken(token: Base64String): ShareToken {
|
private decodeDemoShareToken(token: Base64String): ShareToken {
|
||||||
const jsonString = this.protocolService.crypto.base64Decode(token)
|
const jsonString = this.protocolService.crypto.base64Decode(token)
|
||||||
return JSON.parse(jsonString)
|
return JSON.parse(jsonString)
|
||||||
@@ -674,28 +648,7 @@ export class SNSessionManager
|
|||||||
public async populateSessionFromDemoShareToken(token: Base64String): Promise<void> {
|
public async populateSessionFromDemoShareToken(token: Base64String): Promise<void> {
|
||||||
const sharePayload = this.decodeDemoShareToken(token)
|
const sharePayload = this.decodeDemoShareToken(token)
|
||||||
|
|
||||||
const rootKey = CreateNewRootKey({
|
await this.signIn(sharePayload.email, sharePayload.password, false, true)
|
||||||
masterKey: sharePayload.masterKey,
|
|
||||||
keyParams: sharePayload.keyParams,
|
|
||||||
version: sharePayload.keyParams.version,
|
|
||||||
})
|
|
||||||
|
|
||||||
const user = sharePayload.user
|
|
||||||
|
|
||||||
const sessionOrError = this.createSession(
|
|
||||||
sharePayload.accessToken,
|
|
||||||
sharePayload.accessExpiration,
|
|
||||||
sharePayload.refreshToken,
|
|
||||||
sharePayload.refreshExpiration,
|
|
||||||
sharePayload.readonlyAccess,
|
|
||||||
)
|
|
||||||
if (sessionOrError.isFailed()) {
|
|
||||||
console.error(sessionOrError.getError())
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.populateSession(rootKey, user, sessionOrError.getValue(), sharePayload.host)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async populateSession(
|
private async populateSession(
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { User } from '@standardnotes/responses'
|
|
||||||
import { AnyKeyParamsContent } from '@standardnotes/common'
|
|
||||||
import { RawSessionPayload } from './Sessions/Types'
|
import { RawSessionPayload } from './Sessions/Types'
|
||||||
|
|
||||||
export type ShareToken = RawSessionPayload & {
|
export type ShareToken = RawSessionPayload & {
|
||||||
masterKey: string
|
email: string
|
||||||
keyParams: AnyKeyParamsContent
|
password: string
|
||||||
user: User
|
|
||||||
host: string
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
/* eslint-disable no-undef */
|
|
||||||
import * as Factory from './lib/factory.js'
|
|
||||||
chai.use(chaiAsPromised)
|
|
||||||
const expect = chai.expect
|
|
||||||
|
|
||||||
describe('session sharing', function () {
|
|
||||||
this.timeout(Factory.TenSecondTimeout)
|
|
||||||
|
|
||||||
beforeEach(async function () {
|
|
||||||
localStorage.clear()
|
|
||||||
|
|
||||||
this.context = await Factory.createAppContext()
|
|
||||||
await this.context.launch()
|
|
||||||
|
|
||||||
this.application = this.context.application
|
|
||||||
this.email = this.context.email
|
|
||||||
this.password = this.context.password
|
|
||||||
|
|
||||||
await Factory.registerUserToApplication({
|
|
||||||
application: this.application,
|
|
||||||
email: this.email,
|
|
||||||
password: this.password,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(async function () {
|
|
||||||
await this.context.deinit()
|
|
||||||
this.context = undefined
|
|
||||||
this.application = undefined
|
|
||||||
localStorage.clear()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('share token payloads should include neccessary params', async function () {
|
|
||||||
const token = await this.application.sessions.createDemoShareToken()
|
|
||||||
const payload = await this.application.sessions.decodeDemoShareToken(token)
|
|
||||||
|
|
||||||
const expectedKeys = [
|
|
||||||
'accessToken',
|
|
||||||
'refreshToken',
|
|
||||||
'accessExpiration',
|
|
||||||
'refreshExpiration',
|
|
||||||
'readonlyAccess',
|
|
||||||
'masterKey',
|
|
||||||
'keyParams',
|
|
||||||
'user',
|
|
||||||
'host',
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const key of expectedKeys) {
|
|
||||||
expect(payload[key]).to.not.be.undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('populating session from share token should allow pulling in new items', async function () {
|
|
||||||
const token = await this.application.sessions.createDemoShareToken()
|
|
||||||
|
|
||||||
await Factory.createSyncedNote(this.application, 'demo title', 'demo text')
|
|
||||||
|
|
||||||
const otherContext = await Factory.createAppContext()
|
|
||||||
await otherContext.launch()
|
|
||||||
|
|
||||||
const otherApplication = otherContext.application
|
|
||||||
|
|
||||||
expect(otherApplication.items.getItems(ContentType.Note).length).to.equal(0)
|
|
||||||
|
|
||||||
await otherApplication.sessions.populateSessionFromDemoShareToken(token)
|
|
||||||
|
|
||||||
await otherApplication.sync.sync()
|
|
||||||
|
|
||||||
const notes = otherApplication.items.getItems(ContentType.Note)
|
|
||||||
|
|
||||||
expect(notes.length).to.equal(1)
|
|
||||||
|
|
||||||
const note = notes[0]
|
|
||||||
|
|
||||||
expect(note.title).to.equal('demo title')
|
|
||||||
expect(note.text).to.equal('demo text')
|
|
||||||
|
|
||||||
await otherContext.deinit()
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Demo session tokens can only be created manually via raw SQL entry on the DB side.
|
|
||||||
* There is no API to create share tokens. Therefore, the share token below is made from
|
|
||||||
* a copy of the master session, which is not readonly.
|
|
||||||
*/
|
|
||||||
it.skip('populating session from share token should not allow making changes', async function () {
|
|
||||||
const token = await this.application.sessions.createDemoShareToken()
|
|
||||||
|
|
||||||
await Factory.createSyncedNote(this.application, 'demo title', 'demo text')
|
|
||||||
|
|
||||||
const otherContext = await Factory.createAppContext()
|
|
||||||
await otherContext.launch()
|
|
||||||
|
|
||||||
const otherApplication = otherContext.application
|
|
||||||
|
|
||||||
await otherApplication.sessions.populateSessionFromDemoShareToken(token)
|
|
||||||
|
|
||||||
await otherApplication.sync.sync()
|
|
||||||
|
|
||||||
const note = otherApplication.items.getItems(ContentType.Note)[0]
|
|
||||||
|
|
||||||
const syncResponse = otherContext.awaitNextSyncEvent(SyncEvent.SingleRoundTripSyncCompleted)
|
|
||||||
|
|
||||||
await otherApplication.mutator.changeAndSaveItem(note, (mutator) => {
|
|
||||||
mutator.title = 'unauthorized change'
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await syncResponse
|
|
||||||
|
|
||||||
expect(result.rawResponse.unsaved_items.length).to.equal(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -84,7 +84,6 @@
|
|||||||
<script type="module" src="history.test.js"></script>
|
<script type="module" src="history.test.js"></script>
|
||||||
<script type="module" src="actions.test.js"></script>
|
<script type="module" src="actions.test.js"></script>
|
||||||
<script type="module" src="preferences.test.js"></script>
|
<script type="module" src="preferences.test.js"></script>
|
||||||
<script type="module" src="session-sharing.test.js"></script>
|
|
||||||
<script type="module" src="files.test.js"></script>
|
<script type="module" src="files.test.js"></script>
|
||||||
<script type="module" src="session.test.js"></script>
|
<script type="module" src="session.test.js"></script>
|
||||||
<script type="module" src="subscriptions.test.js"></script>
|
<script type="module" src="subscriptions.test.js"></script>
|
||||||
|
|||||||
@@ -426,6 +426,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
this.getViewControllerManager().accountMenuController.setShow(true)
|
this.getViewControllerManager().accountMenuController.setShow(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideAccountMenu(): void {
|
||||||
|
this.getViewControllerManager().accountMenuController.setShow(false)
|
||||||
|
}
|
||||||
|
|
||||||
geDefaultEditorIdentifier(currentTag?: SNTag): EditorIdentifier {
|
geDefaultEditorIdentifier(currentTag?: SNTag): EditorIdentifier {
|
||||||
return (
|
return (
|
||||||
currentTag?.preferences?.editorIdentifier ||
|
currentTag?.preferences?.editorIdentifier ||
|
||||||
|
|||||||
@@ -90,7 +90,11 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
void application.user.populateSessionFromDemoShareToken(token)
|
const status = application.status.addMessage('Preparing demo...')
|
||||||
|
void application.user.populateSessionFromDemoShareToken(token).then(() => {
|
||||||
|
application.status.removeMessage(status)
|
||||||
|
application.hideAccountMenu()
|
||||||
|
})
|
||||||
}, [application])
|
}, [application])
|
||||||
|
|
||||||
const onAppLaunch = useCallback(() => {
|
const onAppLaunch = useCallback(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user