authentik.sources.scim.tests.test_users_patch

  1from rest_framework.test import APITestCase
  2
  3from authentik.core.tests.utils import create_test_user
  4from authentik.lib.generators import generate_id
  5from authentik.sources.scim.constants import SCIM_URN_USER_ENTERPRISE
  6from authentik.sources.scim.models import SCIMSource, SCIMSourceUser
  7from authentik.sources.scim.patch.processor import SCIMPatchProcessor
  8
  9
 10class TestSCIMUsersPatch(APITestCase):
 11    """Test SCIM User Patch"""
 12
 13    def test_add(self):
 14        req = {
 15            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
 16            "Operations": [
 17                {"op": "Add", "path": "name.givenName", "value": "aqwer"},
 18                {"op": "Add", "path": "name.familyName", "value": "qwerqqqq"},
 19                {"op": "Add", "path": "name.formatted", "value": "aqwer qwerqqqq"},
 20            ],
 21        }
 22        user = create_test_user()
 23        source = SCIMSource.objects.create(slug=generate_id())
 24        connection = SCIMSourceUser.objects.create(
 25            user=user,
 26            id=generate_id(),
 27            source=source,
 28            attributes={
 29                "meta": {"resourceType": "User"},
 30                "active": True,
 31                "schemas": [
 32                    "urn:ietf:params:scim:schemas:core:2.0:User",
 33                    SCIM_URN_USER_ENTERPRISE,
 34                ],
 35                "userName": "test@t.goauthentik.io",
 36                "externalId": "test",
 37                "displayName": "Test MS",
 38            },
 39        )
 40        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
 41        self.assertEqual(
 42            updated,
 43            {
 44                "meta": {"resourceType": "User"},
 45                "active": True,
 46                "name": {
 47                    "givenName": "aqwer",
 48                    "familyName": "qwerqqqq",
 49                    "formatted": "aqwer qwerqqqq",
 50                },
 51                "schemas": [
 52                    "urn:ietf:params:scim:schemas:core:2.0:User",
 53                    SCIM_URN_USER_ENTERPRISE,
 54                ],
 55                "userName": "test@t.goauthentik.io",
 56                "externalId": "test",
 57                "displayName": "Test MS",
 58            },
 59        )
 60
 61    def test_add_no_path(self):
 62        """Test add patch with no path set"""
 63        req = {
 64            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
 65            "Operations": [
 66                {"op": "Add", "value": {"externalId": "aqwer"}},
 67            ],
 68        }
 69        user = create_test_user()
 70        source = SCIMSource.objects.create(slug=generate_id())
 71        connection = SCIMSourceUser.objects.create(
 72            user=user,
 73            id=generate_id(),
 74            source=source,
 75            attributes={
 76                "meta": {"resourceType": "User"},
 77                "active": True,
 78                "schemas": [
 79                    "urn:ietf:params:scim:schemas:core:2.0:User",
 80                    SCIM_URN_USER_ENTERPRISE,
 81                ],
 82                "userName": "test@t.goauthentik.io",
 83                "displayName": "Test MS",
 84            },
 85        )
 86        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
 87        self.assertEqual(
 88            updated,
 89            {
 90                "meta": {"resourceType": "User"},
 91                "active": True,
 92                "schemas": [
 93                    "urn:ietf:params:scim:schemas:core:2.0:User",
 94                    SCIM_URN_USER_ENTERPRISE,
 95                ],
 96                "userName": "test@t.goauthentik.io",
 97                "externalId": "aqwer",
 98                "displayName": "Test MS",
 99            },
100        )
101
102    def test_replace(self):
103        req = {
104            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
105            "Operations": [
106                {"op": "Replace", "path": "name", "value": {"givenName": "aqwer"}},
107            ],
108        }
109        user = create_test_user()
110        source = SCIMSource.objects.create(slug=generate_id())
111        connection = SCIMSourceUser.objects.create(
112            user=user,
113            id=generate_id(),
114            source=source,
115            attributes={
116                "meta": {"resourceType": "User"},
117                "active": True,
118                "schemas": [
119                    "urn:ietf:params:scim:schemas:core:2.0:User",
120                    SCIM_URN_USER_ENTERPRISE,
121                ],
122                "userName": "test@t.goauthentik.io",
123                "externalId": "test",
124                "displayName": "Test MS",
125            },
126        )
127        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
128        self.assertEqual(
129            updated,
130            {
131                "meta": {"resourceType": "User"},
132                "active": True,
133                "name": {
134                    "givenName": "aqwer",
135                },
136                "schemas": [
137                    "urn:ietf:params:scim:schemas:core:2.0:User",
138                    SCIM_URN_USER_ENTERPRISE,
139                ],
140                "userName": "test@t.goauthentik.io",
141                "externalId": "test",
142                "displayName": "Test MS",
143            },
144        )
145
146    def test_replace_no_path(self):
147        """Test value replace with no path"""
148        req = {
149            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
150            "Operations": [
151                {"op": "Replace", "value": {"externalId": "aqwer"}},
152            ],
153        }
154        user = create_test_user()
155        source = SCIMSource.objects.create(slug=generate_id())
156        connection = SCIMSourceUser.objects.create(
157            user=user,
158            id=generate_id(),
159            source=source,
160            attributes={
161                "meta": {"resourceType": "User"},
162                "active": True,
163                "schemas": [
164                    "urn:ietf:params:scim:schemas:core:2.0:User",
165                    SCIM_URN_USER_ENTERPRISE,
166                ],
167                "userName": "test@t.goauthentik.io",
168                "externalId": "test",
169                "displayName": "Test MS",
170            },
171        )
172        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
173        self.assertEqual(
174            updated,
175            {
176                "meta": {"resourceType": "User"},
177                "active": True,
178                "schemas": [
179                    "urn:ietf:params:scim:schemas:core:2.0:User",
180                    SCIM_URN_USER_ENTERPRISE,
181                ],
182                "userName": "test@t.goauthentik.io",
183                "externalId": "aqwer",
184                "displayName": "Test MS",
185            },
186        )
187
188    def test_remove(self):
189        req = {
190            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
191            "Operations": [
192                {"op": "Remove", "path": "name", "value": {"givenName": "aqwer"}},
193            ],
194        }
195        user = create_test_user()
196        source = SCIMSource.objects.create(slug=generate_id())
197        connection = SCIMSourceUser.objects.create(
198            user=user,
199            id=generate_id(),
200            source=source,
201            attributes={
202                "meta": {"resourceType": "User"},
203                "active": True,
204                "name": {
205                    "givenName": "aqwer",
206                },
207                "schemas": [
208                    "urn:ietf:params:scim:schemas:core:2.0:User",
209                    SCIM_URN_USER_ENTERPRISE,
210                ],
211                "userName": "test@t.goauthentik.io",
212                "externalId": "test",
213                "displayName": "Test MS",
214            },
215        )
216        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
217        self.assertEqual(
218            updated,
219            {
220                "meta": {"resourceType": "User"},
221                "active": True,
222                "schemas": [
223                    "urn:ietf:params:scim:schemas:core:2.0:User",
224                    SCIM_URN_USER_ENTERPRISE,
225                ],
226                "userName": "test@t.goauthentik.io",
227                "externalId": "test",
228                "displayName": "Test MS",
229            },
230        )
231
232    def test_large(self):
233        """Large amount of patch operations"""
234        req = {
235            "Operations": [
236                {
237                    "op": "replace",
238                    "path": "emails[primary eq true].value",
239                    "value": "dandre_kling@wintheiser.info",
240                },
241                {
242                    "op": "replace",
243                    "path": "phoneNumbers[primary eq true].value",
244                    "value": "72-634-1548",
245                },
246                {
247                    "op": "replace",
248                    "path": "phoneNumbers[primary eq true].display",
249                    "value": "72-634-1548",
250                },
251                {"op": "replace", "path": "ims[primary eq true].value", "value": "GXSGJKWGHVVS"},
252                {"op": "replace", "path": "ims[primary eq true].display", "value": "IMCHDKUQIPYB"},
253                {
254                    "op": "replace",
255                    "path": "photos[primary eq true].display",
256                    "value": "TWAWLHHSUNIV",
257                },
258                {
259                    "op": "replace",
260                    "path": "addresses[primary eq true].formatted",
261                    "value": "TMINZQAJQDCL",
262                },
263                {
264                    "op": "replace",
265                    "path": "addresses[primary eq true].streetAddress",
266                    "value": "081 Wisoky Key",
267                },
268                {
269                    "op": "replace",
270                    "path": "addresses[primary eq true].locality",
271                    "value": "DPFASBZRPMDP",
272                },
273                {
274                    "op": "replace",
275                    "path": "addresses[primary eq true].region",
276                    "value": "WHSTJSPIPTCF",
277                },
278                {
279                    "op": "replace",
280                    "path": "addresses[primary eq true].postalCode",
281                    "value": "ko28 1qa",
282                },
283                {"op": "replace", "path": "addresses[primary eq true].country", "value": "Taiwan"},
284                {
285                    "op": "replace",
286                    "path": "entitlements[primary eq true].value",
287                    "value": "NGBJMUYZVVBX",
288                },
289                {"op": "replace", "path": "roles[primary eq true].value", "value": "XEELVFMMWCVM"},
290                {
291                    "op": "replace",
292                    "path": "x509Certificates[primary eq true].value",
293                    "value": "UYISMEDOXUZY",
294                },
295                {
296                    "op": "replace",
297                    "value": {
298                        "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
299                        "name.formatted": "Dell",
300                        "name.familyName": "Gay",
301                        "name.givenName": "Kyler",
302                        "name.middleName": "Hannah",
303                        "name.honorificPrefix": "Cassie",
304                        "name.honorificSuffix": "Yolanda",
305                        "displayName": "DPRLIJSFQMTL",
306                        "nickName": "BKSPMIRMFBTI",
307                        "title": "NBZCOAXVYJUY",
308                        "userType": "ZGJMYZRUORZE",
309                        "preferredLanguage": "as-IN",
310                        "locale": "JLOJHLPWZODG",
311                        "timezone": "America/Argentina/Rio_Gallegos",
312                        "active": True,
313                        f"{SCIM_URN_USER_ENTERPRISE}:employeeNumber": "PDFWRRZBQOHB",
314                        f"{SCIM_URN_USER_ENTERPRISE}:costCenter": "HACMZWSEDOTQ",
315                        f"{SCIM_URN_USER_ENTERPRISE}:organization": "LXVHJUOLNCLS",
316                        f"{SCIM_URN_USER_ENTERPRISE}:division": "JASVTPKPBPMG",
317                        f"{SCIM_URN_USER_ENTERPRISE}:department": "GMSBFLMNPABY",
318                    },
319                },
320            ],
321            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
322        }
323        user = create_test_user()
324        source = SCIMSource.objects.create(slug=generate_id())
325        connection = SCIMSourceUser.objects.create(
326            user=user,
327            id=generate_id(),
328            source=source,
329            attributes={
330                "active": True,
331                "addresses": [
332                    {
333                        "primary": "true",
334                        "formatted": "BLJMCNXHYLZK",
335                        "streetAddress": "7801 Jacobs Fork",
336                        "locality": "HZJBJWFAKXDD",
337                        "region": "GJXCXPMIIKWK",
338                        "postalCode": "pv82 8ua",
339                        "country": "India",
340                    }
341                ],
342                "displayName": "KEFXCHKHAFOT",
343                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
344                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
345                "externalId": "448d2786-7bf6-4e03-a4ef-64cbaf162fa7",
346                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
347                "locale": "PJNYJHWJILTI",
348                "name": {
349                    "formatted": "Ladarius",
350                    "familyName": "Manley",
351                    "givenName": "Mazie",
352                    "middleName": "Vernon",
353                    "honorificPrefix": "Melyssa",
354                    "honorificSuffix": "Demarcus",
355                },
356                "nickName": "HTPKOXMWZKHL",
357                "phoneNumbers": [
358                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
359                ],
360                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
361                "preferredLanguage": "wae",
362                "profileUrl": "HPSEOIPXMGOH",
363                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
364                "schemas": [
365                    "urn:ietf:params:scim:schemas:core:2.0:User",
366                    SCIM_URN_USER_ENTERPRISE,
367                ],
368                "timezone": "America/Indiana/Petersburg",
369                "title": "EJWFXLHNHMCD",
370                SCIM_URN_USER_ENTERPRISE: {
371                    "employeeNumber": "XHDMEJUURJNR",
372                    "costCenter": "RXUYBXOTRCZH",
373                    "organization": "CEXWXMBRYAHN",
374                    "division": "XMPFMDCLRKCW",
375                    "department": "BKMNJVMCJUYS",
376                    "manager": "PNGSGXLYVWMV",
377                },
378                "userName": "imelda.auer@kshlerin.co.uk",
379                "userType": "PZFXORVSUAPU",
380                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
381            },
382        )
383        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
384        self.assertEqual(
385            updated,
386            {
387                "active": True,
388                "addresses": [
389                    {
390                        "primary": "true",
391                        "formatted": "BLJMCNXHYLZK",
392                        "streetAddress": "7801 Jacobs Fork",
393                        "locality": "HZJBJWFAKXDD",
394                        "region": "GJXCXPMIIKWK",
395                        "postalCode": "pv82 8ua",
396                        "country": "India",
397                    }
398                ],
399                "displayName": "DPRLIJSFQMTL",
400                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
401                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
402                "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
403                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
404                "locale": "JLOJHLPWZODG",
405                "name": {
406                    "formatted": "Dell",
407                    "familyName": "Gay",
408                    "givenName": "Kyler",
409                    "middleName": "Hannah",
410                    "honorificPrefix": "Cassie",
411                    "honorificSuffix": "Yolanda",
412                },
413                "nickName": "BKSPMIRMFBTI",
414                "phoneNumbers": [
415                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
416                ],
417                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
418                "preferredLanguage": "as-IN",
419                "profileUrl": "HPSEOIPXMGOH",
420                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
421                "schemas": [
422                    "urn:ietf:params:scim:schemas:core:2.0:User",
423                    SCIM_URN_USER_ENTERPRISE,
424                ],
425                "timezone": "America/Argentina/Rio_Gallegos",
426                "title": "NBZCOAXVYJUY",
427                SCIM_URN_USER_ENTERPRISE: {
428                    "employeeNumber": "PDFWRRZBQOHB",
429                    "costCenter": "HACMZWSEDOTQ",
430                    "organization": "LXVHJUOLNCLS",
431                    "division": "JASVTPKPBPMG",
432                    "department": "GMSBFLMNPABY",
433                    "manager": "PNGSGXLYVWMV",
434                },
435                "userName": "imelda.auer@kshlerin.co.uk",
436                "userType": "ZGJMYZRUORZE",
437                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
438            },
439        )
440
441    def test_schema_urn_manager(self):
442        req = {
443            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
444            "Operations": [
445                {
446                    "op": "Add",
447                    "value": {
448                        "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager": "foo"
449                    },
450                },
451            ],
452        }
453        user = create_test_user()
454        source = SCIMSource.objects.create(slug=generate_id())
455        connection = SCIMSourceUser.objects.create(
456            user=user,
457            id=generate_id(),
458            source=source,
459            attributes={
460                "meta": {"resourceType": "User"},
461                "active": True,
462                "schemas": [
463                    "urn:ietf:params:scim:schemas:core:2.0:User",
464                    SCIM_URN_USER_ENTERPRISE,
465                ],
466                "userName": "test@t.goauthentik.io",
467                "externalId": "test",
468                "displayName": "Test MS",
469            },
470        )
471        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
472        self.assertEqual(
473            updated,
474            {
475                "meta": {"resourceType": "User"},
476                "active": True,
477                "schemas": [
478                    "urn:ietf:params:scim:schemas:core:2.0:User",
479                    SCIM_URN_USER_ENTERPRISE,
480                ],
481                "userName": "test@t.goauthentik.io",
482                "externalId": "test",
483                "displayName": "Test MS",
484                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
485                    "manager": {"value": "foo"}
486                },
487            },
488        )
class TestSCIMUsersPatch(rest_framework.test.APITestCase):
 11class TestSCIMUsersPatch(APITestCase):
 12    """Test SCIM User Patch"""
 13
 14    def test_add(self):
 15        req = {
 16            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
 17            "Operations": [
 18                {"op": "Add", "path": "name.givenName", "value": "aqwer"},
 19                {"op": "Add", "path": "name.familyName", "value": "qwerqqqq"},
 20                {"op": "Add", "path": "name.formatted", "value": "aqwer qwerqqqq"},
 21            ],
 22        }
 23        user = create_test_user()
 24        source = SCIMSource.objects.create(slug=generate_id())
 25        connection = SCIMSourceUser.objects.create(
 26            user=user,
 27            id=generate_id(),
 28            source=source,
 29            attributes={
 30                "meta": {"resourceType": "User"},
 31                "active": True,
 32                "schemas": [
 33                    "urn:ietf:params:scim:schemas:core:2.0:User",
 34                    SCIM_URN_USER_ENTERPRISE,
 35                ],
 36                "userName": "test@t.goauthentik.io",
 37                "externalId": "test",
 38                "displayName": "Test MS",
 39            },
 40        )
 41        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
 42        self.assertEqual(
 43            updated,
 44            {
 45                "meta": {"resourceType": "User"},
 46                "active": True,
 47                "name": {
 48                    "givenName": "aqwer",
 49                    "familyName": "qwerqqqq",
 50                    "formatted": "aqwer qwerqqqq",
 51                },
 52                "schemas": [
 53                    "urn:ietf:params:scim:schemas:core:2.0:User",
 54                    SCIM_URN_USER_ENTERPRISE,
 55                ],
 56                "userName": "test@t.goauthentik.io",
 57                "externalId": "test",
 58                "displayName": "Test MS",
 59            },
 60        )
 61
 62    def test_add_no_path(self):
 63        """Test add patch with no path set"""
 64        req = {
 65            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
 66            "Operations": [
 67                {"op": "Add", "value": {"externalId": "aqwer"}},
 68            ],
 69        }
 70        user = create_test_user()
 71        source = SCIMSource.objects.create(slug=generate_id())
 72        connection = SCIMSourceUser.objects.create(
 73            user=user,
 74            id=generate_id(),
 75            source=source,
 76            attributes={
 77                "meta": {"resourceType": "User"},
 78                "active": True,
 79                "schemas": [
 80                    "urn:ietf:params:scim:schemas:core:2.0:User",
 81                    SCIM_URN_USER_ENTERPRISE,
 82                ],
 83                "userName": "test@t.goauthentik.io",
 84                "displayName": "Test MS",
 85            },
 86        )
 87        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
 88        self.assertEqual(
 89            updated,
 90            {
 91                "meta": {"resourceType": "User"},
 92                "active": True,
 93                "schemas": [
 94                    "urn:ietf:params:scim:schemas:core:2.0:User",
 95                    SCIM_URN_USER_ENTERPRISE,
 96                ],
 97                "userName": "test@t.goauthentik.io",
 98                "externalId": "aqwer",
 99                "displayName": "Test MS",
100            },
101        )
102
103    def test_replace(self):
104        req = {
105            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
106            "Operations": [
107                {"op": "Replace", "path": "name", "value": {"givenName": "aqwer"}},
108            ],
109        }
110        user = create_test_user()
111        source = SCIMSource.objects.create(slug=generate_id())
112        connection = SCIMSourceUser.objects.create(
113            user=user,
114            id=generate_id(),
115            source=source,
116            attributes={
117                "meta": {"resourceType": "User"},
118                "active": True,
119                "schemas": [
120                    "urn:ietf:params:scim:schemas:core:2.0:User",
121                    SCIM_URN_USER_ENTERPRISE,
122                ],
123                "userName": "test@t.goauthentik.io",
124                "externalId": "test",
125                "displayName": "Test MS",
126            },
127        )
128        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
129        self.assertEqual(
130            updated,
131            {
132                "meta": {"resourceType": "User"},
133                "active": True,
134                "name": {
135                    "givenName": "aqwer",
136                },
137                "schemas": [
138                    "urn:ietf:params:scim:schemas:core:2.0:User",
139                    SCIM_URN_USER_ENTERPRISE,
140                ],
141                "userName": "test@t.goauthentik.io",
142                "externalId": "test",
143                "displayName": "Test MS",
144            },
145        )
146
147    def test_replace_no_path(self):
148        """Test value replace with no path"""
149        req = {
150            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
151            "Operations": [
152                {"op": "Replace", "value": {"externalId": "aqwer"}},
153            ],
154        }
155        user = create_test_user()
156        source = SCIMSource.objects.create(slug=generate_id())
157        connection = SCIMSourceUser.objects.create(
158            user=user,
159            id=generate_id(),
160            source=source,
161            attributes={
162                "meta": {"resourceType": "User"},
163                "active": True,
164                "schemas": [
165                    "urn:ietf:params:scim:schemas:core:2.0:User",
166                    SCIM_URN_USER_ENTERPRISE,
167                ],
168                "userName": "test@t.goauthentik.io",
169                "externalId": "test",
170                "displayName": "Test MS",
171            },
172        )
173        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
174        self.assertEqual(
175            updated,
176            {
177                "meta": {"resourceType": "User"},
178                "active": True,
179                "schemas": [
180                    "urn:ietf:params:scim:schemas:core:2.0:User",
181                    SCIM_URN_USER_ENTERPRISE,
182                ],
183                "userName": "test@t.goauthentik.io",
184                "externalId": "aqwer",
185                "displayName": "Test MS",
186            },
187        )
188
189    def test_remove(self):
190        req = {
191            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
192            "Operations": [
193                {"op": "Remove", "path": "name", "value": {"givenName": "aqwer"}},
194            ],
195        }
196        user = create_test_user()
197        source = SCIMSource.objects.create(slug=generate_id())
198        connection = SCIMSourceUser.objects.create(
199            user=user,
200            id=generate_id(),
201            source=source,
202            attributes={
203                "meta": {"resourceType": "User"},
204                "active": True,
205                "name": {
206                    "givenName": "aqwer",
207                },
208                "schemas": [
209                    "urn:ietf:params:scim:schemas:core:2.0:User",
210                    SCIM_URN_USER_ENTERPRISE,
211                ],
212                "userName": "test@t.goauthentik.io",
213                "externalId": "test",
214                "displayName": "Test MS",
215            },
216        )
217        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
218        self.assertEqual(
219            updated,
220            {
221                "meta": {"resourceType": "User"},
222                "active": True,
223                "schemas": [
224                    "urn:ietf:params:scim:schemas:core:2.0:User",
225                    SCIM_URN_USER_ENTERPRISE,
226                ],
227                "userName": "test@t.goauthentik.io",
228                "externalId": "test",
229                "displayName": "Test MS",
230            },
231        )
232
233    def test_large(self):
234        """Large amount of patch operations"""
235        req = {
236            "Operations": [
237                {
238                    "op": "replace",
239                    "path": "emails[primary eq true].value",
240                    "value": "dandre_kling@wintheiser.info",
241                },
242                {
243                    "op": "replace",
244                    "path": "phoneNumbers[primary eq true].value",
245                    "value": "72-634-1548",
246                },
247                {
248                    "op": "replace",
249                    "path": "phoneNumbers[primary eq true].display",
250                    "value": "72-634-1548",
251                },
252                {"op": "replace", "path": "ims[primary eq true].value", "value": "GXSGJKWGHVVS"},
253                {"op": "replace", "path": "ims[primary eq true].display", "value": "IMCHDKUQIPYB"},
254                {
255                    "op": "replace",
256                    "path": "photos[primary eq true].display",
257                    "value": "TWAWLHHSUNIV",
258                },
259                {
260                    "op": "replace",
261                    "path": "addresses[primary eq true].formatted",
262                    "value": "TMINZQAJQDCL",
263                },
264                {
265                    "op": "replace",
266                    "path": "addresses[primary eq true].streetAddress",
267                    "value": "081 Wisoky Key",
268                },
269                {
270                    "op": "replace",
271                    "path": "addresses[primary eq true].locality",
272                    "value": "DPFASBZRPMDP",
273                },
274                {
275                    "op": "replace",
276                    "path": "addresses[primary eq true].region",
277                    "value": "WHSTJSPIPTCF",
278                },
279                {
280                    "op": "replace",
281                    "path": "addresses[primary eq true].postalCode",
282                    "value": "ko28 1qa",
283                },
284                {"op": "replace", "path": "addresses[primary eq true].country", "value": "Taiwan"},
285                {
286                    "op": "replace",
287                    "path": "entitlements[primary eq true].value",
288                    "value": "NGBJMUYZVVBX",
289                },
290                {"op": "replace", "path": "roles[primary eq true].value", "value": "XEELVFMMWCVM"},
291                {
292                    "op": "replace",
293                    "path": "x509Certificates[primary eq true].value",
294                    "value": "UYISMEDOXUZY",
295                },
296                {
297                    "op": "replace",
298                    "value": {
299                        "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
300                        "name.formatted": "Dell",
301                        "name.familyName": "Gay",
302                        "name.givenName": "Kyler",
303                        "name.middleName": "Hannah",
304                        "name.honorificPrefix": "Cassie",
305                        "name.honorificSuffix": "Yolanda",
306                        "displayName": "DPRLIJSFQMTL",
307                        "nickName": "BKSPMIRMFBTI",
308                        "title": "NBZCOAXVYJUY",
309                        "userType": "ZGJMYZRUORZE",
310                        "preferredLanguage": "as-IN",
311                        "locale": "JLOJHLPWZODG",
312                        "timezone": "America/Argentina/Rio_Gallegos",
313                        "active": True,
314                        f"{SCIM_URN_USER_ENTERPRISE}:employeeNumber": "PDFWRRZBQOHB",
315                        f"{SCIM_URN_USER_ENTERPRISE}:costCenter": "HACMZWSEDOTQ",
316                        f"{SCIM_URN_USER_ENTERPRISE}:organization": "LXVHJUOLNCLS",
317                        f"{SCIM_URN_USER_ENTERPRISE}:division": "JASVTPKPBPMG",
318                        f"{SCIM_URN_USER_ENTERPRISE}:department": "GMSBFLMNPABY",
319                    },
320                },
321            ],
322            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
323        }
324        user = create_test_user()
325        source = SCIMSource.objects.create(slug=generate_id())
326        connection = SCIMSourceUser.objects.create(
327            user=user,
328            id=generate_id(),
329            source=source,
330            attributes={
331                "active": True,
332                "addresses": [
333                    {
334                        "primary": "true",
335                        "formatted": "BLJMCNXHYLZK",
336                        "streetAddress": "7801 Jacobs Fork",
337                        "locality": "HZJBJWFAKXDD",
338                        "region": "GJXCXPMIIKWK",
339                        "postalCode": "pv82 8ua",
340                        "country": "India",
341                    }
342                ],
343                "displayName": "KEFXCHKHAFOT",
344                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
345                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
346                "externalId": "448d2786-7bf6-4e03-a4ef-64cbaf162fa7",
347                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
348                "locale": "PJNYJHWJILTI",
349                "name": {
350                    "formatted": "Ladarius",
351                    "familyName": "Manley",
352                    "givenName": "Mazie",
353                    "middleName": "Vernon",
354                    "honorificPrefix": "Melyssa",
355                    "honorificSuffix": "Demarcus",
356                },
357                "nickName": "HTPKOXMWZKHL",
358                "phoneNumbers": [
359                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
360                ],
361                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
362                "preferredLanguage": "wae",
363                "profileUrl": "HPSEOIPXMGOH",
364                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
365                "schemas": [
366                    "urn:ietf:params:scim:schemas:core:2.0:User",
367                    SCIM_URN_USER_ENTERPRISE,
368                ],
369                "timezone": "America/Indiana/Petersburg",
370                "title": "EJWFXLHNHMCD",
371                SCIM_URN_USER_ENTERPRISE: {
372                    "employeeNumber": "XHDMEJUURJNR",
373                    "costCenter": "RXUYBXOTRCZH",
374                    "organization": "CEXWXMBRYAHN",
375                    "division": "XMPFMDCLRKCW",
376                    "department": "BKMNJVMCJUYS",
377                    "manager": "PNGSGXLYVWMV",
378                },
379                "userName": "imelda.auer@kshlerin.co.uk",
380                "userType": "PZFXORVSUAPU",
381                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
382            },
383        )
384        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
385        self.assertEqual(
386            updated,
387            {
388                "active": True,
389                "addresses": [
390                    {
391                        "primary": "true",
392                        "formatted": "BLJMCNXHYLZK",
393                        "streetAddress": "7801 Jacobs Fork",
394                        "locality": "HZJBJWFAKXDD",
395                        "region": "GJXCXPMIIKWK",
396                        "postalCode": "pv82 8ua",
397                        "country": "India",
398                    }
399                ],
400                "displayName": "DPRLIJSFQMTL",
401                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
402                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
403                "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
404                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
405                "locale": "JLOJHLPWZODG",
406                "name": {
407                    "formatted": "Dell",
408                    "familyName": "Gay",
409                    "givenName": "Kyler",
410                    "middleName": "Hannah",
411                    "honorificPrefix": "Cassie",
412                    "honorificSuffix": "Yolanda",
413                },
414                "nickName": "BKSPMIRMFBTI",
415                "phoneNumbers": [
416                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
417                ],
418                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
419                "preferredLanguage": "as-IN",
420                "profileUrl": "HPSEOIPXMGOH",
421                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
422                "schemas": [
423                    "urn:ietf:params:scim:schemas:core:2.0:User",
424                    SCIM_URN_USER_ENTERPRISE,
425                ],
426                "timezone": "America/Argentina/Rio_Gallegos",
427                "title": "NBZCOAXVYJUY",
428                SCIM_URN_USER_ENTERPRISE: {
429                    "employeeNumber": "PDFWRRZBQOHB",
430                    "costCenter": "HACMZWSEDOTQ",
431                    "organization": "LXVHJUOLNCLS",
432                    "division": "JASVTPKPBPMG",
433                    "department": "GMSBFLMNPABY",
434                    "manager": "PNGSGXLYVWMV",
435                },
436                "userName": "imelda.auer@kshlerin.co.uk",
437                "userType": "ZGJMYZRUORZE",
438                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
439            },
440        )
441
442    def test_schema_urn_manager(self):
443        req = {
444            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
445            "Operations": [
446                {
447                    "op": "Add",
448                    "value": {
449                        "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager": "foo"
450                    },
451                },
452            ],
453        }
454        user = create_test_user()
455        source = SCIMSource.objects.create(slug=generate_id())
456        connection = SCIMSourceUser.objects.create(
457            user=user,
458            id=generate_id(),
459            source=source,
460            attributes={
461                "meta": {"resourceType": "User"},
462                "active": True,
463                "schemas": [
464                    "urn:ietf:params:scim:schemas:core:2.0:User",
465                    SCIM_URN_USER_ENTERPRISE,
466                ],
467                "userName": "test@t.goauthentik.io",
468                "externalId": "test",
469                "displayName": "Test MS",
470            },
471        )
472        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
473        self.assertEqual(
474            updated,
475            {
476                "meta": {"resourceType": "User"},
477                "active": True,
478                "schemas": [
479                    "urn:ietf:params:scim:schemas:core:2.0:User",
480                    SCIM_URN_USER_ENTERPRISE,
481                ],
482                "userName": "test@t.goauthentik.io",
483                "externalId": "test",
484                "displayName": "Test MS",
485                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
486                    "manager": {"value": "foo"}
487                },
488            },
489        )

