Fix model visibility so users only see:
- Models they created themselves
- Models assigned to their groups by their admin
- NOT models created by other admins
- NOT models created by other users
- DEFAULT BEHAVIOR:
access_control=None= PRIVATE (creator only), NOT public - Models are NEVER public by default - they must be explicitly shared via group assignments
File: backend/open_webui/functions.py
Location: Lines 149-221
Current Issue: Line 158: (user.role == "user") allows ALL users to see ALL pipes
Changes Needed:
-
For Admins: Only show pipes they created
if user.role == "admin": if pipe.created_by != user.email: continue # Skip pipes created by other admins
-
For Users: Only show pipes where:
- Pipe creator has models assigned to user's groups, OR
- User's groups match pipe creator's groups (via group ownership)
Logic:
elif user.role == "user": # Get user's groups user_groups = Groups.get_groups_by_member_id(user.id) user_group_ids = [g.id for g in user_groups] # Get pipe creator (admin) pipe_creator = Users.get_user_by_email(pipe.created_by) if not pipe_creator: continue # Skip if creator not found # Check if pipe creator has any models assigned to user's groups has_access = False creator_models = Models.get_all_models(pipe_creator.id, pipe_creator.email) for model in creator_models: if model.access_control: read_groups = model.access_control.get("read", {}).get("group_ids", []) if any(gid in user_group_ids for gid in read_groups): has_access = True break if not has_access: continue # Skip this pipe
-
For Super Admins: Keep existing behavior (see all)
Dependencies:
Groups.get_groups_by_member_id()- Already existsUsers.get_user_by_email()- Already existsModels.get_all_models()- Already exists
File: backend/open_webui/models/models.py
Location: Lines 179-192 (get_all_models() method)
Current Issue: Models with access_control=None are treated as public (visible to all users)
Changes Needed:
- Change filtering logic to treat
access_control=Noneas PRIVATE:def get_all_models( self, user_id, user_email: str = None, permission: str = "read" ) -> list[ModelModel]: from open_webui.utils.workspace_access import item_assigned_to_user_groups with get_db() as db: raw_models = db.query(Model).all() filtered = [] for model in raw_models: # If user is the creator, always include if model.created_by == user_email: filtered.append(model) continue # ENFORCE: If access_control is None, treat as PRIVATE (skip for other users) # DEFAULT BEHAVIOR: Models are PRIVATE by default, NOT public if model.access_control is None: continue # Skip models without access_control (private to creator only) # Check group assignments (NEW) if item_assigned_to_user_groups(user_id, model, permission): filtered.append(model) continue # Check has_access for models with explicit access_control if has_access(user_id, permission, model.access_control): filtered.append(model) return [ModelModel.model_validate(m) for m in filtered]
Key Changes:
- ✅ Added
item_assigned_to_user_groups()check (Task 3) - ✅
access_control=None→ Skip (private to creator) - ✅
access_control={}→ Checkhas_access()(empty dict = private, but check anyway) - ✅
access_control={"read": {...}}→ Check group assignments andhas_access()
Dependencies:
item_assigned_to_user_groups()- Already exists inbackend/open_webui/utils/workspace_access.py
File: backend/open_webui/models/models.py
Location: Lines 179-192 (get_all_models() method)
Status: ✅ Already included in Task 2 - The item_assigned_to_user_groups() check is added in the updated get_all_models() method.
What it does:
- Checks if model is assigned to any group the user is a member of
- Checks if user owns any group that has access to the model
- Works alongside
has_access()for comprehensive filtering
File: backend/open_webui/routers/models.py
Location: Lines 52-96 (create_new_model())
Verify: When a model is created without access_control, it should default to None (which we now treat as private).
Current Code:
model = Models.insert_new_model(form_data, creator_user_id, creator_email)Check: ModelForm.access_control defaults to None - ✅ This is correct.
File: backend/open_webui/routers/models.py
Location: Lines 182-207 (update_model_by_id())
Verify: When updating a model, if access_control is not provided, it should remain None (private).
Current Code: Uses form_data.access_control - ✅ Should be fine.
File: backend/open_webui/main.py
Location: Lines 1155-1216 (get_models() endpoint)
Verify: The get_filtered_models() function should also respect the private default.
Current Code: Line 1183-1188 includes external models without database entry. Since we only use Portkey/Pipe models, this should be fine, but we should verify it doesn't bypass our filtering.
- Admin A creates a model without setting
access_control - Verify: Model is visible to Admin A only
- Verify: User in Admin A's group does NOT see the model
- Verify: Admin B does NOT see the model
- Admin A creates a model with
access_control={"read": {"group_ids": ["group1"]}} - Add User to
group1 - Verify: User can see the model
- Verify: User not in
group1does NOT see the model
- Admin A creates a pipe/function
- Admin A creates a model using that pipe, assigns to
group1 - User in
group1should see the pipe model - User not in
group1should NOT see the pipe model - Admin B should NOT see Admin A's pipe models
- User creates a model (if allowed)
- Verify: User can see their own model
- Verify: Other users do NOT see the model
- Verify: Admin does NOT see the model (unless assigned to their group)
- Existing models with
access_control={}(empty dict) should remain private - Existing models with
access_control=Noneshould become private (if they were public before) - Existing models with group assignments should continue to work
-
Task 2 First (Enforce default private behavior)
- This is the foundation - ensures models are private by default
- Includes Task 3 (group assignment check)
-
Task 1 Second (Fix pipe/function filtering)
- Builds on Task 2's group assignment logic
- Uses the same filtering principles
- ✅
backend/open_webui/functions.py- Fixget_function_models() - ✅
backend/open_webui/models/models.py- Fixget_all_models()
Total: 2 files to modify
- ✅ No database schema changes
- ✅ Backwards compatible (existing models with
access_control={}still work) - ✅ Uses existing helper functions
⚠️ Some users may lose access to models that were previously public (ifaccess_control=None)- Mitigation: Admins should update models to set
access_controlwith group assignments
- ✅ Test all 5 test cases above
- ✅ Test with multiple admins, groups, and users
- ✅ Verify performance with large number of models
- ✅ Users only see models assigned to their groups
- ✅ Users only see models they created
- ✅ Admins only see models they created (unless assigned to their groups)
- ✅ Default behavior:
access_control=None= PRIVATE (creator only) - ✅ Group assignments work correctly
- ✅ Pipe/function models filtered correctly
- ✅ Backwards compatible with existing models