From e9af665c20d467064e512dafedc0615056c78ed3 Mon Sep 17 00:00:00 2001 From: Carlo <5310264+thecarlo@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:09:16 +1000 Subject: [PATCH] password strength indicator (#7) --- .../GeneratePassword/index.test.tsx | 2 +- src/components/GeneratePassword/index.tsx | 79 ++++++++++++++++++- src/enums/cssClassType.ts | 4 + .../getClassByStrength.ts | 25 ++++++ src/functions/crackTimes.ts | 43 ++++++++++ src/functions/estimateCrackTime.ts | 31 ++++++++ src/interfaces/crackTime.ts | 5 ++ 7 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/enums/cssClassType.ts create mode 100644 src/functions/checkPasswordStrength/getClassByStrength.ts create mode 100644 src/functions/crackTimes.ts create mode 100644 src/functions/estimateCrackTime.ts create mode 100644 src/interfaces/crackTime.ts diff --git a/src/components/GeneratePassword/index.test.tsx b/src/components/GeneratePassword/index.test.tsx index 9f30f86..f1328a9 100644 --- a/src/components/GeneratePassword/index.test.tsx +++ b/src/components/GeneratePassword/index.test.tsx @@ -11,7 +11,7 @@ afterEach(cleanup); describe('GeneratePassword', () => { const passphraseDefaultLength = 3; - const passwordDefaultLength = 15; + const passwordDefaultLength = 12; it('should render the Generate Button', () => { render( diff --git a/src/components/GeneratePassword/index.tsx b/src/components/GeneratePassword/index.tsx index 5a4e486..50d6405 100644 --- a/src/components/GeneratePassword/index.tsx +++ b/src/components/GeneratePassword/index.tsx @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/indent */ import React, { useEffect, useRef, useState } from 'react'; +import { CssClassType } from '@enums/cssClassType'; import { checkPasswordStrength } from '@functions/checkPasswordStrength'; +import { getClassByStrength } from '@functions/checkPasswordStrength/getClassByStrength'; import { getClassNameByStrength } from '@functions/checkPasswordStrength/getClassnameByStrength'; +import { estimateCrackTime } from '@functions/estimateCrackTime'; import { PasswordMode } from '../../enums/passwordMode'; import { randomPassword } from '../../functions/randomPassword'; @@ -15,7 +18,7 @@ export const GeneratePassword = (props: GeneratePasswordProps) => { const passphraseDefaultLength = 3; - const passwordDefaultLength = 15; + const passwordDefaultLength = 12; const defaultLength = mode === PasswordMode.Password @@ -265,7 +268,7 @@ export const GeneratePassword = (props: GeneratePasswordProps) => { ) => { @@ -312,6 +315,78 @@ export const GeneratePassword = (props: GeneratePasswordProps) => { + +
+ {/*

Password Strength

*/} +
+ + + + + + + +
+

+ {passwordStrength} +

+ +

Strength

+
+
+ +
+ + + + + + +
+

+ {estimateCrackTime(password)} +

+

Time to crack password

+
+
+
); }; diff --git a/src/enums/cssClassType.ts b/src/enums/cssClassType.ts new file mode 100644 index 0000000..536ce1b --- /dev/null +++ b/src/enums/cssClassType.ts @@ -0,0 +1,4 @@ +export const enum CssClassType { + Text = 'Text', + Background = 'Background', +} diff --git a/src/functions/checkPasswordStrength/getClassByStrength.ts b/src/functions/checkPasswordStrength/getClassByStrength.ts new file mode 100644 index 0000000..cf9667b --- /dev/null +++ b/src/functions/checkPasswordStrength/getClassByStrength.ts @@ -0,0 +1,25 @@ +import { CssClassType } from '@enums/cssClassType'; + +export const getClassByStrength = ( + strength: string, + classType: CssClassType +): string => { + const cssPrefix = classType === CssClassType.Text ? 'text' : 'bg'; + + switch (strength) { + case 'Weak': + return `${cssPrefix}-red-500`; + + case 'Moderate': + return `${cssPrefix}-yellow-500`; + + case 'Strong': + return `${cssPrefix}-green-500`; + + case 'Very Strong': + return `${cssPrefix}-green-500`; + + default: + return `${cssPrefix}-red-500`; + } +}; diff --git a/src/functions/crackTimes.ts b/src/functions/crackTimes.ts new file mode 100644 index 0000000..7389176 --- /dev/null +++ b/src/functions/crackTimes.ts @@ -0,0 +1,43 @@ +import { CrackTime } from '@interfaces/crackTime'; + +export const crackTimes: Record = { + 8: { + allChars: '28 seconds', + onlyAlphaNum: '2 minutes', + onlyAlpha: '5 minutes', + }, + 9: { allChars: '6 hours', onlyAlphaNum: '2 hours', onlyAlpha: '24 minutes' }, + 10: { allChars: '2 weeks', onlyAlphaNum: '5 days', onlyAlpha: '21 hours' }, + 11: { allChars: '3 years', onlyAlphaNum: '10 months', onlyAlpha: '1 month' }, + 12: { allChars: '226 years', onlyAlphaNum: '53 years', onlyAlpha: '6 years' }, + 13: { + allChars: '15 thousand years', + onlyAlphaNum: '3 thousand years', + onlyAlpha: '332 years', + }, + 14: { + allChars: '1 million years', + onlyAlphaNum: '200 thousand years', + onlyAlpha: '17 thousand years', + }, + 15: { + allChars: '77 million years', + onlyAlphaNum: '12 million years', + onlyAlpha: '900 thousand years', + }, + 16: { + allChars: '5 billion years', + onlyAlphaNum: '779 million years', + onlyAlpha: '46 million years', + }, + 17: { + allChars: '380 billion years', + onlyAlphaNum: '48 billion years', + onlyAlpha: '2 billion years', + }, + 18: { + allChars: '26 trillion years', + onlyAlphaNum: '2 trillion years', + onlyAlpha: '126 billion years', + }, +}; diff --git a/src/functions/estimateCrackTime.ts b/src/functions/estimateCrackTime.ts new file mode 100644 index 0000000..80925eb --- /dev/null +++ b/src/functions/estimateCrackTime.ts @@ -0,0 +1,31 @@ +import { CrackTime } from '@interfaces/crackTime'; + +import { crackTimes } from './crackTimes'; + +export const estimateCrackTime = (password: string): string => { + const hasUpper = /[A-Z]/.test(password); + + const hasLower = /[a-z]/.test(password); + + const hasNumbers = /[0-9]/.test(password); + + const hasSpecial = /[!@#$%^&*()_+\-=[\]{}|;:'",.<>?]/.test(password); + + let key: keyof CrackTime = 'onlyAlpha'; + + if (hasUpper && hasLower && hasNumbers && hasSpecial) { + key = 'allChars'; + } else if (hasUpper && hasLower && hasNumbers) { + key = 'onlyAlphaNum'; + } + + const length = password.length; + + if (length > 18) { + return 'Trillions of years'; + } else if (length >= 8 && crackTimes[length]) { + return crackTimes[length][key]; + } + + return 'Not specified'; +}; diff --git a/src/interfaces/crackTime.ts b/src/interfaces/crackTime.ts new file mode 100644 index 0000000..da1ff0a --- /dev/null +++ b/src/interfaces/crackTime.ts @@ -0,0 +1,5 @@ +export interface CrackTime { + allChars: string; + onlyAlphaNum: string; + onlyAlpha: string; +}