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