refac
This commit is contained in:
@@ -22,6 +22,7 @@ from sqlalchemy import (
|
|||||||
ForeignKey,
|
ForeignKey,
|
||||||
cast,
|
cast,
|
||||||
or_,
|
or_,
|
||||||
|
select,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -99,6 +100,16 @@ class GroupResponse(GroupModel):
|
|||||||
member_count: Optional[int] = None
|
member_count: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class GroupInfoResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
user_id: str
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
member_count: Optional[int] = None
|
||||||
|
created_at: int
|
||||||
|
updated_at: int
|
||||||
|
|
||||||
|
|
||||||
class GroupForm(BaseModel):
|
class GroupForm(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
@@ -181,14 +192,12 @@ class GroupTable:
|
|||||||
|
|
||||||
if member_id:
|
if member_id:
|
||||||
# Also include member-only groups where user is a member
|
# Also include member-only groups where user is a member
|
||||||
member_groups_subq = (
|
member_groups_select = select(GroupMember.group_id).where(
|
||||||
db.query(GroupMember.group_id)
|
GroupMember.user_id == member_id
|
||||||
.filter(GroupMember.user_id == member_id)
|
|
||||||
.subquery()
|
|
||||||
)
|
)
|
||||||
members_only_and_is_member = and_(
|
members_only_and_is_member = and_(
|
||||||
json_share_lower == "members",
|
json_share_lower == "members",
|
||||||
Group.id.in_(member_groups_subq),
|
Group.id.in_(member_groups_select),
|
||||||
)
|
)
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
or_(anyone_can_share, members_only_and_is_member)
|
or_(anyone_can_share, members_only_and_is_member)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from open_webui.models.channels import (
|
|||||||
ChannelWebhookModel,
|
ChannelWebhookModel,
|
||||||
ChannelWebhookForm,
|
ChannelWebhookForm,
|
||||||
)
|
)
|
||||||
|
from open_webui.models.access_grants import AccessGrants, has_public_read_access_grant
|
||||||
from open_webui.models.messages import (
|
from open_webui.models.messages import (
|
||||||
Messages,
|
Messages,
|
||||||
MessageModel,
|
MessageModel,
|
||||||
@@ -60,12 +61,7 @@ from open_webui.utils.chat import generate_chat_completion
|
|||||||
|
|
||||||
|
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import (
|
from open_webui.utils.access_control import has_permission
|
||||||
has_access,
|
|
||||||
get_users_with_access,
|
|
||||||
get_permitted_group_and_user_ids,
|
|
||||||
has_permission,
|
|
||||||
)
|
|
||||||
from open_webui.utils.webhook import post_webhook
|
from open_webui.utils.webhook import post_webhook
|
||||||
from open_webui.utils.channels import extract_mentions, replace_mentions
|
from open_webui.utils.channels import extract_mentions, replace_mentions
|
||||||
from open_webui.internal.db import get_session
|
from open_webui.internal.db import get_session
|
||||||
@@ -76,6 +72,66 @@ log = logging.getLogger(__name__)
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def channel_has_access(
|
||||||
|
user_id: str,
|
||||||
|
channel: ChannelModel,
|
||||||
|
permission: str = "read",
|
||||||
|
strict: bool = True,
|
||||||
|
db: Optional[Session] = None,
|
||||||
|
) -> bool:
|
||||||
|
if AccessGrants.has_access(
|
||||||
|
user_id=user_id,
|
||||||
|
resource_type="channel",
|
||||||
|
resource_id=channel.id,
|
||||||
|
permission=permission,
|
||||||
|
db=db,
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if (
|
||||||
|
not strict
|
||||||
|
and permission == "write"
|
||||||
|
and has_public_read_access_grant(channel.access_grants)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_channel_users_with_access(
|
||||||
|
channel: ChannelModel, permission: str = "read", db: Optional[Session] = None
|
||||||
|
):
|
||||||
|
return AccessGrants.get_users_with_access(
|
||||||
|
resource_type="channel",
|
||||||
|
resource_id=channel.id,
|
||||||
|
permission=permission,
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_channel_permitted_group_and_user_ids(
|
||||||
|
channel: ChannelModel, permission: str = "read"
|
||||||
|
) -> Optional[dict[str, list[str]]]:
|
||||||
|
if permission == "read" and has_public_read_access_grant(channel.access_grants):
|
||||||
|
return None
|
||||||
|
|
||||||
|
user_ids = []
|
||||||
|
group_ids = []
|
||||||
|
|
||||||
|
for grant in channel.access_grants:
|
||||||
|
if grant.permission != permission:
|
||||||
|
continue
|
||||||
|
if grant.principal_type == "group":
|
||||||
|
group_ids.append(grant.principal_id)
|
||||||
|
elif grant.principal_type == "user" and grant.principal_id != "*":
|
||||||
|
user_ids.append(grant.principal_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"user_ids": list(dict.fromkeys(user_ids)),
|
||||||
|
"group_ids": list(dict.fromkeys(group_ids)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# Channels Enabled Dependency
|
# Channels Enabled Dependency
|
||||||
############################
|
############################
|
||||||
@@ -418,22 +474,22 @@ async def get_channel_by_id(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
|
|
||||||
write_access = has_access(
|
write_access = channel_has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="write",
|
channel,
|
||||||
access_control=channel.access_control,
|
permission="write",
|
||||||
strict=False,
|
strict=False,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
|
||||||
user_count = len(get_users_with_access("read", channel.access_control))
|
user_count = len(get_channel_users_with_access(channel, "read", db=db))
|
||||||
|
|
||||||
channel_member = Channels.get_member_by_channel_and_user_id(
|
channel_member = Channels.get_member_by_channel_and_user_id(
|
||||||
channel.id, user.id, db=db
|
channel.id, user.id, db=db
|
||||||
@@ -527,8 +583,8 @@ async def get_channel_members_by_id(
|
|||||||
filter["channel_id"] = channel.id
|
filter["channel_id"] = channel.id
|
||||||
else:
|
else:
|
||||||
filter["roles"] = ["!pending"]
|
filter["roles"] = ["!pending"]
|
||||||
permitted_ids = get_permitted_group_and_user_ids(
|
permitted_ids = get_channel_permitted_group_and_user_ids(
|
||||||
"read", channel.access_control
|
channel, permission="read"
|
||||||
)
|
)
|
||||||
if permitted_ids:
|
if permitted_ids:
|
||||||
filter["user_ids"] = permitted_ids.get("user_ids")
|
filter["user_ids"] = permitted_ids.get("user_ids")
|
||||||
@@ -811,8 +867,8 @@ async def get_channel_messages(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -888,8 +944,8 @@ async def get_pinned_channel_messages(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -946,7 +1002,7 @@ async def get_pinned_channel_messages(
|
|||||||
async def send_notification(
|
async def send_notification(
|
||||||
name, webui_url, channel, message, active_user_ids, db=None
|
name, webui_url, channel, message, active_user_ids, db=None
|
||||||
):
|
):
|
||||||
users = get_users_with_access("read", channel.access_control)
|
users = get_channel_users_with_access(channel, "read", db=db)
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
if (user.id not in active_user_ids) and Channels.is_user_channel_member(
|
if (user.id not in active_user_ids) and Channels.is_user_channel_member(
|
||||||
@@ -1173,10 +1229,10 @@ async def new_message_handler(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="write",
|
channel,
|
||||||
access_control=channel.access_control,
|
permission="write",
|
||||||
strict=False,
|
strict=False,
|
||||||
db=db,
|
db=db,
|
||||||
):
|
):
|
||||||
@@ -1318,8 +1374,8 @@ async def get_channel_message(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -1372,8 +1428,8 @@ async def get_channel_message_data(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -1426,8 +1482,8 @@ async def pin_channel_message(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -1492,8 +1548,8 @@ async def get_channel_thread_messages(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
@@ -1577,8 +1633,8 @@ async def update_message_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and message.user_id != user.id
|
and message.user_id != user.id
|
||||||
and not has_access(
|
and not channel_has_access(
|
||||||
user.id, type="read", access_control=channel.access_control, db=db
|
user.id, channel, permission="read", db=db
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -1644,10 +1700,10 @@ async def add_reaction_to_message(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="write",
|
channel,
|
||||||
access_control=channel.access_control,
|
permission="write",
|
||||||
strict=False,
|
strict=False,
|
||||||
db=db,
|
db=db,
|
||||||
):
|
):
|
||||||
@@ -1723,10 +1779,10 @@ async def remove_reaction_by_id_and_user_id_and_name(
|
|||||||
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if user.role != "admin" and not has_access(
|
if user.role != "admin" and not channel_has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="write",
|
channel,
|
||||||
access_control=channel.access_control,
|
permission="write",
|
||||||
strict=False,
|
strict=False,
|
||||||
db=db,
|
db=db,
|
||||||
):
|
):
|
||||||
@@ -1818,10 +1874,10 @@ async def delete_message_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and message.user_id != user.id
|
and message.user_id != user.id
|
||||||
and not has_access(
|
and not channel_has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="write",
|
channel,
|
||||||
access_control=channel.access_control,
|
permission="write",
|
||||||
strict=False,
|
strict=False,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from open_webui.models.files import (
|
|||||||
from open_webui.models.chats import Chats
|
from open_webui.models.chats import Chats
|
||||||
from open_webui.models.knowledge import Knowledges
|
from open_webui.models.knowledge import Knowledges
|
||||||
from open_webui.models.groups import Groups
|
from open_webui.models.groups import Groups
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
|
|
||||||
from open_webui.routers.retrieval import ProcessFileForm, process_file
|
from open_webui.routers.retrieval import ProcessFileForm, process_file
|
||||||
@@ -47,7 +48,6 @@ from open_webui.storage.provider import Storage
|
|||||||
|
|
||||||
|
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access
|
|
||||||
from open_webui.utils.misc import strict_match_mime_type
|
from open_webui.utils.misc import strict_match_mime_type
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -82,8 +82,13 @@ def has_access_to_file(
|
|||||||
group.id for group in Groups.get_groups_by_member_id(user.id, db=db)
|
group.id for group in Groups.get_groups_by_member_id(user.id, db=db)
|
||||||
}
|
}
|
||||||
for knowledge_base in knowledge_bases:
|
for knowledge_base in knowledge_bases:
|
||||||
if knowledge_base.user_id == user.id or has_access(
|
if knowledge_base.user_id == user.id or AccessGrants.has_access(
|
||||||
user.id, access_type, knowledge_base.access_control, user_group_ids, db=db
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge_base.id,
|
||||||
|
permission=access_type,
|
||||||
|
user_group_ids=user_group_ids,
|
||||||
|
db=db,
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from open_webui.models.users import Users, UserInfoResponse
|
|||||||
from open_webui.models.groups import (
|
from open_webui.models.groups import (
|
||||||
Groups,
|
Groups,
|
||||||
GroupForm,
|
GroupForm,
|
||||||
|
GroupInfoResponse,
|
||||||
GroupUpdateForm,
|
GroupUpdateForm,
|
||||||
GroupResponse,
|
GroupResponse,
|
||||||
UserIdsForm,
|
UserIdsForm,
|
||||||
@@ -104,6 +105,23 @@ async def get_group_by_id(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/id/{id}/info", response_model=Optional[GroupInfoResponse])
|
||||||
|
async def get_group_info_by_id(
|
||||||
|
id: str, user=Depends(get_verified_user), db: Session = Depends(get_session)
|
||||||
|
):
|
||||||
|
group = Groups.get_group_by_id(id, db=db)
|
||||||
|
if group:
|
||||||
|
return GroupInfoResponse(
|
||||||
|
**group.model_dump(),
|
||||||
|
member_count=Groups.get_group_member_count_by_id(group.id, db=db),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# ExportGroupById
|
# ExportGroupById
|
||||||
############################
|
############################
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ from open_webui.storage.provider import Storage
|
|||||||
|
|
||||||
from open_webui.constants import ERROR_MESSAGES
|
from open_webui.constants import ERROR_MESSAGES
|
||||||
from open_webui.utils.auth import get_verified_user, get_admin_user
|
from open_webui.utils.auth import get_verified_user, get_admin_user
|
||||||
from open_webui.utils.access_control import has_access, has_permission
|
from open_webui.utils.access_control import has_permission
|
||||||
|
from open_webui.models.access_grants import AccessGrants, has_public_read_access_grant
|
||||||
|
|
||||||
|
|
||||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||||
@@ -133,8 +134,12 @@ async def get_knowledge_bases(
|
|||||||
write_access=(
|
write_access=(
|
||||||
user.id == knowledge_base.user_id
|
user.id == knowledge_base.user_id
|
||||||
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id, "write", knowledge_base.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge_base.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -180,8 +185,12 @@ async def search_knowledge_bases(
|
|||||||
write_access=(
|
write_access=(
|
||||||
user.id == knowledge_base.user_id
|
user.id == knowledge_base.user_id
|
||||||
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id, "write", knowledge_base.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge_base.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -243,14 +252,14 @@ async def create_new_knowledge(
|
|||||||
# Check if user can share publicly
|
# Check if user can share publicly
|
||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and form_data.access_control == None
|
and has_public_read_access_grant(form_data.access_grants)
|
||||||
and not has_permission(
|
and not has_permission(
|
||||||
user.id,
|
user.id,
|
||||||
"sharing.public_knowledge",
|
"sharing.public_knowledge",
|
||||||
request.app.state.config.USER_PERMISSIONS,
|
request.app.state.config.USER_PERMISSIONS,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
form_data.access_control = {}
|
form_data.access_grants = []
|
||||||
|
|
||||||
knowledge = Knowledges.insert_new_knowledge(user.id, form_data)
|
knowledge = Knowledges.insert_new_knowledge(user.id, form_data)
|
||||||
|
|
||||||
@@ -387,7 +396,13 @@ async def get_knowledge_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or knowledge.user_id == user.id
|
or knowledge.user_id == user.id
|
||||||
or has_access(user.id, "read", knowledge.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
|
|
||||||
return KnowledgeFilesResponse(
|
return KnowledgeFilesResponse(
|
||||||
@@ -395,7 +410,13 @@ async def get_knowledge_by_id(
|
|||||||
write_access=(
|
write_access=(
|
||||||
user.id == knowledge.user_id
|
user.id == knowledge.user_id
|
||||||
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
or (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or has_access(user.id, "write", knowledge.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -435,7 +456,12 @@ async def update_knowledge_by_id(
|
|||||||
# Is the user the original creator, in a group with write access, or an admin
|
# Is the user the original creator, in a group with write access, or an admin
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -446,14 +472,14 @@ async def update_knowledge_by_id(
|
|||||||
# Check if user can share publicly
|
# Check if user can share publicly
|
||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and form_data.access_control == None
|
and has_public_read_access_grant(form_data.access_grants)
|
||||||
and not has_permission(
|
and not has_permission(
|
||||||
user.id,
|
user.id,
|
||||||
"sharing.public_knowledge",
|
"sharing.public_knowledge",
|
||||||
request.app.state.config.USER_PERMISSIONS,
|
request.app.state.config.USER_PERMISSIONS,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
form_data.access_control = {}
|
form_data.access_grants = []
|
||||||
|
|
||||||
knowledge = Knowledges.update_knowledge_by_id(id=id, form_data=form_data)
|
knowledge = Knowledges.update_knowledge_by_id(id=id, form_data=form_data)
|
||||||
if knowledge:
|
if knowledge:
|
||||||
@@ -502,7 +528,13 @@ async def get_knowledge_files_by_id(
|
|||||||
if not (
|
if not (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or knowledge.user_id == user.id
|
or knowledge.user_id == user.id
|
||||||
or has_access(user.id, "read", knowledge.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
@@ -555,7 +587,13 @@ def add_file_to_knowledge_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -624,7 +662,13 @@ def update_file_from_knowledge_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
|
|
||||||
@@ -693,7 +737,13 @@ def remove_file_from_knowledge_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -770,7 +820,13 @@ async def delete_knowledge_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -802,7 +858,7 @@ async def delete_knowledge_by_id(
|
|||||||
base_model_id=model.base_model_id,
|
base_model_id=model.base_model_id,
|
||||||
meta=model.meta,
|
meta=model.meta,
|
||||||
params=model.params,
|
params=model.params,
|
||||||
access_control=model.access_control,
|
access_grants=model.access_grants,
|
||||||
is_active=model.is_active,
|
is_active=model.is_active,
|
||||||
)
|
)
|
||||||
Models.update_model_by_id(model.id, model_form, db=db)
|
Models.update_model_by_id(model.id, model_form, db=db)
|
||||||
@@ -839,7 +895,13 @@ async def reset_knowledge_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -882,7 +944,13 @@ async def add_files_to_knowledge_batch(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
knowledge.user_id != user.id
|
knowledge.user_id != user.id
|
||||||
and not has_access(user.id, "write", knowledge.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from open_webui.models.models import (
|
|||||||
ModelAccessResponse,
|
ModelAccessResponse,
|
||||||
Models,
|
Models,
|
||||||
)
|
)
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from open_webui.constants import ERROR_MESSAGES
|
from open_webui.constants import ERROR_MESSAGES
|
||||||
@@ -30,7 +31,7 @@ from fastapi.responses import FileResponse, StreamingResponse
|
|||||||
|
|
||||||
|
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access, has_permission
|
from open_webui.utils.access_control import has_permission
|
||||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, STATIC_DIR
|
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, STATIC_DIR
|
||||||
from open_webui.internal.db import get_session
|
from open_webui.internal.db import get_session
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -98,7 +99,13 @@ async def get_models(
|
|||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == model.user_id
|
or user.id == model.user_id
|
||||||
or has_access(user.id, "write", model.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for model in result.items
|
for model in result.items
|
||||||
@@ -315,14 +322,26 @@ async def get_model_by_id(
|
|||||||
if (
|
if (
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or model.user_id == user.id
|
or model.user_id == user.id
|
||||||
or has_access(user.id, "read", model.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return ModelAccessResponse(
|
return ModelAccessResponse(
|
||||||
**model.model_dump(),
|
**model.model_dump(),
|
||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == model.user_id
|
or user.id == model.user_id
|
||||||
or has_access(user.id, "write", model.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -393,7 +412,13 @@ async def toggle_model_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or model.user_id == user.id
|
or model.user_id == user.id
|
||||||
or has_access(user.id, "write", model.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
model = Models.toggle_model_by_id(id, db=db)
|
model = Models.toggle_model_by_id(id, db=db)
|
||||||
|
|
||||||
@@ -436,7 +461,13 @@ async def update_model_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
model.user_id != user.id
|
model.user_id != user.id
|
||||||
and not has_access(user.id, "write", model.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -471,7 +502,13 @@ async def delete_model_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and model.user_id != user.id
|
and model.user_id != user.id
|
||||||
and not has_access(user.id, "write", model.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ from open_webui.constants import ERROR_MESSAGES
|
|||||||
|
|
||||||
|
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access, has_permission
|
from open_webui.utils.access_control import has_permission
|
||||||
|
from open_webui.models.access_grants import AccessGrants, has_public_read_access_grant
|
||||||
from open_webui.internal.db import get_session
|
from open_webui.internal.db import get_session
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -200,8 +201,12 @@ async def get_note_by_id(
|
|||||||
if user.role != "admin" and (
|
if user.role != "admin" and (
|
||||||
user.id != note.user_id
|
user.id != note.user_id
|
||||||
and (
|
and (
|
||||||
not has_access(
|
not AccessGrants.has_access(
|
||||||
user.id, type="read", access_control=note.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
@@ -212,13 +217,14 @@ async def get_note_by_id(
|
|||||||
write_access = (
|
write_access = (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or (user.id == note.user_id)
|
or (user.id == note.user_id)
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="write",
|
resource_type="note",
|
||||||
access_control=note.access_control,
|
resource_id=note.id,
|
||||||
strict=False,
|
permission="write",
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
or has_public_read_access_grant(note.access_grants)
|
||||||
)
|
)
|
||||||
|
|
||||||
return NoteResponse(**note.model_dump(), write_access=write_access)
|
return NoteResponse(**note.model_dump(), write_access=write_access)
|
||||||
@@ -253,8 +259,12 @@ async def update_note_by_id(
|
|||||||
|
|
||||||
if user.role != "admin" and (
|
if user.role != "admin" and (
|
||||||
user.id != note.user_id
|
user.id != note.user_id
|
||||||
and not has_access(
|
and not AccessGrants.has_access(
|
||||||
user.id, type="write", access_control=note.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -264,7 +274,7 @@ async def update_note_by_id(
|
|||||||
# Check if user can share publicly
|
# Check if user can share publicly
|
||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and form_data.access_control == None
|
and has_public_read_access_grant(form_data.access_grants)
|
||||||
and not has_permission(
|
and not has_permission(
|
||||||
user.id,
|
user.id,
|
||||||
"sharing.public_notes",
|
"sharing.public_notes",
|
||||||
@@ -272,7 +282,7 @@ async def update_note_by_id(
|
|||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
form_data.access_control = {}
|
form_data.access_grants = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
note = Notes.update_note_by_id(id, form_data, db=db)
|
note = Notes.update_note_by_id(id, form_data, db=db)
|
||||||
@@ -318,8 +328,12 @@ async def delete_note_by_id(
|
|||||||
|
|
||||||
if user.role != "admin" and (
|
if user.role != "admin" and (
|
||||||
user.id != note.user_id
|
user.id != note.user_id
|
||||||
and not has_access(
|
and not AccessGrants.has_access(
|
||||||
user.id, type="write", access_control=note.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ from open_webui.internal.db import get_session
|
|||||||
|
|
||||||
|
|
||||||
from open_webui.models.models import Models
|
from open_webui.models.models import Models
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.utils.misc import (
|
from open_webui.utils.misc import (
|
||||||
calculate_sha256,
|
calculate_sha256,
|
||||||
)
|
)
|
||||||
@@ -53,9 +54,6 @@ from open_webui.utils.payload import (
|
|||||||
apply_system_prompt_to_body,
|
apply_system_prompt_to_body,
|
||||||
)
|
)
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access
|
|
||||||
|
|
||||||
|
|
||||||
from open_webui.config import (
|
from open_webui.config import (
|
||||||
UPLOAD_DIR,
|
UPLOAD_DIR,
|
||||||
)
|
)
|
||||||
@@ -430,8 +428,12 @@ async def get_filtered_models(models, user, db=None):
|
|||||||
for model in models.get("models", []):
|
for model in models.get("models", []):
|
||||||
model_info = Models.get_model_by_id(model["model"], db=db)
|
model_info = Models.get_model_by_id(model["model"], db=db)
|
||||||
if model_info:
|
if model_info:
|
||||||
if user.id == model_info.user_id or has_access(
|
if user.id == model_info.user_id or AccessGrants.has_access(
|
||||||
user.id, type="read", access_control=model_info.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
):
|
):
|
||||||
filtered_models.append(model)
|
filtered_models.append(model)
|
||||||
return filtered_models
|
return filtered_models
|
||||||
@@ -1259,7 +1261,7 @@ async def generate_chat_completion(
|
|||||||
bypass_system_prompt: bool = False,
|
bypass_system_prompt: bool = False,
|
||||||
):
|
):
|
||||||
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
||||||
# Database operations (get_model_by_id, has_access) manage their own short-lived sessions.
|
# Database operations (get_model_by_id, AccessGrants.has_access) manage their own short-lived sessions.
|
||||||
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
||||||
# which would exhaust the connection pool under concurrent load.
|
# which would exhaust the connection pool under concurrent load.
|
||||||
if BYPASS_MODEL_ACCESS_CONTROL:
|
if BYPASS_MODEL_ACCESS_CONTROL:
|
||||||
@@ -1306,10 +1308,11 @@ async def generate_chat_completion(
|
|||||||
if not bypass_filter and user.role == "user":
|
if not bypass_filter and user.role == "user":
|
||||||
if not (
|
if not (
|
||||||
user.id == model_info.user_id
|
user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -1383,7 +1386,7 @@ async def generate_openai_completion(
|
|||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
):
|
):
|
||||||
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
||||||
# Database operations (get_model_by_id, has_access) manage their own short-lived sessions.
|
# Database operations (get_model_by_id, AccessGrants.has_access) manage their own short-lived sessions.
|
||||||
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
||||||
# which would exhaust the connection pool under concurrent load.
|
# which would exhaust the connection pool under concurrent load.
|
||||||
metadata = form_data.pop("metadata", None)
|
metadata = form_data.pop("metadata", None)
|
||||||
@@ -1418,10 +1421,11 @@ async def generate_openai_completion(
|
|||||||
if user.role == "user":
|
if user.role == "user":
|
||||||
if not (
|
if not (
|
||||||
user.id == model_info.user_id
|
user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -1468,7 +1472,7 @@ async def generate_openai_chat_completion(
|
|||||||
user=Depends(get_verified_user),
|
user=Depends(get_verified_user),
|
||||||
):
|
):
|
||||||
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
||||||
# Database operations (get_model_by_id, has_access) manage their own short-lived sessions.
|
# Database operations (get_model_by_id, AccessGrants.has_access) manage their own short-lived sessions.
|
||||||
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
||||||
# which would exhaust the connection pool under concurrent load.
|
# which would exhaust the connection pool under concurrent load.
|
||||||
metadata = form_data.pop("metadata", None)
|
metadata = form_data.pop("metadata", None)
|
||||||
@@ -1507,10 +1511,11 @@ async def generate_openai_chat_completion(
|
|||||||
if user.role == "user":
|
if user.role == "user":
|
||||||
if not (
|
if not (
|
||||||
user.id == model_info.user_id
|
user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -1608,10 +1613,11 @@ async def get_openai_models(
|
|||||||
for model in models:
|
for model in models:
|
||||||
model_info = Models.get_model_by_id(model["id"], db=db)
|
model_info = Models.get_model_by_id(model["id"], db=db)
|
||||||
if model_info:
|
if model_info:
|
||||||
if user.id == model_info.user_id or has_access(
|
if user.id == model_info.user_id or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
db=db,
|
db=db,
|
||||||
):
|
):
|
||||||
filtered_models.append(model)
|
filtered_models.append(model)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from sqlalchemy.orm import Session
|
|||||||
from open_webui.internal.db import get_session
|
from open_webui.internal.db import get_session
|
||||||
|
|
||||||
from open_webui.models.models import Models
|
from open_webui.models.models import Models
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.config import (
|
from open_webui.config import (
|
||||||
CACHE_DIR,
|
CACHE_DIR,
|
||||||
)
|
)
|
||||||
@@ -50,7 +51,6 @@ from open_webui.utils.misc import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access
|
|
||||||
from open_webui.utils.headers import include_user_info_headers
|
from open_webui.utils.headers import include_user_info_headers
|
||||||
|
|
||||||
|
|
||||||
@@ -462,8 +462,12 @@ async def get_filtered_models(models, user, db=None):
|
|||||||
for model in models.get("data", []):
|
for model in models.get("data", []):
|
||||||
model_info = Models.get_model_by_id(model["id"], db=db)
|
model_info = Models.get_model_by_id(model["id"], db=db)
|
||||||
if model_info:
|
if model_info:
|
||||||
if user.id == model_info.user_id or has_access(
|
if user.id == model_info.user_id or AccessGrants.has_access(
|
||||||
user.id, type="read", access_control=model_info.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
):
|
):
|
||||||
filtered_models.append(model)
|
filtered_models.append(model)
|
||||||
return filtered_models
|
return filtered_models
|
||||||
@@ -876,7 +880,7 @@ async def generate_chat_completion(
|
|||||||
bypass_system_prompt: bool = False,
|
bypass_system_prompt: bool = False,
|
||||||
):
|
):
|
||||||
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
# NOTE: We intentionally do NOT use Depends(get_session) here.
|
||||||
# Database operations (get_model_by_id, has_access) manage their own short-lived sessions.
|
# Database operations (get_model_by_id, AccessGrants.has_access) manage their own short-lived sessions.
|
||||||
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
# This prevents holding a connection during the entire LLM call (30-60+ seconds),
|
||||||
# which would exhaust the connection pool under concurrent load.
|
# which would exhaust the connection pool under concurrent load.
|
||||||
if BYPASS_MODEL_ACCESS_CONTROL:
|
if BYPASS_MODEL_ACCESS_CONTROL:
|
||||||
@@ -914,10 +918,11 @@ async def generate_chat_completion(
|
|||||||
if not bypass_filter and user.role == "user":
|
if not bypass_filter and user.role == "user":
|
||||||
if not (
|
if not (
|
||||||
user.id == model_info.user_id
|
user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from open_webui.models.prompts import (
|
|||||||
PromptModel,
|
PromptModel,
|
||||||
Prompts,
|
Prompts,
|
||||||
)
|
)
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.models.groups import Groups
|
from open_webui.models.groups import Groups
|
||||||
from open_webui.models.prompt_history import (
|
from open_webui.models.prompt_history import (
|
||||||
PromptHistories,
|
PromptHistories,
|
||||||
@@ -17,7 +18,7 @@ from open_webui.models.prompt_history import (
|
|||||||
)
|
)
|
||||||
from open_webui.constants import ERROR_MESSAGES
|
from open_webui.constants import ERROR_MESSAGES
|
||||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||||
from open_webui.utils.access_control import has_access, has_permission
|
from open_webui.utils.access_control import has_permission
|
||||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||||
from open_webui.internal.db import get_session
|
from open_webui.internal.db import get_session
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -115,7 +116,13 @@ async def get_prompt_list(
|
|||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == prompt.user_id
|
or user.id == prompt.user_id
|
||||||
or has_access(user.id, "write", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for prompt in result.items
|
for prompt in result.items
|
||||||
@@ -186,14 +193,26 @@ async def get_prompt_by_command(
|
|||||||
if (
|
if (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "read", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return PromptAccessResponse(
|
return PromptAccessResponse(
|
||||||
**prompt.model_dump(),
|
**prompt.model_dump(),
|
||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == prompt.user_id
|
or user.id == prompt.user_id
|
||||||
or has_access(user.id, "write", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -218,14 +237,26 @@ async def get_prompt_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "read", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return PromptAccessResponse(
|
return PromptAccessResponse(
|
||||||
**prompt.model_dump(),
|
**prompt.model_dump(),
|
||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == prompt.user_id
|
or user.id == prompt.user_id
|
||||||
or has_access(user.id, "write", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -258,7 +289,13 @@ async def update_prompt_by_id(
|
|||||||
# Is the user the original creator, in a group with write access, or an admin
|
# Is the user the original creator, in a group with write access, or an admin
|
||||||
if (
|
if (
|
||||||
prompt.user_id != user.id
|
prompt.user_id != user.id
|
||||||
and not has_access(user.id, "write", prompt.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -311,7 +348,13 @@ async def update_prompt_metadata(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
prompt.user_id != user.id
|
prompt.user_id != user.id
|
||||||
and not has_access(user.id, "write", prompt.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -356,7 +399,13 @@ async def set_prompt_version(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
prompt.user_id != user.id
|
prompt.user_id != user.id
|
||||||
and not has_access(user.id, "write", prompt.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -395,7 +444,13 @@ async def delete_prompt_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
prompt.user_id != user.id
|
prompt.user_id != user.id
|
||||||
and not has_access(user.id, "write", prompt.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -434,7 +489,13 @@ async def get_prompt_history(
|
|||||||
if not (
|
if not (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "read", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@@ -469,7 +530,13 @@ async def get_prompt_history_entry(
|
|||||||
if not (
|
if not (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "read", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@@ -508,7 +575,13 @@ async def delete_prompt_history_entry(
|
|||||||
if not (
|
if not (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "write", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@@ -553,7 +626,13 @@ async def get_prompt_diff(
|
|||||||
if not (
|
if not (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or prompt.user_id == user.id
|
or prompt.user_id == user.id
|
||||||
or has_access(user.id, "read", prompt.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="prompt",
|
||||||
|
resource_id=prompt.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from open_webui.models.tools import (
|
|||||||
ToolAccessResponse,
|
ToolAccessResponse,
|
||||||
Tools,
|
Tools,
|
||||||
)
|
)
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.utils.plugin import (
|
from open_webui.utils.plugin import (
|
||||||
load_tool_module_by_id,
|
load_tool_module_by_id,
|
||||||
replace_imports,
|
replace_imports,
|
||||||
@@ -156,7 +157,24 @@ async def get_tools(
|
|||||||
tool
|
tool
|
||||||
for tool in tools
|
for tool in tools
|
||||||
if tool.user_id == user.id
|
if tool.user_id == user.id
|
||||||
or has_access(user.id, "read", tool.access_control, user_group_ids, db=db)
|
or (
|
||||||
|
has_access(
|
||||||
|
user.id,
|
||||||
|
"read",
|
||||||
|
getattr(tool, "access_control", None),
|
||||||
|
user_group_ids,
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
|
if str(tool.id).startswith("server:")
|
||||||
|
else AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tool.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=user_group_ids,
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
return tools
|
return tools
|
||||||
|
|
||||||
@@ -181,7 +199,13 @@ async def get_tool_list(
|
|||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == tool.user_id
|
or user.id == tool.user_id
|
||||||
or has_access(user.id, "write", tool.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tool.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for tool in tools
|
for tool in tools
|
||||||
@@ -382,14 +406,26 @@ async def get_tools_by_id(
|
|||||||
if (
|
if (
|
||||||
user.role == "admin"
|
user.role == "admin"
|
||||||
or tools.user_id == user.id
|
or tools.user_id == user.id
|
||||||
or has_access(user.id, "read", tools.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tools.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return ToolAccessResponse(
|
return ToolAccessResponse(
|
||||||
**tools.model_dump(),
|
**tools.model_dump(),
|
||||||
write_access=(
|
write_access=(
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == tools.user_id
|
or user.id == tools.user_id
|
||||||
or has_access(user.id, "write", tools.access_control, db=db)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tools.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -427,7 +463,13 @@ async def update_tools_by_id(
|
|||||||
# Is the user the original creator, in a group with write access, or an admin
|
# Is the user the original creator, in a group with write access, or an admin
|
||||||
if (
|
if (
|
||||||
tools.user_id != user.id
|
tools.user_id != user.id
|
||||||
and not has_access(user.id, "write", tools.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tools.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -489,7 +531,13 @@ async def delete_tools_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
tools.user_id != user.id
|
tools.user_id != user.id
|
||||||
and not has_access(user.id, "write", tools.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tools.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -588,7 +636,13 @@ async def update_tools_valves_by_id(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
tools.user_id != user.id
|
tools.user_id != user.id
|
||||||
and not has_access(user.id, "write", tools.access_control, db=db)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tools.id,
|
||||||
|
permission="write",
|
||||||
|
db=db,
|
||||||
|
)
|
||||||
and user.role != "admin"
|
and user.role != "admin"
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from open_webui.models.users import (
|
|||||||
UserModel,
|
UserModel,
|
||||||
UserGroupIdsModel,
|
UserGroupIdsModel,
|
||||||
UserGroupIdsListResponse,
|
UserGroupIdsListResponse,
|
||||||
UserInfoListResponse,
|
UserInfoResponse,
|
||||||
UserInfoListResponse,
|
UserInfoListResponse,
|
||||||
UserRoleUpdateForm,
|
UserRoleUpdateForm,
|
||||||
UserStatus,
|
UserStatus,
|
||||||
@@ -446,7 +446,7 @@ class UserActiveResponse(UserStatus):
|
|||||||
|
|
||||||
@router.get("/{user_id}", response_model=UserActiveResponse)
|
@router.get("/{user_id}", response_model=UserActiveResponse)
|
||||||
async def get_user_by_id(
|
async def get_user_by_id(
|
||||||
user_id: str, user=Depends(get_verified_user), db: Session = Depends(get_session)
|
user_id: str, user=Depends(get_admin_user), db: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
# Check if user_id is a shared chat
|
# Check if user_id is a shared chat
|
||||||
# If it is, get the user_id from the chat
|
# If it is, get the user_id from the chat
|
||||||
@@ -478,6 +478,20 @@ async def get_user_by_id(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{user_id}/info", response_model=UserInfoResponse)
|
||||||
|
async def get_user_info_by_id(
|
||||||
|
user_id: str, user=Depends(get_verified_user), db: Session = Depends(get_session)
|
||||||
|
):
|
||||||
|
user = Users.get_user_by_id(user_id, db=db)
|
||||||
|
if user:
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail=ERROR_MESSAGES.USER_NOT_FOUND,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{user_id}/oauth/sessions")
|
@router.get("/{user_id}/oauth/sessions")
|
||||||
async def get_user_oauth_sessions_by_id(
|
async def get_user_oauth_sessions_by_id(
|
||||||
user_id: str, user=Depends(get_admin_user), db: Session = Depends(get_session)
|
user_id: str, user=Depends(get_admin_user), db: Session = Depends(get_session)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ from open_webui.utils.auth import decode_token
|
|||||||
from open_webui.socket.utils import RedisDict, RedisLock, YdocManager
|
from open_webui.socket.utils import RedisDict, RedisLock, YdocManager
|
||||||
from open_webui.tasks import create_task, stop_item_tasks
|
from open_webui.tasks import create_task, stop_item_tasks
|
||||||
from open_webui.utils.redis import get_redis_connection
|
from open_webui.utils.redis import get_redis_connection
|
||||||
from open_webui.utils.access_control import has_access, get_users_with_access
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
|
|
||||||
from open_webui.env import (
|
from open_webui.env import (
|
||||||
@@ -405,7 +405,12 @@ async def join_note(sid, data):
|
|||||||
if (
|
if (
|
||||||
user.role != "admin"
|
user.role != "admin"
|
||||||
and user.id != note.user_id
|
and user.id != note.user_id
|
||||||
and not has_access(user.id, type="read", access_control=note.access_control)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
|
)
|
||||||
):
|
):
|
||||||
log.error(f"User {user.id} does not have access to note {data['note_id']}")
|
log.error(f"User {user.id} does not have access to note {data['note_id']}")
|
||||||
return
|
return
|
||||||
@@ -467,8 +472,11 @@ async def ydoc_document_join(sid, data):
|
|||||||
if (
|
if (
|
||||||
user.get("role") != "admin"
|
user.get("role") != "admin"
|
||||||
and user.get("id") != note.user_id
|
and user.get("id") != note.user_id
|
||||||
and not has_access(
|
and not AccessGrants.has_access(
|
||||||
user.get("id"), type="read", access_control=note.access_control
|
user_id=user.get("id"),
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
log.error(
|
log.error(
|
||||||
@@ -537,8 +545,11 @@ async def document_save_handler(document_id, data, user):
|
|||||||
if (
|
if (
|
||||||
user.get("role") != "admin"
|
user.get("role") != "admin"
|
||||||
and user.get("id") != note.user_id
|
and user.get("id") != note.user_id
|
||||||
and not has_access(
|
and not AccessGrants.has_access(
|
||||||
user.get("id"), type="read", access_control=note.access_control
|
user_id=user.get("id"),
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
log.error(f"User {user.get('id')} does not have access to note {note_id}")
|
log.error(f"User {user.get('id')} does not have access to note {note_id}")
|
||||||
|
|||||||
@@ -743,10 +743,14 @@ async def view_note(
|
|||||||
user_id = __user__.get("id")
|
user_id = __user__.get("id")
|
||||||
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id)]
|
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id)]
|
||||||
|
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
if note.user_id != user_id and not has_access(
|
if note.user_id != user_id and not AccessGrants.has_access(
|
||||||
user_id, "read", note.access_control, user_group_ids
|
user_id=user_id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=set(user_group_ids),
|
||||||
):
|
):
|
||||||
return json.dumps({"error": "Access denied"})
|
return json.dumps({"error": "Access denied"})
|
||||||
|
|
||||||
@@ -797,7 +801,7 @@ async def write_note(
|
|||||||
form = NoteForm(
|
form = NoteForm(
|
||||||
title=title,
|
title=title,
|
||||||
data={"content": {"md": content}},
|
data={"content": {"md": content}},
|
||||||
access_control={}, # Private by default - only owner can access
|
access_grants=[], # Private by default - only owner can access
|
||||||
)
|
)
|
||||||
|
|
||||||
new_note = Notes.insert_new_note(user_id, form)
|
new_note = Notes.insert_new_note(user_id, form)
|
||||||
@@ -852,10 +856,14 @@ async def replace_note_content(
|
|||||||
user_id = __user__.get("id")
|
user_id = __user__.get("id")
|
||||||
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id)]
|
user_group_ids = [group.id for group in Groups.get_groups_by_member_id(user_id)]
|
||||||
|
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
if note.user_id != user_id and not has_access(
|
if note.user_id != user_id and not AccessGrants.has_access(
|
||||||
user_id, "write", note.access_control, user_group_ids
|
user_id=user_id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="write",
|
||||||
|
user_group_ids=set(user_group_ids),
|
||||||
):
|
):
|
||||||
return json.dumps({"error": "Write access denied"})
|
return json.dumps({"error": "Write access denied"})
|
||||||
|
|
||||||
@@ -1532,7 +1540,7 @@ async def view_knowledge_file(
|
|||||||
try:
|
try:
|
||||||
from open_webui.models.files import Files
|
from open_webui.models.files import Files
|
||||||
from open_webui.models.knowledge import Knowledges
|
from open_webui.models.knowledge import Knowledges
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
user_id = __user__.get("id")
|
user_id = __user__.get("id")
|
||||||
user_role = __user__.get("role", "user")
|
user_role = __user__.get("role", "user")
|
||||||
@@ -1551,8 +1559,12 @@ async def view_knowledge_file(
|
|||||||
if (
|
if (
|
||||||
user_role == "admin"
|
user_role == "admin"
|
||||||
or knowledge_base.user_id == user_id
|
or knowledge_base.user_id == user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user_id, "read", knowledge_base.access_control, user_group_ids
|
user_id=user_id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge_base.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=set(user_group_ids),
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
has_knowledge_access = True
|
has_knowledge_access = True
|
||||||
@@ -1631,7 +1643,7 @@ async def query_knowledge_files(
|
|||||||
from open_webui.models.files import Files
|
from open_webui.models.files import Files
|
||||||
from open_webui.models.notes import Notes
|
from open_webui.models.notes import Notes
|
||||||
from open_webui.retrieval.utils import query_collection
|
from open_webui.retrieval.utils import query_collection
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.models.access_grants import AccessGrants
|
||||||
|
|
||||||
user_id = __user__.get("id")
|
user_id = __user__.get("id")
|
||||||
user_role = __user__.get("role", "user")
|
user_role = __user__.get("role", "user")
|
||||||
@@ -1656,8 +1668,12 @@ async def query_knowledge_files(
|
|||||||
if knowledge and (
|
if knowledge and (
|
||||||
user_role == "admin"
|
user_role == "admin"
|
||||||
or knowledge.user_id == user_id
|
or knowledge.user_id == user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user_id, "read", knowledge.access_control, user_group_ids
|
user_id=user_id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=set(user_group_ids),
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
collection_names.append(item_id)
|
collection_names.append(item_id)
|
||||||
@@ -1674,7 +1690,12 @@ async def query_knowledge_files(
|
|||||||
if note and (
|
if note and (
|
||||||
user_role == "admin"
|
user_role == "admin"
|
||||||
or note.user_id == user_id
|
or note.user_id == user_id
|
||||||
or has_access(user_id, "read", note.access_control)
|
or AccessGrants.has_access(
|
||||||
|
user_id=user_id,
|
||||||
|
resource_type="note",
|
||||||
|
resource_id=note.id,
|
||||||
|
permission="read",
|
||||||
|
)
|
||||||
):
|
):
|
||||||
content = note.data.get("content", {}).get("md", "")
|
content = note.data.get("content", {}).get("md", "")
|
||||||
note_results.append(
|
note_results.append(
|
||||||
@@ -1693,8 +1714,12 @@ async def query_knowledge_files(
|
|||||||
if knowledge and (
|
if knowledge and (
|
||||||
user_role == "admin"
|
user_role == "admin"
|
||||||
or knowledge.user_id == user_id
|
or knowledge.user_id == user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user_id, "read", knowledge.access_control, user_group_ids
|
user_id=user_id,
|
||||||
|
resource_type="knowledge",
|
||||||
|
resource_id=knowledge.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=set(user_group_ids),
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
collection_names.append(knowledge_id)
|
collection_names.append(knowledge_id)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from open_webui.functions import get_function_models
|
|||||||
|
|
||||||
from open_webui.models.functions import Functions
|
from open_webui.models.functions import Functions
|
||||||
from open_webui.models.models import Models
|
from open_webui.models.models import Models
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.models.groups import Groups
|
from open_webui.models.groups import Groups
|
||||||
|
|
||||||
|
|
||||||
@@ -354,8 +355,12 @@ def check_model_access(user, model, db=None):
|
|||||||
raise Exception("Model not found")
|
raise Exception("Model not found")
|
||||||
elif not (
|
elif not (
|
||||||
user.id == model_info.user_id
|
user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id, type="read", access_control=model_info.access_control, db=db
|
user_id=user.id,
|
||||||
|
resource_type="model",
|
||||||
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise Exception("Model not found")
|
raise Exception("Model not found")
|
||||||
@@ -395,11 +400,13 @@ def get_filtered_models(models, user, db=None):
|
|||||||
if (
|
if (
|
||||||
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
(user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
or user.id == model_info.user_id
|
or user.id == model_info.user_id
|
||||||
or has_access(
|
or AccessGrants.has_access(
|
||||||
user.id,
|
user_id=user.id,
|
||||||
type="read",
|
resource_type="model",
|
||||||
access_control=model_info.access_control,
|
resource_id=model_info.id,
|
||||||
|
permission="read",
|
||||||
user_group_ids=user_group_ids,
|
user_group_ids=user_group_ids,
|
||||||
|
db=db,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
filtered_models.append(model)
|
filtered_models.append(model)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from open_webui.utils.misc import is_string_allowed
|
|||||||
from open_webui.models.tools import Tools
|
from open_webui.models.tools import Tools
|
||||||
from open_webui.models.users import UserModel
|
from open_webui.models.users import UserModel
|
||||||
from open_webui.models.groups import Groups
|
from open_webui.models.groups import Groups
|
||||||
|
from open_webui.models.access_grants import AccessGrants
|
||||||
from open_webui.utils.plugin import load_tool_module_by_id
|
from open_webui.utils.plugin import load_tool_module_by_id
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.utils.access_control import has_access
|
||||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||||
@@ -165,7 +166,13 @@ async def get_tools(
|
|||||||
if (
|
if (
|
||||||
not (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
not (user.role == "admin" and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||||
and tool.user_id != user.id
|
and tool.user_id != user.id
|
||||||
and not has_access(user.id, "read", tool.access_control, user_group_ids)
|
and not AccessGrants.has_access(
|
||||||
|
user_id=user.id,
|
||||||
|
resource_type="tool",
|
||||||
|
resource_id=tool.id,
|
||||||
|
permission="read",
|
||||||
|
user_group_ids=user_group_ids,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
log.warning(f"Access denied to tool {tool_id} for user {user.id}")
|
log.warning(f"Access denied to tool {tool_id} for user {user.id}")
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user