|
6 | 6 |
|
7 | 7 | from open_webui.internal.db import Base, JSONField, get_async_db_context |
8 | 8 | from pydantic import BaseModel, ConfigDict |
9 | | -from sqlalchemy import JSON, BigInteger, Boolean, Column, Text, delete, func, select |
| 9 | +from sqlalchemy import JSON, BigInteger, Boolean, Column, Text, delete, func, select, or_, and_ |
10 | 10 | from sqlalchemy.ext.asyncio import AsyncSession |
11 | 11 |
|
12 | 12 | log = logging.getLogger(__name__) |
@@ -62,6 +62,20 @@ class FolderNameIdResponse(BaseModel): |
62 | 62 | updated_at: int |
63 | 63 |
|
64 | 64 |
|
| 65 | +class SharedFolderResponse(BaseModel): |
| 66 | + id: str |
| 67 | + name: str |
| 68 | + parent_id: Optional[str] = None |
| 69 | + user_id: str |
| 70 | + owner_name: Optional[str] = None |
| 71 | + permission: str = 'read' |
| 72 | + access_grants: list = [] |
| 73 | + is_expanded: bool = False |
| 74 | + meta: Optional[dict] = None |
| 75 | + created_at: int |
| 76 | + updated_at: int |
| 77 | + |
| 78 | + |
65 | 79 | #################### |
66 | 80 | # Forms |
67 | 81 | #################### |
@@ -130,6 +144,56 @@ async def get_folder_by_id_and_user_id( |
130 | 144 | except Exception: |
131 | 145 | return None |
132 | 146 |
|
| 147 | + async def get_folder_by_id( |
| 148 | + self, id: str, db: Optional[AsyncSession] = None |
| 149 | + ) -> Optional[FolderModel]: |
| 150 | + """Fetch folder by ID only (no user_id filter). Used for shared access.""" |
| 151 | + try: |
| 152 | + async with get_async_db_context(db) as db: |
| 153 | + result = await db.execute(select(Folder).filter_by(id=id)) |
| 154 | + folder = result.scalars().first() |
| 155 | + if not folder: |
| 156 | + return None |
| 157 | + return FolderModel.model_validate(folder) |
| 158 | + except Exception: |
| 159 | + return None |
| 160 | + |
| 161 | + async def get_shared_folder_ids_for_user( |
| 162 | + self, user_id: str, user_group_ids: set[str], |
| 163 | + db: Optional[AsyncSession] = None |
| 164 | + ) -> dict[str, str]: |
| 165 | + """ |
| 166 | + Returns {folder_id: highest_permission} for all folders shared with user. |
| 167 | + Checks direct user grants, group grants, and public (user:*) grants. |
| 168 | + """ |
| 169 | + from open_webui.models.access_grants import AccessGrant |
| 170 | + |
| 171 | + async with get_async_db_context(db) as db: |
| 172 | + conditions = [ |
| 173 | + and_(AccessGrant.principal_type == 'user', AccessGrant.principal_id == '*'), |
| 174 | + and_(AccessGrant.principal_type == 'user', AccessGrant.principal_id == user_id), |
| 175 | + ] |
| 176 | + if user_group_ids: |
| 177 | + conditions.append( |
| 178 | + and_(AccessGrant.principal_type == 'group', |
| 179 | + AccessGrant.principal_id.in_(user_group_ids)) |
| 180 | + ) |
| 181 | + result = await db.execute( |
| 182 | + select(AccessGrant).filter( |
| 183 | + AccessGrant.resource_type == 'folder', |
| 184 | + or_(*conditions), |
| 185 | + ) |
| 186 | + ) |
| 187 | + grants = result.scalars().all() |
| 188 | + |
| 189 | + # Build {folder_id: highest_permission} ('write' > 'read') |
| 190 | + folder_perms = {} |
| 191 | + for g in grants: |
| 192 | + existing = folder_perms.get(g.resource_id) |
| 193 | + if existing != 'write': |
| 194 | + folder_perms[g.resource_id] = g.permission |
| 195 | + return folder_perms |
| 196 | + |
133 | 197 | async def get_children_folders_by_id_and_user_id( |
134 | 198 | self, id: str, user_id: str, db: Optional[AsyncSession] = None |
135 | 199 | ) -> Optional[list[FolderModel]]: |
|
0 commit comments