fix: Fix typeorm .save usage (no-changelog) (#8678)

This commit is contained in:
Tomi Turtiainen 2024-02-20 17:34:54 +02:00 committed by GitHub
parent afd2eb1f4a
commit 05e13a68ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 96 additions and 57 deletions

View file

@ -216,10 +216,13 @@ export const processUsers = async (
export const saveLdapSynchronization = async ( export const saveLdapSynchronization = async (
data: Omit<AuthProviderSyncHistory, 'id' | 'providerType'>, data: Omit<AuthProviderSyncHistory, 'id' | 'providerType'>,
): Promise<void> => { ): Promise<void> => {
await Container.get(AuthProviderSyncHistoryRepository).save({ await Container.get(AuthProviderSyncHistoryRepository).save(
{
...data, ...data,
providerType: 'ldap', providerType: 'ldap',
}); },
{ transaction: false },
);
}; };
/** /**
@ -257,15 +260,20 @@ export const getMappingAttributes = (ldapConfig: LdapConfig): string[] => {
}; };
export const createLdapAuthIdentity = async (user: User, ldapId: 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<User>, ldapId: string) => { export const createLdapUserOnLocalDb = async (data: Partial<User>, ldapId: string) => {
const user = await Container.get(UserRepository).save({ const user = await Container.get(UserRepository).save(
{
password: randomPassword(), password: randomPassword(),
role: 'global:member', role: 'global:member',
...data, ...data,
}); },
{ transaction: false },
);
await createLdapAuthIdentity(user, ldapId); await createLdapAuthIdentity(user, ldapId);
return user; return user;
}; };

View file

@ -163,7 +163,7 @@ export class InvitationController {
invitee.lastName = lastName; invitee.lastName = lastName;
invitee.password = await this.passwordUtility.hash(validPassword); 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); await issueCookie(res, updatedUser);

View file

@ -104,12 +104,13 @@ export class MeController {
*/ */
@Patch('/password') @Patch('/password')
async updatePassword(req: MeRequest.Password, res: Response) { async updatePassword(req: MeRequest.Password, res: Response) {
const { user } = req;
const { currentPassword, newPassword } = req.body; const { currentPassword, newPassword } = req.body;
// If SAML is enabled, we don't allow the user to change their email address // If SAML is enabled, we don't allow the user to change their email address
if (isSamlLicensedAndEnabled()) { if (isSamlLicensedAndEnabled()) {
this.logger.debug('Attempted to change password for user, while SAML is enabled', { this.logger.debug('Attempted to change password for user, while SAML is enabled', {
userId: req.user?.id, userId: user.id,
}); });
throw new BadRequestError( throw new BadRequestError(
'With SAML enabled, users need to use their SAML provider to change passwords', '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.'); throw new BadRequestError('Invalid payload.');
} }
if (!req.user.password) { if (!user.password) {
throw new BadRequestError('Requesting user not set up.'); throw new BadRequestError('Requesting user not set up.');
} }
const isCurrentPwCorrect = await this.passwordUtility.compare( const isCurrentPwCorrect = await this.passwordUtility.compare(currentPassword, user.password);
currentPassword,
req.user.password,
);
if (!isCurrentPwCorrect) { if (!isCurrentPwCorrect) {
throw new BadRequestError('Provided current password is incorrect.'); throw new BadRequestError('Provided current password is incorrect.');
} }
const validPassword = this.passwordUtility.validate(newPassword); 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 }); this.logger.info('Password updated successfully', { userId: user.id });
await issueCookie(res, user); await issueCookie(res, updatedUser);
void this.internalHooks.onUserUpdate({ void this.internalHooks.onUserUpdate({
user, user: updatedUser,
fields_changed: ['password'], 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 }; return { success: true };
} }
@ -168,11 +166,13 @@ export class MeController {
throw new BadRequestError('Personalization answers are mandatory'); throw new BadRequestError('Personalization answers are mandatory');
} }
await this.userRepository.save({ await this.userRepository.save(
{
id: req.user.id, id: req.user.id,
// @ts-ignore
personalizationAnswers, personalizationAnswers,
}); },
{ transaction: false },
);
this.logger.info('User survey updated successfully', { userId: req.user.id }); this.logger.info('User survey updated successfully', { userId: req.user.id });

View file

@ -76,7 +76,7 @@ export class OwnerController {
await validateEntity(owner); 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 }); this.logger.info('Owner was set up successfully', { userId });

View file

@ -12,7 +12,11 @@ export class SettingsRepository extends Repository<Settings> {
} }
async getEncryptedSecretsProviderSettings(): Promise<string | null> { async getEncryptedSecretsProviderSettings(): Promise<string | null> {
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<Settings | null> {
return await this.findOneBy({ key });
} }
async saveEncryptedSecretsProviderSettings(data: string): Promise<void> { async saveEncryptedSecretsProviderSettings(data: string): Promise<void> {
@ -38,7 +42,7 @@ export class SettingsRepository extends Repository<Settings> {
await this.update({ key }, { value, loadOnStartup: true }); await this.update({ key }, { value, loadOnStartup: true });
} else { } else {
value = JSON.stringify([bannerName]); value = JSON.stringify([bannerName]);
await this.save({ key, value, loadOnStartup: true }); await this.save({ key, value, loadOnStartup: true }, { transaction: false });
} }
config.set(key, value); config.set(key, value);
return { success: true }; return { success: true };

View file

@ -492,7 +492,7 @@ export class SourceControlImportService {
key, key,
value: valueOverrides[key], value: valueOverrides[key],
}); });
await Container.get(VariablesRepository).save(newVariable); await Container.get(VariablesRepository).save(newVariable, { transaction: false });
} }
} }

