feat: user permissions

This commit is contained in:
Timothy Jaeryang Baek 2024-11-16 21:31:57 -08:00
parent 057c957f5d
commit cf2dcf1dc3
5 changed files with 16 additions and 221 deletions

View File

@ -17,7 +17,10 @@ from open_webui.constants import ERROR_MESSAGES
from open_webui.env import SRC_LOG_LEVELS
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel
from open_webui.utils.utils import get_admin_user, get_verified_user
from open_webui.utils.access_control import has_permission
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
@ -50,9 +53,10 @@ async def get_session_user_chat_list(
@router.delete("/", response_model=bool)
async def delete_all_user_chats(request: Request, user=Depends(get_verified_user)):
if user.role == "user" and not request.app.state.config.USER_PERMISSIONS.get(
"chat", {}
).get("delete", {}):
if user.role == "user" and not has_permission(
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@ -385,8 +389,8 @@ async def delete_chat_by_id(request: Request, id: str, user=Depends(get_verified
return result
else:
if not request.app.state.config.USER_PERMISSIONS.get("chat", {}).get(
"delete", {}
if not has_permission(
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,

View File

@ -1,214 +0,0 @@
<script lang="ts">
import { getBackendConfig, getModelFilterConfig, updateModelFilterConfig } from '$lib/apis';
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
import { getUserDefaultPermissions, updateUserDefaultPermissions } from '$lib/apis/users';
import { onMount, getContext } from 'svelte';
import { models, config } from '$lib/stores';
import Switch from '$lib/components/common/Switch.svelte';
import { setDefaultModels } from '$lib/apis/configs';
const i18n = getContext('i18n');
export let saveHandler: Function;
let defaultModelId = '';
let whitelistEnabled = false;
let whitelistModels = [''];
let permissions = {
chat: {
deletion: true,
edit: true,
temporary: true
}
};
let chatDeletion = true;
let chatEdit = true;
let chatTemporary = true;
onMount(async () => {
permissions = await getUserDefaultPermissions(localStorage.token);
chatDeletion = permissions?.chat?.deletion ?? true;
chatEdit = permissions?.chat?.editing ?? true;
chatTemporary = permissions?.chat?.temporary ?? true;
const res = await getModelFilterConfig(localStorage.token);
if (res) {
whitelistEnabled = res.enabled;
whitelistModels = res.models.length > 0 ? res.models : [''];
}
defaultModelId = $config.default_models ? $config?.default_models.split(',')[0] : '';
});
</script>
<form
class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={async () => {
// console.log('submit');
await setDefaultModels(localStorage.token, defaultModelId);
await updateUserDefaultPermissions(localStorage.token, {
chat: {
deletion: chatDeletion,
editing: chatEdit,
temporary: chatTemporary
}
});
await updateModelFilterConfig(localStorage.token, whitelistEnabled, whitelistModels);
saveHandler();
await config.set(await getBackendConfig());
}}
>
<div class=" space-y-3 overflow-y-scroll max-h-full pr-1.5">
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('User Permissions')}</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div>
<Switch bind:state={chatDeletion} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Editing')}</div>
<Switch bind:state={chatEdit} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Temporary Chat')}</div>
<Switch bind:state={chatTemporary} />
</div>
</div>
<!-- <hr class=" border-gray-50 dark:border-gray-850 my-2" />
<div class="mt-2 space-y-3">
<div>
<div class="mb-2">
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">{$i18n.t('Manage Models')}</div>
</div>
</div>
<div class=" space-y-1 mb-3">
<div class="mb-2">
<div class="flex justify-between items-center text-xs">
<div class=" text-xs font-medium">{$i18n.t('Default Model')}</div>
</div>
</div>
<div class="flex-1 mr-2">
<select
class="w-full bg-transparent outline-none py-0.5"
bind:value={defaultModelId}
placeholder="Select a model"
>
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{#each $models.filter((model) => model.id) as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
{/each}
</select>
</div>
</div>
<div class=" space-y-1">
<div class="mb-2">
<div class="flex justify-between items-center text-xs my-3 pr-2">
<div class=" text-xs font-medium">{$i18n.t('Model Whitelisting')}</div>
<Switch bind:state={whitelistEnabled} />
</div>
</div>
{#if whitelistEnabled}
<div>
<div class=" space-y-1.5">
{#each whitelistModels as modelId, modelIdx}
<div class="flex w-full">
<div class="flex-1 mr-2">
<select
class="w-full bg-transparent outline-none py-0.5"
bind:value={modelId}
placeholder="Select a model"
>
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{#each $models.filter((model) => model.id) as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700"
>{model.name}</option
>
{/each}
</select>
</div>
{#if modelIdx === 0}
<button
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-900 dark:text-white rounded-lg transition"
type="button"
on:click={() => {
if (whitelistModels.at(-1) !== '') {
whitelistModels = [...whitelistModels, ''];
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
{:else}
<button
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-900 dark:text-white rounded-lg transition"
type="button"
on:click={() => {
whitelistModels.splice(modelIdx, 1);
whitelistModels = whitelistModels;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
{/each}
</div>
<div class="flex justify-end items-center text-xs mt-1.5 text-right">
<div class=" text-xs font-medium">
{whitelistModels.length}
{$i18n.t('Model(s) Whitelisted')}
</div>
</div>
</div>
{/if}
</div>
</div>
</div> -->
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<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"
type="submit"
>
{$i18n.t('Save')}
</button>
</div>
</form>

View File

@ -92,6 +92,11 @@
};
const uploadFileHandler = async (file) => {
if (!($user?.permissions?.chat?.file_upload ?? true)) {
toast.error($i18n.t('You do not have permission to upload files.'));
return null;
}
console.log(file);
const tempItemId = uuidv4();

View File

@ -729,7 +729,7 @@
{#if message.done}
{#if !readOnly}
{#if $user.role === 'user' ? ($config?.permissions?.chat?.editing ?? true) : true}
{#if $user.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
<Tooltip content={$i18n.t('Edit')} placement="bottom">
<button
class="{isLastMessage

View File

@ -48,7 +48,7 @@
model: model
}))}
showTemporaryChatControl={$user.role === 'user'
? ($config?.permissions?.chat?.temporary ?? true)
? ($user?.permissions?.chat?.temporary ?? true)
: true}
bind:value={selectedModel}
/>