Skip to content

Commit

Permalink
refactor: password policies (#30270)
Browse files Browse the repository at this point in the history
Co-authored-by: Hugo Costa <[email protected]>
Co-authored-by: Marcos Spessatto Defendi <[email protected]>
  • Loading branch information
3 people authored Sep 14, 2023
1 parent 7137a19 commit 92f5a02
Show file tree
Hide file tree
Showing 27 changed files with 671 additions and 584 deletions.
3 changes: 2 additions & 1 deletion apps/meteor/app/lib/server/functions/saveUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as Mailer from '../../../mailer/server/api';
import { settings } from '../../../settings/server';
import { safeGetMeteorUser } from '../../../utils/server/functions/safeGetMeteorUser';
import { validateEmailDomain } from '../lib';
import { generatePassword } from '../lib/generatePassword';
import { passwordPolicy } from '../lib/passwordPolicy';
import { checkEmailAvailability } from './checkEmailAvailability';
import { checkUsernameAvailability } from './checkUsernameAvailability';
Expand Down Expand Up @@ -344,7 +345,7 @@ export const saveUser = async function (userId, userData) {

if (userData.hasOwnProperty('setRandomPassword')) {
if (userData.setRandomPassword) {
userData.password = passwordPolicy.generatePassword();
userData.password = generatePassword();
userData.requirePasswordChange = true;
sendPassword = true;
}
Expand Down
31 changes: 31 additions & 0 deletions apps/meteor/app/lib/server/lib/generatePassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import generator from 'generate-password';

import { passwordPolicy } from './passwordPolicy';

export const generatePassword = (): string => {
const policies = passwordPolicy.getPasswordPolicy();

const maxLength: number = (policies.policy.find(([key]) => key === 'get-password-policy-maxLength')?.[1]?.maxLength as number) || -1;
const minLength: number = (policies.policy.find(([key]) => key === 'get-password-policy-minLength')?.[1]?.minLength as number) || -1;

const length = Math.min(Math.max(minLength, 12), maxLength > 0 ? maxLength : Number.MAX_SAFE_INTEGER);

if (policies.enabled) {
for (let i = 0; i < 10; i++) {
const password = generator.generate({
length,
...(policies.policy && { numbers: true }),
...(policies.policy.some(([key]) => key === 'get-password-policy-mustContainAtLeastOneSpecialCharacter') && { symbols: true }),
...(policies.policy.some(([key]) => key === 'get-password-policy-mustContainAtLeastOneLowercase') && { lowercase: true }),
...(policies.policy.some(([key]) => key === 'get-password-policy-mustContainAtLeastOneUppercase') && { uppercase: true }),
strict: true,
});

if (passwordPolicy.validate(password)) {
return password;
}
}
}

return generator.generate({ length: 17 });
};
32 changes: 0 additions & 32 deletions apps/meteor/app/lib/server/lib/passwordPolicy.js

This file was deleted.

64 changes: 64 additions & 0 deletions apps/meteor/app/lib/server/lib/passwordPolicy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { PasswordPolicy } from '@rocket.chat/password-policies';

import { settings } from '../../../settings/server';

const enabled = false;
const minLength = -1;
const maxLength = -1;
const forbidRepeatingCharacters = false;
const forbidRepeatingCharactersCount = 3;
const mustContainAtLeastOneLowercase = false;
const mustContainAtLeastOneUppercase = false;
const mustContainAtLeastOneNumber = false;
const mustContainAtLeastOneSpecialCharacter = false;

export let passwordPolicy = new PasswordPolicy({
enabled,
minLength,
maxLength,
forbidRepeatingCharacters,
forbidRepeatingCharactersCount,
mustContainAtLeastOneLowercase,
mustContainAtLeastOneUppercase,
mustContainAtLeastOneNumber,
mustContainAtLeastOneSpecialCharacter,
throwError: true,
});

settings.watchMultiple(
[
'Accounts_Password_Policy_Enabled',
'Accounts_Password_Policy_MinLength',
'Accounts_Password_Policy_MaxLength',
'Accounts_Password_Policy_ForbidRepeatingCharacters',
'Accounts_Password_Policy_ForbidRepeatingCharactersCount',
'Accounts_Password_Policy_AtLeastOneLowercase',
'Accounts_Password_Policy_AtLeastOneUppercase',
'Accounts_Password_Policy_AtLeastOneNumber',
'Accounts_Password_Policy_AtLeastOneSpecialCharacter',
],
([
enabled,
minLength,
maxLength,
forbidRepeatingCharacters,
forbidRepeatingCharactersCount,
mustContainAtLeastOneLowercase,
mustContainAtLeastOneUppercase,
mustContainAtLeastOneNumber,
mustContainAtLeastOneSpecialCharacter,
]) => {
passwordPolicy = new PasswordPolicy({
enabled: Boolean(enabled),
minLength: Number(minLength),
maxLength: Number(maxLength),
forbidRepeatingCharacters: Boolean(forbidRepeatingCharacters),
forbidRepeatingCharactersCount: Number(forbidRepeatingCharactersCount),
mustContainAtLeastOneLowercase: Boolean(mustContainAtLeastOneLowercase),
mustContainAtLeastOneUppercase: Boolean(mustContainAtLeastOneUppercase),
mustContainAtLeastOneNumber: Boolean(mustContainAtLeastOneNumber),
mustContainAtLeastOneSpecialCharacter: Boolean(mustContainAtLeastOneSpecialCharacter),
throwError: true,
});
},
);
1 change: 1 addition & 0 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
"@rocket.chat/mp3-encoder": "0.24.0",
"@rocket.chat/omnichannel-services": "workspace:^",
"@rocket.chat/onboarding-ui": "next",
"@rocket.chat/password-policies": "workspace:^",
"@rocket.chat/pdf-worker": "workspace:^",
"@rocket.chat/poplib": "workspace:^",
"@rocket.chat/presence": "workspace:^",
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/server/methods/getPasswordPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Meteor.methods<ServerMethods>({
method: 'getPasswordPolicy',
});
}
return passwordPolicy.getPasswordPolicy();
return passwordPolicy.getPasswordPolicy() as {
enabled: boolean;
policy: [name: TranslationKey, options?: Record<string, unknown>][];
};
},
});
10 changes: 10 additions & 0 deletions apps/meteor/server/settings/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -745,50 +745,60 @@ export const createAccountSettings = () =>
await this.section('Password_Policy', async function () {
await this.add('Accounts_Password_Policy_Enabled', false, {
type: 'boolean',
public: true,
});

const enableQuery = {
_id: 'Accounts_Password_Policy_Enabled',
value: true,
public: true,
};

await this.add('Accounts_Password_Policy_MinLength', 7, {
type: 'int',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_MaxLength', -1, {
type: 'int',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_ForbidRepeatingCharacters', true, {
type: 'boolean',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_ForbidRepeatingCharactersCount', 3, {
type: 'int',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_AtLeastOneLowercase', true, {
type: 'boolean',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_AtLeastOneUppercase', true, {
type: 'boolean',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_AtLeastOneNumber', true, {
type: 'boolean',
public: true,
enableQuery,
});

await this.add('Accounts_Password_Policy_AtLeastOneSpecialCharacter', true, {
type: 'boolean',
public: true,
enableQuery,
});
});
Expand Down
Loading

0 comments on commit 92f5a02

Please sign in to comment.