Imported to main repo

This commit is contained in:
Spitap
2022-12-05 16:57:38 +01:00
parent e6d9833012
commit 39af0485f8
19 changed files with 892 additions and 13 deletions
+12
View File
@@ -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)
}
}
+5 -1
View File
@@ -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', {
+15
View File
@@ -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')
}
]
+4 -1
View File
@@ -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>