This commit is contained in:
@@ -36,26 +36,18 @@ To REMOVE: apt-get remove dotnet-runtime-3.0 (replace 3.0 with whatever version
|
||||
User / Contact self password administration and onboarding
|
||||
Still support current method of admin setting login and password as an option
|
||||
Keep the random generator for password only and expose it in the UI as a button beside the password field (magic wand?)
|
||||
Need to be able to have a "RESET" route as per this:
|
||||
https://www.troyhunt.com/everything-you-ever-wanted-to-know/
|
||||
However, it is also part of the onboarding process so the wording needs to be done carefully as this is also how staff trigger a new user account
|
||||
For security reasons, Contact can't initiate the reset, it must be done by internal staff (though it doesn't need to be super high rights, limited should be ok, it's a caretaking issue really)
|
||||
Reset sends via user email a time limited reset code as part of an url to go to the reset form in the AyaNova UI where they can enter new password which are sent along with the reset code to a User route
|
||||
if the reset code is valid and brings up a user then checks if expired, if not then accepts the new login and password and redirects them to the login page
|
||||
|
||||
Login name is fixed and set by internal staff only
|
||||
Password only can be changed by the user
|
||||
Can this be a bulk job for Users?
|
||||
Because if hacked or import lots of users or migrate from v7 might want to do in bulk?
|
||||
TTM v.next?
|
||||
|
||||
todo: Translation strings for title and body, follow norms in linked docs regarding best practices
|
||||
remember this is for reset but also for onboarding
|
||||
todo: need reset button / menu option for BOTH User edit forms
|
||||
-------------------
|
||||
Hello {user_name},\n\nYour login account for service is available once you set your password to login.\nYou can use the following link for the next 48 hours to set your password.\n\nSet your password: {action_link}\n\nIf you did not request an account or password reset, please ignore this email.\n\nThanks,\n{registered_to}
|
||||
------------
|
||||
|
||||
todo: need reset route and form for user
|
||||
|
||||
todo: User update / save password and login, if try to change login but don't give password it bombs with server error
|
||||
todo: if authenticated and click reset link sb logged out before reset form opens
|
||||
todo: SEEDER
|
||||
Sample customer contacts need random contact info also entered so it's visible as example
|
||||
|
||||
|
||||
@@ -339,8 +339,10 @@ export default {
|
||||
toPath = undefined;
|
||||
}
|
||||
|
||||
let isReset = toPath && toPath.includes("home-reset");
|
||||
|
||||
//redirect to login if not authenticated
|
||||
if (!vm.$store.state.authenticated) {
|
||||
if (!vm.$store.state.authenticated && !isReset) {
|
||||
//If a direct open path was being used but user is not logged in this will catch it
|
||||
//otherwise they will just go on to that path directly
|
||||
|
||||
|
||||
@@ -127,14 +127,12 @@ export default new Router({
|
||||
/* webpackChunkName: "ay-common" */ "./views/home-user-settings.vue"
|
||||
)
|
||||
},
|
||||
// {
|
||||
// path: "/home-translation",
|
||||
// name: "home-translation",
|
||||
// component: () =>
|
||||
// import(
|
||||
// /* webpackChunkName: "ay-common" */ "./views/home-translation.vue"
|
||||
// )
|
||||
// },
|
||||
{
|
||||
path: "/home-reset",
|
||||
name: "home-reset",
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "ay-common" */ "./views/home-reset.vue")
|
||||
},
|
||||
{
|
||||
path: "/home-password",
|
||||
name: "home-password",
|
||||
|
||||
@@ -855,7 +855,25 @@ export default {
|
||||
window.$gz.errorHandler.handleFormError(error, vm);
|
||||
}
|
||||
}
|
||||
},
|
||||
async sendResetCode() {
|
||||
let vm = this;
|
||||
window.$gz.form.deleteAllErrorBoxErrors(vm);
|
||||
try {
|
||||
let res = await window.$gz.api.post(
|
||||
`auth/request-reset-password/${vm.obj.id}`,
|
||||
null
|
||||
);
|
||||
if (res.error) {
|
||||
vm.formState.serverError = res.error;
|
||||
window.$gz.form.setErrorBoxErrors(vm);
|
||||
}
|
||||
} catch (error) {
|
||||
window.$gz.errorHandler.handleFormError(error, vm);
|
||||
}
|
||||
}
|
||||
|
||||
//------more above here
|
||||
}
|
||||
};
|
||||
|
||||
@@ -923,6 +941,9 @@ async function clickHandler(menuItem) {
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "sendreset":
|
||||
m.vm.sendResetCode();
|
||||
break;
|
||||
default:
|
||||
window.$gz.eventBus.$emit(
|
||||
"notify-warning",
|
||||
@@ -1007,12 +1028,23 @@ function generateMenu(vm) {
|
||||
});
|
||||
}
|
||||
|
||||
menuOptions.menuItems.push({
|
||||
title: "DirectNotification",
|
||||
icon: "$ayiCommentAlt",
|
||||
key: FORM_KEY + ":directnotify",
|
||||
vm: vm
|
||||
});
|
||||
if (vm.$route.params.recordid != 0) {
|
||||
menuOptions.menuItems.push({
|
||||
title: "DirectNotification",
|
||||
icon: "$ayiCommentAlt",
|
||||
key: FORM_KEY + ":directnotify",
|
||||
vm: vm
|
||||
});
|
||||
|
||||
if (vm.rights.change) {
|
||||
menuOptions.menuItems.push({
|
||||
title: "SendPasswordResetCode",
|
||||
icon: null,
|
||||
key: FORM_KEY + ":sendreset",
|
||||
vm: vm
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.$gz.eventBus.$emit("menu-change", menuOptions);
|
||||
}
|
||||
|
||||
113
ayanova/src/views/home-reset.vue
Normal file
113
ayanova/src/views/home-reset.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<v-form ref="form">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="obj.newPassword"
|
||||
:readonly="formState.readOnly"
|
||||
:append-outer-icon="reveal ? '$ayiEye' : '$ayiEyeSlash'"
|
||||
prepend-icon="$ayiKey"
|
||||
:label="$ay.t('NewPassword')"
|
||||
:type="reveal ? 'text' : 'password'"
|
||||
:rules="[form().required(this, 'newPassword')]"
|
||||
:error-messages="form().serverErrors(this, 'newPassword')"
|
||||
ref="newPassword"
|
||||
@input="fieldValueChanged('newPassword')"
|
||||
@click:append-outer="reveal = !reveal"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="obj.confirmPassword"
|
||||
:readonly="formState.readOnly"
|
||||
:append-outer-icon="reveal ? '$ayiEye' : '$ayiEyeSlash'"
|
||||
prepend-icon="$ayiKey"
|
||||
:label="$ay.t('ConfirmPassword')"
|
||||
:type="reveal ? 'text' : 'password'"
|
||||
:rules="[
|
||||
form().required(this, 'confirmPassword'),
|
||||
form().confirmMatch(this, 'newPassword', 'confirmPassword')
|
||||
]"
|
||||
:error-messages="form().serverErrors(this, 'confirmPassword')"
|
||||
ref="confirmPassword"
|
||||
@input="fieldValueChanged('confirmPassword')"
|
||||
@click:append-outer="reveal = !reveal"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" md="7" mt-1 mb-5>
|
||||
<v-btn color="primary" v-on:click="login()" value="LOGIN">
|
||||
<v-icon>$ayiSignIn</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* Xeslint-disable */
|
||||
export default {
|
||||
async created() {},
|
||||
data() {
|
||||
return {
|
||||
obj: {
|
||||
newPassword: null,
|
||||
confirmPassword: null,
|
||||
passwordResetCode: null
|
||||
},
|
||||
reveal: true,
|
||||
formState: {
|
||||
ready: true,
|
||||
dirty: false,
|
||||
valid: true,
|
||||
readOnly: false,
|
||||
loading: false,
|
||||
errorBoxMessage: null,
|
||||
appError: null,
|
||||
serverError: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
translation() {
|
||||
return window.$gz.translation;
|
||||
},
|
||||
form() {
|
||||
return window.$gz.form;
|
||||
},
|
||||
fieldValueChanged(ref) {
|
||||
if (!this.formState.loading && !this.formState.readOnly) {
|
||||
window.$gz.form.fieldValueChanged(this, ref);
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
let vm = this;
|
||||
if (vm.canSave) {
|
||||
vm.formState.loading = true;
|
||||
|
||||
let url = "auth/reset-password";
|
||||
|
||||
try {
|
||||
let res = await window.$gz.api.upsert(url, {
|
||||
PasswordResetCode: obj.passwordResetCode,
|
||||
Password: obj.confirmPassword
|
||||
});
|
||||
|
||||
if (res.error) {
|
||||
vm.formState.serverError = res.error;
|
||||
window.$gz.form.setErrorBoxErrors(vm);
|
||||
} else {
|
||||
vm.$router.push({
|
||||
name: "login"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
window.$gz.errorHandler.handleFormError(error, vm);
|
||||
} finally {
|
||||
vm.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user