mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-04-22 16:17:23 +08:00
fix(web): require password confirmation in auth forms
Require users to enter new passwords twice in the registration and password change forms so typos are caught before credentials are stored.
This commit is contained in:
@@ -366,6 +366,7 @@ web:
|
||||
password_empty: 密码不能为空
|
||||
password_min_length: 密码至少需要 8 位
|
||||
password_too_weak: 密码强度不足
|
||||
password_mismatch: 两次输入的密码不一致
|
||||
password_strength_hint: 密码至少 8 位,且需包含大小写字母、数字、特殊字符中的至少 2 类
|
||||
enable: 开启
|
||||
disable: 关闭
|
||||
|
||||
@@ -366,6 +366,7 @@ web:
|
||||
password_empty: Password cannot be empty
|
||||
password_min_length: Password must be at least 8 characters long
|
||||
password_too_weak: Password is too weak
|
||||
password_mismatch: Passwords do not match
|
||||
password_strength_hint: Password must be at least 8 characters and include at least 2 of uppercase letters, lowercase letters, numbers, or special characters
|
||||
enable: Enable
|
||||
disable: Disable
|
||||
|
||||
@@ -13,10 +13,12 @@ const dialogRef = inject<any>('dialogRef');
|
||||
const api = computed<ApiClient>(() => dialogRef.value.data.api);
|
||||
|
||||
const password = ref('');
|
||||
const confirmPassword = ref('');
|
||||
const toast = useToast();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const passwordValidation = computed(() => validatePasswordStrength(password.value));
|
||||
const passwordMatches = computed(() => password.value === confirmPassword.value);
|
||||
const passwordErrorMessage = computed(() => {
|
||||
if (password.value.length === 0 || passwordValidation.value.valid) {
|
||||
return '';
|
||||
@@ -24,6 +26,14 @@ const passwordErrorMessage = computed(() => {
|
||||
|
||||
return t(passwordValidation.value.reasonKey!);
|
||||
});
|
||||
const confirmPasswordErrorMessage = computed(() => {
|
||||
if (confirmPassword.value.length === 0 || passwordMatches.value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return t('web.common.password_mismatch');
|
||||
});
|
||||
const canSubmit = computed(() => passwordValidation.value.valid && passwordMatches.value);
|
||||
|
||||
const changePassword = async () => {
|
||||
if (!passwordValidation.value.valid) {
|
||||
@@ -36,6 +46,16 @@ const changePassword = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!passwordMatches.value) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: t('web.common.warning'),
|
||||
detail: t('web.common.password_mismatch'),
|
||||
life: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.value.change_password(password.value);
|
||||
toast.add({
|
||||
@@ -69,14 +89,19 @@ const changePassword = async () => {
|
||||
<div class="flex flex-col space-y-4">
|
||||
<Password v-model="password" :placeholder="t('web.settings.new_password')" :feedback="false"
|
||||
toggleMask />
|
||||
<Password v-model="confirmPassword" :placeholder="t('web.settings.confirm_password')"
|
||||
:feedback="false" toggleMask />
|
||||
<small class="text-surface-500 dark:text-surface-400">
|
||||
{{ t('web.common.password_strength_hint') }}
|
||||
</small>
|
||||
<small v-if="passwordErrorMessage" class="text-red-500 dark:text-red-400">
|
||||
{{ passwordErrorMessage }}
|
||||
</small>
|
||||
<small v-if="confirmPasswordErrorMessage" class="text-red-500 dark:text-red-400">
|
||||
{{ confirmPasswordErrorMessage }}
|
||||
</small>
|
||||
<Button @click="changePassword" :label="t('web.common.confirm')"
|
||||
:disabled="!passwordValidation.valid" />
|
||||
:disabled="!canSubmit" />
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -24,9 +24,11 @@ const username = ref('');
|
||||
const password = ref('');
|
||||
const registerUsername = ref('');
|
||||
const registerPassword = ref('');
|
||||
const registerConfirmPassword = ref('');
|
||||
const captcha = ref('');
|
||||
const captchaSrc = computed(() => api.value.captcha_url());
|
||||
const registerPasswordValidation = computed(() => validatePasswordStrength(registerPassword.value));
|
||||
const registerPasswordsMatch = computed(() => registerPassword.value === registerConfirmPassword.value);
|
||||
const registerPasswordErrorMessage = computed(() => {
|
||||
if (registerPassword.value.length === 0 || registerPasswordValidation.value.valid) {
|
||||
return '';
|
||||
@@ -34,6 +36,14 @@ const registerPasswordErrorMessage = computed(() => {
|
||||
|
||||
return t(registerPasswordValidation.value.reasonKey!);
|
||||
});
|
||||
const registerConfirmPasswordErrorMessage = computed(() => {
|
||||
if (registerConfirmPassword.value.length === 0 || registerPasswordsMatch.value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return t('web.common.password_mismatch');
|
||||
});
|
||||
const canRegister = computed(() => registerPasswordValidation.value.valid && registerPasswordsMatch.value);
|
||||
|
||||
|
||||
const onSubmit = async () => {
|
||||
@@ -64,6 +74,16 @@ const onRegister = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!registerPasswordsMatch.value) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: t('web.common.warning'),
|
||||
detail: t('web.common.password_mismatch'),
|
||||
life: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
saveApiHost(apiHost.value);
|
||||
const credential: Credential = { username: registerUsername.value, password: registerPassword.value };
|
||||
const registerReq: RegisterData = { credentials: credential, captcha: captcha.value };
|
||||
@@ -184,6 +204,17 @@ onBeforeUnmount(() => {
|
||||
{{ registerPasswordErrorMessage }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="p-field">
|
||||
<label for="register-confirm-password" class="block text-sm font-medium">
|
||||
{{ t('web.settings.confirm_password') }}
|
||||
</label>
|
||||
<Password id="register-confirm-password" v-model="registerConfirmPassword" required toggleMask
|
||||
:feedback="false" class="w-full" />
|
||||
<small v-if="registerConfirmPasswordErrorMessage"
|
||||
class="block text-red-500 dark:text-red-400">
|
||||
{{ registerConfirmPasswordErrorMessage }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="p-field">
|
||||
<label for="captcha" class="block text-sm font-medium">{{ t('web.login.captcha') }}</label>
|
||||
<InputText id="captcha" v-model="captcha" required class="w-full" />
|
||||
@@ -191,7 +222,7 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<Button :label="t('web.login.register')" type="submit" class="w-full"
|
||||
:disabled="!registerPasswordValidation.valid" />
|
||||
:disabled="!canRegister" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<Button :label="t('web.login.back_to_login')" type="button" class="w-full"
|
||||
|
||||
Reference in New Issue
Block a user