Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysyngsun committed Feb 3, 2025
1 parent 95e058c commit 3776ce4
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 33 deletions.
2 changes: 1 addition & 1 deletion main/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import ulid
from django.contrib.auth.models import Group, User
from factory import LazyFunction, RelatedFactory, SubFactory, Trait, Faker
from factory import Faker, LazyFunction, RelatedFactory, SubFactory, Trait
from factory.django import DjangoModelFactory
from factory.fuzzy import FuzzyText
from social_django.models import UserSocialAuth
Expand Down
2 changes: 1 addition & 1 deletion main/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path, re_path
from django.urls import include, re_path
from django.views.generic.base import RedirectView
from rest_framework.routers import DefaultRouter

Expand Down
3 changes: 2 additions & 1 deletion profiles/factories.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Factories for making test data"""

import uuid

from factory import Faker, Sequence, SubFactory, SelfAttribute, LazyFunction
from factory import Faker, LazyFunction, SelfAttribute, Sequence, SubFactory
from factory.django import DjangoModelFactory
from factory.fuzzy import FuzzyChoice
from faker.providers import BaseProvider
Expand Down
44 changes: 28 additions & 16 deletions scim/adapters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import operator
from typing import Optional, Union

from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -44,6 +45,7 @@ class LearnSCIMUser(SCIMUser):
("active", None, None): "is_active",
("name", "givenName", None): "first_name",
("name", "familyName", None): "last_name",
("userName", None, None): "username",
}

IGNORED_PATHS = {
Expand Down Expand Up @@ -193,7 +195,7 @@ def parse_scim_for_keycloak_payload(self, payload: str) -> dict:

if isinstance(value, dict):
for nested_key, nested_value in value.items():
result[f"{key}.{nested_key}"] = nested_value
result[self.split_path(f"{key}.{nested_key}")] = nested_value
else:
result[key] = value

Expand All @@ -202,17 +204,32 @@ def parse_scim_for_keycloak_payload(self, payload: str) -> dict:
def parse_path_and_values(
self, path: Optional[str], value: Union[str, list, dict]
) -> list:
if not path and isinstance(value, str):
"""Parse the incoming value(s)"""
if isinstance(value, str):
# scim-for-keycloak sends this as a noncompliant JSON-encoded string
value = self.parse_scim_for_keycloak_payload(value)
if path is None:
val = json.loads(value)
else:
msg = "Called with a non-null path and a str value"
raise ValueError(msg)
else:
val = value

return super().parse_path_and_values(path, value)
results = []

def validate_email(self, *args, **kwargs):
print("validate_email")
print((args, kwargs))
result = super().validate_email(*args, **kwargs)
return result
for attr_path, attr_value in val.items():
if isinstance(attr_value, dict):
# nested object, we want to recursively flatten it to `first.second`
results.extend(self.parse_path_and_values(attr_path, attr_value))
else:
flattened_path = (
f"{path}.{attr_path}" if path is not None else attr_path
)
new_path = self.split_path(flattened_path)
new_value = attr_value
results.append((new_path, new_value))

return results

def handle_replace(
self,
Expand All @@ -227,25 +244,20 @@ def handle_replace(
"""

if not isinstance(value, dict):
print("not a dict")
print((path, value))
# Restructure for use in loop below.
value = {path: value}

print(value)
for nested_path, nested_value in (value or {}).items():
print((nested_path, nested_value))
if nested_path.first_path in self.ATTR_MAP:
setattr(
self.obj, self.ATTR_MAP.get(nested_path.first_path), nested_value
self.obj, self.ATTR_MAP[nested_path.first_path], nested_value
)

elif nested_path.first_path == ("fullName", None, None):
self.obj.profile.name = nested_value
elif nested_path.first_path == ("emailOptIn", None, None):
self.obj.profile.email_optin = nested_value == 1
elif nested_path.first_path == ("emails", None, None):
self.parse_emails(value)
self.parse_emails(nested_value)
elif nested_path.first_path not in self.IGNORED_PATHS:
logger.debug(
"Ignoring SCIM update for path: %s", nested_path.first_path
Expand Down
1 change: 1 addition & 0 deletions scim/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""SCIM constants"""


class SchemaURI:
BULK_REQUEST = "urn:ietf:params:scim:api:messages:2.0:BulkRequest"

Expand Down
3 changes: 1 addition & 2 deletions scim/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""URL configurations for profiles"""

from django.urls import include, re_path
from scim.views import BulkView

from scim.views import BulkView

ol_scim_urls = (
[
Expand All @@ -15,4 +15,3 @@
re_path("^scim/v2/", include(ol_scim_urls)),
re_path("^scim/v2/", include("django_scim.urls", namespace="scim")),
]

6 changes: 4 additions & 2 deletions scim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

from django.http import HttpRequest, HttpResponse
from django.urls import Resolver404, resolve
from django_scim import exceptions, constants as djs_constants, views as djs_views
from django_scim import constants as djs_constants
from django_scim import exceptions
from django_scim import views as djs_views

from scim import constants

Expand Down Expand Up @@ -112,7 +114,7 @@ def _attempt_operation(self, bulk_request, operation):
}

if result.status_code >= 400:
response["response"] = json.loads(result.content.decode("utf-8")),
response["response"] = (json.loads(result.content.decode("utf-8")),)

if "Location" in result.headers:
response["location"] = result.headers["Location"]
Expand Down
13 changes: 3 additions & 10 deletions scim/views_test.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from collections.abc import Callable
import itertools
import json
import operator
import random
from collections.abc import Callable
from functools import reduce
from types import SimpleNamespace

import pytest
from anys import ANY_STR
from deepmerge import always_merger
from django.contrib.auth import get_user_model
from django.urls import reverse
from django_scim import constants as djs_constants
import pytest

from main.factories import UserFactory
from scim import constants
Expand Down Expand Up @@ -240,11 +240,7 @@ def _expected_patch_value(field):
"Operations": [
{
"op": "replace",
"value": reduce(
always_merger.merge,
field_updates,
{}
)
"value": reduce(always_merger.merge, field_updates, {}),
}
],
},
Expand Down Expand Up @@ -349,8 +345,6 @@ def bulk_test_data():
]
random.shuffle(operations)

print(operations)

return SimpleNamespace(
existing_users=existing_users,
remaining_users=remaining_users,
Expand Down Expand Up @@ -388,7 +382,6 @@ def test_bulk_post(staff_client, bulk_test_data):
}

for operation in bulk_test_data.operations:
print(operation.payload)
assert (
results_by_bulk_id[operation.payload["bulkId"]]
== operation.expected_response
Expand Down

0 comments on commit 3776ce4

Please sign in to comment.