Test SCIM User Patch

def test_add(self):
14    def test_add(self):
15        req = {
16            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
17            "Operations": [
18                {"op": "Add", "path": "name.givenName", "value": "aqwer"},
19                {"op": "Add", "path": "name.familyName", "value": "qwerqqqq"},
20                {"op": "Add", "path": "name.formatted", "value": "aqwer qwerqqqq"},
21            ],
22        }
23        user = create_test_user()
24        source = SCIMSource.objects.create(slug=generate_id())
25        connection = SCIMSourceUser.objects.create(
26            user=user,
27            id=generate_id(),
28            source=source,
29            attributes={
30                "meta": {"resourceType": "User"},
31                "active": True,
32                "schemas": [
33                    "urn:ietf:params:scim:schemas:core:2.0:User",
34                    SCIM_URN_USER_ENTERPRISE,
35                ],
36                "userName": "test@t.goauthentik.io",
37                "externalId": "test",
38                "displayName": "Test MS",
39            },
40        )
41        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
42        self.assertEqual(
43            updated,
44            {
45                "meta": {"resourceType": "User"},
46                "active": True,
47                "name": {
48                    "givenName": "aqwer",
49                    "familyName": "qwerqqqq",
50                    "formatted": "aqwer qwerqqqq",
51                },
52                "schemas": [
53                    "urn:ietf:params:scim:schemas:core:2.0:User",
54                    SCIM_URN_USER_ENTERPRISE,
55                ],
56                "userName": "test@t.goauthentik.io",
57                "externalId": "test",
58                "displayName": "Test MS",
59            },
60        )
def test_add_no_path(self):
 62    def test_add_no_path(self):
 63        """Test add patch with no path set"""
 64        req = {
 65            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
 66            "Operations": [
 67                {"op": "Add", "value": {"externalId": "aqwer"}},
 68            ],
 69        }
 70        user = create_test_user()
 71        source = SCIMSource.objects.create(slug=generate_id())
 72        connection = SCIMSourceUser.objects.create(
 73            user=user,
 74            id=generate_id(),
 75            source=source,
 76            attributes={
 77                "meta": {"resourceType": "User"},
 78                "active": True,
 79                "schemas": [
 80                    "urn:ietf:params:scim:schemas:core:2.0:User",
 81                    SCIM_URN_USER_ENTERPRISE,
 82                ],
 83                "userName": "test@t.goauthentik.io",
 84                "displayName": "Test MS",
 85            },
 86        )
 87        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
 88        self.assertEqual(
 89            updated,
 90            {
 91                "meta": {"resourceType": "User"},
 92                "active": True,
 93                "schemas": [
 94                    "urn:ietf:params:scim:schemas:core:2.0:User",
 95                    SCIM_URN_USER_ENTERPRISE,
 96                ],
 97                "userName": "test@t.goauthentik.io",
 98                "externalId": "aqwer",
 99                "displayName": "Test MS",
100            },
101        )

