diff --git a/src/components/UserMasterPanel.js b/src/components/UserMasterPanel.js index 8d138e7..68b10b4 100644 --- a/src/components/UserMasterPanel.js +++ b/src/components/UserMasterPanel.js @@ -13,6 +13,8 @@ import { TextInput, PublishedComponent, ValidatedTextInput, + passwordGenerator, + validatePassword, } from "@openimis/fe-core"; import { CLAIM_ADMIN_USER_TYPE, ENROLMENT_OFFICER_USER_TYPE, EMAIL_REGEX_PATTERN, DEFAULT } from "../constants"; import { @@ -26,8 +28,6 @@ import { fetchPasswordPolicy, } from "../actions"; -import { passwordGenerator } from "../helpers/passwordGenerator"; -import { validatePassword } from "../helpers/passwordValidator"; const styles = (theme) => ({ tableTitle: theme.table.title, diff --git a/src/helpers/passwordGenerator.js b/src/helpers/passwordGenerator.js deleted file mode 100644 index 4f3698c..0000000 --- a/src/helpers/passwordGenerator.js +++ /dev/null @@ -1,55 +0,0 @@ -export const passwordGenerator = (options) => { - // This method should not be used to create safe/permament passwords for users - // Passwords should be generated by browsers or password managers - // Only justifiable use-case is creating temporary passwords valid until first login - const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const lowercase = uppercase.toLowerCase(); - const numbers = "0123456789"; - const specialCharacters = "!@#$%^&*()_+-=[]{}|;:,.<>?"; - const length = options?.length ?? 12; - const isNumberRequired = options?.isNumberRequired ?? true; - const isLowerCaseRequired = options?.isLowerCaseRequired ?? true; - const isUpperCaseRequired = options?.isUpperCaseRequired ?? true; - const isSpecialSymbolRequired = options?.isSpecialSymbolRequired ?? true; - - let password = ""; - - const getCategoriesArray = () => { - const availableCategories = []; - if (isLowerCaseRequired) availableCategories.push(lowercase); - if (isUpperCaseRequired) availableCategories.push(uppercase); - if (isNumberRequired) availableCategories.push(numbers); - if (isSpecialSymbolRequired) availableCategories.push(specialCharacters); - return availableCategories; - }; - - const categoriesArray = getCategoriesArray(); - - categoriesArray.forEach((category) => { - password += getRandomOfType(category); - }); - - for (let i = categoriesArray.length; i < length; i++) { - const randomCategory = categoriesArray[getSafeRandomNumberArray(1, categoriesArray.length)]; - password += getRandomOfType(randomCategory); - } - - password = shuffle(password); - - return password; -}; - -function shuffle(str) { - return str.split('').sort(() => getSafeRandomNumberArray(1, 3) - 0.5).join(''); -} - -function getRandomOfType(charset) { - return charset.charAt(getSafeRandomNumberArray(1, charset.length)); -} - -function getSafeRandomNumberArray(length, modulo) { - // crypto.getRandomValues is coded in a way that is cryptographically secure - // do not use Math.Random to generate password - const seedArray = self.crypto.getRandomValues(new Uint32Array(length)); - return Array.from(seedArray, (value) => value % modulo); -} diff --git a/src/helpers/passwordValidator.js b/src/helpers/passwordValidator.js deleted file mode 100644 index e2c3bb3..0000000 --- a/src/helpers/passwordValidator.js +++ /dev/null @@ -1,71 +0,0 @@ -import zxcvbn from 'zxcvbn'; - -export const addSuggestion = (suggestions, condition, message) => { - if (condition) { - suggestions.push(message); - } -}; - -export const generateFeedback = (suggestions, formatMessageWithValues) => { - if (suggestions.length > 0) { - const requirements = suggestions.join(', '); - const formattedMessage = formatMessageWithValues("admin.password.requirements", { requirements }); - return { feedback: formattedMessage, score: 0 }; - } - return null; -}; - -export const validatePassword = (password, passwordPolicy, formatMessage, formatMessageWithValues) => { - if (!passwordPolicy || !password) { - return { feedback: "", score: 0 }; - } - - const jsonPasswordPolicy = JSON.parse(passwordPolicy); - const suggestions = []; - - const { - min_length, - require_lower_case, - require_upper_case, - require_numbers, - require_special_characters - } = jsonPasswordPolicy || {}; - - addSuggestion(suggestions, password.length < min_length, formatMessageWithValues("admin.password.minLength", { count: min_length })); - addSuggestion(suggestions, require_lower_case && !/[a-z]/.test(password), formatMessageWithValues("admin.password.lowerCase", { count: require_lower_case })); - addSuggestion(suggestions, require_upper_case && !/[A-Z]/.test(password), formatMessageWithValues("admin.password.upperCase", { count: require_upper_case })); - addSuggestion(suggestions, require_numbers && !/[0-9]/.test(password), formatMessageWithValues("admin.password.numbers", { count: require_numbers })); - addSuggestion(suggestions, require_special_characters && !/[^a-zA-Z0-9]/.test(password), formatMessageWithValues("admin.password.specialCharacters", { count: require_special_characters })); - - const feedbackResult = generateFeedback(suggestions, formatMessageWithValues); - if (feedbackResult) { - return feedbackResult; - } - - const result = zxcvbn(password); - const feedback = result.feedback.suggestions.join(" ") || formatMessage("admin.password.strong"); - const score = result.score; - - let scoreDescription; - switch (score) { - case 1: - scoreDescription = `${formatMessage("admin.password.weak")}. ${feedback}`; - break; - case 2: - scoreDescription = `${formatMessage("admin.password.medium")}. ${feedback}`; - break; - case 3: - scoreDescription = `${formatMessage("admin.password.strong")}`; - break; - case 4: - scoreDescription = `${formatMessage("admin.password.veryStrong")}`; - break; - default: - scoreDescription = formatMessage("admin.password.unknownScore"); - } - - return { - feedback: scoreDescription, - score, - }; -};