diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index dbf9fad19b..4cc39e11c4 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1465,6 +1465,10 @@ USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING = ( os.environ.get('USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING', 'False').lower() == 'true' ) +USER_PERMISSIONS_CALENDAR_ALLOW_PUBLIC_SHARING = ( + os.environ.get('USER_PERMISSIONS_CALENDAR_ALLOW_PUBLIC_SHARING', 'False').lower() == 'true' +) + USER_PERMISSIONS_ACCESS_GRANTS_ALLOW_USERS = ( os.environ.get('USER_PERMISSIONS_ACCESS_GRANTS_ALLOW_USERS', 'True').lower() == 'true' ) @@ -1585,6 +1589,7 @@ DEFAULT_USER_PERMISSIONS = { 'notes': USER_PERMISSIONS_NOTES_ALLOW_SHARING, 'public_notes': USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING, 'public_chats': USER_PERMISSIONS_CHAT_ALLOW_PUBLIC_SHARING, + 'public_calendars': USER_PERMISSIONS_CALENDAR_ALLOW_PUBLIC_SHARING, }, 'access_grants': { 'allow_users': USER_PERMISSIONS_ACCESS_GRANTS_ALLOW_USERS, diff --git a/backend/open_webui/routers/calendar.py b/backend/open_webui/routers/calendar.py index c95888ebfa..bdc06e819b 100644 --- a/backend/open_webui/routers/calendar.py +++ b/backend/open_webui/routers/calendar.py @@ -22,7 +22,7 @@ from open_webui.models.access_grants import AccessGrants from open_webui.models.groups import Groups from open_webui.models.users import UserModel from open_webui.utils.auth import get_verified_user -from open_webui.utils.access_control import has_permission +from open_webui.utils.access_control import has_permission, filter_allowed_access_grants from open_webui.utils.calendar import expand_recurring_event from open_webui.constants import ERROR_MESSAGES @@ -112,6 +112,17 @@ async def get_calendars(request: Request, user: UserModel = Depends(get_verified async def create_calendar(request: Request, form_data: CalendarForm, user: UserModel = Depends(get_verified_user)): """Create a new user calendar.""" await check_calendar_permission(request, user) + # Strip public/user grants the requesting user is not permitted to assign + # (matches the channel/notes/models pattern). Without this, any verified user + # could create a calendar with `principal_id='*' permission='read'|'write'`, + # making their events readable or writable by any other verified user. + form_data.access_grants = await filter_allowed_access_grants( + request.app.state.config.USER_PERMISSIONS, + user.id, + user.role, + form_data.access_grants, + 'sharing.public_calendars', + ) return await Calendars.insert_new_calendar(user.id, form_data) @@ -350,6 +361,20 @@ async def update_calendar( if form_data.access_grants is not None and cal.user_id != user.id and user.role != 'admin': raise HTTPException(status_code=403, detail='Only owner can manage sharing') + # Strip public/user grants the requesting user is not permitted to assign + # (matches the channel/notes/models pattern). The owner-only check above + # only restricts WHO can set grants; this filter restricts WHICH grants + # they may set, so a non-admin owner cannot make their calendar + # publicly readable/writable without the corresponding sharing permission. + if form_data.access_grants is not None: + form_data.access_grants = await filter_allowed_access_grants( + request.app.state.config.USER_PERMISSIONS, + user.id, + user.role, + form_data.access_grants, + 'sharing.public_calendars', + ) + updated = await Calendars.update_calendar_by_id(calendar_id, form_data) if not updated: raise HTTPException(status_code=500, detail='Failed to update') diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index bcf11936e2..7fe5fcd2dc 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -194,6 +194,7 @@ class SharingPermissions(BaseModel): notes: bool = False public_notes: bool = True public_chats: bool = False + public_calendars: bool = False class AccessGrantsPermissions(BaseModel): @@ -235,6 +236,7 @@ class FeaturesPermissions(BaseModel): code_interpreter: bool = True memories: bool = True automations: bool = False + calendar: bool = True class SettingsPermissions(BaseModel): diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte index 313834bfdc..6523419531 100644 --- a/src/lib/components/admin/Users/Groups/Permissions.svelte +++ b/src/lib/components/admin/Users/Groups/Permissions.svelte @@ -410,6 +410,24 @@ {/if} {/if} + + {#if permissions.features.calendar} +