[core] Allow users to see passwords if wished
This commit is contained in:
parent
1b1888d4fd
commit
c61b5fcc0c
|
@ -7,6 +7,7 @@
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:rules="customRules"
|
:rules="customRules"
|
||||||
:clearable="clearable"
|
:clearable="clearable"
|
||||||
|
v-bind="attrs"
|
||||||
@clear="dateTime = ''"
|
@clear="dateTime = ''"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
|
@ -58,7 +59,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: { 'update:modelValue': (date?: Date) => !!date || !date },
|
emits: { 'update:modelValue': (date?: Date) => !!date || !date },
|
||||||
setup(props, { emit }) {
|
setup(props, { emit, attrs }) {
|
||||||
const customRules = computed(() => [
|
const customRules = computed(() => [
|
||||||
props.type == 'date' ? stringIsDate : props.type == 'time' ? stringIsTime : stringIsDateTime,
|
props.type == 'date' ? stringIsDate : props.type == 'time' ? stringIsTime : stringIsDateTime,
|
||||||
...props.rules,
|
...props.rules,
|
||||||
|
@ -138,12 +139,13 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
attrs,
|
||||||
clearable,
|
clearable,
|
||||||
date,
|
|
||||||
time,
|
|
||||||
dateTime,
|
|
||||||
customRules,
|
customRules,
|
||||||
|
date,
|
||||||
|
dateTime,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
time,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<template>
|
||||||
|
<q-input v-model="password" v-bind="attrs" :label="label" :type="type">
|
||||||
|
<template #append><q-icon :name="name" class="cursor-pointer" @click="toggle" /></template
|
||||||
|
></q-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PasswordInput',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
'update:modelValue': (value: string) => !!value,
|
||||||
|
},
|
||||||
|
setup(props, { emit, attrs }) {
|
||||||
|
const isPassword = ref(true);
|
||||||
|
const type = computed(() => (isPassword.value ? 'password' : 'text'));
|
||||||
|
const name = computed(() => (isPassword.value ? 'visibility_off' : 'visibility'));
|
||||||
|
const password = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value: string) => emit('update:modelValue', value),
|
||||||
|
});
|
||||||
|
function toggle() {
|
||||||
|
isPassword.value = !isPassword.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
attrs,
|
||||||
|
isPassword,
|
||||||
|
name,
|
||||||
|
password,
|
||||||
|
toggle,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -6,20 +6,21 @@
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-form ref="LoginForm" class="q-gutter-md" @submit="doLogin">
|
<q-form class="q-gutter-md" @submit="doLogin">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="userid"
|
v-model="userid"
|
||||||
filled
|
filled
|
||||||
label="Benutzername oder E-Mail"
|
label="Benutzername oder E-Mail"
|
||||||
:rules="rules"
|
autocomplete="username"
|
||||||
|
:rules="[notEmpty]"
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<password-input
|
||||||
v-model="password"
|
v-model="password"
|
||||||
filled
|
filled
|
||||||
type="password"
|
label="Passwort"
|
||||||
label="Password"
|
autocomplete="cureent-password"
|
||||||
:rules="rules"
|
:rules="[notEmpty]"
|
||||||
tabindex="2"
|
tabindex="2"
|
||||||
/>
|
/>
|
||||||
<div class="row justify-between">
|
<div class="row justify-between">
|
||||||
|
@ -54,15 +55,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useMainStore } from 'src/stores';
|
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { Loading, Notify } from 'quasar';
|
import { Loading, Notify } from 'quasar';
|
||||||
|
import { useMainStore } from 'src/stores';
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { setBaseURL, api } from 'boot/axios';
|
import { setBaseURL, api } from 'boot/axios';
|
||||||
|
import { notEmpty } from 'src/utils/validators';
|
||||||
import { useUserStore } from 'src/plugins/user/store';
|
import { useUserStore } from 'src/plugins/user/store';
|
||||||
|
import PasswordInput from 'src/components/utils/PasswordInput.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
|
components: { PasswordInput },
|
||||||
setup() {
|
setup() {
|
||||||
const mainStore = useMainStore();
|
const mainStore = useMainStore();
|
||||||
const mainRoute = { name: 'dashboard' };
|
const mainRoute = { name: 'dashboard' };
|
||||||
|
@ -71,7 +75,6 @@ export default defineComponent({
|
||||||
/* Stuff for the real login page */
|
/* Stuff for the real login page */
|
||||||
const userid = ref('');
|
const userid = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const rules = [(val: string) => (val && val.length > 0) || 'Feld darf nicht leer sein!'];
|
|
||||||
const server = ref<string | undefined>(api.defaults.baseURL);
|
const server = ref<string | undefined>(api.defaults.baseURL);
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
|
|
||||||
|
@ -138,15 +141,15 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userid,
|
changeUrl,
|
||||||
password,
|
|
||||||
doLogin,
|
doLogin,
|
||||||
doReset,
|
doReset,
|
||||||
rules,
|
notEmpty,
|
||||||
server,
|
|
||||||
changeUrl,
|
|
||||||
visible,
|
|
||||||
openServerSettings,
|
openServerSettings,
|
||||||
|
password,
|
||||||
|
server,
|
||||||
|
userid,
|
||||||
|
visible,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,44 +2,48 @@
|
||||||
<q-form @submit="save" @reset="reset">
|
<q-form @submit="save" @reset="reset">
|
||||||
<q-card-section class="fit row justify-start content-center items-center">
|
<q-card-section class="fit row justify-start content-center items-center">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="user_model.firstname"
|
v-model="userModel.firstname"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Vorname"
|
label="Vorname"
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
|
autocomplete="given-name"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="user_model.lastname"
|
v-model="userModel.lastname"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Nachname"
|
label="Nachname"
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
|
autocomplete="family-name"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
v-model="user_model.display_name"
|
v-model="userModel.display_name"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Angezeigter Name"
|
label="Angezeigter Name"
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
|
autocomplete="nickname"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="user_model.mail"
|
v-model="userModel.mail"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="E-Mail"
|
label="E-Mail"
|
||||||
:rules="[isEmail, notEmpty]"
|
:rules="[isEmail, notEmpty]"
|
||||||
|
autocomplete="email"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="user_model.userid"
|
v-model="userModel.userid"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Benutzername"
|
label="Benutzername"
|
||||||
:readonly="!newUser"
|
:readonly="!newUser"
|
||||||
:rules="[isUseridUsed, notEmpty]"
|
:rules="[isUIDUsed, notEmpty]"
|
||||||
|
autocomplete="username"
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
<q-select
|
<q-select
|
||||||
v-model="user_model.roles"
|
v-model="userModel.roles"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Rollen"
|
label="Rollen"
|
||||||
filled
|
filled
|
||||||
|
@ -51,9 +55,10 @@
|
||||||
option-value="name"
|
option-value="name"
|
||||||
/>
|
/>
|
||||||
<IsoDateInput
|
<IsoDateInput
|
||||||
v-model="user_model.birthday"
|
v-model="userModel.birthday"
|
||||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
label="Geburtstag"
|
label="Geburtstag"
|
||||||
|
autocomplete="bday"
|
||||||
/>
|
/>
|
||||||
<q-file
|
<q-file
|
||||||
v-model="avatar"
|
v-model="avatar"
|
||||||
|
@ -72,31 +77,22 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-separator v-if="!newUser" />
|
<q-separator v-if="!newUser" />
|
||||||
<q-card-section v-if="!newUser" class="fit row justify-start content-center items-center">
|
<q-card-section v-if="!newUser" class="fit row justify-start content-center items-center">
|
||||||
<q-input
|
<PasswordInput
|
||||||
v-if="isCurrentUser"
|
v-if="isCurrentUser"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
hint="Password muss immer eingetragen werden"
|
|
||||||
:rules="[notEmpty]"
|
:rules="[notEmpty]"
|
||||||
filled
|
filled
|
||||||
|
label="Passwort"
|
||||||
|
autocomplete="current-password"
|
||||||
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
|
hint="Passwort muss immer eingetragen werden"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<PasswordInput
|
||||||
v-model="new_password"
|
v-model="newPassword"
|
||||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
filled
|
||||||
label="Neues Password"
|
label="Neues Password"
|
||||||
type="password"
|
autocomplete="new-password"
|
||||||
filled
|
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||||
/>
|
|
||||||
<q-input
|
|
||||||
v-model="new_password2"
|
|
||||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
|
||||||
label="Wiederhole neues Password"
|
|
||||||
type="password"
|
|
||||||
:disable="new_password.length == 0"
|
|
||||||
:rules="[samePassword]"
|
|
||||||
filled
|
|
||||||
/>
|
/>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
|
@ -109,14 +105,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Notify } from 'quasar';
|
import { Notify } from 'quasar';
|
||||||
import { hasPermission } from 'src/utils/permission';
|
import { hasPermission } from 'src/utils/permission';
|
||||||
|
import { notEmpty, isEmail } from 'src/utils/validators';
|
||||||
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
import IsoDateInput from 'src/components/utils/IsoDateInput.vue';
|
||||||
|
import PasswordInput from 'src/components/utils/PasswordInput.vue';
|
||||||
import { defineComponent, computed, ref, onBeforeMount, PropType } from 'vue';
|
import { defineComponent, computed, ref, onBeforeMount, PropType } from 'vue';
|
||||||
import { useUserStore } from '../../store';
|
import { useUserStore } from '../../store';
|
||||||
import { useMainStore } from 'src/stores';
|
import { useMainStore } from 'src/stores';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MainUserSettings',
|
name: 'MainUserSettings',
|
||||||
components: { IsoDateInput },
|
components: { IsoDateInput, PasswordInput },
|
||||||
props: {
|
props: {
|
||||||
user: {
|
user: {
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -131,17 +129,19 @@ export default defineComponent({
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const mainStore = useMainStore();
|
const mainStore = useMainStore();
|
||||||
|
|
||||||
const user_model = ref(props.user);
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
void userStore.getRoles(false);
|
void userStore.getRoles(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isCurrentUser = computed(() => user_model.value.userid === mainStore.currentUser.userid);
|
const password = ref('');
|
||||||
|
const newPassword = ref('');
|
||||||
|
const avatar = ref<string[]>([]);
|
||||||
|
const userModel = ref(props.user);
|
||||||
|
|
||||||
const canSetRoles = computed(() => hasPermission('users_set_roles'));
|
const canSetRoles = computed(() => hasPermission('users_set_roles'));
|
||||||
|
const allRoles = computed(() => userStore.roles.map((role) => role.name));
|
||||||
|
const isCurrentUser = computed(() => userModel.value.userid === mainStore.currentUser.userid);
|
||||||
|
|
||||||
const avatar = ref([] as string[]);
|
|
||||||
function onAvatarRejected() {
|
function onAvatarRejected() {
|
||||||
Notify.create({
|
Notify.create({
|
||||||
group: false,
|
group: false,
|
||||||
|
@ -154,20 +154,15 @@ export default defineComponent({
|
||||||
avatar.value = [];
|
avatar.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const allRoles = computed(() => userStore.roles.map((role) => role.name));
|
|
||||||
const password = ref('');
|
|
||||||
const new_password = ref('');
|
|
||||||
const new_password2 = ref('');
|
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
let changed = user_model.value;
|
let changed = userModel.value;
|
||||||
if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday);
|
if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday);
|
||||||
changed = Object.assign(changed, {
|
changed = Object.assign(changed, {
|
||||||
password: password.value,
|
password: password.value,
|
||||||
});
|
});
|
||||||
if (new_password.value != '') {
|
if (newPassword.value != '') {
|
||||||
changed = Object.assign(changed, {
|
changed = Object.assign(changed, {
|
||||||
new_password: new_password.value,
|
new_password: newPassword.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,52 +178,32 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
user_model.value = props.user;
|
userModel.value = props.user;
|
||||||
password.value = '';
|
password.value = '';
|
||||||
new_password.value = '';
|
newPassword.value = '';
|
||||||
new_password2.value = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function samePassword(val: string) {
|
function isUIDUsed(val: string) {
|
||||||
return val == new_password.value || 'Passwörter sind nicht identisch!';
|
|
||||||
}
|
|
||||||
|
|
||||||
function notEmpty(val: string) {
|
|
||||||
return !!val || 'Feld darf nicht leer sein!';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmail(val: string | null) {
|
|
||||||
return (
|
return (
|
||||||
!val || /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) || 'E-Mail ist nicht valide.'
|
userStore.users.findIndex((user) => user.userid === val) !== -1 ||
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUseridUsed(val: string) {
|
|
||||||
return (
|
|
||||||
!userStore.users.find((user: FG.User) => {
|
|
||||||
return user.userid == val;
|
|
||||||
}) ||
|
|
||||||
!props.newUser ||
|
|
||||||
'Benutzername ist schon vergeben'
|
'Benutzername ist schon vergeben'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
avatar,
|
|
||||||
user_model,
|
|
||||||
onAvatarRejected,
|
|
||||||
allRoles,
|
allRoles,
|
||||||
|
avatar,
|
||||||
canSetRoles,
|
canSetRoles,
|
||||||
password,
|
|
||||||
new_password,
|
|
||||||
new_password2,
|
|
||||||
samePassword,
|
|
||||||
isCurrentUser,
|
isCurrentUser,
|
||||||
isEmail,
|
isEmail,
|
||||||
|
isUIDUsed,
|
||||||
|
newPassword,
|
||||||
notEmpty,
|
notEmpty,
|
||||||
isUseridUsed,
|
onAvatarRejected,
|
||||||
save,
|
password,
|
||||||
reset,
|
reset,
|
||||||
|
save,
|
||||||
|
userModel,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,3 +15,9 @@ export function stringIsTime(val: string) {
|
||||||
export function stringIsDateTime(val: string) {
|
export function stringIsDateTime(val: string) {
|
||||||
return !val || /^\d{4}-\d\d-\d\d \d\d:\d\d$/.test(val) || 'Datum und Zeit ist nicht gültig.';
|
return !val || /^\d{4}-\d\d-\d\d \d\d:\d\d$/.test(val) || 'Datum und Zeit ist nicht gültig.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isEmail(val: string) {
|
||||||
|
return (
|
||||||
|
!val || /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) || 'E-Mail ist nicht gültig.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue