mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-11-11 08:43:55 +01:00
Imported to main repo
This commit is contained in:
@@ -3,5 +3,17 @@ import repository from './repository'
|
||||
export default {
|
||||
requestToken (data) {
|
||||
return repository.post('/token/', data)
|
||||
},
|
||||
recoverPassword (data) {
|
||||
return repository.post('/password_reset/', data)
|
||||
},
|
||||
checkSmsTotp (data) {
|
||||
return repository.post('/confirm_sms_totp/', data)
|
||||
},
|
||||
changePassword (data) {
|
||||
return repository.post('/reset_confirm/', data)
|
||||
},
|
||||
resendSmsTotp (data) {
|
||||
return repository.post('/reset_sms_resend/', data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate'
|
||||
import { confirmed, numeric, required } from 'vee-validate/dist/rules'
|
||||
import { confirmed, email, numeric, required } from 'vee-validate/dist/rules'
|
||||
import { translate } from 'vue-gettext'
|
||||
import account from '@/api/account'
|
||||
|
||||
@@ -19,6 +19,10 @@ extend('required', {
|
||||
...required,
|
||||
message: () => $gettext('This field is required')
|
||||
})
|
||||
extend('email', {
|
||||
...email,
|
||||
message: () => $gettext('Specify an email address')
|
||||
})
|
||||
|
||||
// Add custom rules here
|
||||
extend('validPassword', {
|
||||
|
||||
@@ -172,6 +172,21 @@ const routes = [
|
||||
requiresAuth: true,
|
||||
layout: 'user'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/password_recovery',
|
||||
name: 'PasswordRecoveryForm',
|
||||
component: () => import('../views/user/PasswordRecoveryForm.vue')
|
||||
},
|
||||
{
|
||||
path: '/password_recovery/confirm/:id?/:token?/',
|
||||
name: 'PasswordRecoveryChangeForm',
|
||||
component: () => import('../views/user/PasswordRecoveryChangeForm.vue')
|
||||
},
|
||||
{
|
||||
path: '/password_recovery/sms_confirm',
|
||||
name: 'PasswordRecoverySms',
|
||||
component: () => import('../views/user/PasswordRecoverySmsTotpForm.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
outlined
|
||||
/>
|
||||
</validation-provider>
|
||||
<a href="" class="float-right primary--text"><translate>Forgot password?</translate></a>
|
||||
<a @click="recoverPass" class="float-right primary--text"><translate>Forgot password?</translate></a>
|
||||
<v-checkbox
|
||||
v-model="rememberMe"
|
||||
:label="'Remember me'|translate"
|
||||
@@ -70,6 +70,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
recoverPass () {
|
||||
this.$router.push({ name: 'PasswordRecoveryForm' })
|
||||
},
|
||||
async authenticate () {
|
||||
const isValid = await this.$refs.observer.validate()
|
||||
if (!isValid) {
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<v-row style="height: 100%; margin: 0 auto">
|
||||
<v-col cols="0" sm="6" class="primary" style="height: 100%">
|
||||
<v-img
|
||||
src="../../assets/Modoboa_RVB-BLANC-SANS.png"
|
||||
max-width="300"
|
||||
class="mt-10 ml-8"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" class="d-flex flex-column justify-center pa-10" @keyup.enter="changePassword">
|
||||
<span class="primary--text mb-6 text-h4"><translate>Forgot password?</translate></span>
|
||||
<validation-observer ref="observer">
|
||||
<validation-provider
|
||||
vid="password"
|
||||
v-slot="{ errors }"
|
||||
rules="required"
|
||||
>
|
||||
<p id="password_validation"></p>
|
||||
<label class="m-label"><translate>Change password</translate></label>
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
:error-messages="errors"
|
||||
type="password"
|
||||
outlined
|
||||
/>
|
||||
</validation-provider>
|
||||
<validation-provider
|
||||
vid="password_confirm"
|
||||
v-slot="{ errors }"
|
||||
rules="required|samePassword:password"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="password_confirmed"
|
||||
:error-messages="errors"
|
||||
type="password"
|
||||
outlined
|
||||
/>
|
||||
</validation-provider>
|
||||
</validation-observer>
|
||||
<div class="d-flex justify-center">
|
||||
<v-btn
|
||||
class="flex-grow-1"
|
||||
color="primary"
|
||||
large
|
||||
@click="changePassword"
|
||||
:loading="loading"
|
||||
>
|
||||
<translate>Submit</translate>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div>
|
||||
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login?</translate></a>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import auth from '@/api/auth'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
password: '',
|
||||
password_confirmed: '',
|
||||
errors: {},
|
||||
token: '',
|
||||
id: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkUrl: function () {
|
||||
if (this.$route.params.token === undefined) {
|
||||
if (this.token === '' || this.id === '') {
|
||||
this.$router.push({ name: 'PasswordRecoveryForm' })
|
||||
}
|
||||
} else {
|
||||
const decodedId = atob(this.$route.params.id)
|
||||
if (!/^\d+$/.test(decodedId)) {
|
||||
console.error('Received ID is invalid')
|
||||
this.$router.push({ name: 'PasswordRecoveryForm' })
|
||||
} else {
|
||||
this.id = this.$route.params.id
|
||||
this.token = this.$route.params.token
|
||||
this.$router.push({ name: 'PasswordRecoveryChangeForm' })
|
||||
}
|
||||
}
|
||||
},
|
||||
returnLogin () {
|
||||
this.$router.push({ name: 'Login' })
|
||||
},
|
||||
async changePassword () {
|
||||
const isValid = await this.$refs.observer.validate()
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
new_password1: this.password,
|
||||
new_password2: this.password_confirmed,
|
||||
token: this.token,
|
||||
id: this.id
|
||||
}
|
||||
this.loading = true
|
||||
auth.changePassword(payload).then(resp => {
|
||||
document.getElementById('password_validation').innerHTML = ''
|
||||
this.loading = false
|
||||
this.returnLogin()
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
if (err.response.status === 401) {
|
||||
this.$refs.observer.setErrors({
|
||||
password_confirmed: this.$gettext('Invalid reset token.')
|
||||
})
|
||||
} else if (err.response.status === 404) {
|
||||
this.$refs.observer.setErrors({
|
||||
password_confirmed: this.$gettext('User unknown.')
|
||||
})
|
||||
} else if (err.response.status === 455) {
|
||||
let message = ''
|
||||
err.response.data.forEach(element => {
|
||||
message += this.$gettext(element) + '<br>'
|
||||
})
|
||||
document.getElementById('password_validation').innerHTML = message
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeMount () {
|
||||
this.checkUrl()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#password_validation {
|
||||
color:crimson;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<v-row style="height: 100%; margin: 0 auto">
|
||||
<v-col cols="0" sm="6" class="primary" style="height: 100%">
|
||||
<v-img
|
||||
src="../../assets/Modoboa_RVB-BLANC-SANS.png"
|
||||
max-width="300"
|
||||
class="mt-10 ml-8"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" class="d-flex flex-column justify-center pa-10" @keyup.enter="recoverPassword">
|
||||
<span class="primary--text mb-6 text-h4"><translate>Forgot password?</translate></span>
|
||||
<validation-observer ref="observer">
|
||||
<validation-provider
|
||||
vid="email"
|
||||
v-slot="{ errors }"
|
||||
rules="required|email"
|
||||
>
|
||||
<label class="m-label"><translate>Please fill-in your primary email address</translate></label>
|
||||
<v-text-field
|
||||
v-model="email"
|
||||
:error-messages="errors"
|
||||
outlined
|
||||
/>
|
||||
</validation-provider>
|
||||
</validation-observer>
|
||||
<div class="d-flex justify-center">
|
||||
<v-btn
|
||||
class="flex-grow-1"
|
||||
color="primary"
|
||||
large
|
||||
@click="recoverPassword"
|
||||
:loading="loading"
|
||||
>
|
||||
<translate>Submit</translate>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div>
|
||||
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login?</translate></a>
|
||||
</div>
|
||||
</v-col>
|
||||
<confirm-dialog ref="dialog_response">
|
||||
</confirm-dialog>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import auth from '@/api/auth'
|
||||
import ConfirmDialog from '@/components/layout/ConfirmDialog'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConfirmDialog
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
email: '',
|
||||
errors: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
returnLogin () {
|
||||
this.$router.push({ name: 'Login' })
|
||||
},
|
||||
async showDialogEmail () {
|
||||
const confirm = await this.$refs.dialog_response.open(
|
||||
this.$gettext('Info'),
|
||||
this.$gettext(
|
||||
'Email sent, please check your inbox'
|
||||
),
|
||||
{
|
||||
color: 'info',
|
||||
cancelLabel: this.$gettext('Ok'),
|
||||
agreeLabel: this.$gettext('Return to login')
|
||||
})
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
this.returnLogin()
|
||||
},
|
||||
async emailFailedToSend () {
|
||||
const confirm = await this.$refs.dialog_response.open(
|
||||
this.$gettext('Error'),
|
||||
this.$gettext(
|
||||
'Failed to send email, please check with you administrator'
|
||||
),
|
||||
{
|
||||
color: 'error',
|
||||
cancelLabel: this.$gettext('Ok'),
|
||||
agreeLabel: this.$gettext('Return to login')
|
||||
})
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
this.returnLogin()
|
||||
},
|
||||
async recoverPassword () {
|
||||
const isValid = await this.$refs.observer.validate()
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
email: this.email
|
||||
}
|
||||
this.loading = true
|
||||
auth.recoverPassword(payload).then(resp => {
|
||||
this.loading = false
|
||||
if (resp.status === 233) {
|
||||
this.$router.push({ name: 'PasswordRecoverySmsTotpForm' })
|
||||
} else if (resp.status === 210) {
|
||||
this.showDialogEmail()
|
||||
}
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
if (err.response.status === 404) {
|
||||
// User not found
|
||||
this.$refs.observer.setErrors({
|
||||
email: this.$gettext('Invalid email')
|
||||
})
|
||||
} else if (err.response.status === 502) {
|
||||
this.emailFailedToSend()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<v-row style="height: 100%; margin: 0 auto">
|
||||
<v-col cols="0" sm="6" class="primary" style="height: 100%">
|
||||
<v-img
|
||||
src="../../assets/Modoboa_RVB-BLANC-SANS.png"
|
||||
max-width="300"
|
||||
class="mt-10 ml-8"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" class="d-flex flex-column justify-center pa-10" @keyup.enter="changePassword">
|
||||
<confirm-dialog ref="confirm_wrong_user">
|
||||
</confirm-dialog>
|
||||
<span class="primary--text mb-6 text-h4"><translate>Password recovert</translate></span>
|
||||
<validation-observer ref="observer">
|
||||
<validation-provider
|
||||
vid="sms_totp"
|
||||
v-slot="{ errors }"
|
||||
rules="required|length:6"
|
||||
>
|
||||
<label class="m-label"><translate>Enter the code you've just received by SMS</translate></label>
|
||||
<v-text-field
|
||||
v-model="sms_totp"
|
||||
:error-messages="errors"
|
||||
type="totp"
|
||||
outlined
|
||||
/>
|
||||
</validation-provider>
|
||||
</validation-observer>
|
||||
<div class="d-flex justify-center">
|
||||
<v-btn
|
||||
class="flex-grow-1"
|
||||
color="primary"
|
||||
large
|
||||
@click="changePassword"
|
||||
:loading="loading"
|
||||
>
|
||||
<translate>Submit</translate>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div>
|
||||
<a @click="resendSms" class="float-left primary--text"><translate>Resend SMS.</translate></a>
|
||||
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login?</translate></a>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import auth from '@/api/auth'
|
||||
import ConfirmDialog from '@/components/layout/ConfirmDialog'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConfirmDialog
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
sms_totp: '',
|
||||
password: '',
|
||||
password_confirmed: '',
|
||||
user_pk: -1,
|
||||
errors: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
returnLogin () {
|
||||
this.$router.push({ name: 'Login' })
|
||||
},
|
||||
async userPkError () {
|
||||
const confirm = await this.$refs.confirm.open(
|
||||
this.$gettext('Warning'),
|
||||
this.$gettext(
|
||||
'User seems wrong, return to login or restart reset process?'
|
||||
),
|
||||
{
|
||||
color: 'error',
|
||||
cancelLabel: this.$gettext('Return to login'),
|
||||
agreeLabel: this.$gettext('Retry reset')
|
||||
}
|
||||
)
|
||||
if (!confirm) {
|
||||
this.returnLogin()
|
||||
} else {
|
||||
this.$router.push({ name: 'PasswordRecoveryForm' })
|
||||
}
|
||||
},
|
||||
async resendSms () {
|
||||
auth.resendSmsTotp().then(resp => {
|
||||
this.loading = false
|
||||
// Reset done !
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.loading = false
|
||||
this.userPkError()
|
||||
})
|
||||
},
|
||||
async checkSmsTotp () {
|
||||
const isValid = await this.$refs.observer.validate()
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
sms_totp: this.sms_totp
|
||||
}
|
||||
this.loading = true
|
||||
auth.checkSmsTotp(payload).then(resp => {
|
||||
this.loading = false
|
||||
if (resp.response.status === 200) {
|
||||
// Code is good, redirecting to change password form.
|
||||
this.$router.push({ name: 'PasswordRecoveryChangeForm', data: resp.response.data })
|
||||
}
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
if (err.response.status === 500) {
|
||||
this.$refs.observer.setErrors({
|
||||
password_confirmed: this.$gettext('Invalid totp, try resend')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user