- add password strength meter for creating or editing user passwords
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m0s
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m0s
- add public opensearch api host
This commit is contained in:
parent
f4854d70b9
commit
010bead723
13 changed files with 392 additions and 23 deletions
|
@ -0,0 +1,9 @@
|
|||
export default class TrieNode {
|
||||
children: { [key: string]: TrieNode };
|
||||
isEndOfWord: boolean;
|
||||
|
||||
constructor() {
|
||||
this.children = {};
|
||||
this.isEndOfWord = false;
|
||||
}
|
||||
}
|
30
resources/js/Components/SimplePasswordMeter/logic/Trie.ts
Normal file
30
resources/js/Components/SimplePasswordMeter/logic/Trie.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import TrieNode from './TieNode';
|
||||
|
||||
export default class Trie {
|
||||
private root: TrieNode;
|
||||
constructor() {
|
||||
this.root = new TrieNode();
|
||||
}
|
||||
|
||||
insert(word: string) {
|
||||
let node: TrieNode = this.root;
|
||||
for (let char of word) {
|
||||
if (!node.children[char]) {
|
||||
node.children[char] = new TrieNode();
|
||||
}
|
||||
node = node.children[char];
|
||||
}
|
||||
node.isEndOfWord = true;
|
||||
}
|
||||
|
||||
search(word: string) {
|
||||
let node = this.root;
|
||||
for (let char of word) {
|
||||
if (!node.children[char]) {
|
||||
return false;
|
||||
}
|
||||
node = node.children[char];
|
||||
}
|
||||
return node.isEndOfWord;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import commonPasswords from '../data/commonPasswords';
|
||||
import Trie from './Trie';
|
||||
|
||||
|
||||
|
||||
const checkStrength = (pass: string) => {
|
||||
const score = scorePassword(pass);
|
||||
const scoreLabel = mapScoreToLabel(score);
|
||||
return {
|
||||
score,
|
||||
scoreLabel
|
||||
}
|
||||
};
|
||||
|
||||
export default checkStrength;
|
||||
|
||||
// Function to score the password based on different criteria
|
||||
const scorePassword = (password: string): number => {
|
||||
if (password.length <= 6) return 0;
|
||||
if (isCommonPassword(password)) return 0;
|
||||
|
||||
let score = 0;
|
||||
score += getLengthScore(password);
|
||||
score += getSpecialCharScore(password);
|
||||
score += getCaseMixScore(password);
|
||||
score += getNumberMixScore(password);
|
||||
|
||||
return Math.min(score, 4); // Maximum score is 4
|
||||
};
|
||||
|
||||
// Initialize the Trie with common passwords
|
||||
const trie = new Trie();
|
||||
commonPasswords.forEach(password => trie.insert(password));
|
||||
const isCommonPassword = (password: string): boolean => {
|
||||
// return commonPasswords.includes(password);
|
||||
return trie.search(password);
|
||||
};
|
||||
|
||||
// Function to get the score based on password length
|
||||
const getLengthScore = (password: string): number => {
|
||||
if (password.length > 20 && !hasRepeatChars(password)) return 3;
|
||||
if (password.length > 12 && !hasRepeatChars(password)) return 2;
|
||||
if (password.length > 8) return 1;
|
||||
return 0;
|
||||
};
|
||||
// Function to check if the password contains repeated characters
|
||||
const hasRepeatChars = (password: string): boolean => {
|
||||
const repeatCharRegex = /(\w)(\1+\1+\1+\1+)/g;
|
||||
return repeatCharRegex.test(password);
|
||||
};
|
||||
|
||||
// Function to get the score based on the presence of special characters
|
||||
const getSpecialCharScore = (password: string): number => {
|
||||
const specialCharRegex = /[^A-Za-z0-9]/g;
|
||||
return specialCharRegex.test(password) ? 1 : 0;
|
||||
};
|
||||
|
||||
// Function to get the score based on the mix of uppercase and lowercase letters
|
||||
const getCaseMixScore = (password: string): number => {
|
||||
const hasUpperCase = /[A-Z]/.test(password);
|
||||
const hasLowerCase = /[a-z]/.test(password);
|
||||
return hasUpperCase && hasLowerCase ? 1 : 0;
|
||||
};
|
||||
|
||||
// Function to get the score based on the mix of letters and numbers
|
||||
const getNumberMixScore = (password: string): number => {
|
||||
const hasLetter = /[A-Za-z]/.test(password);
|
||||
const hasNumber = /[0-9]/.test(password);
|
||||
return hasLetter && hasNumber ? 1 : 0;
|
||||
};
|
||||
|
||||
// Function to map the score to a corresponding label
|
||||
const mapScoreToLabel = (score: number): string => {
|
||||
const labels = ['risky', 'guessable', 'weak', 'safe', 'secure'];
|
||||
return labels[score] || '';
|
||||
};
|
||||
|
||||
// const nameScore = (score: number): string => {
|
||||
// switch (score) {
|
||||
// case 0:
|
||||
// return 'risky';
|
||||
// case 1:
|
||||
// return 'guessable';
|
||||
// case 2:
|
||||
// return 'weak';
|
||||
// case 3:
|
||||
// return 'safe';
|
||||
// case 4:
|
||||
// return 'secure';
|
||||
// default:
|
||||
// return '';
|
||||
// }
|
||||
// };
|
|
@ -0,0 +1,5 @@
|
|||
// import scorePassword from './scorePassword'
|
||||
// import nameScore from './nameScore'
|
||||
import checkStrength from './checkStrength'
|
||||
|
||||
export { checkStrength }
|
|
@ -0,0 +1,66 @@
|
|||
// import { isCommonPassword } from "./isCommonPassword"
|
||||
import commonPasswords from '../data/commonPasswords';
|
||||
|
||||
const scorePassword = (pass: string): number => {
|
||||
let score = 0;
|
||||
let length = 0;
|
||||
let specialChar = 0;
|
||||
let caseMix = 0;
|
||||
let numCharMix = 0;
|
||||
|
||||
const specialCharRegex = /[^A-Za-z0-9]/g;
|
||||
const lowercaseRegex = /(.*[a-z].*)/g;
|
||||
const uppercaseRegex = /(.*[A-Z].*)/g;
|
||||
const numberRegex = /(.*[0-9].*)/g;
|
||||
const repeatCharRegex = /(\w)(\1+\1+\1+\1+)/g;
|
||||
|
||||
const hasSpecialChar = specialCharRegex.test(pass);
|
||||
const hasLowerCase = lowercaseRegex.test(pass);
|
||||
const hasUpperCase = uppercaseRegex.test(pass);
|
||||
const hasNumber = numberRegex.test(pass);
|
||||
const hasRepeatChars = repeatCharRegex.test(pass);
|
||||
|
||||
if (pass.length > 4) {
|
||||
if (isCommonPassword(pass)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((hasLowerCase || hasUpperCase) && hasNumber) {
|
||||
numCharMix = 1;
|
||||
}
|
||||
|
||||
if (hasUpperCase && hasLowerCase) {
|
||||
caseMix = 1;
|
||||
}
|
||||
|
||||
if ((hasLowerCase || hasUpperCase || hasNumber) && hasSpecialChar) {
|
||||
specialChar = 1;
|
||||
}
|
||||
|
||||
if (pass.length > 8) {
|
||||
length = 1;
|
||||
}
|
||||
|
||||
if (pass.length > 12 && !hasRepeatChars) {
|
||||
length = 2;
|
||||
}
|
||||
|
||||
if (pass.length > 20 && !hasRepeatChars) {
|
||||
length = 3;
|
||||
}
|
||||
|
||||
score = length + specialChar + caseMix + numCharMix;
|
||||
|
||||
if (score > 4) {
|
||||
score = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
export default scorePassword;
|
||||
|
||||
const isCommonPassword = (password: string): boolean => {
|
||||
return commonPasswords.includes(password);
|
||||
};
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue