123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- """
- Fixed tests for SCIM 2.0 endpoints with proper authentication mocking
- """
- import json
- import pytest
- from unittest.mock import patch, MagicMock, Mock
- from fastapi.testclient import TestClient
- from datetime import datetime, timezone
- import time
- from open_webui.main import app
- from open_webui.models.users import UserModel
- from open_webui.models.groups import GroupModel
- class TestSCIMEndpointsFixed:
- """Test SCIM 2.0 endpoints with proper auth mocking"""
-
- @pytest.fixture
- def client(self):
- return TestClient(app)
-
- @pytest.fixture
- def admin_token(self):
- """Mock admin token for authentication"""
- return "mock-admin-token"
-
- @pytest.fixture
- def mock_admin_user(self):
- """Mock admin user"""
- return UserModel(
- id="admin-123",
- name="Admin User",
- email="admin@example.com",
- role="admin",
- profile_image_url="/user.png",
- created_at=1234567890,
- updated_at=1234567890,
- last_active_at=1234567890
- )
-
- @pytest.fixture
- def mock_user(self):
- """Mock regular user"""
- return UserModel(
- id="user-456",
- name="Test User",
- email="test@example.com",
- role="user",
- profile_image_url="/user.png",
- created_at=1234567890,
- updated_at=1234567890,
- last_active_at=1234567890
- )
-
- @pytest.fixture
- def mock_group(self):
- """Mock group"""
- return GroupModel(
- id="group-789",
- user_id="admin-123",
- name="Test Group",
- description="Test group description",
- user_ids=["user-456"],
- created_at=1234567890,
- updated_at=1234567890
- )
-
- @pytest.fixture
- def auth_headers(self, admin_token):
- """Authorization headers for requests"""
- return {"Authorization": f"Bearer {admin_token}"}
-
- @pytest.fixture
- def valid_token_data(self):
- """Valid token data"""
- return {
- "id": "admin-123",
- "email": "admin@example.com",
- "name": "Admin User",
- "role": "admin",
- "exp": int(time.time()) + 3600 # Valid for 1 hour
- }
-
- # Service Provider Config Tests (No auth required)
- def test_get_service_provider_config(self, client):
- """Test getting SCIM Service Provider Configuration"""
- response = client.get("/api/v1/scim/v2/ServiceProviderConfig")
- assert response.status_code == 200
-
- data = response.json()
- assert "schemas" in data
- assert data["schemas"] == ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"]
- assert "patch" in data
- assert data["patch"]["supported"] == True
- assert "filter" in data
- assert data["filter"]["supported"] == True
-
- # Mock the entire authentication dependency
- @patch('open_webui.routers.scim.get_scim_auth')
- @patch('open_webui.models.users.Users.get_users')
- @patch('open_webui.models.groups.Groups.get_groups_by_member_id')
- def test_get_users_with_mocked_auth(self, mock_get_groups, mock_get_users, mock_get_scim_auth, client, auth_headers, mock_user):
- """Test listing SCIM users with mocked authentication"""
- # Mock the authentication to always return True
- mock_get_scim_auth.return_value = True
-
- # Mock the database calls
- mock_get_users.return_value = {
- "users": [mock_user],
- "total": 1
- }
- mock_get_groups.return_value = []
-
- response = client.get("/api/v1/scim/v2/Users", headers=auth_headers)
- assert response.status_code == 200
-
- data = response.json()
- assert data["schemas"] == ["urn:ietf:params:scim:api:messages:2.0:ListResponse"]
- assert data["totalResults"] == 1
- assert data["itemsPerPage"] == 1
- assert data["startIndex"] == 1
- assert len(data["Resources"]) == 1
-
- user = data["Resources"][0]
- assert user["id"] == "user-456"
- assert user["userName"] == "test@example.com"
- assert user["displayName"] == "Test User"
- assert user["active"] == True
-
- # Alternative approach: Mock at the decode_token level
- def test_get_users_with_token_mock(self, client, auth_headers, mock_admin_user, mock_user, valid_token_data):
- """Test listing SCIM users with token decoding mocked"""
- with patch('open_webui.routers.scim.decode_token') as mock_decode_token, \
- patch('open_webui.models.users.Users.get_user_by_id') as mock_get_user_by_id, \
- patch('open_webui.models.users.Users.get_users') as mock_get_users, \
- patch('open_webui.models.groups.Groups.get_groups_by_member_id') as mock_get_groups:
-
- # Setup mocks
- mock_decode_token.return_value = valid_token_data
- mock_get_user_by_id.return_value = mock_admin_user
- mock_get_users.return_value = {
- "users": [mock_user],
- "total": 1
- }
- mock_get_groups.return_value = []
-
- response = client.get("/api/v1/scim/v2/Users", headers=auth_headers)
- assert response.status_code == 200
-
- data = response.json()
- assert data["totalResults"] == 1
-
- # Test authentication failures
- def test_unauthorized_access_no_header(self, client):
- """Test accessing SCIM endpoints without authentication header"""
- response = client.get("/api/v1/scim/v2/Users")
- assert response.status_code == 401
-
- def test_unauthorized_access_invalid_token(self, client):
- """Test accessing SCIM endpoints with invalid token"""
- with patch('open_webui.routers.scim.decode_token') as mock_decode_token:
- mock_decode_token.return_value = None # Invalid token
-
- response = client.get("/api/v1/scim/v2/Users", headers={"Authorization": "Bearer invalid-token"})
- assert response.status_code == 401
-
- def test_non_admin_access(self, client, mock_user):
- """Test accessing SCIM endpoints as non-admin user"""
- with patch('open_webui.routers.scim.decode_token') as mock_decode_token, \
- patch('open_webui.models.users.Users.get_user_by_id') as mock_get_user_by_id:
-
- # Mock token for non-admin user
- mock_decode_token.return_value = {"id": "user-456"}
- mock_get_user_by_id.return_value = mock_user # Non-admin user
-
- response = client.get("/api/v1/scim/v2/Users", headers={"Authorization": "Bearer user-token"})
- assert response.status_code == 403
-
- # Create user test with proper mocking
- @patch('open_webui.routers.scim.get_scim_auth')
- @patch('open_webui.models.users.Users.get_user_by_email')
- @patch('open_webui.models.users.Users.insert_new_user')
- def test_create_user(self, mock_insert_user, mock_get_user_by_email, mock_get_scim_auth, client, auth_headers):
- """Test creating a SCIM user"""
- mock_get_scim_auth.return_value = True
- mock_get_user_by_email.return_value = None # User doesn't exist
-
- new_user = UserModel(
- id="new-user-123",
- name="New User",
- email="newuser@example.com",
- role="user",
- profile_image_url="/user.png",
- created_at=1234567890,
- updated_at=1234567890,
- last_active_at=1234567890
- )
- mock_insert_user.return_value = new_user
-
- create_data = {
- "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
- "userName": "newuser@example.com",
- "displayName": "New User",
- "emails": [{"value": "newuser@example.com", "primary": True}],
- "active": True
- }
-
- response = client.post("/api/v1/scim/v2/Users", headers=auth_headers, json=create_data)
- assert response.status_code == 201
-
- data = response.json()
- assert data["userName"] == "newuser@example.com"
- assert data["displayName"] == "New User"
-
- # Group tests
- @patch('open_webui.routers.scim.get_scim_auth')
- @patch('open_webui.models.groups.Groups.get_groups')
- @patch('open_webui.models.users.Users.get_user_by_id')
- def test_get_groups(self, mock_get_user_by_id, mock_get_groups, mock_get_scim_auth, client, auth_headers, mock_group, mock_user):
- """Test listing SCIM groups"""
- mock_get_scim_auth.return_value = True
- mock_get_groups.return_value = [mock_group]
- mock_get_user_by_id.return_value = mock_user
-
- response = client.get("/api/v1/scim/v2/Groups", headers=auth_headers)
- assert response.status_code == 200
-
- data = response.json()
- assert data["schemas"] == ["urn:ietf:params:scim:api:messages:2.0:ListResponse"]
- assert data["totalResults"] == 1
- assert len(data["Resources"]) == 1
-
- group = data["Resources"][0]
- assert group["id"] == "group-789"
- assert group["displayName"] == "Test Group"
|