feat(permissions): add reset to defaults button in permissions modals (#25931)

Adds a 'Reset to Defaults' button to both the Edit Default Permissions
modal and the Edit User Group permissions modal.

- Default permissions modal: resets to stock/env-var configuration
- User group modal: resets to current global default permissions

Backend: new GET /api/v1/users/default/permissions/defaults endpoint
that returns DEFAULT_USER_PERMISSIONS (env-var-based initial defaults).
This commit is contained in:
G30 authored and GitHub committed 2026-06-17 00:25:20 +02:00
1 parent 7ee75a0c04
commit 6b2d962cd6
3 files changed
+101 -1

No files matched your search

+14
View File
@@ -269,6 +269,20 @@ async def update_default_user_permissions(request: Request, form_data: UserPermi
return request.app.state.config.USER_PERMISSIONS return request.app.state.config.USER_PERMISSIONS
@router.get('/default/permissions/defaults', response_model=UserPermissions)
async def get_default_user_permissions_defaults(user=Depends(get_admin_user)):
from open_webui.config import DEFAULT_USER_PERMISSIONS
return {
'workspace': WorkspacePermissions(**DEFAULT_USER_PERMISSIONS.get('workspace', {})),
'sharing': SharingPermissions(**DEFAULT_USER_PERMISSIONS.get('sharing', {})),
'access_grants': AccessGrantsPermissions(**DEFAULT_USER_PERMISSIONS.get('access_grants', {})),
'chat': ChatPermissions(**DEFAULT_USER_PERMISSIONS.get('chat', {})),
'features': FeaturesPermissions(**DEFAULT_USER_PERMISSIONS.get('features', {})),
'settings': SettingsPermissions(**DEFAULT_USER_PERMISSIONS.get('settings', {})),
}
############################ ############################
# GetUserSettingsBySessionUser # GetUserSettingsBySessionUser
############################ ############################
+27
View File
@@ -85,6 +85,33 @@ export const updateUserDefaultPermissions = async (token: string, permissions: o
return res; return res;
}; };
export const getUserDefaultPermissionsDefaults = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/users/default/permissions/defaults`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.error(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const updateUserRole = async (token: string, id: string, role: string) => { export const updateUserRole = async (token: string, id: string, role: string) => {
let error = null; let error = null;
@@ -10,9 +10,11 @@
import Users from './Users.svelte'; import Users from './Users.svelte';
import GroupPreviewPanel from './GroupPreviewPanel.svelte'; import GroupPreviewPanel from './GroupPreviewPanel.svelte';
import { DEFAULT_PERMISSIONS } from '$lib/constants/permissions'; import { DEFAULT_PERMISSIONS } from '$lib/constants/permissions';
import { getUserDefaultPermissions, getUserDefaultPermissionsDefaults } from '$lib/apis/users';
import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte'; import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte'; import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import XMark from '$lib/components/icons/XMark.svelte'; import XMark from '$lib/components/icons/XMark.svelte';
export let onSubmit: Function = () => {}; export let onSubmit: Function = () => {};
@@ -31,6 +33,7 @@
let selectedTab = 'general'; let selectedTab = 'general';
let loading = false; let loading = false;
let showDeleteConfirmDialog = false; let showDeleteConfirmDialog = false;
let showResetConfirmDialog = false;
let userCount = 0; let userCount = 0;
@@ -56,6 +59,29 @@
show = false; show = false;
}; };
const resetToDefaultsHandler = async () => {
try {
// For user groups: reset to current global default permissions
// For default permissions modal: reset to stock/env-var configuration
const defaults = custom
? await getUserDefaultPermissions(localStorage.token)
: await getUserDefaultPermissionsDefaults(localStorage.token);
if (defaults) {
permissions = {
workspace: { ...DEFAULT_PERMISSIONS.workspace, ...defaults.workspace },
sharing: { ...DEFAULT_PERMISSIONS.sharing, ...defaults.sharing },
access_grants: { ...DEFAULT_PERMISSIONS.access_grants, ...defaults.access_grants },
chat: { ...DEFAULT_PERMISSIONS.chat, ...defaults.chat },
features: { ...DEFAULT_PERMISSIONS.features, ...defaults.features },
settings: { ...DEFAULT_PERMISSIONS.settings, ...defaults.settings }
};
toast.success($i18n.t('Permissions reset to defaults'));
}
} catch (error) {
toast.error(`${error}`);
}
};
const init = () => { const init = () => {
if (group) { if (group) {
name = group.name; name = group.name;
@@ -94,6 +120,17 @@
}} }}
/> />
<ConfirmDialog
bind:show={showResetConfirmDialog}
title={$i18n.t('Reset to Defaults')}
message={$i18n.t(
'Are you sure you want to reset all permissions to their default values? You will still need to save to apply the changes.'
)}
on:confirm={() => {
resetToDefaultsHandler();
}}
/>
<Modal size="lg" bind:show> <Modal size="lg" bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5"> <div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
@@ -250,7 +287,29 @@
</div> </div>
{#if ['general', 'permissions'].includes(selectedTab)} {#if ['general', 'permissions'].includes(selectedTab)}
<div class="flex justify-end pt-3 text-sm font-medium gap-1.5"> <div class="flex justify-between items-center pt-3 text-sm font-medium gap-1.5">
<div>
{#if selectedTab === 'permissions'}
<Tooltip
content={custom
? $i18n.t(
'Reset group permissions to match the current default user permissions'
)
: $i18n.t('Reset all permissions to their initial configuration values')}
>
<button
class="text-sm font-normal text-gray-500 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-300 transition hover:underline"
type="button"
on:click={() => {
showResetConfirmDialog = true;
}}
>
{$i18n.t('Reset to Defaults')}
</button>
</Tooltip>
{/if}
</div>
<button <button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex items-center gap-2 whitespace-nowrap {loading class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex items-center gap-2 whitespace-nowrap {loading
? ' cursor-not-allowed' ? ' cursor-not-allowed'