mirror of
https://github.com/standardnotes/server
synced 2026-10-20 03:03:38 -04:00
fix: Updates subscription invites after email change
This commit is contained in:
@@ -1299,6 +1299,7 @@ export class ContainerConfigLoader {
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
container.get<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser),
|
||||
container.get<SharedSubscriptionInvitationRepositoryInterface>(TYPES.Auth_SharedSubscriptionInvitationRepository),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
+2
@@ -11,4 +11,6 @@ export interface SharedSubscriptionInvitationRepositoryInterface {
|
||||
inviterEmail: string,
|
||||
): Promise<SharedSubscriptionInvitation | null>
|
||||
countByInviterEmailAndStatus(inviterEmail: string, statuses: InvitationStatus[]): Promise<number>
|
||||
updateInviterIdentifierEmail(fromEmail: string, toEmail: string, updatedAt: number): Promise<void>
|
||||
updateInviteeIdentifierEmail(fromEmail: string, toEmail: string, updatedAt: number): Promise<void>
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { Session } from '../../Session/Session'
|
||||
import { Logger } from 'winston'
|
||||
import { SharedSubscriptionInvitationRepositoryInterface } from '../../SharedSubscription/SharedSubscriptionInvitationRepositoryInterface'
|
||||
|
||||
describe('ChangeCredentials', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
@@ -26,6 +27,7 @@ describe('ChangeCredentials', () => {
|
||||
let timer: TimerInterface
|
||||
let user: User
|
||||
let deleteOtherSessionsForUser: DeleteOtherSessionsForUser
|
||||
let sharedSubscriptionInvitationRepository: SharedSubscriptionInvitationRepositoryInterface
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () =>
|
||||
@@ -36,6 +38,7 @@ describe('ChangeCredentials', () => {
|
||||
domainEventFactory,
|
||||
timer,
|
||||
deleteOtherSessionsForUser,
|
||||
sharedSubscriptionInvitationRepository,
|
||||
logger,
|
||||
)
|
||||
|
||||
@@ -68,9 +71,14 @@ describe('ChangeCredentials', () => {
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(2)
|
||||
|
||||
deleteOtherSessionsForUser = {} as jest.Mocked<DeleteOtherSessionsForUser>
|
||||
deleteOtherSessionsForUser.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
|
||||
sharedSubscriptionInvitationRepository = {} as jest.Mocked<SharedSubscriptionInvitationRepositoryInterface>
|
||||
sharedSubscriptionInvitationRepository.updateInviterIdentifierEmail = jest.fn()
|
||||
sharedSubscriptionInvitationRepository.updateInviteeIdentifierEmail = jest.fn()
|
||||
})
|
||||
|
||||
it('should change password', async () => {
|
||||
@@ -176,6 +184,16 @@ describe('ChangeCredentials', () => {
|
||||
updatedAt: new Date(1),
|
||||
})
|
||||
expect(domainEventFactory.createUserEmailChangedEvent).toHaveBeenCalledWith('1-2-3', '[email protected]', '[email protected]')
|
||||
expect(sharedSubscriptionInvitationRepository.updateInviterIdentifierEmail).toHaveBeenCalledWith(
|
||||
'[email protected]',
|
||||
'[email protected]',
|
||||
2,
|
||||
)
|
||||
expect(sharedSubscriptionInvitationRepository.updateInviteeIdentifierEmail).toHaveBeenCalledWith(
|
||||
'[email protected]',
|
||||
'[email protected]',
|
||||
2,
|
||||
)
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(deleteOtherSessionsForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
@@ -202,6 +220,8 @@ describe('ChangeCredentials', () => {
|
||||
|
||||
expect(userRepository.save).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||
expect(sharedSubscriptionInvitationRepository.updateInviterIdentifierEmail).not.toHaveBeenCalled()
|
||||
expect(sharedSubscriptionInvitationRepository.updateInviteeIdentifierEmail).not.toHaveBeenCalled()
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { getBody, getSubject } from '../../Email/UserEmailChanged'
|
||||
import { Logger } from 'winston'
|
||||
import { AuthResponseCreationResult } from '../../Auth/AuthResponseCreationResult'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { SharedSubscriptionInvitationRepositoryInterface } from '../../SharedSubscription/SharedSubscriptionInvitationRepositoryInterface'
|
||||
|
||||
export class ChangeCredentials implements UseCaseInterface<AuthResponseCreationResult> {
|
||||
constructor(
|
||||
@@ -23,6 +24,7 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponseCreationR
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private timer: TimerInterface,
|
||||
private deleteOtherSessionsForUserUseCase: DeleteOtherSessionsForUser,
|
||||
private sharedSubscriptionInvitationRepository: SharedSubscriptionInvitationRepositoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -82,6 +84,8 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponseCreationR
|
||||
const updatedUser = await this.userRepository.save(user)
|
||||
|
||||
if (userEmailChangedEvent !== undefined) {
|
||||
await this.syncSharedSubscriptionInvitationsAfterEmailChange(existingEmailAddress, updatedUser.email)
|
||||
|
||||
await this.domainEventPublisher.publish(userEmailChangedEvent)
|
||||
|
||||
await this.sendEmailChangedNotification(existingEmailAddress, updatedUser.email)
|
||||
@@ -106,6 +110,13 @@ export class ChangeCredentials implements UseCaseInterface<AuthResponseCreationR
|
||||
return Result.ok(authResponse)
|
||||
}
|
||||
|
||||
private async syncSharedSubscriptionInvitationsAfterEmailChange(fromEmail: string, toEmail: string): Promise<void> {
|
||||
const updatedAt = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
await this.sharedSubscriptionInvitationRepository.updateInviterIdentifierEmail(fromEmail, toEmail, updatedAt)
|
||||
await this.sharedSubscriptionInvitationRepository.updateInviteeIdentifierEmail(fromEmail, toEmail, updatedAt)
|
||||
}
|
||||
|
||||
private async deleteOtherSessionsForUserIfNeeded(
|
||||
userUuid: string,
|
||||
session: Session,
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Repository } from 'typeorm'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
|
||||
import { InvitationStatus } from '../../Domain/SharedSubscription/InvitationStatus'
|
||||
import { InviteeIdentifierType } from '../../Domain/SharedSubscription/InviteeIdentifierType'
|
||||
import { InviterIdentifierType } from '../../Domain/SharedSubscription/InviterIdentifierType'
|
||||
import { SharedSubscriptionInvitation } from '../../Domain/SharedSubscription/SharedSubscriptionInvitation'
|
||||
import { SharedSubscriptionInvitationRepositoryInterface } from '../../Domain/SharedSubscription/SharedSubscriptionInvitationRepositoryInterface'
|
||||
|
||||
@@ -67,4 +69,28 @@ export class TypeORMSharedSubscriptionInvitationRepository implements SharedSubs
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async updateInviterIdentifierEmail(fromEmail: string, toEmail: string, updatedAt: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update(SharedSubscriptionInvitation)
|
||||
.set({ inviterIdentifier: toEmail, updatedAt })
|
||||
.where('inviter_identifier = :fromEmail AND inviter_identifier_type = :inviterType', {
|
||||
fromEmail,
|
||||
inviterType: InviterIdentifierType.Email,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
async updateInviteeIdentifierEmail(fromEmail: string, toEmail: string, updatedAt: number): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.update(SharedSubscriptionInvitation)
|
||||
.set({ inviteeIdentifier: toEmail, updatedAt })
|
||||
.where('invitee_identifier = :fromEmail AND invitee_identifier_type = :inviteeType', {
|
||||
fromEmail,
|
||||
inviteeType: InviteeIdentifierType.Email,
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user