diff --git a/packages/cli/src/Ldap/helpers.ts b/packages/cli/src/Ldap/helpers.ts index 0dab8e3982..12f1b402e6 100644 --- a/packages/cli/src/Ldap/helpers.ts +++ b/packages/cli/src/Ldap/helpers.ts @@ -216,10 +216,13 @@ export const processUsers = async ( export const saveLdapSynchronization = async ( data: Omit, ): Promise => { - await Container.get(AuthProviderSyncHistoryRepository).save({ - ...data, - providerType: 'ldap', - }); + await Container.get(AuthProviderSyncHistoryRepository).save( + { + ...data, + providerType: 'ldap', + }, + { transaction: false }, + ); }; /** @@ -257,15 +260,20 @@ export const getMappingAttributes = (ldapConfig: LdapConfig): string[] => { }; export const createLdapAuthIdentity = async (user: User, ldapId: string) => { - return await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId)); + return await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId), { + transaction: false, + }); }; export const createLdapUserOnLocalDb = async (data: Partial, ldapId: string) => { - const user = await Container.get(UserRepository).save({ - password: randomPassword(), - role: 'global:member', - ...data, - }); + const user = await Container.get(UserRepository).save( + { + password: randomPassword(), + role: 'global:member', + ...data, + }, + { transaction: false }, + ); await createLdapAuthIdentity(user, ldapId); return user; }; diff --git a/packages/cli/src/controllers/invitation.controller.ts b/packages/cli/src/controllers/invitation.controller.ts index cba846965d..022ead6ed1 100644 --- a/packages/cli/src/controllers/invitation.controller.ts +++ b/packages/cli/src/controllers/invitation.controller.ts @@ -163,7 +163,7 @@ export class InvitationController { invitee.lastName = lastName; invitee.password = await this.passwordUtility.hash(validPassword); - const updatedUser = await this.userRepository.save(invitee); + const updatedUser = await this.userRepository.save(invitee, { transaction: false }); await issueCookie(res, updatedUser); diff --git a/packages/cli/src/controllers/me.controller.ts b/packages/cli/src/controllers/me.controller.ts index bf7337de78..1d09cb8ce6 100644 --- a/packages/cli/src/controllers/me.controller.ts +++ b/packages/cli/src/controllers/me.controller.ts @@ -104,12 +104,13 @@ export class MeController { */ @Patch('/password') async updatePassword(req: MeRequest.Password, res: Response) { + const { user } = req; const { currentPassword, newPassword } = req.body; // If SAML is enabled, we don't allow the user to change their email address if (isSamlLicensedAndEnabled()) { this.logger.debug('Attempted to change password for user, while SAML is enabled', { - userId: req.user?.id, + userId: user.id, }); throw new BadRequestError( 'With SAML enabled, users need to use their SAML provider to change passwords', @@ -120,33 +121,30 @@ export class MeController { throw new BadRequestError('Invalid payload.'); } - if (!req.user.password) { + if (!user.password) { throw new BadRequestError('Requesting user not set up.'); } - const isCurrentPwCorrect = await this.passwordUtility.compare( - currentPassword, - req.user.password, - ); + const isCurrentPwCorrect = await this.passwordUtility.compare(currentPassword, user.password); if (!isCurrentPwCorrect) { throw new BadRequestError('Provided current password is incorrect.'); } const validPassword = this.passwordUtility.validate(newPassword); - req.user.password = await this.passwordUtility.hash(validPassword); + user.password = await this.passwordUtility.hash(validPassword); - const user = await this.userRepository.save(req.user); + const updatedUser = await this.userRepository.save(user, { transaction: false }); this.logger.info('Password updated successfully', { userId: user.id }); - await issueCookie(res, user); + await issueCookie(res, updatedUser); void this.internalHooks.onUserUpdate({ - user, + user: updatedUser, fields_changed: ['password'], }); - await this.externalHooks.run('user.password.update', [user.email, req.user.password]); + await this.externalHooks.run('user.password.update', [updatedUser.email, updatedUser.password]); return { success: true }; } @@ -168,11 +166,13 @@ export class MeController { throw new BadRequestError('Personalization answers are mandatory'); } - await this.userRepository.save({ - id: req.user.id, - // @ts-ignore - personalizationAnswers, - }); + await this.userRepository.save( + { + id: req.user.id, + personalizationAnswers, + }, + { transaction: false }, + ); this.logger.info('User survey updated successfully', { userId: req.user.id }); diff --git a/packages/cli/src/controllers/owner.controller.ts b/packages/cli/src/controllers/owner.controller.ts index fe0484021f..15d0bc2c5c 100644 --- a/packages/cli/src/controllers/owner.controller.ts +++ b/packages/cli/src/controllers/owner.controller.ts @@ -76,7 +76,7 @@ export class OwnerController { await validateEntity(owner); - owner = await this.userRepository.save(owner); + owner = await this.userRepository.save(owner, { transaction: false }); this.logger.info('Owner was set up successfully', { userId }); diff --git a/packages/cli/src/databases/repositories/settings.repository.ts b/packages/cli/src/databases/repositories/settings.repository.ts index 937b56b2c4..94cb32305a 100644 --- a/packages/cli/src/databases/repositories/settings.repository.ts +++ b/packages/cli/src/databases/repositories/settings.repository.ts @@ -12,7 +12,11 @@ export class SettingsRepository extends Repository { } async getEncryptedSecretsProviderSettings(): Promise { - return (await this.findOne({ where: { key: EXTERNAL_SECRETS_DB_KEY } }))?.value ?? null; + return (await this.findByKey(EXTERNAL_SECRETS_DB_KEY))?.value ?? null; + } + + async findByKey(key: string): Promise { + return await this.findOneBy({ key }); } async saveEncryptedSecretsProviderSettings(data: string): Promise { @@ -38,7 +42,7 @@ export class SettingsRepository extends Repository { await this.update({ key }, { value, loadOnStartup: true }); } else { value = JSON.stringify([bannerName]); - await this.save({ key, value, loadOnStartup: true }); + await this.save({ key, value, loadOnStartup: true }, { transaction: false }); } config.set(key, value); return { success: true }; diff --git a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts index e4f464d322..25626d1a66 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlImport.service.ee.ts @@ -492,7 +492,7 @@ export class SourceControlImportService { key, value: valueOverrides[key], }); - await Container.get(VariablesRepository).save(newVariable); + await Container.get(VariablesRepository).save(newVariable, { transaction: false }); } } diff --git a/packages/cli/src/environments/sourceControl/sourceControlPreferences.service.ee.ts b/packages/cli/src/environments/sourceControl/sourceControlPreferences.service.ee.ts index 3b8696b32b..4da3221ab5 100644 --- a/packages/cli/src/environments/sourceControl/sourceControlPreferences.service.ee.ts +++ b/packages/cli/src/environments/sourceControl/sourceControlPreferences.service.ee.ts @@ -173,11 +173,14 @@ export class SourceControlPreferencesService { if (saveToDb) { const settingsValue = JSON.stringify(this._sourceControlPreferences); try { - await Container.get(SettingsRepository).save({ - key: SOURCE_CONTROL_PREFERENCES_DB_KEY, - value: settingsValue, - loadOnStartup: true, - }); + await Container.get(SettingsRepository).save( + { + key: SOURCE_CONTROL_PREFERENCES_DB_KEY, + value: settingsValue, + loadOnStartup: true, + }, + { transaction: false }, + ); } catch (error) { throw new ApplicationError('Failed to save source control preferences', { cause: error }); } diff --git a/packages/cli/src/environments/variables/variables.service.ee.ts b/packages/cli/src/environments/variables/variables.service.ee.ts index 0ea2cc6e57..80a4846252 100644 --- a/packages/cli/src/environments/variables/variables.service.ee.ts +++ b/packages/cli/src/environments/variables/variables.service.ee.ts @@ -71,10 +71,13 @@ export class VariablesService { this.validateVariable(variable); void Container.get(InternalHooks).onVariableCreated({ variable_type: variable.type }); - const saveResult = await this.variablesRepository.save({ - ...variable, - id: generateNanoId(), - }); + const saveResult = await this.variablesRepository.save( + { + ...variable, + id: generateNanoId(), + }, + { transaction: false }, + ); await this.updateCache(); return saveResult; } diff --git a/packages/cli/src/services/tag.service.ts b/packages/cli/src/services/tag.service.ts index c32ceef18c..27f4ebd0f2 100644 --- a/packages/cli/src/services/tag.service.ts +++ b/packages/cli/src/services/tag.service.ts @@ -27,7 +27,7 @@ export class TagService { await this.externalHooks.run(`tag.before${action}`, [tag]); - const savedTag = this.tagRepository.save(tag); + const savedTag = this.tagRepository.save(tag, { transaction: false }); await this.externalHooks.run(`tag.after${action}`, [tag]); diff --git a/packages/cli/src/sso/saml/saml.service.ee.ts b/packages/cli/src/sso/saml/saml.service.ee.ts index b3e16861f1..c4873feaec 100644 --- a/packages/cli/src/sso/saml/saml.service.ee.ts +++ b/packages/cli/src/sso/saml/saml.service.ee.ts @@ -287,13 +287,18 @@ export class SamlService { let result: Settings; if (samlPreferences) { samlPreferences.value = settingsValue; - result = await Container.get(SettingsRepository).save(samlPreferences); - } else { - result = await Container.get(SettingsRepository).save({ - key: SAML_PREFERENCES_DB_KEY, - value: settingsValue, - loadOnStartup: true, + result = await Container.get(SettingsRepository).save(samlPreferences, { + transaction: false, }); + } else { + result = await Container.get(SettingsRepository).save( + { + key: SAML_PREFERENCES_DB_KEY, + value: settingsValue, + loadOnStartup: true, + }, + { transaction: false }, + ); } if (result) return jsonParse(result.value); return; diff --git a/packages/cli/src/sso/saml/samlHelpers.ts b/packages/cli/src/sso/saml/samlHelpers.ts index 799277eba8..e87d73ba70 100644 --- a/packages/cli/src/sso/saml/samlHelpers.ts +++ b/packages/cli/src/sso/saml/samlHelpers.ts @@ -109,10 +109,12 @@ export async function createUserFromSamlAttributes(attributes: SamlUserAttribute authIdentity.providerId = attributes.userPrincipalName; authIdentity.providerType = 'saml'; authIdentity.user = user; - const resultAuthIdentity = await Container.get(AuthIdentityRepository).save(authIdentity); + const resultAuthIdentity = await Container.get(AuthIdentityRepository).save(authIdentity, { + transaction: false, + }); if (!resultAuthIdentity) throw new AuthError('Could not create AuthIdentity'); user.authIdentities = [authIdentity]; - const resultUser = await Container.get(UserRepository).save(user); + const resultUser = await Container.get(UserRepository).save(user, { transaction: false }); if (!resultUser) throw new AuthError('Could not create User'); return resultUser; } @@ -133,10 +135,10 @@ export async function updateUserFromSamlAttributes( } else { samlAuthIdentity.providerId = attributes.userPrincipalName; } - await Container.get(AuthIdentityRepository).save(samlAuthIdentity); + await Container.get(AuthIdentityRepository).save(samlAuthIdentity, { transaction: false }); user.firstName = attributes.firstName; user.lastName = attributes.lastName; - const resultUser = await Container.get(UserRepository).save(user); + const resultUser = await Container.get(UserRepository).save(user, { transaction: false }); if (!resultUser) throw new AuthError('Could not create User'); return resultUser; } diff --git a/packages/cli/src/sso/ssoHelpers.ts b/packages/cli/src/sso/ssoHelpers.ts index fa1abf2371..7a0b2b140e 100644 --- a/packages/cli/src/sso/ssoHelpers.ts +++ b/packages/cli/src/sso/ssoHelpers.ts @@ -13,11 +13,14 @@ export async function setCurrentAuthenticationMethod( authenticationMethod: AuthProviderType, ): Promise { config.set('userManagement.authenticationMethod', authenticationMethod); - await Container.get(SettingsRepository).save({ - key: 'userManagement.authenticationMethod', - value: authenticationMethod, - loadOnStartup: true, - }); + await Container.get(SettingsRepository).save( + { + key: 'userManagement.authenticationMethod', + value: authenticationMethod, + loadOnStartup: true, + }, + { transaction: false }, + ); } export function getCurrentAuthenticationMethod(): AuthProviderType { diff --git a/packages/cli/test/unit/controllers/me.controller.test.ts b/packages/cli/test/unit/controllers/me.controller.test.ts index 3359e592aa..0ebca0bd77 100644 --- a/packages/cli/test/unit/controllers/me.controller.test.ts +++ b/packages/cli/test/unit/controllers/me.controller.test.ts @@ -192,6 +192,17 @@ describe('MeController', () => { }); }); + describe('storeSurveyAnswers', () => { + it('should throw BadRequestError if answers are missing in the payload', async () => { + const req = mock({ + body: undefined, + }); + await expect(controller.storeSurveyAnswers(req)).rejects.toThrowError( + new BadRequestError('Personalization answers are mandatory'), + ); + }); + }); + describe('API Key methods', () => { let req: AuthenticatedRequest; beforeAll(() => { diff --git a/packages/cli/test/unit/controllers/owner.controller.test.ts b/packages/cli/test/unit/controllers/owner.controller.test.ts index 97191a7a69..f142e16b99 100644 --- a/packages/cli/test/unit/controllers/owner.controller.test.ts +++ b/packages/cli/test/unit/controllers/owner.controller.test.ts @@ -95,7 +95,7 @@ describe('OwnerController', () => { await controller.setupOwner(req, res); - expect(userRepository.save).toHaveBeenCalledWith(user); + expect(userRepository.save).toHaveBeenCalledWith(user, { transaction: false }); const cookieOptions = captor(); expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, 'signed-token', cookieOptions);