mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-13 04:45:19 +00:00
updated decryption patch to 0.6.18
This commit is contained in:
parent
b8912aa671
commit
5788293d23
6 changed files with 191 additions and 1 deletions
|
|
@ -2250,6 +2250,28 @@ RAG_ALLOWED_FILE_EXTENSIONS = PersistentConfig(
|
|||
],
|
||||
)
|
||||
|
||||
# File decryption settings
|
||||
ENABLE_FILE_DECRYPTION = PersistentConfig(
|
||||
"ENABLE_FILE_DECRYPTION",
|
||||
"file_decryption.enable",
|
||||
os.environ.get("ENABLE_FILE_DECRYPTION", "False").lower() == "true",
|
||||
)
|
||||
FILE_DECRYPTION_ENDPOINT = PersistentConfig(
|
||||
"FILE_DECRYPTION_ENDPOINT",
|
||||
"file_decryption.endpoint",
|
||||
os.environ.get("FILE_DECRYPTION_ENDPOINT", ""),
|
||||
)
|
||||
FILE_DECRYPTION_API_KEY = PersistentConfig(
|
||||
"FILE_DECRYPTION_API_KEY",
|
||||
"file_decryption.api_key",
|
||||
os.environ.get("FILE_DECRYPTION_API_KEY", ""),
|
||||
)
|
||||
FILE_DECRYPTION_TIMEOUT = PersistentConfig(
|
||||
"FILE_DECRYPTION_TIMEOUT",
|
||||
"file_decryption.timeout",
|
||||
int(os.environ.get("FILE_DECRYPTION_TIMEOUT", "30")),
|
||||
)
|
||||
|
||||
RAG_EMBEDDING_ENGINE = PersistentConfig(
|
||||
"RAG_EMBEDDING_ENGINE",
|
||||
"rag.embedding_engine",
|
||||
|
|
|
|||
|
|
@ -223,6 +223,10 @@ from open_webui.config import (
|
|||
RAG_AZURE_OPENAI_API_VERSION,
|
||||
RAG_OLLAMA_BASE_URL,
|
||||
RAG_OLLAMA_API_KEY,
|
||||
ENABLE_FILE_DECRYPTION,
|
||||
FILE_DECRYPTION_ENDPOINT,
|
||||
FILE_DECRYPTION_API_KEY,
|
||||
FILE_DECRYPTION_TIMEOUT,
|
||||
CHUNK_OVERLAP,
|
||||
CHUNK_SIZE,
|
||||
CONTENT_EXTRACTION_ENGINE,
|
||||
|
|
@ -818,6 +822,11 @@ app.state.config.RAG_AZURE_OPENAI_API_VERSION = RAG_AZURE_OPENAI_API_VERSION
|
|||
app.state.config.RAG_OLLAMA_BASE_URL = RAG_OLLAMA_BASE_URL
|
||||
app.state.config.RAG_OLLAMA_API_KEY = RAG_OLLAMA_API_KEY
|
||||
|
||||
app.state.config.ENABLE_FILE_DECRYPTION = ENABLE_FILE_DECRYPTION
|
||||
app.state.config.FILE_DECRYPTION_ENDPOINT = FILE_DECRYPTION_ENDPOINT
|
||||
app.state.config.FILE_DECRYPTION_API_KEY = FILE_DECRYPTION_API_KEY
|
||||
app.state.config.FILE_DECRYPTION_TIMEOUT = FILE_DECRYPTION_TIMEOUT
|
||||
|
||||
app.state.config.PDF_EXTRACT_IMAGES = PDF_EXTRACT_IMAGES
|
||||
|
||||
app.state.config.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE
|
||||
|
|
@ -1616,6 +1625,7 @@ async def get_app_config(request: Request):
|
|||
"width": app.state.config.FILE_IMAGE_COMPRESSION_WIDTH,
|
||||
"height": app.state.config.FILE_IMAGE_COMPRESSION_HEIGHT,
|
||||
},
|
||||
"decryption_enabled": app.state.config.ENABLE_FILE_DECRYPTION,
|
||||
},
|
||||
"permissions": {**app.state.config.USER_PERMISSIONS},
|
||||
"google_drive": {
|
||||
|
|
|
|||
|
|
@ -397,6 +397,11 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
|||
"TOP_K_RERANKER": request.app.state.config.TOP_K_RERANKER,
|
||||
"RELEVANCE_THRESHOLD": request.app.state.config.RELEVANCE_THRESHOLD,
|
||||
"HYBRID_BM25_WEIGHT": request.app.state.config.HYBRID_BM25_WEIGHT,
|
||||
# File decryption settings
|
||||
"ENABLE_FILE_DECRYPTION": request.app.state.config.ENABLE_FILE_DECRYPTION,
|
||||
"FILE_DECRYPTION_ENDPOINT": request.app.state.config.FILE_DECRYPTION_ENDPOINT,
|
||||
"FILE_DECRYPTION_API_KEY": request.app.state.config.FILE_DECRYPTION_API_KEY,
|
||||
"FILE_DECRYPTION_TIMEOUT": request.app.state.config.FILE_DECRYPTION_TIMEOUT,
|
||||
# Content extraction settings
|
||||
"CONTENT_EXTRACTION_ENGINE": request.app.state.config.CONTENT_EXTRACTION_ENGINE,
|
||||
"PDF_EXTRACT_IMAGES": request.app.state.config.PDF_EXTRACT_IMAGES,
|
||||
|
|
@ -611,6 +616,12 @@ class ConfigForm(BaseModel):
|
|||
ENABLE_GOOGLE_DRIVE_INTEGRATION: Optional[bool] = None
|
||||
ENABLE_ONEDRIVE_INTEGRATION: Optional[bool] = None
|
||||
|
||||
# File decryption settings
|
||||
ENABLE_FILE_DECRYPTION: Optional[bool] = None
|
||||
FILE_DECRYPTION_ENDPOINT: Optional[str] = None
|
||||
FILE_DECRYPTION_API_KEY: Optional[str] = None
|
||||
FILE_DECRYPTION_TIMEOUT: Optional[int] = None
|
||||
|
||||
# Web search settings
|
||||
web: Optional[WebConfig] = None
|
||||
|
||||
|
|
@ -889,6 +900,28 @@ async def update_rag_config(
|
|||
else request.app.state.config.ENABLE_ONEDRIVE_INTEGRATION
|
||||
)
|
||||
|
||||
# File decryption settings
|
||||
request.app.state.config.ENABLE_FILE_DECRYPTION = (
|
||||
form_data.ENABLE_FILE_DECRYPTION
|
||||
if form_data.ENABLE_FILE_DECRYPTION is not None
|
||||
else request.app.state.config.ENABLE_FILE_DECRYPTION
|
||||
)
|
||||
request.app.state.config.FILE_DECRYPTION_ENDPOINT = (
|
||||
form_data.FILE_DECRYPTION_ENDPOINT
|
||||
if form_data.FILE_DECRYPTION_ENDPOINT is not None
|
||||
else request.app.state.config.FILE_DECRYPTION_ENDPOINT
|
||||
)
|
||||
request.app.state.config.FILE_DECRYPTION_API_KEY = (
|
||||
form_data.FILE_DECRYPTION_API_KEY
|
||||
if form_data.FILE_DECRYPTION_API_KEY is not None
|
||||
else request.app.state.config.FILE_DECRYPTION_API_KEY
|
||||
)
|
||||
request.app.state.config.FILE_DECRYPTION_TIMEOUT = (
|
||||
form_data.FILE_DECRYPTION_TIMEOUT
|
||||
if form_data.FILE_DECRYPTION_TIMEOUT is not None
|
||||
else request.app.state.config.FILE_DECRYPTION_TIMEOUT
|
||||
)
|
||||
|
||||
if form_data.web is not None:
|
||||
# Web search settings
|
||||
request.app.state.config.ENABLE_WEB_SEARCH = form_data.web.ENABLE_WEB_SEARCH
|
||||
|
|
@ -1045,6 +1078,11 @@ async def update_rag_config(
|
|||
# Integration settings
|
||||
"ENABLE_GOOGLE_DRIVE_INTEGRATION": request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION,
|
||||
"ENABLE_ONEDRIVE_INTEGRATION": request.app.state.config.ENABLE_ONEDRIVE_INTEGRATION,
|
||||
# File decryption settings
|
||||
"ENABLE_FILE_DECRYPTION": request.app.state.config.ENABLE_FILE_DECRYPTION,
|
||||
"FILE_DECRYPTION_ENDPOINT": request.app.state.config.FILE_DECRYPTION_ENDPOINT,
|
||||
"FILE_DECRYPTION_API_KEY": request.app.state.config.FILE_DECRYPTION_API_KEY,
|
||||
"FILE_DECRYPTION_TIMEOUT": request.app.state.config.FILE_DECRYPTION_TIMEOUT,
|
||||
# Web search settings
|
||||
"web": {
|
||||
"ENABLE_WEB_SEARCH": request.app.state.config.ENABLE_WEB_SEARCH,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ from open_webui.config import (
|
|||
AZURE_STORAGE_KEY,
|
||||
STORAGE_PROVIDER,
|
||||
UPLOAD_DIR,
|
||||
ENABLE_FILE_DECRYPTION,
|
||||
FILE_DECRYPTION_ENDPOINT,
|
||||
FILE_DECRYPTION_API_KEY,
|
||||
FILE_DECRYPTION_TIMEOUT,
|
||||
)
|
||||
from google.cloud import storage
|
||||
from google.cloud.exceptions import GoogleCloudError, NotFound
|
||||
|
|
@ -35,11 +39,36 @@ from azure.storage.blob import BlobServiceClient
|
|||
from azure.core.exceptions import ResourceNotFoundError
|
||||
from open_webui.env import SRC_LOG_LEVELS
|
||||
|
||||
from open_webui.utils.decryption import (
|
||||
decrypt_file_via_azure,
|
||||
DecryptionError,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(SRC_LOG_LEVELS["MAIN"])
|
||||
|
||||
|
||||
def _decrypt_content_if_enabled(filename: str, contents: bytes) -> bytes:
|
||||
"""Checks config and decrypts file contents if enabled."""
|
||||
|
||||
if ENABLE_FILE_DECRYPTION and FILE_DECRYPTION_ENDPOINT and FILE_DECRYPTION_API_KEY:
|
||||
try:
|
||||
decrypted_contents = decrypt_file_via_azure(
|
||||
filename,
|
||||
contents,
|
||||
FILE_DECRYPTION_ENDPOINT,
|
||||
FILE_DECRYPTION_API_KEY,
|
||||
FILE_DECRYPTION_TIMEOUT,
|
||||
)
|
||||
log.info(f"File {filename} decrypted successfully.")
|
||||
return decrypted_contents
|
||||
except Exception as e:
|
||||
log.error(f"File decryption failed: {e}")
|
||||
# Raise a specific error to be caught by the API layer
|
||||
raise DecryptionError(f"File decryption failed: {e}")
|
||||
return contents
|
||||
|
||||
|
||||
class StorageProvider(ABC):
|
||||
@abstractmethod
|
||||
def get_file(self, file_path: str) -> str:
|
||||
|
|
@ -68,6 +97,10 @@ class LocalStorageProvider(StorageProvider):
|
|||
contents = file.read()
|
||||
if not contents:
|
||||
raise ValueError(ERROR_MESSAGES.EMPTY_CONTENT)
|
||||
|
||||
# Decrypt contents if decryption is enabled in the config
|
||||
contents = _decrypt_content_if_enabled(filename, contents)
|
||||
|
||||
file_path = f"{UPLOAD_DIR}/{filename}"
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(contents)
|
||||
|
|
|
|||
|
|
@ -136,6 +136,14 @@
|
|||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
if (
|
||||
RAGConfig.ENABLE_FILE_DECRYPTION &&
|
||||
(!RAGConfig.FILE_DECRYPTION_ENDPOINT || !RAGConfig.FILE_DECRYPTION_API_KEY)
|
||||
) {
|
||||
toast.error($i18n.t('Endpoint URL and API Key are required when file decryption is enabled.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
RAGConfig.CONTENT_EXTRACTION_ENGINE === 'external' &&
|
||||
RAGConfig.EXTERNAL_DOCUMENT_LOADER_URL === ''
|
||||
|
|
@ -192,6 +200,10 @@
|
|||
|
||||
const res = await updateRAGConfig(localStorage.token, {
|
||||
...RAGConfig,
|
||||
ENABLE_FILE_DECRYPTION: RAGConfig.ENABLE_FILE_DECRYPTION ?? false,
|
||||
FILE_DECRYPTION_ENDPOINT: RAGConfig.FILE_DECRYPTION_ENDPOINT ?? '',
|
||||
FILE_DECRYPTION_API_KEY: RAGConfig.FILE_DECRYPTION_API_KEY ?? '',
|
||||
FILE_DECRYPTION_TIMEOUT: RAGConfig.FILE_DECRYPTION_TIMEOUT ?? 30,
|
||||
ALLOWED_FILE_EXTENSIONS: RAGConfig.ALLOWED_FILE_EXTENSIONS.split(',')
|
||||
.map((ext) => ext.trim())
|
||||
.filter((ext) => ext !== ''),
|
||||
|
|
@ -1058,6 +1070,62 @@
|
|||
|
||||
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
||||
<div class="mb-2.5 flex w-full justify-between">
|
||||
<div class="self-center text-xs font-medium">{$i18n.t('File Decryption')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<Switch bind:state={RAGConfig.ENABLE_FILE_DECRYPTION} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if RAGConfig.ENABLE_FILE_DECRYPTION}
|
||||
<div
|
||||
class="space-y-2.5 pl-6 border-l-2 border-gray-100 dark:border-gray-800 ml-1"
|
||||
>
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="self-center text-xs font-medium">
|
||||
{$i18n.t('Decryption Endpoint URL')}
|
||||
</div>
|
||||
<input
|
||||
class="flex-1 w-full text-sm bg-transparent outline-hidden text-right"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Azure Function Endpoint')}
|
||||
bind:value={RAGConfig.FILE_DECRYPTION_ENDPOINT}
|
||||
autocomplete="off"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="self-center text-xs font-medium">{$i18n.t('API Key')}</div>
|
||||
<div class="max-w-[180px]">
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('Enter API Key')}
|
||||
bind:value={RAGConfig.FILE_DECRYPTION_API_KEY}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="self-center text-xs font-medium">{$i18n.t('Timeout (seconds)')}</div>
|
||||
<input
|
||||
class="w-auto text-sm bg-transparent outline-hidden text-right"
|
||||
type="number"
|
||||
placeholder="30"
|
||||
bind:value={RAGConfig.FILE_DECRYPTION_TIMEOUT}
|
||||
autocomplete="off"
|
||||
min="1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if !RAGConfig.FILE_DECRYPTION_ENDPOINT || !RAGConfig.FILE_DECRYPTION_API_KEY}
|
||||
<div class="text-xs text-red-500 text-right">
|
||||
{$i18n.t('Endpoint URL and API Key are required when file decryption is enabled.')}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class=" mb-2.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Allowed File Extensions')}</div>
|
||||
<div class="flex items-center relative">
|
||||
|
|
|
|||
|
|
@ -533,7 +533,15 @@
|
|||
files = [...files, fileItem];
|
||||
|
||||
if (!$temporaryChatEnabled) {
|
||||
let loadingToastId = null;
|
||||
|
||||
try {
|
||||
// Show loading toast for decryption if enabled in config
|
||||
if ($config?.file?.decryption_enabled ?? false) {
|
||||
loadingToastId = toast.loading($i18n.t('Decrypting and uploading file...'));
|
||||
} else {
|
||||
loadingToastId = toast.loading($i18n.t('Uploading file...'));
|
||||
}
|
||||
// If the file is an audio file, provide the language for STT.
|
||||
let metadata = null;
|
||||
if (
|
||||
|
|
@ -568,11 +576,22 @@
|
|||
fileItem.url = `${WEBUI_API_BASE_URL}/files/${uploadedFile.id}`;
|
||||
|
||||
files = files;
|
||||
toast.success($i18n.t('File uploaded successfully.'), { id: loadingToastId });
|
||||
} else {
|
||||
files = files.filter((item) => item?.itemId !== tempItemId);
|
||||
toast.dismiss(loadingToastId);
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error(`${e}`);
|
||||
toast.dismiss(loadingToastId);
|
||||
const errMsg = typeof e === 'string' ? e : (e?.message || `${e}`);
|
||||
if (
|
||||
errMsg.toLowerCase().includes('decryption') ||
|
||||
errMsg.toLowerCase().includes('azure function error')
|
||||
) {
|
||||
toast.error($i18n.t('File decryption failed: {{error}}', { error: errMsg }));
|
||||
} else {
|
||||
toast.error(`${errMsg}`);
|
||||
}
|
||||
files = files.filter((item) => item?.itemId !== tempItemId);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue