This commit is contained in:
2021-03-11 18:23:20 +00:00
parent d1d2dd4814
commit 66fcd9515d
6 changed files with 250 additions and 3 deletions

View File

@@ -195,13 +195,15 @@ todo: 2fa is going to be an absolute must have pretty soon, look into what's inv
SIGN UP
(copied a bit from digital ocean)
User settings has a SECURITY section where control 2fa stuff
user enables at which point a secret key for 2fa is generated and stored in the User account
user chooses 2fa button to setup, a dialog pops up sends a request to server at which point a secret key for 2fa is generated and stored in the User account
and gets back the secret to display in a qr code on screen to searching
/auth/setup-hotp
user is redirected to a client form with the qr code displayed for teh secret
User gets QR code then displayed to sign up with auth software
User has to enter a valid code to save or enable 2fa fully otherwise it's not enabled if they cancel out
until the correct code is entered it will not be enabled yet
If user moves out of 2fa area without validating then it generates a new secret next time they go In
DISABLE
user goes to user settings->Security and click on disable 2fa button which is only enabled to click when the account has 2fa already enabled
this removes the 2fa secret from their account and sets 2fa off.

View File

@@ -55,7 +55,8 @@ export function processLogin(authResponse, loggedInWithKnownPassword) {
userName: authResponse.name,
roles: authResponse.roles,
userType: authResponse.usertype,
dlt: authResponse.dlt
dlt: authResponse.dlt,
tfaEnabled: authResponse.tfa
});
//log the login
window.$gz.store.commit(

View File

@@ -206,6 +206,12 @@ export default new Router({
component: () =>
import(/* webpackChunkName: "ay-common" */ "./views/home-password.vue")
},
{
path: "/home-security",
name: "home-security",
component: () =>
import(/* webpackChunkName: "ay-common" */ "./views/home-security.vue")
},
{
path: "/home-notify-subscriptions",
name: "home-notify-subscriptions",

View File

@@ -25,6 +25,7 @@ export default new Vuex.Store({
helpUrl: "",
apiToken: "-",
downloadToken: "-",
tfaEnabled: undefined,
userId: 0,
userName: "NOT AUTHENTICATED",
roles: 0,
@@ -68,11 +69,13 @@ export default new Vuex.Store({
state.userName = data.userName;
state.userType = data.userType;
state.downloadToken = data.dlt;
state.tfaEnabled = data.tfaEnabled;
},
logout(state) {
//Things that are reset on logout
state.apiToken = "-";
state.downloadToken = "-";
state.tfaEnabled = undefined;
state.authenticated = false;
state.userId = 0;
state.userName = "NOT AUTHENTICATED";

View File

@@ -0,0 +1,228 @@
<template>
<v-row v-if="formState.ready">
<v-col>
<v-form ref="form">
<v-row>
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
tfaEnabled:{{ tfaEnabled }}
ui here if tfa enabled then only option is disable tfa if not enabled
then user sees code to scan into tfa app
</v-row>
</v-form>
</v-col>
</v-row>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Xeslint-disable */
////////////////////////////////////////////////////////////////////////////////////////////////////////////
const FORM_KEY = "home-security";
const API_BASE_URL = "auth/";
export default {
async created() {
let vm = this;
try {
await initForm(vm);
vm.rights = window.$gz.role.fullRightsObject();
generateMenu(vm);
vm.formState.ready = true;
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true,
loading: false,
readOnly: false
});
window.$gz.eventBus.$on("menu-click", clickHandler);
//fetch tfa secret and display here if tfa not enabled currently
console.log("tfaEnabled:", this.tfaEnabled);
//------------------
} catch (err) {
vm.formState.ready = true;
window.$gz.errorHandler.handleFormError(err, vm);
}
},
async beforeRouteLeave(to, from, next) {
if (!this.formState.dirty) {
next();
return;
}
if ((await window.$gz.dialog.confirmLeaveUnsaved()) === true) {
next();
} else {
next(false);
}
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
components: {},
data() {
return {
obj: {
s: null, // = u.HotpSecret,
qr: null // = qrCodeImageAsBase64
},
tfaEnabled: window.$gz.store.state.tfaEnabled,
formState: {
ready: false,
dirty: false,
valid: true,
readOnly: false,
loading: true,
errorBoxMessage: null,
appError: null,
serverError: {}
},
rights: window.$gz.role.fullRightsObject()
};
},
//WATCHERS
watch: {
formState: {
handler: function(val) {
//,oldval is available here too if necessary
if (this.formState.loading) {
return;
}
//enable / disable save button
let canSave = val.dirty && val.valid && !val.readOnly;
if (canSave) {
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":save");
} else {
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
}
},
deep: true
}
},
computed: {
canSave: function() {
return this.formState.valid && this.formState.dirty;
}
},
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;
//always submit from this form for the current logged in user id
let url = API_BASE_URL;
//clear any errors vm might be around from previous submit
window.$gz.form.deleteAllErrorBoxErrors(vm);
try {
let res = await window.$gz.api.upsert(url, vm.obj);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//Only a post, no data returned
window.$gz.form.setFormState({
vm: vm,
dirty: false
});
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
} finally {
vm.loading = false;
}
}
}
}
};
/////////////////////////////
//
//
function clickHandler(menuItem) {
if (!menuItem) {
return;
}
let m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
case "save":
m.vm.submit();
break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
let menuOptions = {
isMain: true,
icon: "$ayiKey",
title: "SetLoginPassword",
helpUrl: "home-password",
formData: {
ayaType: window.$gz.type.UserOptions
},
menuItems: []
};
if (vm.rights.change) {
menuOptions.menuItems.push({
title: "Save",
icon: "$ayiSave",
surface: true,
key: FORM_KEY + ":save",
vm: vm
});
}
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText(vm);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText(vm) {
await window.$gz.translation.cacheTranslations([
"UserLogin",
"OldPassword",
"NewPassword",
"ConfirmPassword",
"KnownPasswordWarning"
]);
}
</script>

View File

@@ -477,6 +477,13 @@ function generateMenu(vm) {
data: "home-password",
key: "app:nav"
});
menuOptions.menuItems.push({
title: "Two factor authentication",
icon: "$ayiKey",
data: "home-security",
key: "app:nav:SECURITY"
});
menuOptions.menuItems.push({ divider: true, inset: false });
window.$gz.eventBus.$emit("menu-change", menuOptions);
}