fix: validate current password with a modal (#5805)

This commit is contained in:
Ariel Leyva
2026-03-06 09:26:41 -05:00
committed by GitHub
parent 4af3f85e64
commit 177c7cfcce
4 changed files with 102 additions and 25 deletions

View File

@@ -0,0 +1,58 @@
<template>
<div class="card floating">
<div class="card-title">
<h2>{{ $t("prompts.currentPassword") }}</h2>
</div>
<div class="card-content">
<p>
{{ $t("prompts.currentPasswordMessage") }}
</p>
<input
id="focus-prompt"
class="input input--block"
type="text"
@keyup.enter="submit"
v-model="password"
/>
</div>
<div class="card-action">
<button
class="button button--flat button--grey"
@click="cancel"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')"
>
{{ $t("buttons.cancel") }}
</button>
<button
@click="submit"
class="button button--flat"
type="submit"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')"
>
{{ $t("buttons.ok") }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useLayoutStore } from "@/stores/layout";
const layoutStore = useLayoutStore();
const { currentPrompt } = layoutStore;
const password = ref("");
const submit = (event: Event) => {
currentPrompt?.confirm(event, password.value);
};
const cancel = () => {
layoutStore.closeHovers();
};
</script>

View File

@@ -28,6 +28,7 @@ import ShareDelete from "./ShareDelete.vue";
import Upload from "./Upload.vue"; import Upload from "./Upload.vue";
import DiscardEditorChanges from "./DiscardEditorChanges.vue"; import DiscardEditorChanges from "./DiscardEditorChanges.vue";
import ResolveConflict from "./ResolveConflict.vue"; import ResolveConflict from "./ResolveConflict.vue";
import CurrentPassword from "./CurrentPassword.vue";
const layoutStore = useLayoutStore(); const layoutStore = useLayoutStore();
@@ -50,6 +51,7 @@ const components = new Map<string, any>([
["deleteUser", DeleteUser], ["deleteUser", DeleteUser],
["discardEditorChanges", DiscardEditorChanges], ["discardEditorChanges", DiscardEditorChanges],
["resolve-conflict", ResolveConflict], ["resolve-conflict", ResolveConflict],
["current-password", CurrentPassword],
]); ]);
const modal = computed(() => { const modal = computed(() => {

View File

@@ -175,7 +175,9 @@
"filesInDest": "Files in destination", "filesInDest": "Files in destination",
"override": "Overwrite", "override": "Overwrite",
"skip": "Skip", "skip": "Skip",
"forbiddenError": "Forbidden Error" "forbiddenError": "Forbidden Error",
"currentPassword": "Your password",
"currentPasswordMessage": "Enter your password to validate this action."
}, },
"search": { "search": {
"images": "Images", "images": "Images",

View File

@@ -15,19 +15,6 @@
:isDefault="false" :isDefault="false"
:isNew="isNew" :isNew="isNew"
/> />
<p v-if="isCurrentPasswordRequired">
<label for="currentPassword">{{
t("settings.currentPassword")
}}</label>
<input
class="input input--block"
type="password"
v-model="currentPassword"
id="currentPassword"
autocomplete="current-password"
/>
</p>
</div> </div>
<div class="card-action"> <div class="card-action">
@@ -77,7 +64,6 @@ const error = ref<StatusError>();
const originalUser = ref<IUser>(); const originalUser = ref<IUser>();
const user = ref<IUser>(); const user = ref<IUser>();
const createUserDir = ref<boolean>(false); const createUserDir = ref<boolean>(false);
const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false); const isCurrentPasswordRequired = ref<boolean>(false);
const $showError = inject<IToastError>("$showError")!; const $showError = inject<IToastError>("$showError")!;
@@ -134,16 +120,30 @@ const fetchData = async () => {
} }
}; };
const deletePrompt = () => const deletePrompt = () => {
layoutStore.showHover({ prompt: "deleteUser", confirm: deleteUser }); if (isCurrentPasswordRequired.value) {
layoutStore.showHover({
prompt: "current-password",
confirm: (event: Event, currentPassword: string) => {
event.preventDefault();
layoutStore.closeHovers();
deleteUser(currentPassword);
},
});
} else {
layoutStore.showHover({
prompt: "deleteUser",
confirm: () => deleteUser(""),
});
}
};
const deleteUser = async (e: Event) => { const deleteUser = async (currentPassword: string) => {
e.preventDefault();
if (!user.value) { if (!user.value) {
return false; return false;
} }
try { try {
await api.remove(user.value.id, currentPassword.value); await api.remove(user.value.id, currentPassword);
router.push({ path: "/settings/users" }); router.push({ path: "/settings/users" });
$showSuccess(t("settings.userDeleted")); $showSuccess(t("settings.userDeleted"));
} catch (err) { } catch (err) {
@@ -157,8 +157,25 @@ const deleteUser = async (e: Event) => {
return true; return true;
}; };
const save = async (event: Event) => { const save = (event: Event) => {
event.preventDefault(); event.preventDefault();
if (isCurrentPasswordRequired.value) {
layoutStore.showHover({
prompt: "current-password",
confirm: (event: Event, currentPassword: string) => {
event.preventDefault();
layoutStore.closeHovers();
send(currentPassword);
},
});
} else {
send("");
}
return true;
};
const send = async (currentPassword: string) => {
if (!user.value) { if (!user.value) {
return false; return false;
} }
@@ -170,11 +187,11 @@ const save = async (event: Event) => {
...user.value, ...user.value,
}; };
const loc = await api.create(newUser, currentPassword.value); const loc = await api.create(newUser, currentPassword);
router.push({ path: loc || "/settings/users" }); router.push({ path: loc || "/settings/users" });
$showSuccess(t("settings.userCreated")); $showSuccess(t("settings.userCreated"));
} else { } else {
await api.update(user.value, ["all"], currentPassword.value); await api.update(user.value, ["all"], currentPassword);
if (user.value.id === authStore.user?.id) { if (user.value.id === authStore.user?.id) {
authStore.updateUser(user.value); authStore.updateUser(user.value);
@@ -185,7 +202,5 @@ const save = async (event: Event) => {
} catch (e: any) { } catch (e: any) {
$showError(e); $showError(e);
} }
return true;
}; };
</script> </script>