feat(auth): add view as user and role functionality for owners
Allow owners to impersonate users or roles for debugging permissions. Add view-as targets to user sessions and resolve effective permissions. Display a persistent banner in the app shell to exit view-as mode.
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
iconDelete,
|
||||
iconDish,
|
||||
iconEdit,
|
||||
iconEye,
|
||||
iconHabitat,
|
||||
iconItem,
|
||||
iconKey,
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
import { defaultLocale, getCurrentLocale, loadSystemWordings, setCurrentLocale } from '../i18n';
|
||||
import {
|
||||
api,
|
||||
notifyAuthChange,
|
||||
type AncientArtifact,
|
||||
type AiModerationApiFormat,
|
||||
type AiModerationAuthMode,
|
||||
@@ -370,6 +372,7 @@ const activeConfigTab = computed({
|
||||
}
|
||||
});
|
||||
const canEdit = computed(() => can('admin.access'));
|
||||
const canUseViewAs = computed(() => currentUser.value?.roles.some((role) => role.key === 'owner') === true && !currentUser.value?.viewAs);
|
||||
const showAdminSkeleton = computed(() => busy.value && !message.value && (!currentUser.value || contentLoading.value));
|
||||
const canSetLanguageDefault = computed(() => languageForm.value.code === 'en');
|
||||
const configModalTitle = computed(() =>
|
||||
@@ -822,6 +825,14 @@ function openUserRoles(user: AdminUser) {
|
||||
userRoleModalOpen.value = true;
|
||||
}
|
||||
|
||||
async function viewAsUser(user: AdminUser) {
|
||||
await run(async () => {
|
||||
const response = await api.viewAsUser(user.id);
|
||||
currentUser.value = response.user;
|
||||
notifyAuthChange();
|
||||
});
|
||||
}
|
||||
|
||||
function closeUserRoleModal() {
|
||||
userRoleModalOpen.value = false;
|
||||
resetUserRoleForm();
|
||||
@@ -854,6 +865,14 @@ function editRolePermissions(role: RoleDetail) {
|
||||
rolePermissionsModalOpen.value = true;
|
||||
}
|
||||
|
||||
async function viewAsRole(role: RoleDetail) {
|
||||
await run(async () => {
|
||||
const response = await api.viewAsRole(role.id);
|
||||
currentUser.value = response.user;
|
||||
notifyAuthChange();
|
||||
});
|
||||
}
|
||||
|
||||
function closeRolePermissionsModal() {
|
||||
rolePermissionsModalOpen.value = false;
|
||||
resetRolePermissionForm();
|
||||
@@ -1851,6 +1870,10 @@ onMounted(() => {
|
||||
</span>
|
||||
</span>
|
||||
<span class="row-actions">
|
||||
<button v-if="canUseViewAs" type="button" :disabled="busy" @click="viewAsUser(user)">
|
||||
<Icon :icon="iconEye" class="ui-icon" aria-hidden="true" />
|
||||
{{ t('viewAs.userAction') }}
|
||||
</button>
|
||||
<button v-if="can('admin.users.update') && can('admin.roles.read')" type="button" :disabled="busy" @click="openUserRoles(user)">
|
||||
<Icon :icon="iconEdit" class="ui-icon" aria-hidden="true" />
|
||||
{{ t('pages.admin.userRoles') }}
|
||||
@@ -1883,6 +1906,10 @@ onMounted(() => {
|
||||
</span>
|
||||
</span>
|
||||
<span class="row-actions">
|
||||
<button v-if="canUseViewAs && role.enabled" type="button" :disabled="busy" @click="viewAsRole(role)">
|
||||
<Icon :icon="iconEye" class="ui-icon" aria-hidden="true" />
|
||||
{{ t('viewAs.roleAction') }}
|
||||
</button>
|
||||
<button v-if="can('admin.roles.update')" type="button" :disabled="busy" @click="editRole(role)">
|
||||
<Icon :icon="iconEdit" class="ui-icon" aria-hidden="true" />
|
||||
{{ t('common.edit') }}
|
||||
|
||||
Reference in New Issue
Block a user