Test add patch with no path set

def test_replace(self):
103    def test_replace(self):
104        req = {
105            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
106            "Operations": [
107                {"op": "Replace", "path": "name", "value": {"givenName": "aqwer"}},
108            ],
109        }
110        user = create_test_user()
111        source = SCIMSource.objects.create(slug=generate_id())
112        connection = SCIMSourceUser.objects.create(
113            user=user,
114            id=generate_id(),
115            source=source,
116            attributes={
117                "meta": {"resourceType": "User"},
118                "active": True,
119                "schemas": [
120                    "urn:ietf:params:scim:schemas:core:2.0:User",
121                    SCIM_URN_USER_ENTERPRISE,
122                ],
123                "userName": "test@t.goauthentik.io",
124                "externalId": "test",
125                "displayName": "Test MS",
126            },
127        )
128        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
129        self.assertEqual(
130            updated,
131            {
132                "meta": {"resourceType": "User"},
133                "active": True,
134                "name": {
135                    "givenName": "aqwer",
136                },
137                "schemas": [
138                    "urn:ietf:params:scim:schemas:core:2.0:User",
139                    SCIM_URN_USER_ENTERPRISE,
140                ],
141                "userName": "test@t.goauthentik.io",
142                "externalId": "test",
143                "displayName": "Test MS",
144            },
145        )
def test_replace_no_path(self):
147    def test_replace_no_path(self):
148        """Test value replace with no path"""
149        req = {
150            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
151            "Operations": [
152                {"op": "Replace", "value": {"externalId": "aqwer"}},
153            ],
154        }
155        user = create_test_user()
156        source = SCIMSource.objects.create(slug=generate_id())
157        connection = SCIMSourceUser.objects.create(
158            user=user,
159            id=generate_id(),
160            source=source,
161            attributes={
162                "meta": {"resourceType": "User"},
163                "active": True,
164                "schemas": [
165                    "urn:ietf:params:scim:schemas:core:2.0:User",
166                    SCIM_URN_USER_ENTERPRISE,
167                ],
168                "userName": "test@t.goauthentik.io",
169                "externalId": "test",
170                "displayName": "Test MS",
171            },
172        )
173        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
174        self.assertEqual(
175            updated,
176            {
177                "meta": {"resourceType": "User"},
178                "active": True,
179                "schemas": [
180                    "urn:ietf:params:scim:schemas:core:2.0:User",
181                    SCIM_URN_USER_ENTERPRISE,
182                ],
183                "userName": "test@t.goauthentik.io",
184                "externalId": "aqwer",
185                "displayName": "Test MS",
186            },
187        )

