Refactor password update functionality

This commit is contained in:
Douglas Barone 2023-12-18 15:09:36 -04:00
parent e6381d30c4
commit e4dc910865
5 changed files with 87 additions and 30 deletions

View File

@ -39,18 +39,18 @@ async function getUserDN(username: string): Promise<string> {
export async function updatePassword({ export async function updatePassword({
username, username,
password, currentPassword,
newPassword newPassword
}: { }: {
username: string username: string
password: string currentPassword: string
newPassword: string newPassword: string
}): Promise<'SUCCESS' | 'FAIL'> { }): Promise<'SUCCESS' | 'FAIL'> {
try { try {
const userDN = await getUserDN(username) const userDN = await getUserDN(username)
// Check if user can bind with current password // Check if user can bind with current password
await ldapClient.bind(userDN, password) await ldapClient.bind(userDN, currentPassword)
await ldapClient.unbind() await ldapClient.unbind()
// Bind with admin user to change password // Bind with admin user to change password
@ -68,7 +68,7 @@ export async function updatePassword({
// operation: 'delete', // operation: 'delete',
// modification: new Attribute({ // modification: new Attribute({
// type: 'unicodePwd', // type: 'unicodePwd',
// values: [encodePassword(password)] // values: [encodePassword(currentPassword)]
// }) // })
// }), // }),

View File

@ -16,15 +16,16 @@ export const appRouter = router({
updatePassword: input( updatePassword: input(
z.object({ z.object({
username: z.string(), username: z.string(),
password: z.string(), currentPassword: z.string(),
newPassword: z.string().min(8) newPassword: z.string().min(8)
}) })
).mutation(async ({ input }) => { ).mutation(async ({ input }) => {
const { username, password, newPassword } = input const { username, currentPassword, newPassword } = input
try { try {
await updatePassword({ await updatePassword({
username, username,
password, currentPassword,
newPassword newPassword
}) })

View File

@ -1,6 +1,6 @@
<template> <template>
<v-form @submit.prevent="submit"> <v-form ref="form" @submit.prevent="submit" validate-on="input">
<v-card :elevation="2"> <v-card :elevation="2" :loading="loading" :disabled="loading">
<v-card-title class="mb-6 pa-6 text-center"> <v-card-title class="mb-6 pa-6 text-center">
<span class="headline font-weight-light text-h4">Trocar senha</span> <span class="headline font-weight-light text-h4">Trocar senha</span>
</v-card-title> </v-card-title>
@ -13,7 +13,7 @@
autocomplete="username" autocomplete="username"
hint="SIAPE para servidores, CPF para alunos." hint="SIAPE para servidores, CPF para alunos."
prepend-inner-icon="mdi-account" prepend-inner-icon="mdi-account"
:rules="[v => !!v || 'Campo obrigatório']" :rules="[v => !!v || 'O usuário é obrigatório']"
required required
density="compact" density="compact"
/> />
@ -27,7 +27,7 @@
prepend-inner-icon="mdi-form-textbox-password" prepend-inner-icon="mdi-form-textbox-password"
:append-inner-icon="showCurrent ? 'mdi-eye' : 'mdi-eye-off'" :append-inner-icon="showCurrent ? 'mdi-eye' : 'mdi-eye-off'"
@click:append-inner="showCurrent = !showCurrent" @click:append-inner="showCurrent = !showCurrent"
:rules="[v => !!v || 'Campo obrigatório']" :rules="[v => !!v || 'A senha atual é obrigatória']"
required required
density="compact" density="compact"
/> />
@ -42,7 +42,7 @@
prepend-inner-icon="mdi-lock-check" prepend-inner-icon="mdi-lock-check"
:append-inner-icon="showNew ? 'mdi-eye' : 'mdi-eye-off'" :append-inner-icon="showNew ? 'mdi-eye' : 'mdi-eye-off'"
@click:append-inner="showNew = !showNew" @click:append-inner="showNew = !showNew"
:rules="[v => !!v || 'Campo obrigatório']" :rules="newPasswordRules"
required required
density="compact" density="compact"
/> />
@ -56,7 +56,7 @@
:append-inner-icon="showConfirm ? 'mdi-eye' : 'mdi-eye-off'" :append-inner-icon="showConfirm ? 'mdi-eye' : 'mdi-eye-off'"
@click:append-inner="showConfirm = !showConfirm" @click:append-inner="showConfirm = !showConfirm"
:rules="[ :rules="[
v => !!v || 'Campo obrigatório', v => !!v || 'A confirmação da senha é obrigatória',
v => v === newPassword || 'As senhas não coincidem' v => v === newPassword || 'As senhas não coincidem'
]" ]"
required required
@ -66,19 +66,31 @@
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer /> <v-spacer />
<v-btn type="submit" color="primary">Trocar senha</v-btn> <v-btn
type="submit"
color="primary"
variant="flat"
size="large"
:disabled="!valid || loading"
>
Trocar senha
</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-form> </v-form>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref, computed } from 'vue'
import PasswordChecker from './PasswordChecker.vue' import PasswordChecker from './PasswordChecker.vue'
const emit = defineEmits<{ const emit = defineEmits<{
submit: [{ username: string; currentPassword: string; newPassword: string }] submit: [{ username: string; currentPassword: string; newPassword: string }]
}>() }>()
defineProps<{
loading: boolean
}>()
const username = ref('') const username = ref('')
const password = ref('') const password = ref('')
const newPassword = ref('') const newPassword = ref('')
@ -88,7 +100,28 @@ const showCurrent = ref(false)
const showNew = ref(false) const showNew = ref(false)
const showConfirm = ref(false) const showConfirm = ref(false)
const submit = () => { // use form refs to validate
const form = ref<HTMLFormElement | null>(null)
const valid = computed(() => {
return form.value?.isValid || false
})
const newPasswordRules = [
(v: string) => !!v || 'A nova senha é obrigatória',
(v: string) =>
/[a-z]/.test(v) || 'A nova senha deve ter pelo menos uma letra minúscula',
(v: string) =>
/[A-Z]/.test(v) || 'A nova senha deve ter pelo menos uma letra maiúscula',
(v: string) =>
v.length >= 8 || 'A nova senha deve ter pelo menos 8 caracteres',
(v: string) =>
/[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/.test(v) ||
'A nova senha deve ter pelo menos um caractere especial'
]
async function submit() {
if (await form.value?.validate())
emit('submit', { emit('submit', {
username: username.value, username: username.value,
currentPassword: password.value, currentPassword: password.value,

View File

@ -1,15 +1,15 @@
<template> <template>
<div> <div>
<div class="font-weight-light mb-2">Regras para a nova senha:</div>
<v-alert <v-alert
v-for="alert in alerts" v-for="alert in alerts"
:key="alert.text" :key="alert.text"
:type="alertType(password, alert.rule)" :type="alertType(password, alert.rule)"
class="mb-1" class="mb-2 rule"
variant="tonal" variant="tonal"
density="compact" density="compact"
> :text="alert.text"
{{ alert.text }} />
</v-alert>
</div> </div>
</template> </template>
@ -44,3 +44,9 @@ const alerts = [
} }
] ]
</script> </script>
<style scoped>
.rule {
transition: all 0.2s ease-in-out;
}
</style>

View File

@ -1,19 +1,23 @@
<template> <template>
<v-container> <v-container>
<v-row> <v-row justify="center">
<v-col cols="8" offset="2"> <v-col xl="5" lg="6" md="7" sm="10">
<logo class="mx-auto my-3" :style="{ maxWidth: '128px' }" /> <logo class="mx-auto my-3" :style="{ maxWidth: '128px' }" />
<main-form @submit="submit" /> <main-form @submit="handleSubmit" :loading="loading" />
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import Logo from '../components/Logo.vue' import Logo from '../components/Logo.vue'
import MainForm from '../components/MainForm.vue' import MainForm from '../components/MainForm.vue'
import { trpc } from '../trpc'
function submit({ const loading = ref(false)
async function handleSubmit({
username, username,
currentPassword, currentPassword,
newPassword newPassword
@ -22,6 +26,19 @@ function submit({
currentPassword: string currentPassword: string
newPassword: string newPassword: string
}) { }) {
console.log('submit', { username, currentPassword, newPassword }) try {
loading.value = true
await trpc.updatePassword.mutate({
username,
currentPassword,
newPassword
})
} catch (error) {
console.error(error)
} finally {
setTimeout(() => {
loading.value = false
}, 1000)
}
} }
</script> </script>