View file

@ -173,11 +173,14 @@ export class SourceControlPreferencesService {
if (saveToDb) { if (saveToDb) {
const settingsValue = JSON.stringify(this._sourceControlPreferences); const settingsValue = JSON.stringify(this._sourceControlPreferences);
try { try {
await Container.get(SettingsRepository).save({ await Container.get(SettingsRepository).save(
{
key: SOURCE_CONTROL_PREFERENCES_DB_KEY, key: SOURCE_CONTROL_PREFERENCES_DB_KEY,
value: settingsValue, value: settingsValue,
loadOnStartup: true, loadOnStartup: true,
}); },
{ transaction: false },
);
} catch (error) { } catch (error) {
throw new ApplicationError('Failed to save source control preferences', { cause: error }); throw new ApplicationError('Failed to save source control preferences', { cause: error });
} }

View file

@ -71,10 +71,13 @@ export class VariablesService {
this.validateVariable(variable); this.validateVariable(variable);
void Container.get(InternalHooks).onVariableCreated({ variable_type: variable.type }); void Container.get(InternalHooks).onVariableCreated({ variable_type: variable.type });
const saveResult = await this.variablesRepository.save({ const saveResult = await this.variablesRepository.save(
{
...variable, ...variable,
id: generateNanoId(), id: generateNanoId(),
}); },
{ transaction: false },
);
await this.updateCache(); await this.updateCache();
return saveResult; return saveResult;
} }

View file

@ -27,7 +27,7 @@ export class TagService {
await this.externalHooks.run(`tag.before${action}`, [tag]); 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]); await this.externalHooks.run(`tag.after${action}`, [tag]);

View file

@ -287,13 +287,18 @@ export class SamlService {
let result: Settings; let result: Settings;
if (samlPreferences) { if (samlPreferences) {
samlPreferences.value = settingsValue; samlPreferences.value = settingsValue;
result = await Container.get(SettingsRepository).save(samlPreferences); result = await Container.get(SettingsRepository).save(samlPreferences, {
transaction: false,
});
} else { } else {
result = await Container.get(SettingsRepository).save({ result = await Container.get(SettingsRepository).save(
{
key: SAML_PREFERENCES_DB_KEY, key: SAML_PREFERENCES_DB_KEY,
value: settingsValue, value: settingsValue,
loadOnStartup: true, loadOnStartup: true,
}); },
{ transaction: false },
);
} }
if (result) return jsonParse<SamlPreferences>(result.value); if (result) return jsonParse<SamlPreferences>(result.value);
return; return;

View file

@ -109,10 +109,12 @@ export async function createUserFromSamlAttributes(attributes: SamlUserAttribute
authIdentity.providerId = attributes.userPrincipalName; authIdentity.providerId = attributes.userPrincipalName;
authIdentity.providerType = 'saml'; authIdentity.providerType = 'saml';
authIdentity.user = user; 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'); if (!resultAuthIdentity) throw new AuthError('Could not create AuthIdentity');
user.authIdentities = [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'); if (!resultUser) throw new AuthError('Could not create User');
return resultUser; return resultUser;
} }
@ -133,10 +135,10 @@ export async function updateUserFromSamlAttributes(
} else { } else {
samlAuthIdentity.providerId = attributes.userPrincipalName; samlAuthIdentity.providerId = attributes.userPrincipalName;
} }
await Container.get(AuthIdentityRepository).save(samlAuthIdentity); await Container.get(AuthIdentityRepository).save(samlAuthIdentity, { transaction: false });
user.firstName = attributes.firstName; user.firstName = attributes.firstName;
user.lastName = attributes.lastName; 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'); if (!resultUser) throw new AuthError('Could not create User');
return resultUser; return resultUser;
} }

View file

@ -13,11 +13,14 @@ export async function setCurrentAuthenticationMethod(
authenticationMethod: AuthProviderType, authenticationMethod: AuthProviderType,
): Promise<void> { ): Promise<void> {
config.set('userManagement.authenticationMethod', authenticationMethod); config.set('userManagement.authenticationMethod', authenticationMethod);
await Container.get(SettingsRepository).save({ await Container.get(SettingsRepository).save(
{
key: 'userManagement.authenticationMethod', key: 'userManagement.authenticationMethod',
value: authenticationMethod, value: authenticationMethod,
loadOnStartup: true, loadOnStartup: true,
}); },
{ transaction: false },
);
} }
export function getCurrentAuthenticationMethod(): AuthProviderType { export function getCurrentAuthenticationMethod(): AuthProviderType {

View file

@ -192,6 +192,17 @@ describe('MeController', () => {
}); });
}); });
describe('storeSurveyAnswers', () => {
it('should throw BadRequestError if answers are missing in the payload', async () => {
const req = mock<MeRequest.SurveyAnswers>({
body: undefined,
});
await expect(controller.storeSurveyAnswers(req)).rejects.toThrowError(
new BadRequestError('Personalization answers are mandatory'),
);
});
});
describe('API Key methods', () => { describe('API Key methods', () => {
let req: AuthenticatedRequest; let req: AuthenticatedRequest;
beforeAll(() => { beforeAll(() => {

View file

@ -95,7 +95,7 @@ describe('OwnerController', () => {
await controller.setupOwner(req, res); await controller.setupOwner(req, res);
expect(userRepository.save).toHaveBeenCalledWith(user); expect(userRepository.save).toHaveBeenCalledWith(user, { transaction: false });
const cookieOptions = captor<CookieOptions>(); const cookieOptions = captor<CookieOptions>();
expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, 'signed-token', cookieOptions); expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, 'signed-token', cookieOptions);