Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Password validation feature / Fix for 4638 #6154

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "Required field {field}",
"FieldsDoNotMatch": "{field} don't match {field2}",
"ConnectingToServer": "Connecting to server....",
"IncorrectValue": "Incorrect value {field}"
"IncorrectValue": "Incorrect value {field}",
"PasswordLength": "Password must be at least 8 characters long",
"PasswordNumber": "Password must contain at least one number",
"PasswordLowercase": "Password must contain at least one lowercase letter",
"PasswordUppercase": "Password must contain at least one uppercase letter"
},
"string": {
"LogIn": "Log In",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "A message has been sent to your email containing a link to confirm your address.",
"ConfirmationSent2": "Please follow the link to complete your sign up.",
"Slogan": "A unique place to manage all of your work\nWelcome to the Platform",
"ContinueWith": "Continue with {provider}"
"ContinueWith": "Continue with {provider}",
"PasswordSuggestion": "General Password Suggestion for Improved Security",
"PasswordWeak": "Password is Weak",
"PasswordModerate": "Password is Moderate",
"PasswordStrong": "Password is Strong",
"PasswordVeryStrong": "Password is Very Strong"
}
}
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "Campo obligatorio: {field}",
"FieldsDoNotMatch": "{field} no coincide con {field2}",
"ConnectingToServer": "Conectando al servidor...",
"IncorrectValue": "Valor incorrecto para {field}"
"IncorrectValue": "Valor incorrecto para {field}",
"PasswordLength": "La contraseña debe tener al menos 8 caracteres",
"PasswordNumber": "La contraseña debe contener al menos un número",
"PasswordLowercase": "La contraseña debe contener al menos una letra minúscula",
"PasswordUppercase": "La contraseña debe contener al menos una letra mayúscula"
},
"string": {
"LogIn": "Iniciar sesión",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "Se ha enviado un mensaje a su correo electrónico con un enlace para confirmar su dirección.",
"ConfirmationSent2": "Por favor, siga el enlace para completar su registro.",
"Slogan": "Un lugar único para gestionar todo tu trabajo\nBienvenido/a a la Plataforma",
"ContinueWith": "Continuar com {provider}"
"ContinueWith": "Continuar com {provider}",
"PasswordSuggestion": "Sugerencia General de Contraseña para una Mejor Seguridad",
"PasswordWeak": "Débil",
"PasswordModerate": "Moderado",
"PasswordStrong": "Fuerte",
"PasswordVeryStrong": "Muy Fuerte"
}
}
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "Champ requis {field}",
"FieldsDoNotMatch": "{field} ne correspond pas à {field2}",
"ConnectingToServer": "Connexion au serveur...",
"IncorrectValue": "Valeur incorrecte {field}"
"IncorrectValue": "Valeur incorrecte {field}",
"PasswordLength": "Le mot de passe doit comporter au moins 8 caractères",
"PasswordNumber": "Le mot de passe doit contenir au moins un chiffre",
"PasswordLowercase": "Le mot de passe doit contenir au moins une lettre minuscule",
"PasswordUppercase": "Le mot de passe doit contenir au moins une lettre majuscule"
},
"string": {
"LogIn": "Connexion",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "Un message a été envoyé à votre adresse e-mail contenant un lien pour confirmer votre adresse.",
"ConfirmationSent2": "Veuillez suivre le lien pour compléter votre inscription.",
"Slogan": "Un lieu unique pour gérer tout votre travail\nBienvenue sur la plateforme",
"ContinueWith": "Continuer avec {provider}"
"ContinueWith": "Continuer avec {provider}",
"PasswordSuggestion": "Conseil Général pour un Mot de Passe Sécurisé",
"PasswordWeak": "Faible",
"PasswordModerate": "Modéré",
"PasswordStrong": "Fort",
"PasswordVeryStrong": "Très Fort"
}
}
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "Campo obrigatório {field}",
"FieldsDoNotMatch": "{field} não coincidem com {field2}",
"ConnectingToServer": "A ligar ao servidor....",
"IncorrectValue": "Valor incorreto {field}"
"IncorrectValue": "Valor incorreto {field}",
"PasswordLength": "A senha deve ter pelo menos 8 caracteres",
"PasswordNumber": "A senha deve conter pelo menos um número",
"PasswordLowercase": "A senha deve conter pelo menos uma letra minúscula",
"PasswordUppercase": "A senha deve conter pelo menos uma letra maiúscula"
},
"string": {
"LogIn": "Iniciar Sessão",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "Foi enviada uma mensagem para o seu email contendo um link para confirmar o seu endereço.",
"ConfirmationSent2": "Por favor, siga o link para concluir o seu registo.",
"Slogan": "Um local único para gerir todo o seu trabalho\nBem-vindo à Plataforma",
"ContinueWith": "Continuar com {provider}"
"ContinueWith": "Continuar com {provider}",
"PasswordSuggestion": "Sugestão Geral de Senha para Melhor Segurança",
"PasswordWeak": "Fraca",
"PasswordModerate": "Moderada",
"PasswordStrong": "Forte",
"PasswordVeryStrong": "Muito Forte"
}
}
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "Требуется заполнить {field}",
"FieldsDoNotMatch": "{field} не совпадает {field2}",
"ConnectingToServer": "Подключение к серверу....",
"IncorrectValue": "Неправильное значение {field}"
"IncorrectValue": "Неправильное значение {field}",
"PasswordLength": "Пароль должен быть длиной не менее 8 символов",
"PasswordNumber": "Пароль должен содержать хотя бы одну цифру",
"PasswordLowercase": "Пароль должен содержать хотя бы одну строчную букву",
"PasswordUppercase": "Пароль должен содержать хотя бы одну заглавную букву"
},
"string": {
"LogIn": "Вход",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "На Вашу почту отправлено сообщение, c ссылкой для подтверждения email.",
"ConfirmationSent2": "Пожалуйста, перейдите по ссылке для завершения регистрации.",
"Slogan": "Уникальное место для организации всей вашей работы\nДобро пожаловать в Платформу",
"ContinueWith": "Войти через {provider}"
"ContinueWith": "Войти через {provider}",
"PasswordSuggestion": "Общий Совет по Паролям для Улучшения Безопасности",
"PasswordWeak": "Слабый пароль",
"PasswordModerate": "Умеренный пароль",
"PasswordStrong": "Надежный пароль",
"PasswordVeryStrong": "Очень Надежный пароль"
}
}
13 changes: 11 additions & 2 deletions plugins/login-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"RequiredField": "必填字段 {field}",
"FieldsDoNotMatch": "{field} 与 {field2} 不匹配",
"ConnectingToServer": "正在连接服务器....",
"IncorrectValue": "不正确的值 {field}"
"IncorrectValue": "不正确的值 {field}",
"PasswordLength": "密码长度必须至少为8个字符",
"PasswordNumber": "密码必须包含至少一个数字",
"PasswordLowercase": "密码必须包含至少一个小写字母",
"PasswordUppercase": "密码必须包含至少一个大写字母"
},
"string": {
"LogIn": "登录",
Expand Down Expand Up @@ -42,6 +46,11 @@
"ConfirmationSent": "已发送确认邮件到您的邮箱,包含一个确认地址的链接。",
"ConfirmationSent2": "请点击链接完成注册。",
"Slogan": "一个独特的地方来管理你所有的工作\n欢迎来到平台",
"ContinueWith": "继续使用 {provider}"
"ContinueWith": "继续使用 {provider}",
"PasswordSuggestion": "提高安全性的密码一般建议",
"PasswordWeak": "弱",
"PasswordModerate": "中等",
"PasswordStrong": "强",
"PasswordVeryStrong": "非常强"
}
}
124 changes: 120 additions & 4 deletions plugins/login-resources/src/components/Form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
deviceOptionsStore as deviceInfo,
getCurrentLocation,
navigate,
themeStore
themeStore, Popup
} from '@hcengineering/ui'
import StatusControl from './StatusControl.svelte'

