Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
hugorodgerbrown committed Dec 12, 2023
1 parent b9de7b6 commit 380873c
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 250 deletions.
2 changes: 1 addition & 1 deletion demo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
urlpatterns = [
path("", debug.default_urlconf),
path("admin/", admin.site.urls),
path("scim/v2/", include("simple_scim.urls")),
path("scim/v2/", include("simple_scim.urls", namespace="scim")),
]
204 changes: 104 additions & 100 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ classifiers = [
packages = [{ include = "simple_scim" }]

[tool.poetry.dependencies]
python = "3.12.0"
python = "3.12.1"
django = "^3.2 || ^4.0 || ^5.0"

[tool.poetry.group.dev.dependencies]
Expand Down
3 changes: 2 additions & 1 deletion simple_scim/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

class SimpleSCIMConfig(AppConfig):
name = "simple_scim"
verbose_name = "SCIM API"
verbose_name = "SCIM API (Users and Groups)"
default_auto_field = "django.db.models.AutoField"
60 changes: 60 additions & 0 deletions simple_scim/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by Django 5.0 on 2023-12-12 17:37

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="SCIMEvent",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
),
migrations.CreateModel(
name="SCIMUser",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"external_id",
models.CharField(
help_text="ID supplied by the identity provider as 'externalId'.",
max_length=100,
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
19 changes: 19 additions & 0 deletions simple_scim/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _lazy


class SCIMUser(models.Model):
"""SCIM version of the user model."""

user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True
)
external_id = models.CharField(
max_length=100,
help_text=_lazy("ID supplied by the identity provider as 'externalId'."),
)


class SCIMEvent(models.Model):
"""Event log for SCIM events."""
88 changes: 44 additions & 44 deletions simple_scim/serializers.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
# serializers.py
from django.contrib.auth.models import User
from django.urls import reverse
from rest_framework import serializers
# # serializers.py
# from django.contrib.auth.models import User
# from django.urls import reverse
# from rest_framework import serializers

schema = {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "2819c223-7f76-453a-413861904646",
"externalId": "701984",
"userName": "[email protected]",
"name": {
"formatted": "Ms. Barbara J Jensen, III",
"familyName": "Jensen",
"givenName": "Barbara",
"middleName": "Jane",
"honorificPrefix": "Ms.",
"honorificSuffix": "III",
},
"meta": {
"resourceType": "User",
"created": "2010-01-23T04:56:22Z",
"lastModified": "2011-05-13T04:42:34Z",
"version": 'W\/"3694e05e9dff591"',
"location": "https://example.com/v2/Users/2819c223-7f76-453a-413861904646",
},
}
# schema = {
# "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
# "id": "2819c223-7f76-453a-413861904646",
# "externalId": "701984",
# "userName": "[email protected]",
# "name": {
# "formatted": "Ms. Barbara J Jensen, III",
# "familyName": "Jensen",
# "givenName": "Barbara",
# "middleName": "Jane",
# "honorificPrefix": "Ms.",
# "honorificSuffix": "III",
# },
# "meta": {
# "resourceType": "User",
# "created": "2010-01-23T04:56:22Z",
# "lastModified": "2011-05-13T04:42:34Z",
# "version": 'W\/"3694e05e9dff591"',
# "location": "https://example.com/v2/Users/2819c223-7f76-453a-413861904646",
# },
# }


class SCIMUserSerializer(serializers.ModelSerializer):
userName = serializers.CharField(source="username")
name = serializers.SerializerMethodField()
meta = serializers.SerializerMethodField()
# class SCIMUserSerializer(serializers.ModelSerializer):
# userName = serializers.CharField(source="username")
# name = serializers.SerializerMethodField()
# meta = serializers.SerializerMethodField()

def get_name(self, obj: User) -> dict:
return {
"formatted": obj.get_full_name(),
"givenName": obj.first_name,
"familyName": obj.last_name,
}
# def get_name(self, obj: User) -> dict:
# return {
# "formatted": obj.get_full_name(),
# "givenName": obj.first_name,
# "familyName": obj.last_name,
# }

def get_meta(self, obj: User) -> dict:
return {
"resourceType": "User",
"created": obj.date_joined.isoformat(),
"location": reverse("simple_scim:user-detail", kwargs={"pk": obj.id}),
}
# def get_meta(self, obj: User) -> dict:
# return {
# "resourceType": "User",
# "created": obj.date_joined.isoformat(),
# "location": reverse("simple_scim:user-detail", kwargs={"pk": obj.id}),
# }

class Meta:
model = User
fields = ("name", "userName", "meta")
# class Meta:
# model = User
# fields = ("name", "userName", "meta")
27 changes: 27 additions & 0 deletions simple_scim/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Use these signals to hook into the user creation, update and deletion process.
These are provided as an alternative to subclassing the views and overriding
the methods. The signals are fired before and after the user is created,
updated or deleted.
If you want to abort the user creation, update or deletion process, raise an
exception in the pre-signal handler.
"""
import django.dispatch

# kwargs: user: User, user_data: dict
pre_create_scim_user = django.dispatch.Signal()
# kwargs: user: User, user_data: dict
post_create_scim_user = django.dispatch.Signal()

# kwargs: user: User, user_data: dict
pre_update_scim_user = django.dispatch.Signal()
# kwargs: user: User, user_data: dict
post_update_scim_user = django.dispatch.Signal()

# kwargs: user: User
pre_delete_scim_user = django.dispatch.Signal()
# kwargs: user: User
post_delete_scim_user = django.dispatch.Signal()
14 changes: 9 additions & 5 deletions simple_scim/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# urls.py
from django.urls import path

from .views import ServiceProviderConfig, UserDetail, UserList
from .views import SCIMUserView, resource_types, schemas, service_provider_config

app_name = "simple_scim"
app_name = "scim"
urlpatterns = [
path("users/", UserList.as_view(), name="user-list"),
path("users/<str:user_id>/", UserDetail.as_view(), name="user-detail"),
path("config/", ServiceProviderConfig.as_view(), name="service-provider-config"),
# handles the create request (POST, no user_id)
path("Users/", SCIMUserView.as_view(), name="scim-user"),
# handles the update, delete and fetch requests (PUT, PATCH, DELETE, GET)
path("Users/<str:user_id>/", SCIMUserView.as_view(), name="scim-user"),
path("ServiceProviderConfig", service_provider_config, name="scim-config"),
path("ResourceTypes", resource_types, name="scim-resource-types"),
path("Schemas", schemas, name="scim-schemas"),
]
Loading

0 comments on commit 380873c

Please sign in to comment.