Test value replace with no path

def test_remove(self):
189    def test_remove(self):
190        req = {
191            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
192            "Operations": [
193                {"op": "Remove", "path": "name", "value": {"givenName": "aqwer"}},
194            ],
195        }
196        user = create_test_user()
197        source = SCIMSource.objects.create(slug=generate_id())
198        connection = SCIMSourceUser.objects.create(
199            user=user,
200            id=generate_id(),
201            source=source,
202            attributes={
203                "meta": {"resourceType": "User"},
204                "active": True,
205                "name": {
206                    "givenName": "aqwer",
207                },
208                "schemas": [
209                    "urn:ietf:params:scim:schemas:core:2.0:User",
210                    SCIM_URN_USER_ENTERPRISE,
211                ],
212                "userName": "test@t.goauthentik.io",
213                "externalId": "test",
214                "displayName": "Test MS",
215            },
216        )
217        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
218        self.assertEqual(
219            updated,
220            {
221                "meta": {"resourceType": "User"},
222                "active": True,
223                "schemas": [
224                    "urn:ietf:params:scim:schemas:core:2.0:User",
225                    SCIM_URN_USER_ENTERPRISE,
226                ],
227                "userName": "test@t.goauthentik.io",
228                "externalId": "test",
229                "displayName": "Test MS",
230            },
231        )
def test_large(self):
233    def test_large(self):
234        """Large amount of patch operations"""
235        req = {
236            "Operations": [
237                {
238                    "op": "replace",
239                    "path": "emails[primary eq true].value",
240                    "value": "dandre_kling@wintheiser.info",
241                },
242                {
243                    "op": "replace",
244                    "path": "phoneNumbers[primary eq true].value",
245                    "value": "72-634-1548",
246                },
247                {
248                    "op": "replace",
249                    "path": "phoneNumbers[primary eq true].display",
250                    "value": "72-634-1548",
251                },
252                {"op": "replace", "path": "ims[primary eq true].value", "value": "GXSGJKWGHVVS"},
253                {"op": "replace", "path": "ims[primary eq true].display", "value": "IMCHDKUQIPYB"},
254                {
255                    "op": "replace",
256                    "path": "photos[primary eq true].display",
257                    "value": "TWAWLHHSUNIV",
258                },
259                {
260                    "op": "replace",
261                    "path": "addresses[primary eq true].formatted",
262                    "value": "TMINZQAJQDCL",
263                },
264                {
265                    "op": "replace",
266                    "path": "addresses[primary eq true].streetAddress",
267                    "value": "081 Wisoky Key",
268                },
269                {
270                    "op": "replace",
271                    "path": "addresses[primary eq true].locality",
272                    "value": "DPFASBZRPMDP",
273                },
274                {
275                    "op": "replace",
276                    "path": "addresses[primary eq true].region",
277                    "value": "WHSTJSPIPTCF",
278                },
279                {
280                    "op": "replace",
281                    "path": "addresses[primary eq true].postalCode",
282                    "value": "ko28 1qa",
283                },
284                {"op": "replace", "path": "addresses[primary eq true].country", "value": "Taiwan"},
285                {
286                    "op": "replace",
287                    "path": "entitlements[primary eq true].value",
288                    "value": "NGBJMUYZVVBX",
289                },
290                {"op": "replace", "path": "roles[primary eq true].value", "value": "XEELVFMMWCVM"},
291                {
292                    "op": "replace",
293                    "path": "x509Certificates[primary eq true].value",
294                    "value": "UYISMEDOXUZY",
295                },
296                {
297                    "op": "replace",
298                    "value": {
299                        "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
300                        "name.formatted": "Dell",
301                        "name.familyName": "Gay",
302                        "name.givenName": "Kyler",
303                        "name.middleName": "Hannah",
304                        "name.honorificPrefix": "Cassie",
305                        "name.honorificSuffix": "Yolanda",
306                        "displayName": "DPRLIJSFQMTL",
307                        "nickName": "BKSPMIRMFBTI",
308                        "title": "NBZCOAXVYJUY",
309                        "userType": "ZGJMYZRUORZE",
310                        "preferredLanguage": "as-IN",
311                        "locale": "JLOJHLPWZODG",
312                        "timezone": "America/Argentina/Rio_Gallegos",
313                        "active": True,
314                        f"{SCIM_URN_USER_ENTERPRISE}:employeeNumber": "PDFWRRZBQOHB",
315                        f"{SCIM_URN_USER_ENTERPRISE}:costCenter": "HACMZWSEDOTQ",
316                        f"{SCIM_URN_USER_ENTERPRISE}:organization": "LXVHJUOLNCLS",
317                        f"{SCIM_URN_USER_ENTERPRISE}:division": "JASVTPKPBPMG",
318                        f"{SCIM_URN_USER_ENTERPRISE}:department": "GMSBFLMNPABY",
319                    },
320                },
321            ],
322            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
323        }
324        user = create_test_user()
325        source = SCIMSource.objects.create(slug=generate_id())
326        connection = SCIMSourceUser.objects.create(
327            user=user,
328            id=generate_id(),
329            source=source,
330            attributes={
331                "active": True,
332                "addresses": [
333                    {
334                        "primary": "true",
335                        "formatted": "BLJMCNXHYLZK",
336                        "streetAddress": "7801 Jacobs Fork",
337                        "locality": "HZJBJWFAKXDD",
338                        "region": "GJXCXPMIIKWK",
339                        "postalCode": "pv82 8ua",
340                        "country": "India",
341                    }
342                ],
343                "displayName": "KEFXCHKHAFOT",
344                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
345                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
346                "externalId": "448d2786-7bf6-4e03-a4ef-64cbaf162fa7",
347                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
348                "locale": "PJNYJHWJILTI",
349                "name": {
350                    "formatted": "Ladarius",
351                    "familyName": "Manley",
352                    "givenName": "Mazie",
353                    "middleName": "Vernon",
354                    "honorificPrefix": "Melyssa",
355                    "honorificSuffix": "Demarcus",
356                },
357                "nickName": "HTPKOXMWZKHL",
358                "phoneNumbers": [
359                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
360                ],
361                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
362                "preferredLanguage": "wae",
363                "profileUrl": "HPSEOIPXMGOH",
364                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
365                "schemas": [
366                    "urn:ietf:params:scim:schemas:core:2.0:User",
367                    SCIM_URN_USER_ENTERPRISE,
368                ],
369                "timezone": "America/Indiana/Petersburg",
370                "title": "EJWFXLHNHMCD",
371                SCIM_URN_USER_ENTERPRISE: {
372                    "employeeNumber": "XHDMEJUURJNR",
373                    "costCenter": "RXUYBXOTRCZH",
374                    "organization": "CEXWXMBRYAHN",
375                    "division": "XMPFMDCLRKCW",
376                    "department": "BKMNJVMCJUYS",
377                    "manager": "PNGSGXLYVWMV",
378                },
379                "userName": "imelda.auer@kshlerin.co.uk",
380                "userType": "PZFXORVSUAPU",
381                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
382            },
383        )
384        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
385        self.assertEqual(
386            updated,
387            {
388                "active": True,
389                "addresses": [
390                    {
391                        "primary": "true",
392                        "formatted": "BLJMCNXHYLZK",
393                        "streetAddress": "7801 Jacobs Fork",
394                        "locality": "HZJBJWFAKXDD",
395                        "region": "GJXCXPMIIKWK",
396                        "postalCode": "pv82 8ua",
397                        "country": "India",
398                    }
399                ],
400                "displayName": "DPRLIJSFQMTL",
401                "emails": [{"primary": "true", "value": "scot@zemlak.uk"}],
402                "entitlements": [{"primary": "true", "value": "FTTUXWYDAAQC"}],
403                "externalId": "7faaefb0-0774-4d8e-8f6d-863c361bc72c",
404                "ims": [{"primary": "true", "value": "IGWZUUMCMKXS", "display": "PJVGMMKYYHRU"}],
405                "locale": "JLOJHLPWZODG",
406                "name": {
407                    "formatted": "Dell",
408                    "familyName": "Gay",
409                    "givenName": "Kyler",
410                    "middleName": "Hannah",
411                    "honorificPrefix": "Cassie",
412                    "honorificSuffix": "Yolanda",
413                },
414                "nickName": "BKSPMIRMFBTI",
415                "phoneNumbers": [
416                    {"primary": "true", "value": "50-608-7660", "display": "50-608-7660"}
417                ],
418                "photos": [{"primary": "true", "display": "KCONLNLSYTBP"}],
419                "preferredLanguage": "as-IN",
420                "profileUrl": "HPSEOIPXMGOH",
421                "roles": [{"primary": "true", "value": "TLGYITOIZGKP"}],
422                "schemas": [
423                    "urn:ietf:params:scim:schemas:core:2.0:User",
424                    SCIM_URN_USER_ENTERPRISE,
425                ],
426                "timezone": "America/Argentina/Rio_Gallegos",
427                "title": "NBZCOAXVYJUY",
428                SCIM_URN_USER_ENTERPRISE: {
429                    "employeeNumber": "PDFWRRZBQOHB",
430                    "costCenter": "HACMZWSEDOTQ",
431                    "organization": "LXVHJUOLNCLS",
432                    "division": "JASVTPKPBPMG",
433                    "department": "GMSBFLMNPABY",
434                    "manager": "PNGSGXLYVWMV",
435                },
436                "userName": "imelda.auer@kshlerin.co.uk",
437                "userType": "ZGJMYZRUORZE",
438                "x509Certificates": [{"primary": "true", "value": "KOVKWGIVVEHH"}],
439            },
440        )

