authentik.enterprise.providers.microsoft_entra.tests.test_groups
Microsoft Entra Group tests
1"""Microsoft Entra Group tests""" 2 3from unittest.mock import AsyncMock, MagicMock, patch 4 5from azure.identity.aio import ClientSecretCredential 6from django.test import TestCase 7from msgraph.generated.models.group import Group as MSGroup 8from msgraph.generated.models.group_collection_response import GroupCollectionResponse 9from msgraph.generated.models.organization import Organization 10from msgraph.generated.models.organization_collection_response import OrganizationCollectionResponse 11from msgraph.generated.models.user import User as MSUser 12from msgraph.generated.models.user_collection_response import UserCollectionResponse 13from msgraph.generated.models.verified_domain import VerifiedDomain 14 15from authentik.blueprints.tests import apply_blueprint 16from authentik.core.models import Application, Group, User 17from authentik.core.tests.utils import create_test_user 18from authentik.enterprise.providers.microsoft_entra.models import ( 19 MicrosoftEntraProvider, 20 MicrosoftEntraProviderGroup, 21 MicrosoftEntraProviderMapping, 22 MicrosoftEntraProviderUser, 23) 24from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync 25from authentik.events.models import Event, EventAction 26from authentik.lib.generators import generate_id 27from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction 28from authentik.tenants.models import Tenant 29 30 31class MicrosoftEntraGroupTests(TestCase): 32 """Microsoft Entra Group tests""" 33 34 @apply_blueprint("system/providers-microsoft-entra.yaml") 35 def setUp(self) -> None: 36 # Delete all groups and groups as the mocked HTTP responses only return one ID 37 # which will cause errors with multiple groups 38 Tenant.objects.update(avatars="none") 39 User.objects.all().exclude_anonymous().delete() 40 Group.objects.all().delete() 41 self.provider: MicrosoftEntraProvider = MicrosoftEntraProvider.objects.create( 42 name=generate_id(), 43 client_id=generate_id(), 44 client_secret=generate_id(), 45 tenant_id=generate_id(), 46 exclude_users_service_account=True, 47 ) 48 self.app: Application = Application.objects.create( 49 name=generate_id(), 50 slug=generate_id(), 51 ) 52 self.app.backchannel_providers.add(self.provider) 53 self.provider.property_mappings.add( 54 MicrosoftEntraProviderMapping.objects.get( 55 managed="goauthentik.io/providers/microsoft_entra/user" 56 ) 57 ) 58 self.provider.property_mappings_group.add( 59 MicrosoftEntraProviderMapping.objects.get( 60 managed="goauthentik.io/providers/microsoft_entra/group" 61 ) 62 ) 63 self.creds = ClientSecretCredential(generate_id(), generate_id(), generate_id()) 64 65 def test_group_create(self): 66 """Test group creation""" 67 uid = generate_id() 68 with ( 69 patch( 70 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 71 MagicMock(return_value={"credentials": self.creds}), 72 ), 73 patch( 74 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 75 AsyncMock( 76 return_value=OrganizationCollectionResponse( 77 value=[ 78 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 79 ] 80 ) 81 ), 82 ), 83 patch( 84 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 85 AsyncMock(return_value=MSGroup(id=generate_id())), 86 ) as group_create, 87 ): 88 group = Group.objects.create(name=uid) 89 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 90 provider=self.provider, group=group 91 ).first() 92 self.assertIsNotNone(microsoft_group) 93 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 94 group_create.assert_called_once() 95 96 def test_group_not_created(self): 97 """Test without group property mappings, no group is created""" 98 self.provider.property_mappings_group.clear() 99 uid = generate_id() 100 with ( 101 patch( 102 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 103 MagicMock(return_value={"credentials": self.creds}), 104 ), 105 patch( 106 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 107 AsyncMock( 108 return_value=OrganizationCollectionResponse( 109 value=[ 110 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 111 ] 112 ) 113 ), 114 ), 115 patch( 116 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 117 AsyncMock(return_value=MSGroup(id=generate_id())), 118 ) as group_create, 119 ): 120 group = Group.objects.create(name=uid) 121 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 122 provider=self.provider, group=group 123 ).first() 124 self.assertIsNone(microsoft_group) 125 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 126 group_create.assert_not_called() 127 128 def test_group_create_update(self): 129 """Test group updating""" 130 uid = generate_id() 131 ext_id = generate_id() 132 with ( 133 patch( 134 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 135 MagicMock(return_value={"credentials": self.creds}), 136 ), 137 patch( 138 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 139 AsyncMock( 140 return_value=OrganizationCollectionResponse( 141 value=[ 142 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 143 ] 144 ) 145 ), 146 ), 147 patch( 148 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 149 AsyncMock(return_value=MSGroup(id=ext_id)), 150 ) as group_create, 151 patch( 152 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 153 AsyncMock(return_value=MSGroup(id=ext_id)), 154 ) as group_patch, 155 ): 156 group = Group.objects.create(name=uid) 157 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 158 provider=self.provider, group=group 159 ).first() 160 self.assertIsNotNone(microsoft_group) 161 162 group.name = "new name" 163 group.save() 164 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 165 group_create.assert_called_once() 166 group_patch.assert_called_once() 167 168 def test_group_create_delete(self): 169 """Test group deletion""" 170 uid = generate_id() 171 ext_id = generate_id() 172 with ( 173 patch( 174 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 175 AsyncMock( 176 return_value=OrganizationCollectionResponse( 177 value=[ 178 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 179 ] 180 ) 181 ), 182 ), 183 patch( 184 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 185 MagicMock(return_value={"credentials": self.creds}), 186 ), 187 patch( 188 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 189 AsyncMock(return_value=MSGroup(id=ext_id)), 190 ) as group_create, 191 patch( 192 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 193 AsyncMock(return_value=MSGroup(id=ext_id)), 194 ) as group_delete, 195 ): 196 group = Group.objects.create(name=uid) 197 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 198 provider=self.provider, group=group 199 ).first() 200 self.assertIsNotNone(microsoft_group) 201 202 group.delete() 203 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 204 group_create.assert_called_once() 205 group_delete.assert_called_once() 206 207 def test_group_create_member_add(self): 208 """Test group creation""" 209 uid = generate_id() 210 with ( 211 patch( 212 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 213 MagicMock(return_value={"credentials": self.creds}), 214 ), 215 patch( 216 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 217 AsyncMock( 218 return_value=OrganizationCollectionResponse( 219 value=[ 220 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 221 ] 222 ) 223 ), 224 ), 225 patch( 226 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 227 AsyncMock(return_value=MSUser(id=generate_id())), 228 ) as user_create, 229 patch( 230 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 231 AsyncMock(return_value=MSUser(id=generate_id())), 232 ), 233 patch( 234 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 235 AsyncMock(return_value=MSGroup(id=uid)), 236 ) as group_create, 237 patch( 238 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 239 AsyncMock(), 240 ) as member_add, 241 ): 242 user = create_test_user(uid) 243 group = Group.objects.create(name=uid) 244 group.users.add(user) 245 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 246 provider=self.provider, group=group 247 ).first() 248 self.assertIsNotNone(microsoft_group) 249 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 250 user_create.assert_called_once() 251 group_create.assert_called_once() 252 member_add.assert_called_once() 253 self.assertEqual( 254 member_add.call_args[0][0].odata_id, 255 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 256 MicrosoftEntraProviderUser.objects.filter( 257 provider=self.provider, 258 ) 259 .first() 260 .microsoft_id 261 }", 262 ) 263 264 def test_group_create_member_remove(self): 265 """Test group creation""" 266 uid = generate_id() 267 with ( 268 patch( 269 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 270 MagicMock(return_value={"credentials": self.creds}), 271 ), 272 patch( 273 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 274 AsyncMock( 275 return_value=OrganizationCollectionResponse( 276 value=[ 277 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 278 ] 279 ) 280 ), 281 ), 282 patch( 283 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 284 AsyncMock(return_value=MSUser(id=generate_id())), 285 ) as user_create, 286 patch( 287 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 288 AsyncMock(return_value=MSUser(id=generate_id())), 289 ), 290 patch( 291 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 292 AsyncMock(return_value=MSGroup(id=uid)), 293 ) as group_create, 294 patch( 295 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 296 AsyncMock(), 297 ) as member_add, 298 patch( 299 "msgraph.generated.groups.item.members.item.ref.ref_request_builder.RefRequestBuilder.delete", 300 AsyncMock(), 301 ) as member_remove, 302 ): 303 user = create_test_user(uid) 304 group = Group.objects.create(name=uid) 305 group.users.add(user) 306 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 307 provider=self.provider, group=group 308 ).first() 309 self.assertIsNotNone(microsoft_group) 310 group.users.remove(user) 311 312 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 313 user_create.assert_called_once() 314 group_create.assert_called_once() 315 member_add.assert_called_once() 316 self.assertEqual( 317 member_add.call_args[0][0].odata_id, 318 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 319 MicrosoftEntraProviderUser.objects.filter( 320 provider=self.provider, 321 ) 322 .first() 323 .microsoft_id 324 }", 325 ) 326 member_remove.assert_called_once() 327 328 def test_group_create_delete_do_nothing(self): 329 """Test group deletion (delete action = do nothing)""" 330 self.provider.group_delete_action = OutgoingSyncDeleteAction.DO_NOTHING 331 self.provider.save() 332 uid = generate_id() 333 with ( 334 patch( 335 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 336 MagicMock(return_value={"credentials": self.creds}), 337 ), 338 patch( 339 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 340 AsyncMock( 341 return_value=OrganizationCollectionResponse( 342 value=[ 343 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 344 ] 345 ) 346 ), 347 ), 348 patch( 349 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 350 AsyncMock(return_value=MSGroup(id=uid)), 351 ) as group_create, 352 patch( 353 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 354 AsyncMock(return_value=MSGroup(id=uid)), 355 ) as group_delete, 356 ): 357 group = Group.objects.create(name=uid) 358 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 359 provider=self.provider, group=group 360 ).first() 361 self.assertIsNotNone(microsoft_group) 362 363 group.delete() 364 self.assertFalse( 365 MicrosoftEntraProviderGroup.objects.filter( 366 provider=self.provider, group__name=uid 367 ).exists() 368 ) 369 group_create.assert_called_once() 370 group_delete.assert_not_called() 371 372 def test_sync_discover(self): 373 """Test group discovery""" 374 uid = generate_id() 375 self.app.backchannel_providers.remove(self.provider) 376 different_group = Group.objects.create( 377 name=uid, 378 ) 379 self.app.backchannel_providers.add(self.provider) 380 with ( 381 patch( 382 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 383 MagicMock(return_value={"credentials": self.creds}), 384 ), 385 patch( 386 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 387 AsyncMock( 388 return_value=OrganizationCollectionResponse( 389 value=[ 390 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 391 ] 392 ) 393 ), 394 ), 395 patch( 396 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 397 AsyncMock(return_value=MSUser(id=generate_id())), 398 ), 399 patch( 400 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 401 AsyncMock(return_value=MSGroup(id=generate_id())), 402 ), 403 patch( 404 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 405 AsyncMock(return_value=MSGroup(id=uid)), 406 ), 407 patch( 408 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 409 AsyncMock( 410 return_value=UserCollectionResponse( 411 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 412 ) 413 ), 414 ) as user_list, 415 patch( 416 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 417 AsyncMock( 418 return_value=GroupCollectionResponse( 419 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 420 ) 421 ), 422 ) as group_list, 423 ): 424 microsoft_entra_sync.send(self.provider.pk).get_result() 425 self.assertTrue( 426 MicrosoftEntraProviderGroup.objects.filter( 427 group=different_group, provider=self.provider 428 ).exists() 429 ) 430 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 431 user_list.assert_called_once() 432 group_list.assert_called_once() 433 434 def test_sync_discover_multiple(self): 435 """Test group discovery""" 436 uid = generate_id() 437 self.app.backchannel_providers.remove(self.provider) 438 different_group = Group.objects.create( 439 name=uid, 440 ) 441 self.app.backchannel_providers.add(self.provider) 442 with ( 443 patch( 444 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 445 MagicMock(return_value={"credentials": self.creds}), 446 ), 447 patch( 448 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 449 AsyncMock( 450 return_value=OrganizationCollectionResponse( 451 value=[ 452 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 453 ] 454 ) 455 ), 456 ), 457 patch( 458 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 459 AsyncMock(return_value=MSUser(id=generate_id())), 460 ), 461 patch( 462 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 463 AsyncMock(return_value=MSGroup(id=generate_id())), 464 ), 465 patch( 466 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 467 AsyncMock(return_value=MSGroup(id=uid)), 468 ), 469 patch( 470 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 471 AsyncMock( 472 return_value=UserCollectionResponse( 473 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 474 ) 475 ), 476 ) as user_list, 477 patch( 478 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 479 AsyncMock( 480 return_value=GroupCollectionResponse( 481 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 482 ) 483 ), 484 ) as group_list, 485 ): 486 microsoft_entra_sync.send(self.provider.pk).get_result() 487 self.assertTrue( 488 MicrosoftEntraProviderGroup.objects.filter( 489 group=different_group, provider=self.provider 490 ).exists() 491 ) 492 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 493 user_list.assert_called_once() 494 group_list.assert_called_once() 495 496 with patch( 497 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 498 AsyncMock( 499 return_value=GroupCollectionResponse( 500 value=[ 501 MSGroup(display_name=uid, unique_name=uid, id=uid, description="foo") 502 ] 503 ) 504 ), 505 ) as mod_group_list: 506 microsoft_entra_sync.send(self.provider.pk).get_result() 507 self.assertTrue( 508 MicrosoftEntraProviderGroup.objects.filter( 509 group=different_group, provider=self.provider 510 ).exists() 511 ) 512 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 513 mod_group_list.assert_called_once()
class
MicrosoftEntraGroupTests(django.test.testcases.TestCase):
32class MicrosoftEntraGroupTests(TestCase): 33 """Microsoft Entra Group tests""" 34 35 @apply_blueprint("system/providers-microsoft-entra.yaml") 36 def setUp(self) -> None: 37 # Delete all groups and groups as the mocked HTTP responses only return one ID 38 # which will cause errors with multiple groups 39 Tenant.objects.update(avatars="none") 40 User.objects.all().exclude_anonymous().delete() 41 Group.objects.all().delete() 42 self.provider: MicrosoftEntraProvider = MicrosoftEntraProvider.objects.create( 43 name=generate_id(), 44 client_id=generate_id(), 45 client_secret=generate_id(), 46 tenant_id=generate_id(), 47 exclude_users_service_account=True, 48 ) 49 self.app: Application = Application.objects.create( 50 name=generate_id(), 51 slug=generate_id(), 52 ) 53 self.app.backchannel_providers.add(self.provider) 54 self.provider.property_mappings.add( 55 MicrosoftEntraProviderMapping.objects.get( 56 managed="goauthentik.io/providers/microsoft_entra/user" 57 ) 58 ) 59 self.provider.property_mappings_group.add( 60 MicrosoftEntraProviderMapping.objects.get( 61 managed="goauthentik.io/providers/microsoft_entra/group" 62 ) 63 ) 64 self.creds = ClientSecretCredential(generate_id(), generate_id(), generate_id()) 65 66 def test_group_create(self): 67 """Test group creation""" 68 uid = generate_id() 69 with ( 70 patch( 71 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 72 MagicMock(return_value={"credentials": self.creds}), 73 ), 74 patch( 75 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 76 AsyncMock( 77 return_value=OrganizationCollectionResponse( 78 value=[ 79 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 80 ] 81 ) 82 ), 83 ), 84 patch( 85 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 86 AsyncMock(return_value=MSGroup(id=generate_id())), 87 ) as group_create, 88 ): 89 group = Group.objects.create(name=uid) 90 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 91 provider=self.provider, group=group 92 ).first() 93 self.assertIsNotNone(microsoft_group) 94 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 95 group_create.assert_called_once() 96 97 def test_group_not_created(self): 98 """Test without group property mappings, no group is created""" 99 self.provider.property_mappings_group.clear() 100 uid = generate_id() 101 with ( 102 patch( 103 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 104 MagicMock(return_value={"credentials": self.creds}), 105 ), 106 patch( 107 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 108 AsyncMock( 109 return_value=OrganizationCollectionResponse( 110 value=[ 111 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 112 ] 113 ) 114 ), 115 ), 116 patch( 117 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 118 AsyncMock(return_value=MSGroup(id=generate_id())), 119 ) as group_create, 120 ): 121 group = Group.objects.create(name=uid) 122 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 123 provider=self.provider, group=group 124 ).first() 125 self.assertIsNone(microsoft_group) 126 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 127 group_create.assert_not_called() 128 129 def test_group_create_update(self): 130 """Test group updating""" 131 uid = generate_id() 132 ext_id = generate_id() 133 with ( 134 patch( 135 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 136 MagicMock(return_value={"credentials": self.creds}), 137 ), 138 patch( 139 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 140 AsyncMock( 141 return_value=OrganizationCollectionResponse( 142 value=[ 143 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 144 ] 145 ) 146 ), 147 ), 148 patch( 149 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 150 AsyncMock(return_value=MSGroup(id=ext_id)), 151 ) as group_create, 152 patch( 153 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 154 AsyncMock(return_value=MSGroup(id=ext_id)), 155 ) as group_patch, 156 ): 157 group = Group.objects.create(name=uid) 158 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 159 provider=self.provider, group=group 160 ).first() 161 self.assertIsNotNone(microsoft_group) 162 163 group.name = "new name" 164 group.save() 165 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 166 group_create.assert_called_once() 167 group_patch.assert_called_once() 168 169 def test_group_create_delete(self): 170 """Test group deletion""" 171 uid = generate_id() 172 ext_id = generate_id() 173 with ( 174 patch( 175 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 176 AsyncMock( 177 return_value=OrganizationCollectionResponse( 178 value=[ 179 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 180 ] 181 ) 182 ), 183 ), 184 patch( 185 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 186 MagicMock(return_value={"credentials": self.creds}), 187 ), 188 patch( 189 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 190 AsyncMock(return_value=MSGroup(id=ext_id)), 191 ) as group_create, 192 patch( 193 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 194 AsyncMock(return_value=MSGroup(id=ext_id)), 195 ) as group_delete, 196 ): 197 group = Group.objects.create(name=uid) 198 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 199 provider=self.provider, group=group 200 ).first() 201 self.assertIsNotNone(microsoft_group) 202 203 group.delete() 204 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 205 group_create.assert_called_once() 206 group_delete.assert_called_once() 207 208 def test_group_create_member_add(self): 209 """Test group creation""" 210 uid = generate_id() 211 with ( 212 patch( 213 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 214 MagicMock(return_value={"credentials": self.creds}), 215 ), 216 patch( 217 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 218 AsyncMock( 219 return_value=OrganizationCollectionResponse( 220 value=[ 221 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 222 ] 223 ) 224 ), 225 ), 226 patch( 227 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 228 AsyncMock(return_value=MSUser(id=generate_id())), 229 ) as user_create, 230 patch( 231 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 232 AsyncMock(return_value=MSUser(id=generate_id())), 233 ), 234 patch( 235 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 236 AsyncMock(return_value=MSGroup(id=uid)), 237 ) as group_create, 238 patch( 239 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 240 AsyncMock(), 241 ) as member_add, 242 ): 243 user = create_test_user(uid) 244 group = Group.objects.create(name=uid) 245 group.users.add(user) 246 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 247 provider=self.provider, group=group 248 ).first() 249 self.assertIsNotNone(microsoft_group) 250 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 251 user_create.assert_called_once() 252 group_create.assert_called_once() 253 member_add.assert_called_once() 254 self.assertEqual( 255 member_add.call_args[0][0].odata_id, 256 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 257 MicrosoftEntraProviderUser.objects.filter( 258 provider=self.provider, 259 ) 260 .first() 261 .microsoft_id 262 }", 263 ) 264 265 def test_group_create_member_remove(self): 266 """Test group creation""" 267 uid = generate_id() 268 with ( 269 patch( 270 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 271 MagicMock(return_value={"credentials": self.creds}), 272 ), 273 patch( 274 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 275 AsyncMock( 276 return_value=OrganizationCollectionResponse( 277 value=[ 278 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 279 ] 280 ) 281 ), 282 ), 283 patch( 284 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 285 AsyncMock(return_value=MSUser(id=generate_id())), 286 ) as user_create, 287 patch( 288 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 289 AsyncMock(return_value=MSUser(id=generate_id())), 290 ), 291 patch( 292 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 293 AsyncMock(return_value=MSGroup(id=uid)), 294 ) as group_create, 295 patch( 296 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 297 AsyncMock(), 298 ) as member_add, 299 patch( 300 "msgraph.generated.groups.item.members.item.ref.ref_request_builder.RefRequestBuilder.delete", 301 AsyncMock(), 302 ) as member_remove, 303 ): 304 user = create_test_user(uid) 305 group = Group.objects.create(name=uid) 306 group.users.add(user) 307 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 308 provider=self.provider, group=group 309 ).first() 310 self.assertIsNotNone(microsoft_group) 311 group.users.remove(user) 312 313 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 314 user_create.assert_called_once() 315 group_create.assert_called_once() 316 member_add.assert_called_once() 317 self.assertEqual( 318 member_add.call_args[0][0].odata_id, 319 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 320 MicrosoftEntraProviderUser.objects.filter( 321 provider=self.provider, 322 ) 323 .first() 324 .microsoft_id 325 }", 326 ) 327 member_remove.assert_called_once() 328 329 def test_group_create_delete_do_nothing(self): 330 """Test group deletion (delete action = do nothing)""" 331 self.provider.group_delete_action = OutgoingSyncDeleteAction.DO_NOTHING 332 self.provider.save() 333 uid = generate_id() 334 with ( 335 patch( 336 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 337 MagicMock(return_value={"credentials": self.creds}), 338 ), 339 patch( 340 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 341 AsyncMock( 342 return_value=OrganizationCollectionResponse( 343 value=[ 344 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 345 ] 346 ) 347 ), 348 ), 349 patch( 350 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 351 AsyncMock(return_value=MSGroup(id=uid)), 352 ) as group_create, 353 patch( 354 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 355 AsyncMock(return_value=MSGroup(id=uid)), 356 ) as group_delete, 357 ): 358 group = Group.objects.create(name=uid) 359 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 360 provider=self.provider, group=group 361 ).first() 362 self.assertIsNotNone(microsoft_group) 363 364 group.delete() 365 self.assertFalse( 366 MicrosoftEntraProviderGroup.objects.filter( 367 provider=self.provider, group__name=uid 368 ).exists() 369 ) 370 group_create.assert_called_once() 371 group_delete.assert_not_called() 372 373 def test_sync_discover(self): 374 """Test group discovery""" 375 uid = generate_id() 376 self.app.backchannel_providers.remove(self.provider) 377 different_group = Group.objects.create( 378 name=uid, 379 ) 380 self.app.backchannel_providers.add(self.provider) 381 with ( 382 patch( 383 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 384 MagicMock(return_value={"credentials": self.creds}), 385 ), 386 patch( 387 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 388 AsyncMock( 389 return_value=OrganizationCollectionResponse( 390 value=[ 391 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 392 ] 393 ) 394 ), 395 ), 396 patch( 397 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 398 AsyncMock(return_value=MSUser(id=generate_id())), 399 ), 400 patch( 401 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 402 AsyncMock(return_value=MSGroup(id=generate_id())), 403 ), 404 patch( 405 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 406 AsyncMock(return_value=MSGroup(id=uid)), 407 ), 408 patch( 409 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 410 AsyncMock( 411 return_value=UserCollectionResponse( 412 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 413 ) 414 ), 415 ) as user_list, 416 patch( 417 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 418 AsyncMock( 419 return_value=GroupCollectionResponse( 420 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 421 ) 422 ), 423 ) as group_list, 424 ): 425 microsoft_entra_sync.send(self.provider.pk).get_result() 426 self.assertTrue( 427 MicrosoftEntraProviderGroup.objects.filter( 428 group=different_group, provider=self.provider 429 ).exists() 430 ) 431 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 432 user_list.assert_called_once() 433 group_list.assert_called_once() 434 435 def test_sync_discover_multiple(self): 436 """Test group discovery""" 437 uid = generate_id() 438 self.app.backchannel_providers.remove(self.provider) 439 different_group = Group.objects.create( 440 name=uid, 441 ) 442 self.app.backchannel_providers.add(self.provider) 443 with ( 444 patch( 445 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 446 MagicMock(return_value={"credentials": self.creds}), 447 ), 448 patch( 449 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 450 AsyncMock( 451 return_value=OrganizationCollectionResponse( 452 value=[ 453 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 454 ] 455 ) 456 ), 457 ), 458 patch( 459 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 460 AsyncMock(return_value=MSUser(id=generate_id())), 461 ), 462 patch( 463 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 464 AsyncMock(return_value=MSGroup(id=generate_id())), 465 ), 466 patch( 467 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 468 AsyncMock(return_value=MSGroup(id=uid)), 469 ), 470 patch( 471 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 472 AsyncMock( 473 return_value=UserCollectionResponse( 474 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 475 ) 476 ), 477 ) as user_list, 478 patch( 479 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 480 AsyncMock( 481 return_value=GroupCollectionResponse( 482 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 483 ) 484 ), 485 ) as group_list, 486 ): 487 microsoft_entra_sync.send(self.provider.pk).get_result() 488 self.assertTrue( 489 MicrosoftEntraProviderGroup.objects.filter( 490 group=different_group, provider=self.provider 491 ).exists() 492 ) 493 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 494 user_list.assert_called_once() 495 group_list.assert_called_once() 496 497 with patch( 498 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 499 AsyncMock( 500 return_value=GroupCollectionResponse( 501 value=[ 502 MSGroup(display_name=uid, unique_name=uid, id=uid, description="foo") 503 ] 504 ) 505 ), 506 ) as mod_group_list: 507 microsoft_entra_sync.send(self.provider.pk).get_result() 508 self.assertTrue( 509 MicrosoftEntraProviderGroup.objects.filter( 510 group=different_group, provider=self.provider 511 ).exists() 512 ) 513 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 514 mod_group_list.assert_called_once()
Microsoft Entra Group tests
@apply_blueprint('system/providers-microsoft-entra.yaml')
def
setUp(self) -> None:
35 @apply_blueprint("system/providers-microsoft-entra.yaml") 36 def setUp(self) -> None: 37 # Delete all groups and groups as the mocked HTTP responses only return one ID 38 # which will cause errors with multiple groups 39 Tenant.objects.update(avatars="none") 40 User.objects.all().exclude_anonymous().delete() 41 Group.objects.all().delete() 42 self.provider: MicrosoftEntraProvider = MicrosoftEntraProvider.objects.create( 43 name=generate_id(), 44 client_id=generate_id(), 45 client_secret=generate_id(), 46 tenant_id=generate_id(), 47 exclude_users_service_account=True, 48 ) 49 self.app: Application = Application.objects.create( 50 name=generate_id(), 51 slug=generate_id(), 52 ) 53 self.app.backchannel_providers.add(self.provider) 54 self.provider.property_mappings.add( 55 MicrosoftEntraProviderMapping.objects.get( 56 managed="goauthentik.io/providers/microsoft_entra/user" 57 ) 58 ) 59 self.provider.property_mappings_group.add( 60 MicrosoftEntraProviderMapping.objects.get( 61 managed="goauthentik.io/providers/microsoft_entra/group" 62 ) 63 ) 64 self.creds = ClientSecretCredential(generate_id(), generate_id(), generate_id())
Hook method for setting up the test fixture before exercising it.
def
test_group_create(self):
66 def test_group_create(self): 67 """Test group creation""" 68 uid = generate_id() 69 with ( 70 patch( 71 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 72 MagicMock(return_value={"credentials": self.creds}), 73 ), 74 patch( 75 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 76 AsyncMock( 77 return_value=OrganizationCollectionResponse( 78 value=[ 79 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 80 ] 81 ) 82 ), 83 ), 84 patch( 85 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 86 AsyncMock(return_value=MSGroup(id=generate_id())), 87 ) as group_create, 88 ): 89 group = Group.objects.create(name=uid) 90 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 91 provider=self.provider, group=group 92 ).first() 93 self.assertIsNotNone(microsoft_group) 94 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 95 group_create.assert_called_once()
Test group creation
def
test_group_not_created(self):
97 def test_group_not_created(self): 98 """Test without group property mappings, no group is created""" 99 self.provider.property_mappings_group.clear() 100 uid = generate_id() 101 with ( 102 patch( 103 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 104 MagicMock(return_value={"credentials": self.creds}), 105 ), 106 patch( 107 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 108 AsyncMock( 109 return_value=OrganizationCollectionResponse( 110 value=[ 111 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 112 ] 113 ) 114 ), 115 ), 116 patch( 117 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 118 AsyncMock(return_value=MSGroup(id=generate_id())), 119 ) as group_create, 120 ): 121 group = Group.objects.create(name=uid) 122 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 123 provider=self.provider, group=group 124 ).first() 125 self.assertIsNone(microsoft_group) 126 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 127 group_create.assert_not_called()
Test without group property mappings, no group is created
def
test_group_create_update(self):
129 def test_group_create_update(self): 130 """Test group updating""" 131 uid = generate_id() 132 ext_id = generate_id() 133 with ( 134 patch( 135 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 136 MagicMock(return_value={"credentials": self.creds}), 137 ), 138 patch( 139 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 140 AsyncMock( 141 return_value=OrganizationCollectionResponse( 142 value=[ 143 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 144 ] 145 ) 146 ), 147 ), 148 patch( 149 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 150 AsyncMock(return_value=MSGroup(id=ext_id)), 151 ) as group_create, 152 patch( 153 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 154 AsyncMock(return_value=MSGroup(id=ext_id)), 155 ) as group_patch, 156 ): 157 group = Group.objects.create(name=uid) 158 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 159 provider=self.provider, group=group 160 ).first() 161 self.assertIsNotNone(microsoft_group) 162 163 group.name = "new name" 164 group.save() 165 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 166 group_create.assert_called_once() 167 group_patch.assert_called_once()
Test group updating
def
test_group_create_delete(self):
169 def test_group_create_delete(self): 170 """Test group deletion""" 171 uid = generate_id() 172 ext_id = generate_id() 173 with ( 174 patch( 175 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 176 AsyncMock( 177 return_value=OrganizationCollectionResponse( 178 value=[ 179 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 180 ] 181 ) 182 ), 183 ), 184 patch( 185 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 186 MagicMock(return_value={"credentials": self.creds}), 187 ), 188 patch( 189 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 190 AsyncMock(return_value=MSGroup(id=ext_id)), 191 ) as group_create, 192 patch( 193 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 194 AsyncMock(return_value=MSGroup(id=ext_id)), 195 ) as group_delete, 196 ): 197 group = Group.objects.create(name=uid) 198 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 199 provider=self.provider, group=group 200 ).first() 201 self.assertIsNotNone(microsoft_group) 202 203 group.delete() 204 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 205 group_create.assert_called_once() 206 group_delete.assert_called_once()
Test group deletion
def
test_group_create_member_add(self):
208 def test_group_create_member_add(self): 209 """Test group creation""" 210 uid = generate_id() 211 with ( 212 patch( 213 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 214 MagicMock(return_value={"credentials": self.creds}), 215 ), 216 patch( 217 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 218 AsyncMock( 219 return_value=OrganizationCollectionResponse( 220 value=[ 221 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 222 ] 223 ) 224 ), 225 ), 226 patch( 227 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 228 AsyncMock(return_value=MSUser(id=generate_id())), 229 ) as user_create, 230 patch( 231 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 232 AsyncMock(return_value=MSUser(id=generate_id())), 233 ), 234 patch( 235 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 236 AsyncMock(return_value=MSGroup(id=uid)), 237 ) as group_create, 238 patch( 239 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 240 AsyncMock(), 241 ) as member_add, 242 ): 243 user = create_test_user(uid) 244 group = Group.objects.create(name=uid) 245 group.users.add(user) 246 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 247 provider=self.provider, group=group 248 ).first() 249 self.assertIsNotNone(microsoft_group) 250 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 251 user_create.assert_called_once() 252 group_create.assert_called_once() 253 member_add.assert_called_once() 254 self.assertEqual( 255 member_add.call_args[0][0].odata_id, 256 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 257 MicrosoftEntraProviderUser.objects.filter( 258 provider=self.provider, 259 ) 260 .first() 261 .microsoft_id 262 }", 263 )
Test group creation
def
test_group_create_member_remove(self):
265 def test_group_create_member_remove(self): 266 """Test group creation""" 267 uid = generate_id() 268 with ( 269 patch( 270 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 271 MagicMock(return_value={"credentials": self.creds}), 272 ), 273 patch( 274 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 275 AsyncMock( 276 return_value=OrganizationCollectionResponse( 277 value=[ 278 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 279 ] 280 ) 281 ), 282 ), 283 patch( 284 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.post", 285 AsyncMock(return_value=MSUser(id=generate_id())), 286 ) as user_create, 287 patch( 288 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 289 AsyncMock(return_value=MSUser(id=generate_id())), 290 ), 291 patch( 292 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 293 AsyncMock(return_value=MSGroup(id=uid)), 294 ) as group_create, 295 patch( 296 "msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post", 297 AsyncMock(), 298 ) as member_add, 299 patch( 300 "msgraph.generated.groups.item.members.item.ref.ref_request_builder.RefRequestBuilder.delete", 301 AsyncMock(), 302 ) as member_remove, 303 ): 304 user = create_test_user(uid) 305 group = Group.objects.create(name=uid) 306 group.users.add(user) 307 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 308 provider=self.provider, group=group 309 ).first() 310 self.assertIsNotNone(microsoft_group) 311 group.users.remove(user) 312 313 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 314 user_create.assert_called_once() 315 group_create.assert_called_once() 316 member_add.assert_called_once() 317 self.assertEqual( 318 member_add.call_args[0][0].odata_id, 319 f"https://graph.microsoft.com/v1.0/directoryObjects/{ 320 MicrosoftEntraProviderUser.objects.filter( 321 provider=self.provider, 322 ) 323 .first() 324 .microsoft_id 325 }", 326 ) 327 member_remove.assert_called_once()
Test group creation
def
test_group_create_delete_do_nothing(self):
329 def test_group_create_delete_do_nothing(self): 330 """Test group deletion (delete action = do nothing)""" 331 self.provider.group_delete_action = OutgoingSyncDeleteAction.DO_NOTHING 332 self.provider.save() 333 uid = generate_id() 334 with ( 335 patch( 336 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 337 MagicMock(return_value={"credentials": self.creds}), 338 ), 339 patch( 340 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 341 AsyncMock( 342 return_value=OrganizationCollectionResponse( 343 value=[ 344 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 345 ] 346 ) 347 ), 348 ), 349 patch( 350 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 351 AsyncMock(return_value=MSGroup(id=uid)), 352 ) as group_create, 353 patch( 354 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete", 355 AsyncMock(return_value=MSGroup(id=uid)), 356 ) as group_delete, 357 ): 358 group = Group.objects.create(name=uid) 359 microsoft_group = MicrosoftEntraProviderGroup.objects.filter( 360 provider=self.provider, group=group 361 ).first() 362 self.assertIsNotNone(microsoft_group) 363 364 group.delete() 365 self.assertFalse( 366 MicrosoftEntraProviderGroup.objects.filter( 367 provider=self.provider, group__name=uid 368 ).exists() 369 ) 370 group_create.assert_called_once() 371 group_delete.assert_not_called()
Test group deletion (delete action = do nothing)
def
test_sync_discover(self):
373 def test_sync_discover(self): 374 """Test group discovery""" 375 uid = generate_id() 376 self.app.backchannel_providers.remove(self.provider) 377 different_group = Group.objects.create( 378 name=uid, 379 ) 380 self.app.backchannel_providers.add(self.provider) 381 with ( 382 patch( 383 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 384 MagicMock(return_value={"credentials": self.creds}), 385 ), 386 patch( 387 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 388 AsyncMock( 389 return_value=OrganizationCollectionResponse( 390 value=[ 391 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 392 ] 393 ) 394 ), 395 ), 396 patch( 397 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 398 AsyncMock(return_value=MSUser(id=generate_id())), 399 ), 400 patch( 401 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 402 AsyncMock(return_value=MSGroup(id=generate_id())), 403 ), 404 patch( 405 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 406 AsyncMock(return_value=MSGroup(id=uid)), 407 ), 408 patch( 409 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 410 AsyncMock( 411 return_value=UserCollectionResponse( 412 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 413 ) 414 ), 415 ) as user_list, 416 patch( 417 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 418 AsyncMock( 419 return_value=GroupCollectionResponse( 420 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 421 ) 422 ), 423 ) as group_list, 424 ): 425 microsoft_entra_sync.send(self.provider.pk).get_result() 426 self.assertTrue( 427 MicrosoftEntraProviderGroup.objects.filter( 428 group=different_group, provider=self.provider 429 ).exists() 430 ) 431 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 432 user_list.assert_called_once() 433 group_list.assert_called_once()
Test group discovery
def
test_sync_discover_multiple(self):
435 def test_sync_discover_multiple(self): 436 """Test group discovery""" 437 uid = generate_id() 438 self.app.backchannel_providers.remove(self.provider) 439 different_group = Group.objects.create( 440 name=uid, 441 ) 442 self.app.backchannel_providers.add(self.provider) 443 with ( 444 patch( 445 "authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials", 446 MagicMock(return_value={"credentials": self.creds}), 447 ), 448 patch( 449 "msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get", 450 AsyncMock( 451 return_value=OrganizationCollectionResponse( 452 value=[ 453 Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")]) 454 ] 455 ) 456 ), 457 ), 458 patch( 459 "msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch", 460 AsyncMock(return_value=MSUser(id=generate_id())), 461 ), 462 patch( 463 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post", 464 AsyncMock(return_value=MSGroup(id=generate_id())), 465 ), 466 patch( 467 "msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch", 468 AsyncMock(return_value=MSGroup(id=uid)), 469 ), 470 patch( 471 "msgraph.generated.users.users_request_builder.UsersRequestBuilder.get", 472 AsyncMock( 473 return_value=UserCollectionResponse( 474 value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)] 475 ) 476 ), 477 ) as user_list, 478 patch( 479 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 480 AsyncMock( 481 return_value=GroupCollectionResponse( 482 value=[MSGroup(display_name=uid, unique_name=uid, id=uid)] 483 ) 484 ), 485 ) as group_list, 486 ): 487 microsoft_entra_sync.send(self.provider.pk).get_result() 488 self.assertTrue( 489 MicrosoftEntraProviderGroup.objects.filter( 490 group=different_group, provider=self.provider 491 ).exists() 492 ) 493 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 494 user_list.assert_called_once() 495 group_list.assert_called_once() 496 497 with patch( 498 "msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get", 499 AsyncMock( 500 return_value=GroupCollectionResponse( 501 value=[ 502 MSGroup(display_name=uid, unique_name=uid, id=uid, description="foo") 503 ] 504 ) 505 ), 506 ) as mod_group_list: 507 microsoft_entra_sync.send(self.provider.pk).get_result() 508 self.assertTrue( 509 MicrosoftEntraProviderGroup.objects.filter( 510 group=different_group, provider=self.provider 511 ).exists() 512 ) 513 self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists()) 514 mod_group_list.assert_called_once()
Test group discovery