Skip to content

Commit

Permalink
feat: added in-page validation for password
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaile committed Nov 6, 2023
1 parent 7793bdd commit c2b0cf1
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 23 deletions.
17 changes: 13 additions & 4 deletions src/i18n/en-US.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"nethvoice": "Nethvoice CTI",
"nethservice": "Nethservice COLLABORATION",
"errors": {
"connection_aborted": "Couldn't connect to the server",
"network": "Network error"
Expand All @@ -19,8 +21,6 @@
"title": "Account settings",
"change_password": "Change password",
"change_password_description": "Setting a new password for your account will affect all connected services:",
"nethvoice": "Nethvoice CTI",
"nethservice": "Nethservice COLLABORATION",
"current_password": "Current password",
"new_password": "New password",
"confirm_password": "Confirm new password",
Expand All @@ -29,10 +29,19 @@
"invalid_credentials": "Wrong password",
"password_minimum_age": "Too little time has passed since the last password change",
"password_complexity": "Password is too simple",
"password_length": "Password too short",
"password_length": "Password must be at least {count} characters long",
"password_uppercase": "Password must at least have a uppercase character | Password must at least have {count} uppercase characters",
"password_lowercase": "Password must at least have a lowercase character | Password must at least have {count} lowercase characters",
"password_number": "Password must at least have a number | Password must at least have {count} numbers",
"password_special": "Password must at least have a special character | Password must at least have {count} special characters",
"password_history": "Password already used",
"generic_error": "Can't change password right now, please try again later",
"password_changed": "Password changed",
"password_changed_description": "The password has been changed successfully"
"password_changed_description": "The password has been changed successfully",
"minimum_characters": "Minimum {count} characters",
"minimum_uppercase": "At least {count} uppercase (A-Z)",
"minimum_lowercase": "At least {count} lowercase (a-z)",
"minimum_number": "At least {count} number (0-9) | At least {count} numbers (0-9)",
"minimum_special": "At least {count} special character (!{'@'}#^&_?-) | At least {count} special characters (!{'@'}#^&_?-)"
}
}
80 changes: 61 additions & 19 deletions src/views/UserAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ const oldPassword = ref('')
const newPassword = ref('')
const confirmPassword = ref('')
// TODO: replace with product list from server
const products = ref(['nethvoice', 'nethservice'])
// TODO: replace with password policy from server
const minimumPasswordLength = ref(8)
const minimumUppercaseCharacters = ref(1)
const minimumLowercaseCharacters = ref(1)
const minimumNumberCharacters = ref(1)
const minimumSpecialCharacters = ref(1)
const loading = ref(false)
const errorMessage = ref<string>()
const validationMessages = ref(new MessageBag())
Expand All @@ -33,6 +42,30 @@ function validate(): boolean {
if (newPassword.value != confirmPassword.value) {
validationMessages.value.append('confirm_password', t('account_settings.passwords_mismatch'))
}
if (!newPassword.value.match(`(?=(?:.*[A-Z]){${minimumUppercaseCharacters.value},})`)) {
validationMessages.value.append(
'new_password',
t('account_settings.password_uppercase', minimumUppercaseCharacters.value)
)
}
if (!newPassword.value.match(`(?=(?:.*[a-z]){${minimumLowercaseCharacters.value},})`)) {
validationMessages.value.append(
'new_password',
t('account_settings.password_lowercase', minimumLowercaseCharacters.value)
)
}
if (!newPassword.value.match(`(?=(?:.*[0-9]){${minimumNumberCharacters.value},})`)) {
validationMessages.value.append(
'new_password',
t('account_settings.password_number', minimumNumberCharacters.value)
)
}
if (!newPassword.value.match(`(?=(?:.*[^A-Za-z0-9]){${minimumSpecialCharacters.value},})`)) {
validationMessages.value.append(
'new_password',
t('account_settings.password_special', minimumSpecialCharacters.value)
)
}
return validationMessages.value.size < 1
}
Expand Down Expand Up @@ -69,7 +102,10 @@ async function changePassword() {
)
break
case 'error_password_length':
validationMessages.value.append('new_password', t('account_settings.password_length'))
validationMessages.value.append(
'new_password',
t('account_settings.password_length', minimumPasswordLength.value)
)
break
case 'error_password_history':
validationMessages.value.append('new_password', t('account_settings.password_history'))
Expand All @@ -81,7 +117,7 @@ async function changePassword() {
)
break
default:
errorMessage.value = 'account_settings.generic_error'
errorMessage.value = t('account_settings.generic_error')
}
}
}
Expand Down Expand Up @@ -110,16 +146,13 @@ async function changePassword() {
</h3>
<p class="description-text">{{ $t('account_settings.change_password_description') }}</p>
<ul class="description-text list-disc pl-6">
<li>{{ $t('account_settings.nethvoice') }}</li>
<li>{{ $t('account_settings.nethservice') }}</li>
<li v-for="(product, key) in products" :key="key">
{{ $t(product) }}
</li>
</ul>
</div>
<form class="flex flex-col gap-y-8" @submit.prevent="changePassword()">
<NeInlineNotification
v-if="errorMessage"
:title="$t(`errors.${errorMessage}`)"
kind="error"
/>
<NeInlineNotification v-if="errorMessage" :title="errorMessage" kind="error" />
<!-- This helps autocompletion -->
<NeTextInput :value="uid" autocomplete="username" class="hidden" />
<NeTextInput
Expand All @@ -133,16 +166,25 @@ async function changePassword() {
name="old_password"
required
/>
<NeTextInput
v-model="newPassword"
:disabled="loading"
:invalid-message="validationMessages.getFirstMessage('new_password')"
:label="$t('account_settings.new_password')"
autocomplete="new-password"
is-password
name="new_password"
required
/>
<div class="space-y-4">
<NeTextInput
v-model="newPassword"
:disabled="loading"
:invalid-message="validationMessages.getFirstMessage('new_password')"
:label="$t('account_settings.new_password')"
autocomplete="new-password"
is-password
name="new_password"
required
/>
<ul class="description-text ml-6 list-disc">
<li>{{ $t('account_settings.minimum_characters', minimumPasswordLength) }}</li>
<li>{{ $t('account_settings.minimum_uppercase', minimumUppercaseCharacters) }}</li>
<li>{{ $t('account_settings.minimum_lowercase', minimumLowercaseCharacters) }}</li>
<li>{{ $t('account_settings.minimum_number', minimumNumberCharacters) }}</li>
<li>{{ $t('account_settings.minimum_special', minimumSpecialCharacters) }}</li>
</ul>
</div>
<NeTextInput
v-model="confirmPassword"
:disabled="loading"
Expand Down

0 comments on commit c2b0cf1

Please sign in to comment.