Large amount of patch operations

def test_schema_urn_manager(self):
442    def test_schema_urn_manager(self):
443        req = {
444            "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
445            "Operations": [
446                {
447                    "op": "Add",
448                    "value": {
449                        "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager": "foo"
450                    },
451                },
452            ],
453        }
454        user = create_test_user()
455        source = SCIMSource.objects.create(slug=generate_id())
456        connection = SCIMSourceUser.objects.create(
457            user=user,
458            id=generate_id(),
459            source=source,
460            attributes={
461                "meta": {"resourceType": "User"},
462                "active": True,
463                "schemas": [
464                    "urn:ietf:params:scim:schemas:core:2.0:User",
465                    SCIM_URN_USER_ENTERPRISE,
466                ],
467                "userName": "test@t.goauthentik.io",
468                "externalId": "test",
469                "displayName": "Test MS",
470            },
471        )
472        updated = SCIMPatchProcessor().apply_patches(connection.attributes, req["Operations"])
473        self.assertEqual(
474            updated,
475            {
476                "meta": {"resourceType": "User"},
477                "active": True,
478                "schemas": [
479                    "urn:ietf:params:scim:schemas:core:2.0:User",
480                    SCIM_URN_USER_ENTERPRISE,
481                ],
482                "userName": "test@t.goauthentik.io",
483                "externalId": "test",
484                "displayName": "Test MS",
485                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
486                    "manager": {"value": "foo"}
487                },
488            },
489        )