Expand All @@ -45,6 +45,7 @@
rule: RegExp
notMatch: boolean
ruleDescr: IntlString
disabled: boolean
}[]
}

Expand All @@ -67,6 +68,8 @@

$: $themeStore.language && validate($themeStore.language)

let passwordStrength: string = '';

const validate = makeSequential(async function validateAsync (language: string): Promise<boolean> {
if (ignoreInitialValidation) return true
for (const field of fields) {
Expand Down Expand Up @@ -94,8 +97,15 @@
if (f.rules !== undefined) {
for (const rule of f.rules) {
if (rule.rule.test(v) === rule.notMatch) {
status = new Status(Severity.INFO, rule.ruleDescr, {})
return false
const passwordField = fields.find(f => f.name === 'password');

if (passwordField) {
const password = object[passwordField.name] as string;
passwordStrength = calculatePasswordStrength(password);
}

status = OK
return true
}
}
}
Expand All @@ -105,6 +115,8 @@
})
validate($themeStore.language)

let contentPanelElement: HTMLElement | undefined;

let inAction = false

function performAction (action: Action): void {
Expand All @@ -129,6 +141,44 @@
navigate(loc)
}
$: loginState = caption === login.string.LogIn ? 'login' : caption === login.string.SignUp ? 'signup' : 'none'

let rulesList: { description: string }[] = []

async function fetchRules() {
const language = $themeStore.language
const rulePromises = fields.flatMap(field =>
field.rules ? field.rules.filter(rule => !rule.disabled).map(rule =>
translate(rule.ruleDescr, {}, language).then(description => ({
description
}))
) : []
);
rulesList = await Promise.all(rulePromises)
}

function calculatePasswordStrength(password: string): string {
if(caption === login.string.SignUp){
const passwordValidationRules = fields.find(f => f.rules);
const satisfiedRulesCount = passwordValidationRules.rules.filter(rule => !rule.disabled && rule.rule.test(password) !== rule.notMatch).length;

if (satisfiedRulesCount <= 1) {
return login.string.PasswordWeak;
} else if (satisfiedRulesCount === 2) {
return login.string.PasswordModerate;
} else if(satisfiedRulesCount === 3) {
return login.string.PasswordStrong;
}else if(satisfiedRulesCount === 4) {
return login.string.PasswordVeryStrong;
} else {
return ''
}
}

}

$:$themeStore.language, fetchRules()

let isVisible : boolean = false
</script>

<form
Expand Down Expand Up @@ -188,14 +238,45 @@
name={field.id}
password={field.password}
bind:value={object[field.name]}
on:input={() => validate($themeStore.language)}
on:input={() => {
if(field.name === 'password'){
isVisible = object[field.name] !== ''
}

validate($themeStore.language);
if (field.password) {

passwordStrength = calculatePasswordStrength(object[field.name]);

}
}}
on:blur={() => {
trim(field.name)
}}
/>
</div>
{/each}

<Popup contentPanel={contentPanelElement} />



{#if isVisible && caption === login.string.SignUp}

<div class={'password-strength'}>
<Label label={passwordStrength}/>
</div>

<div class={'password-suggestion-wrapper'}>
<div class={'password-suggestion'}><Label label={login.string.PasswordSuggestion}/></div>
<div bind:this={contentPanelElement} class='info-text' >
{#each rulesList as rule}
<div><Label label={rule.description}/></div>
{/each}
</div>
</div>
{/if}

<div class="status">
<StatusControl {status} />
</div>
Expand Down Expand Up @@ -292,6 +373,8 @@
column-gap: 0.75rem;
row-gap: 1.5rem;
margin-top: 1.5rem;
position: relative;


.form-row {
grid-column-start: 1;
Expand Down Expand Up @@ -324,5 +407,38 @@
color: var(--theme-content-color);
}
}

.info-text {
font-size: 0.75rem;
color: var(--theme-info-color);
padding: 0.875rem 1.25rem;
width: 100%;
grid-column: 1 / span 2;
background-color: var(--theme-button-default);
border: 1px solid var(--theme-button-border);
border-radius: 0.75rem;
caret-color: var(--theme-caret-color);
}

.password-strength{
font-size: 0.75rem;
color: var(--theme-info-color);
background-color: var(--theme-info-bg-color);
position:absolute;
top: 13rem;
padding-left: 1.25rem;
}

.password-suggestion-wrapper{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
grid-column: 1 / span 2;
}

.password-suggestion{
margin-bottom: 0.5rem;
}
}
</style>
29 changes: 28 additions & 1 deletion plugins/login-resources/src/components/SignupForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,38 @@
import { goTo, signUp } from '../utils'
import Form from './Form.svelte'

const passwordValidationRules = [
{
rule: /.{8,}/,
notMatch: false,
ruleDescr: login.status.PasswordLength,
disabled: true
},
{
rule: /[0-9]/,
notMatch: false,
ruleDescr: login.status.PasswordNumber,
disabled: false
},
{
rule: /[a-z]/,
notMatch: false,
ruleDescr: login.status.PasswordLowercase,
disabled: false
},
{
rule: /[A-Z]/,
notMatch: false,
ruleDescr: login.status.PasswordUppercase,
disabled: false
}
]

const fields = [
{ id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true },
{ id: 'family-name', name: 'last', i18n: login.string.LastName, short: true },
{ id: 'email', name: 'username', i18n: login.string.Email },
{ id: 'new-password', name: 'password', i18n: login.string.Password, password: true },
{ id: 'new-password', name: 'password', i18n: login.string.Password, password: true, rules: passwordValidationRules },
{ id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true }
]

Expand Down
Loading