authentik.providers.scim.tests.test_membership
SCIM Membership tests
1"""SCIM Membership tests""" 2 3from django.test import TestCase 4from requests_mock import Mocker 5 6from authentik.blueprints.tests import apply_blueprint 7from authentik.core.models import Application, Group, User 8from authentik.lib.generators import generate_id 9from authentik.providers.scim.clients.schema import ServiceProviderConfiguration 10from authentik.providers.scim.models import SCIMCompatibilityMode, SCIMMapping, SCIMProvider 11from authentik.providers.scim.tasks import scim_sync 12from authentik.tenants.models import Tenant 13 14 15class SCIMMembershipTests(TestCase): 16 """SCIM Membership tests""" 17 18 provider: SCIMProvider 19 app: Application 20 21 def setUp(self) -> None: 22 # Delete all users and groups as the mocked HTTP responses only return one ID 23 # which will cause errors with multiple users 24 User.objects.all().exclude_anonymous().delete() 25 Group.objects.all().delete() 26 Tenant.objects.update(avatars="none") 27 28 @apply_blueprint("system/providers-scim.yaml") 29 def configure(self, **kwargs) -> None: 30 """Configure provider""" 31 self.provider: SCIMProvider = SCIMProvider.objects.create( 32 name=generate_id(), 33 url="https://localhost", 34 token=generate_id(), 35 **kwargs, 36 ) 37 self.app: Application = Application.objects.create( 38 name=generate_id(), 39 slug=generate_id(), 40 ) 41 self.app.backchannel_providers.add(self.provider) 42 self.provider.save() 43 self.provider.property_mappings.set( 44 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/user")] 45 ) 46 self.provider.property_mappings_group.set( 47 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")] 48 ) 49 50 def test_member_add(self): 51 """Test member add""" 52 config = ServiceProviderConfiguration.default() 53 54 config.patch.supported = True 55 user_scim_id = generate_id() 56 group_scim_id = generate_id() 57 uid = generate_id() 58 group = Group.objects.create( 59 name=uid, 60 ) 61 62 user = User.objects.create(username=generate_id()) 63 64 with Mocker() as mocker: 65 mocker.get( 66 "https://localhost/ServiceProviderConfig", 67 json=config.model_dump(), 68 ) 69 mocker.post( 70 "https://localhost/Users", 71 json={ 72 "id": user_scim_id, 73 }, 74 ) 75 mocker.post( 76 "https://localhost/Groups", 77 json={ 78 "id": group_scim_id, 79 }, 80 ) 81 82 self.configure() 83 scim_sync.send(self.provider.pk) 84 85 self.assertEqual(mocker.call_count, 3) 86 self.assertEqual(mocker.request_history[0].method, "GET") 87 self.assertEqual(mocker.request_history[1].method, "POST") 88 self.assertEqual(mocker.request_history[2].method, "POST") 89 self.assertJSONEqual( 90 mocker.request_history[1].body, 91 { 92 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 93 "emails": [], 94 "active": True, 95 "externalId": user.uid, 96 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 97 "displayName": "", 98 "userName": user.username, 99 }, 100 ) 101 self.assertJSONEqual( 102 mocker.request_history[2].body, 103 { 104 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 105 "externalId": str(group.pk), 106 "displayName": group.name, 107 }, 108 ) 109 110 with Mocker() as mocker: 111 mocker.get( 112 "https://localhost/ServiceProviderConfig", 113 json=config.model_dump(), 114 ) 115 mocker.patch( 116 f"https://localhost/Groups/{group_scim_id}", 117 json={}, 118 ) 119 group.users.add(user) 120 self.assertEqual(mocker.call_count, 1) 121 self.assertEqual(mocker.request_history[0].method, "PATCH") 122 self.assertJSONEqual( 123 mocker.request_history[0].body, 124 { 125 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 126 "Operations": [ 127 { 128 "op": "add", 129 "path": "members", 130 "value": [{"value": user_scim_id}], 131 } 132 ], 133 }, 134 ) 135 136 def test_member_remove(self): 137 """Test member remove""" 138 config = ServiceProviderConfiguration.default() 139 140 config.patch.supported = True 141 user_scim_id = generate_id() 142 group_scim_id = generate_id() 143 uid = generate_id() 144 group = Group.objects.create( 145 name=uid, 146 ) 147 148 user = User.objects.create(username=generate_id()) 149 150 with Mocker() as mocker: 151 mocker.get( 152 "https://localhost/ServiceProviderConfig", 153 json=config.model_dump(), 154 ) 155 mocker.post( 156 "https://localhost/Users", 157 json={ 158 "id": user_scim_id, 159 }, 160 ) 161 mocker.post( 162 "https://localhost/Groups", 163 json={ 164 "id": group_scim_id, 165 }, 166 ) 167 168 self.configure() 169 scim_sync.send(self.provider.pk) 170 171 self.assertEqual(mocker.call_count, 3) 172 self.assertEqual(mocker.request_history[0].method, "GET") 173 self.assertEqual(mocker.request_history[1].method, "POST") 174 self.assertEqual(mocker.request_history[2].method, "POST") 175 self.assertJSONEqual( 176 mocker.request_history[1].body, 177 { 178 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 179 "active": True, 180 "displayName": "", 181 "emails": [], 182 "externalId": user.uid, 183 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 184 "userName": user.username, 185 }, 186 ) 187 self.assertJSONEqual( 188 mocker.request_history[2].body, 189 { 190 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 191 "externalId": str(group.pk), 192 "displayName": group.name, 193 }, 194 ) 195 196 with Mocker() as mocker: 197 mocker.get( 198 "https://localhost/ServiceProviderConfig", 199 json=config.model_dump(), 200 ) 201 mocker.patch( 202 f"https://localhost/Groups/{group_scim_id}", 203 json={}, 204 ) 205 group.users.add(user) 206 self.assertEqual(mocker.call_count, 1) 207 self.assertEqual(mocker.request_history[0].method, "PATCH") 208 self.assertJSONEqual( 209 mocker.request_history[0].body, 210 { 211 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 212 "Operations": [ 213 { 214 "op": "add", 215 "path": "members", 216 "value": [{"value": user_scim_id}], 217 } 218 ], 219 }, 220 ) 221 222 with Mocker() as mocker: 223 mocker.get( 224 "https://localhost/ServiceProviderConfig", 225 json=config.model_dump(), 226 ) 227 mocker.patch( 228 f"https://localhost/Groups/{group_scim_id}", 229 json={}, 230 ) 231 group.users.remove(user) 232 self.assertEqual(mocker.call_count, 1) 233 self.assertEqual(mocker.request_history[0].method, "PATCH") 234 self.assertJSONEqual( 235 mocker.request_history[0].body, 236 { 237 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 238 "Operations": [ 239 { 240 "op": "remove", 241 "path": "members", 242 "value": [{"value": user_scim_id}], 243 } 244 ], 245 }, 246 ) 247 248 def test_member_add_save(self): 249 """Test member add + save""" 250 config = ServiceProviderConfiguration.default() 251 252 config.patch.supported = True 253 user_scim_id = generate_id() 254 group_scim_id = generate_id() 255 uid = generate_id() 256 group = Group.objects.create( 257 name=uid, 258 ) 259 260 user = User.objects.create(username=generate_id()) 261 262 # Test initial sync of group creation 263 with Mocker() as mocker: 264 mocker.get( 265 "https://localhost/ServiceProviderConfig", 266 json=config.model_dump(), 267 ) 268 mocker.post( 269 "https://localhost/Users", 270 json={ 271 "id": user_scim_id, 272 }, 273 ) 274 mocker.post( 275 "https://localhost/Groups", 276 json={ 277 "id": group_scim_id, 278 }, 279 ) 280 281 self.configure() 282 scim_sync.send(self.provider.pk) 283 284 self.assertEqual(mocker.call_count, 3) 285 self.assertEqual(mocker.request_history[0].method, "GET") 286 self.assertEqual(mocker.request_history[1].method, "POST") 287 self.assertEqual(mocker.request_history[2].method, "POST") 288 self.assertJSONEqual( 289 mocker.request_history[1].body, 290 { 291 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 292 "emails": [], 293 "active": True, 294 "externalId": user.uid, 295 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 296 "displayName": "", 297 "userName": user.username, 298 }, 299 ) 300 self.assertJSONEqual( 301 mocker.request_history[2].body, 302 { 303 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 304 "externalId": str(group.pk), 305 "displayName": group.name, 306 }, 307 ) 308 309 with Mocker() as mocker: 310 mocker.get( 311 "https://localhost/ServiceProviderConfig", 312 json=config.model_dump(), 313 ) 314 mocker.get( 315 f"https://localhost/Groups/{group_scim_id}", 316 json={}, 317 ) 318 mocker.patch( 319 f"https://localhost/Groups/{group_scim_id}", 320 json={}, 321 ) 322 group.users.add(user) 323 group.save() 324 self.assertEqual(mocker.call_count, 3) 325 self.assertEqual(mocker.request_history[0].method, "PATCH") 326 self.assertEqual(mocker.request_history[1].method, "PATCH") 327 self.assertEqual(mocker.request_history[2].method, "GET") 328 self.assertJSONEqual( 329 mocker.request_history[0].body, 330 { 331 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 332 "Operations": [ 333 { 334 "op": "add", 335 "path": "members", 336 "value": [{"value": user_scim_id}], 337 } 338 ], 339 }, 340 ) 341 self.assertJSONEqual( 342 mocker.request_history[1].body, 343 { 344 "Operations": [ 345 { 346 "op": "replace", 347 "value": { 348 "id": group_scim_id, 349 "displayName": group.name, 350 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 351 "externalId": str(group.pk), 352 }, 353 } 354 ] 355 }, 356 ) 357 358 def test_member_add_save_compat_webex(self): 359 """Test member add + save""" 360 config = ServiceProviderConfiguration.default() 361 362 config.patch.supported = True 363 user_scim_id = generate_id() 364 group_scim_id = generate_id() 365 uid = generate_id() 366 group = Group.objects.create( 367 name=uid, 368 ) 369 370 user = User.objects.create(username=generate_id()) 371 372 # Test initial sync of group creation 373 with Mocker() as mocker: 374 mocker.get( 375 "https://localhost/ServiceProviderConfig", 376 json=config.model_dump(), 377 ) 378 mocker.post( 379 "https://localhost/Users", 380 json={ 381 "id": user_scim_id, 382 }, 383 ) 384 mocker.post( 385 "https://localhost/Groups", 386 json={ 387 "id": group_scim_id, 388 }, 389 ) 390 391 self.configure(compatibility_mode=SCIMCompatibilityMode.WEBEX) 392 scim_sync.send(self.provider.pk) 393 394 self.assertEqual(mocker.call_count, 3) 395 self.assertEqual(mocker.request_history[0].method, "GET") 396 self.assertEqual(mocker.request_history[1].method, "POST") 397 self.assertEqual(mocker.request_history[2].method, "POST") 398 self.assertJSONEqual( 399 mocker.request_history[1].body, 400 { 401 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 402 "emails": [], 403 "active": True, 404 "externalId": user.uid, 405 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 406 "displayName": "", 407 "userName": user.username, 408 }, 409 ) 410 self.assertJSONEqual( 411 mocker.request_history[2].body, 412 { 413 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 414 "externalId": str(group.pk), 415 "displayName": group.name, 416 }, 417 ) 418 419 with Mocker() as mocker: 420 mocker.get( 421 "https://localhost/ServiceProviderConfig", 422 json=config.model_dump(), 423 ) 424 mocker.get( 425 f"https://localhost/Groups/{group_scim_id}", 426 json={}, 427 ) 428 mocker.patch( 429 f"https://localhost/Groups/{group_scim_id}", 430 json={}, 431 ) 432 group.users.add(user) 433 group.save() 434 self.assertEqual(mocker.call_count, 3) 435 self.assertEqual(mocker.request_history[0].method, "PATCH") 436 self.assertEqual(mocker.request_history[1].method, "PATCH") 437 self.assertEqual(mocker.request_history[2].method, "GET") 438 self.assertJSONEqual( 439 mocker.request_history[0].body, 440 { 441 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 442 "Operations": [ 443 { 444 "op": "add", 445 "path": "members", 446 "value": [{"value": user_scim_id, "type": "user"}], 447 } 448 ], 449 }, 450 ) 451 self.assertJSONEqual( 452 mocker.request_history[1].body, 453 { 454 "Operations": [ 455 { 456 "op": "replace", 457 "value": { 458 "id": group_scim_id, 459 "displayName": group.name, 460 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 461 "externalId": str(group.pk), 462 }, 463 } 464 ] 465 }, 466 )
class
SCIMMembershipTests(django.test.testcases.TestCase):
16class SCIMMembershipTests(TestCase): 17 """SCIM Membership tests""" 18 19 provider: SCIMProvider 20 app: Application 21 22 def setUp(self) -> None: 23 # Delete all users and groups as the mocked HTTP responses only return one ID 24 # which will cause errors with multiple users 25 User.objects.all().exclude_anonymous().delete() 26 Group.objects.all().delete() 27 Tenant.objects.update(avatars="none") 28 29 @apply_blueprint("system/providers-scim.yaml") 30 def configure(self, **kwargs) -> None: 31 """Configure provider""" 32 self.provider: SCIMProvider = SCIMProvider.objects.create( 33 name=generate_id(), 34 url="https://localhost", 35 token=generate_id(), 36 **kwargs, 37 ) 38 self.app: Application = Application.objects.create( 39 name=generate_id(), 40 slug=generate_id(), 41 ) 42 self.app.backchannel_providers.add(self.provider) 43 self.provider.save() 44 self.provider.property_mappings.set( 45 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/user")] 46 ) 47 self.provider.property_mappings_group.set( 48 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")] 49 ) 50 51 def test_member_add(self): 52 """Test member add""" 53 config = ServiceProviderConfiguration.default() 54 55 config.patch.supported = True 56 user_scim_id = generate_id() 57 group_scim_id = generate_id() 58 uid = generate_id() 59 group = Group.objects.create( 60 name=uid, 61 ) 62 63 user = User.objects.create(username=generate_id()) 64 65 with Mocker() as mocker: 66 mocker.get( 67 "https://localhost/ServiceProviderConfig", 68 json=config.model_dump(), 69 ) 70 mocker.post( 71 "https://localhost/Users", 72 json={ 73 "id": user_scim_id, 74 }, 75 ) 76 mocker.post( 77 "https://localhost/Groups", 78 json={ 79 "id": group_scim_id, 80 }, 81 ) 82 83 self.configure() 84 scim_sync.send(self.provider.pk) 85 86 self.assertEqual(mocker.call_count, 3) 87 self.assertEqual(mocker.request_history[0].method, "GET") 88 self.assertEqual(mocker.request_history[1].method, "POST") 89 self.assertEqual(mocker.request_history[2].method, "POST") 90 self.assertJSONEqual( 91 mocker.request_history[1].body, 92 { 93 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 94 "emails": [], 95 "active": True, 96 "externalId": user.uid, 97 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 98 "displayName": "", 99 "userName": user.username, 100 }, 101 ) 102 self.assertJSONEqual( 103 mocker.request_history[2].body, 104 { 105 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 106 "externalId": str(group.pk), 107 "displayName": group.name, 108 }, 109 ) 110 111 with Mocker() as mocker: 112 mocker.get( 113 "https://localhost/ServiceProviderConfig", 114 json=config.model_dump(), 115 ) 116 mocker.patch( 117 f"https://localhost/Groups/{group_scim_id}", 118 json={}, 119 ) 120 group.users.add(user) 121 self.assertEqual(mocker.call_count, 1) 122 self.assertEqual(mocker.request_history[0].method, "PATCH") 123 self.assertJSONEqual( 124 mocker.request_history[0].body, 125 { 126 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 127 "Operations": [ 128 { 129 "op": "add", 130 "path": "members", 131 "value": [{"value": user_scim_id}], 132 } 133 ], 134 }, 135 ) 136 137 def test_member_remove(self): 138 """Test member remove""" 139 config = ServiceProviderConfiguration.default() 140 141 config.patch.supported = True 142 user_scim_id = generate_id() 143 group_scim_id = generate_id() 144 uid = generate_id() 145 group = Group.objects.create( 146 name=uid, 147 ) 148 149 user = User.objects.create(username=generate_id()) 150 151 with Mocker() as mocker: 152 mocker.get( 153 "https://localhost/ServiceProviderConfig", 154 json=config.model_dump(), 155 ) 156 mocker.post( 157 "https://localhost/Users", 158 json={ 159 "id": user_scim_id, 160 }, 161 ) 162 mocker.post( 163 "https://localhost/Groups", 164 json={ 165 "id": group_scim_id, 166 }, 167 ) 168 169 self.configure() 170 scim_sync.send(self.provider.pk) 171 172 self.assertEqual(mocker.call_count, 3) 173 self.assertEqual(mocker.request_history[0].method, "GET") 174 self.assertEqual(mocker.request_history[1].method, "POST") 175 self.assertEqual(mocker.request_history[2].method, "POST") 176 self.assertJSONEqual( 177 mocker.request_history[1].body, 178 { 179 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 180 "active": True, 181 "displayName": "", 182 "emails": [], 183 "externalId": user.uid, 184 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 185 "userName": user.username, 186 }, 187 ) 188 self.assertJSONEqual( 189 mocker.request_history[2].body, 190 { 191 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 192 "externalId": str(group.pk), 193 "displayName": group.name, 194 }, 195 ) 196 197 with Mocker() as mocker: 198 mocker.get( 199 "https://localhost/ServiceProviderConfig", 200 json=config.model_dump(), 201 ) 202 mocker.patch( 203 f"https://localhost/Groups/{group_scim_id}", 204 json={}, 205 ) 206 group.users.add(user) 207 self.assertEqual(mocker.call_count, 1) 208 self.assertEqual(mocker.request_history[0].method, "PATCH") 209 self.assertJSONEqual( 210 mocker.request_history[0].body, 211 { 212 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 213 "Operations": [ 214 { 215 "op": "add", 216 "path": "members", 217 "value": [{"value": user_scim_id}], 218 } 219 ], 220 }, 221 ) 222 223 with Mocker() as mocker: 224 mocker.get( 225 "https://localhost/ServiceProviderConfig", 226 json=config.model_dump(), 227 ) 228 mocker.patch( 229 f"https://localhost/Groups/{group_scim_id}", 230 json={}, 231 ) 232 group.users.remove(user) 233 self.assertEqual(mocker.call_count, 1) 234 self.assertEqual(mocker.request_history[0].method, "PATCH") 235 self.assertJSONEqual( 236 mocker.request_history[0].body, 237 { 238 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 239 "Operations": [ 240 { 241 "op": "remove", 242 "path": "members", 243 "value": [{"value": user_scim_id}], 244 } 245 ], 246 }, 247 ) 248 249 def test_member_add_save(self): 250 """Test member add + save""" 251 config = ServiceProviderConfiguration.default() 252 253 config.patch.supported = True 254 user_scim_id = generate_id() 255 group_scim_id = generate_id() 256 uid = generate_id() 257 group = Group.objects.create( 258 name=uid, 259 ) 260 261 user = User.objects.create(username=generate_id()) 262 263 # Test initial sync of group creation 264 with Mocker() as mocker: 265 mocker.get( 266 "https://localhost/ServiceProviderConfig", 267 json=config.model_dump(), 268 ) 269 mocker.post( 270 "https://localhost/Users", 271 json={ 272 "id": user_scim_id, 273 }, 274 ) 275 mocker.post( 276 "https://localhost/Groups", 277 json={ 278 "id": group_scim_id, 279 }, 280 ) 281 282 self.configure() 283 scim_sync.send(self.provider.pk) 284 285 self.assertEqual(mocker.call_count, 3) 286 self.assertEqual(mocker.request_history[0].method, "GET") 287 self.assertEqual(mocker.request_history[1].method, "POST") 288 self.assertEqual(mocker.request_history[2].method, "POST") 289 self.assertJSONEqual( 290 mocker.request_history[1].body, 291 { 292 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 293 "emails": [], 294 "active": True, 295 "externalId": user.uid, 296 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 297 "displayName": "", 298 "userName": user.username, 299 }, 300 ) 301 self.assertJSONEqual( 302 mocker.request_history[2].body, 303 { 304 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 305 "externalId": str(group.pk), 306 "displayName": group.name, 307 }, 308 ) 309 310 with Mocker() as mocker: 311 mocker.get( 312 "https://localhost/ServiceProviderConfig", 313 json=config.model_dump(), 314 ) 315 mocker.get( 316 f"https://localhost/Groups/{group_scim_id}", 317 json={}, 318 ) 319 mocker.patch( 320 f"https://localhost/Groups/{group_scim_id}", 321 json={}, 322 ) 323 group.users.add(user) 324 group.save() 325 self.assertEqual(mocker.call_count, 3) 326 self.assertEqual(mocker.request_history[0].method, "PATCH") 327 self.assertEqual(mocker.request_history[1].method, "PATCH") 328 self.assertEqual(mocker.request_history[2].method, "GET") 329 self.assertJSONEqual( 330 mocker.request_history[0].body, 331 { 332 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 333 "Operations": [ 334 { 335 "op": "add", 336 "path": "members", 337 "value": [{"value": user_scim_id}], 338 } 339 ], 340 }, 341 ) 342 self.assertJSONEqual( 343 mocker.request_history[1].body, 344 { 345 "Operations": [ 346 { 347 "op": "replace", 348 "value": { 349 "id": group_scim_id, 350 "displayName": group.name, 351 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 352 "externalId": str(group.pk), 353 }, 354 } 355 ] 356 }, 357 ) 358 359 def test_member_add_save_compat_webex(self): 360 """Test member add + save""" 361 config = ServiceProviderConfiguration.default() 362 363 config.patch.supported = True 364 user_scim_id = generate_id() 365 group_scim_id = generate_id() 366 uid = generate_id() 367 group = Group.objects.create( 368 name=uid, 369 ) 370 371 user = User.objects.create(username=generate_id()) 372 373 # Test initial sync of group creation 374 with Mocker() as mocker: 375 mocker.get( 376 "https://localhost/ServiceProviderConfig", 377 json=config.model_dump(), 378 ) 379 mocker.post( 380 "https://localhost/Users", 381 json={ 382 "id": user_scim_id, 383 }, 384 ) 385 mocker.post( 386 "https://localhost/Groups", 387 json={ 388 "id": group_scim_id, 389 }, 390 ) 391 392 self.configure(compatibility_mode=SCIMCompatibilityMode.WEBEX) 393 scim_sync.send(self.provider.pk) 394 395 self.assertEqual(mocker.call_count, 3) 396 self.assertEqual(mocker.request_history[0].method, "GET") 397 self.assertEqual(mocker.request_history[1].method, "POST") 398 self.assertEqual(mocker.request_history[2].method, "POST") 399 self.assertJSONEqual( 400 mocker.request_history[1].body, 401 { 402 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 403 "emails": [], 404 "active": True, 405 "externalId": user.uid, 406 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 407 "displayName": "", 408 "userName": user.username, 409 }, 410 ) 411 self.assertJSONEqual( 412 mocker.request_history[2].body, 413 { 414 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 415 "externalId": str(group.pk), 416 "displayName": group.name, 417 }, 418 ) 419 420 with Mocker() as mocker: 421 mocker.get( 422 "https://localhost/ServiceProviderConfig", 423 json=config.model_dump(), 424 ) 425 mocker.get( 426 f"https://localhost/Groups/{group_scim_id}", 427 json={}, 428 ) 429 mocker.patch( 430 f"https://localhost/Groups/{group_scim_id}", 431 json={}, 432 ) 433 group.users.add(user) 434 group.save() 435 self.assertEqual(mocker.call_count, 3) 436 self.assertEqual(mocker.request_history[0].method, "PATCH") 437 self.assertEqual(mocker.request_history[1].method, "PATCH") 438 self.assertEqual(mocker.request_history[2].method, "GET") 439 self.assertJSONEqual( 440 mocker.request_history[0].body, 441 { 442 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 443 "Operations": [ 444 { 445 "op": "add", 446 "path": "members", 447 "value": [{"value": user_scim_id, "type": "user"}], 448 } 449 ], 450 }, 451 ) 452 self.assertJSONEqual( 453 mocker.request_history[1].body, 454 { 455 "Operations": [ 456 { 457 "op": "replace", 458 "value": { 459 "id": group_scim_id, 460 "displayName": group.name, 461 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 462 "externalId": str(group.pk), 463 }, 464 } 465 ] 466 }, 467 )
SCIM Membership tests
def
setUp(self) -> None:
22 def setUp(self) -> None: 23 # Delete all users and groups as the mocked HTTP responses only return one ID 24 # which will cause errors with multiple users 25 User.objects.all().exclude_anonymous().delete() 26 Group.objects.all().delete() 27 Tenant.objects.update(avatars="none")
Hook method for setting up the test fixture before exercising it.
@apply_blueprint('system/providers-scim.yaml')
def
configure(self, **kwargs) -> None:
29 @apply_blueprint("system/providers-scim.yaml") 30 def configure(self, **kwargs) -> None: 31 """Configure provider""" 32 self.provider: SCIMProvider = SCIMProvider.objects.create( 33 name=generate_id(), 34 url="https://localhost", 35 token=generate_id(), 36 **kwargs, 37 ) 38 self.app: Application = Application.objects.create( 39 name=generate_id(), 40 slug=generate_id(), 41 ) 42 self.app.backchannel_providers.add(self.provider) 43 self.provider.save() 44 self.provider.property_mappings.set( 45 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/user")] 46 ) 47 self.provider.property_mappings_group.set( 48 [SCIMMapping.objects.get(managed="goauthentik.io/providers/scim/group")] 49 )
Configure provider
def
test_member_add(self):
51 def test_member_add(self): 52 """Test member add""" 53 config = ServiceProviderConfiguration.default() 54 55 config.patch.supported = True 56 user_scim_id = generate_id() 57 group_scim_id = generate_id() 58 uid = generate_id() 59 group = Group.objects.create( 60 name=uid, 61 ) 62 63 user = User.objects.create(username=generate_id()) 64 65 with Mocker() as mocker: 66 mocker.get( 67 "https://localhost/ServiceProviderConfig", 68 json=config.model_dump(), 69 ) 70 mocker.post( 71 "https://localhost/Users", 72 json={ 73 "id": user_scim_id, 74 }, 75 ) 76 mocker.post( 77 "https://localhost/Groups", 78 json={ 79 "id": group_scim_id, 80 }, 81 ) 82 83 self.configure() 84 scim_sync.send(self.provider.pk) 85 86 self.assertEqual(mocker.call_count, 3) 87 self.assertEqual(mocker.request_history[0].method, "GET") 88 self.assertEqual(mocker.request_history[1].method, "POST") 89 self.assertEqual(mocker.request_history[2].method, "POST") 90 self.assertJSONEqual( 91 mocker.request_history[1].body, 92 { 93 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 94 "emails": [], 95 "active": True, 96 "externalId": user.uid, 97 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 98 "displayName": "", 99 "userName": user.username, 100 }, 101 ) 102 self.assertJSONEqual( 103 mocker.request_history[2].body, 104 { 105 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 106 "externalId": str(group.pk), 107 "displayName": group.name, 108 }, 109 ) 110 111 with Mocker() as mocker: 112 mocker.get( 113 "https://localhost/ServiceProviderConfig", 114 json=config.model_dump(), 115 ) 116 mocker.patch( 117 f"https://localhost/Groups/{group_scim_id}", 118 json={}, 119 ) 120 group.users.add(user) 121 self.assertEqual(mocker.call_count, 1) 122 self.assertEqual(mocker.request_history[0].method, "PATCH") 123 self.assertJSONEqual( 124 mocker.request_history[0].body, 125 { 126 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 127 "Operations": [ 128 { 129 "op": "add", 130 "path": "members", 131 "value": [{"value": user_scim_id}], 132 } 133 ], 134 }, 135 )
Test member add
def
test_member_remove(self):
137 def test_member_remove(self): 138 """Test member remove""" 139 config = ServiceProviderConfiguration.default() 140 141 config.patch.supported = True 142 user_scim_id = generate_id() 143 group_scim_id = generate_id() 144 uid = generate_id() 145 group = Group.objects.create( 146 name=uid, 147 ) 148 149 user = User.objects.create(username=generate_id()) 150 151 with Mocker() as mocker: 152 mocker.get( 153 "https://localhost/ServiceProviderConfig", 154 json=config.model_dump(), 155 ) 156 mocker.post( 157 "https://localhost/Users", 158 json={ 159 "id": user_scim_id, 160 }, 161 ) 162 mocker.post( 163 "https://localhost/Groups", 164 json={ 165 "id": group_scim_id, 166 }, 167 ) 168 169 self.configure() 170 scim_sync.send(self.provider.pk) 171 172 self.assertEqual(mocker.call_count, 3) 173 self.assertEqual(mocker.request_history[0].method, "GET") 174 self.assertEqual(mocker.request_history[1].method, "POST") 175 self.assertEqual(mocker.request_history[2].method, "POST") 176 self.assertJSONEqual( 177 mocker.request_history[1].body, 178 { 179 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 180 "active": True, 181 "displayName": "", 182 "emails": [], 183 "externalId": user.uid, 184 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 185 "userName": user.username, 186 }, 187 ) 188 self.assertJSONEqual( 189 mocker.request_history[2].body, 190 { 191 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 192 "externalId": str(group.pk), 193 "displayName": group.name, 194 }, 195 ) 196 197 with Mocker() as mocker: 198 mocker.get( 199 "https://localhost/ServiceProviderConfig", 200 json=config.model_dump(), 201 ) 202 mocker.patch( 203 f"https://localhost/Groups/{group_scim_id}", 204 json={}, 205 ) 206 group.users.add(user) 207 self.assertEqual(mocker.call_count, 1) 208 self.assertEqual(mocker.request_history[0].method, "PATCH") 209 self.assertJSONEqual( 210 mocker.request_history[0].body, 211 { 212 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 213 "Operations": [ 214 { 215 "op": "add", 216 "path": "members", 217 "value": [{"value": user_scim_id}], 218 } 219 ], 220 }, 221 ) 222 223 with Mocker() as mocker: 224 mocker.get( 225 "https://localhost/ServiceProviderConfig", 226 json=config.model_dump(), 227 ) 228 mocker.patch( 229 f"https://localhost/Groups/{group_scim_id}", 230 json={}, 231 ) 232 group.users.remove(user) 233 self.assertEqual(mocker.call_count, 1) 234 self.assertEqual(mocker.request_history[0].method, "PATCH") 235 self.assertJSONEqual( 236 mocker.request_history[0].body, 237 { 238 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 239 "Operations": [ 240 { 241 "op": "remove", 242 "path": "members", 243 "value": [{"value": user_scim_id}], 244 } 245 ], 246 }, 247 )
Test member remove
def
test_member_add_save(self):
249 def test_member_add_save(self): 250 """Test member add + save""" 251 config = ServiceProviderConfiguration.default() 252 253 config.patch.supported = True 254 user_scim_id = generate_id() 255 group_scim_id = generate_id() 256 uid = generate_id() 257 group = Group.objects.create( 258 name=uid, 259 ) 260 261 user = User.objects.create(username=generate_id()) 262 263 # Test initial sync of group creation 264 with Mocker() as mocker: 265 mocker.get( 266 "https://localhost/ServiceProviderConfig", 267 json=config.model_dump(), 268 ) 269 mocker.post( 270 "https://localhost/Users", 271 json={ 272 "id": user_scim_id, 273 }, 274 ) 275 mocker.post( 276 "https://localhost/Groups", 277 json={ 278 "id": group_scim_id, 279 }, 280 ) 281 282 self.configure() 283 scim_sync.send(self.provider.pk) 284 285 self.assertEqual(mocker.call_count, 3) 286 self.assertEqual(mocker.request_history[0].method, "GET") 287 self.assertEqual(mocker.request_history[1].method, "POST") 288 self.assertEqual(mocker.request_history[2].method, "POST") 289 self.assertJSONEqual( 290 mocker.request_history[1].body, 291 { 292 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 293 "emails": [], 294 "active": True, 295 "externalId": user.uid, 296 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 297 "displayName": "", 298 "userName": user.username, 299 }, 300 ) 301 self.assertJSONEqual( 302 mocker.request_history[2].body, 303 { 304 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 305 "externalId": str(group.pk), 306 "displayName": group.name, 307 }, 308 ) 309 310 with Mocker() as mocker: 311 mocker.get( 312 "https://localhost/ServiceProviderConfig", 313 json=config.model_dump(), 314 ) 315 mocker.get( 316 f"https://localhost/Groups/{group_scim_id}", 317 json={}, 318 ) 319 mocker.patch( 320 f"https://localhost/Groups/{group_scim_id}", 321 json={}, 322 ) 323 group.users.add(user) 324 group.save() 325 self.assertEqual(mocker.call_count, 3) 326 self.assertEqual(mocker.request_history[0].method, "PATCH") 327 self.assertEqual(mocker.request_history[1].method, "PATCH") 328 self.assertEqual(mocker.request_history[2].method, "GET") 329 self.assertJSONEqual( 330 mocker.request_history[0].body, 331 { 332 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 333 "Operations": [ 334 { 335 "op": "add", 336 "path": "members", 337 "value": [{"value": user_scim_id}], 338 } 339 ], 340 }, 341 ) 342 self.assertJSONEqual( 343 mocker.request_history[1].body, 344 { 345 "Operations": [ 346 { 347 "op": "replace", 348 "value": { 349 "id": group_scim_id, 350 "displayName": group.name, 351 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 352 "externalId": str(group.pk), 353 }, 354 } 355 ] 356 }, 357 )
Test member add + save
def
test_member_add_save_compat_webex(self):
359 def test_member_add_save_compat_webex(self): 360 """Test member add + save""" 361 config = ServiceProviderConfiguration.default() 362 363 config.patch.supported = True 364 user_scim_id = generate_id() 365 group_scim_id = generate_id() 366 uid = generate_id() 367 group = Group.objects.create( 368 name=uid, 369 ) 370 371 user = User.objects.create(username=generate_id()) 372 373 # Test initial sync of group creation 374 with Mocker() as mocker: 375 mocker.get( 376 "https://localhost/ServiceProviderConfig", 377 json=config.model_dump(), 378 ) 379 mocker.post( 380 "https://localhost/Users", 381 json={ 382 "id": user_scim_id, 383 }, 384 ) 385 mocker.post( 386 "https://localhost/Groups", 387 json={ 388 "id": group_scim_id, 389 }, 390 ) 391 392 self.configure(compatibility_mode=SCIMCompatibilityMode.WEBEX) 393 scim_sync.send(self.provider.pk) 394 395 self.assertEqual(mocker.call_count, 3) 396 self.assertEqual(mocker.request_history[0].method, "GET") 397 self.assertEqual(mocker.request_history[1].method, "POST") 398 self.assertEqual(mocker.request_history[2].method, "POST") 399 self.assertJSONEqual( 400 mocker.request_history[1].body, 401 { 402 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], 403 "emails": [], 404 "active": True, 405 "externalId": user.uid, 406 "name": {"familyName": " ", "formatted": " ", "givenName": ""}, 407 "displayName": "", 408 "userName": user.username, 409 }, 410 ) 411 self.assertJSONEqual( 412 mocker.request_history[2].body, 413 { 414 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 415 "externalId": str(group.pk), 416 "displayName": group.name, 417 }, 418 ) 419 420 with Mocker() as mocker: 421 mocker.get( 422 "https://localhost/ServiceProviderConfig", 423 json=config.model_dump(), 424 ) 425 mocker.get( 426 f"https://localhost/Groups/{group_scim_id}", 427 json={}, 428 ) 429 mocker.patch( 430 f"https://localhost/Groups/{group_scim_id}", 431 json={}, 432 ) 433 group.users.add(user) 434 group.save() 435 self.assertEqual(mocker.call_count, 3) 436 self.assertEqual(mocker.request_history[0].method, "PATCH") 437 self.assertEqual(mocker.request_history[1].method, "PATCH") 438 self.assertEqual(mocker.request_history[2].method, "GET") 439 self.assertJSONEqual( 440 mocker.request_history[0].body, 441 { 442 "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], 443 "Operations": [ 444 { 445 "op": "add", 446 "path": "members", 447 "value": [{"value": user_scim_id, "type": "user"}], 448 } 449 ], 450 }, 451 ) 452 self.assertJSONEqual( 453 mocker.request_history[1].body, 454 { 455 "Operations": [ 456 { 457 "op": "replace", 458 "value": { 459 "id": group_scim_id, 460 "displayName": group.name, 461 "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], 462 "externalId": str(group.pk), 463 }, 464 } 465 ] 466 }, 467 )
Test member add + save