fix: trim/lowercase email on email change (#2886)
This commit is contained in:
@@ -40,6 +40,10 @@ import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInt
|
|||||||
import { ReencryptTypeAItems } from '../Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
import { ReencryptTypeAItems } from '../Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
||||||
import { DecryptErroredPayloads } from '../Encryption/UseCase/DecryptErroredPayloads'
|
import { DecryptErroredPayloads } from '../Encryption/UseCase/DecryptErroredPayloads'
|
||||||
|
|
||||||
|
const cleanedEmailString = (email: string) => {
|
||||||
|
return email.trim().toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
export class UserService
|
export class UserService
|
||||||
extends AbstractService<AccountEvent, AccountEventData>
|
extends AbstractService<AccountEvent, AccountEventData>
|
||||||
implements UserServiceInterface, InternalEventHandlerInterface
|
implements UserServiceInterface, InternalEventHandlerInterface
|
||||||
@@ -593,13 +597,15 @@ export class UserService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newEmail = parameters.newEmail ? cleanedEmailString(parameters.newEmail) : undefined
|
||||||
|
|
||||||
const user = this.sessions.getUser() as User
|
const user = this.sessions.getUser() as User
|
||||||
const currentEmail = user.email
|
const currentEmail = user.email
|
||||||
const { currentRootKey, newRootKey } = await this.recomputeRootKeysForCredentialChange({
|
const { currentRootKey, newRootKey } = await this.recomputeRootKeysForCredentialChange({
|
||||||
currentPassword: parameters.currentPassword,
|
currentPassword: parameters.currentPassword,
|
||||||
currentEmail,
|
currentEmail,
|
||||||
origination: parameters.origination,
|
origination: parameters.origination,
|
||||||
newEmail: parameters.newEmail,
|
newEmail: newEmail,
|
||||||
newPassword: parameters.newPassword,
|
newPassword: parameters.newPassword,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -609,7 +615,7 @@ export class UserService
|
|||||||
currentServerPassword: currentRootKey.serverPassword as string,
|
currentServerPassword: currentRootKey.serverPassword as string,
|
||||||
newRootKey: newRootKey,
|
newRootKey: newRootKey,
|
||||||
wrappingKey,
|
wrappingKey,
|
||||||
newEmail: parameters.newEmail,
|
newEmail: newEmail,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.unlockSyncing()
|
this.unlockSyncing()
|
||||||
|
|||||||
@@ -70,15 +70,12 @@ import {
|
|||||||
UserApiServiceInterface,
|
UserApiServiceInterface,
|
||||||
UserRegistrationResponseBody,
|
UserRegistrationResponseBody,
|
||||||
} from '@standardnotes/api'
|
} from '@standardnotes/api'
|
||||||
|
import { cleanedEmailString } from './cleanedEmailString'
|
||||||
|
|
||||||
export const MINIMUM_PASSWORD_LENGTH = 8
|
export const MINIMUM_PASSWORD_LENGTH = 8
|
||||||
export const MissingAccountParams = 'missing-params'
|
export const MissingAccountParams = 'missing-params'
|
||||||
const ThirtyMinutes = 30 * 60 * 1000
|
const ThirtyMinutes = 30 * 60 * 1000
|
||||||
|
|
||||||
const cleanedEmailString = (email: string) => {
|
|
||||||
return email.trim().toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The session manager is responsible for loading initial user state, and any relevant
|
* The session manager is responsible for loading initial user state, and any relevant
|
||||||
* server credentials, such as the session token. It also exposes methods for registering
|
* server credentials, such as the session token. It also exposes methods for registering
|
||||||
@@ -659,7 +656,7 @@ export class SessionManager
|
|||||||
currentServerPassword: parameters.currentServerPassword,
|
currentServerPassword: parameters.currentServerPassword,
|
||||||
newServerPassword: parameters.newRootKey.serverPassword as string,
|
newServerPassword: parameters.newRootKey.serverPassword as string,
|
||||||
newKeyParams: parameters.newRootKey.keyParams,
|
newKeyParams: parameters.newRootKey.keyParams,
|
||||||
newEmail: parameters.newEmail,
|
newEmail: parameters.newEmail ? cleanedEmailString(parameters.newEmail) : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
const oldKeys = this._getKeyPairs.execute()
|
const oldKeys = this._getKeyPairs.execute()
|
||||||
|
|||||||
3
packages/snjs/lib/Services/Session/cleanedEmailString.ts
Normal file
3
packages/snjs/lib/Services/Session/cleanedEmailString.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const cleanedEmailString = (email: string) => {
|
||||||
|
return email.trim().toLowerCase()
|
||||||
|
}
|
||||||
@@ -453,6 +453,37 @@ describe('keys', function () {
|
|||||||
await Factory.safeDeinit(application)
|
await Factory.safeDeinit(application)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('changing email to all uppercase should allow sign in with lowercase', async function () {
|
||||||
|
await Factory.safeDeinit(application)
|
||||||
|
const realCryptoContext = await Factory.createAppContextWithRealCrypto()
|
||||||
|
await realCryptoContext.launch()
|
||||||
|
|
||||||
|
application = realCryptoContext.application
|
||||||
|
|
||||||
|
await Factory.registerUserToApplication({
|
||||||
|
application: application,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
})
|
||||||
|
const mixedCaseEmail = `TheFooBar@bar.${UuidGenerator.GenerateUuid()}`
|
||||||
|
|
||||||
|
const changeEmailResponse = await application.changeEmail(mixedCaseEmail, password)
|
||||||
|
|
||||||
|
expect(changeEmailResponse.error).to.not.be.ok
|
||||||
|
|
||||||
|
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||||
|
const loginResponse = await Factory.loginToApplication({
|
||||||
|
application: application,
|
||||||
|
email: mixedCaseEmail.toLowerCase(),
|
||||||
|
password: password,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(loginResponse).to.be.ok
|
||||||
|
expect(loginResponse.status).to.equal(200)
|
||||||
|
|
||||||
|
await Factory.safeDeinit(application)
|
||||||
|
}).timeout(Factory.TwentySecondTimeout)
|
||||||
|
|
||||||
it('compares root keys', async function () {
|
it('compares root keys', async function () {
|
||||||
const keyParams = {}
|
const keyParams = {}
|
||||||
const a1 = await CreateNewRootKey({
|
const a1 = await CreateNewRootKey({
|
||||||
|
|||||||
@@ -51,7 +51,14 @@ export async function createAppContextWithRealCrypto(identifier) {
|
|||||||
return createAppContext({ identifier, crypto: new SNWebCrypto() })
|
return createAppContext({ identifier, crypto: new SNWebCrypto() })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAppContext({ identifier, crypto, email, password, host, syncCallsThresholdPerMinute } = {}) {
|
export async function createAppContext({
|
||||||
|
identifier,
|
||||||
|
crypto,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
host,
|
||||||
|
syncCallsThresholdPerMinute,
|
||||||
|
} = {}) {
|
||||||
const context = new AppContext({ identifier, crypto, email, password, host, syncCallsThresholdPerMinute })
|
const context = new AppContext({ identifier, crypto, email, password, host, syncCallsThresholdPerMinute })
|
||||||
await context.initialize()
|
await context.initialize()
|
||||||
return context
|
return context
|
||||||
@@ -250,7 +257,14 @@ export async function awaitFunctionInvokation(object, functionName) {
|
|||||||
* A new one must be created.
|
* A new one must be created.
|
||||||
*/
|
*/
|
||||||
export async function signOutApplicationAndReturnNew(application) {
|
export async function signOutApplicationAndReturnNew(application) {
|
||||||
const isRealCrypto = application.crypto instanceof SNWebCrypto
|
if (!application) {
|
||||||
|
throw Error('[signOutApplicationAndReturnNew] Application is undefined')
|
||||||
|
}
|
||||||
|
if (!application.options.crypto) {
|
||||||
|
throw Error('[signOutApplicationAndReturnNew] Application.options.crypto is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRealCrypto = application.options.crypto instanceof SNWebCrypto
|
||||||
await application.user.signOut()
|
await application.user.signOut()
|
||||||
if (isRealCrypto) {
|
if (isRealCrypto) {
|
||||||
return createInitAppWithRealCrypto()
|
return createInitAppWithRealCrypto()
|
||||||
@@ -260,7 +274,7 @@ export async function signOutApplicationAndReturnNew(application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function signOutAndBackIn(application, email, password) {
|
export async function signOutAndBackIn(application, email, password) {
|
||||||
const isRealCrypto = application.crypto instanceof SNWebCrypto
|
const isRealCrypto = application.options.crypto instanceof SNWebCrypto
|
||||||
await application.user.signOut()
|
await application.user.signOut()
|
||||||
const newApplication = isRealCrypto ? await createInitAppWithRealCrypto() : await createInitAppWithFakeCrypto()
|
const newApplication = isRealCrypto ? await createInitAppWithRealCrypto() : await createInitAppWithFakeCrypto()
|
||||||
await this.loginToApplication({
|
await this.loginToApplication({
|
||||||
|
|||||||
Reference in New Issue
Block a user