Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F7124754
D5219.id18694.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Subscribers
None
D5219.id18694.diff
View Options
diff --git a/mypy.ini b/mypy.ini
--- a/mypy.ini
+++ b/mypy.ini
@@ -13,3 +13,6 @@
[mypy-keycloak.*]
ignore_missing_imports = True
+
+[mypy-django.*]
+ignore_missing_imports = True
diff --git a/requirements-django.txt b/requirements-django.txt
new file mode 100644
--- /dev/null
+++ b/requirements-django.txt
@@ -0,0 +1 @@
+Django<3
diff --git a/requirements-test.txt b/requirements-test.txt
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,2 +1,3 @@
pytest
requests_mock
+pytest-django
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -50,7 +50,10 @@
tests_require=parse_requirements("test"),
setup_requires=["setuptools-scm"],
use_scm_version=True,
- extras_require={"testing": parse_requirements("test")},
+ extras_require={
+ "django": parse_requirements("django"),
+ "testing": parse_requirements("test"),
+ },
include_package_data=True,
# entry_points="""
# [swh.cli.subcommands]
diff --git a/swh/auth/django.py b/swh/auth/django.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/django.py
@@ -0,0 +1,84 @@
+# Copyright (C) 2020-2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from datetime import datetime
+from typing import Optional, Set
+
+from django.contrib.auth.models import User
+
+
+class OIDCUser(User):
+ """
+ Custom User proxy model for remote users storing OpenID Connect
+ related data: profile containing authentication tokens.
+
+ The model is also not saved to database as all users are already stored
+ in the Keycloak one.
+ """
+
+ # OIDC subject identifier
+ sub: str = ""
+
+ # OIDC tokens and session related data, only relevant when a user
+ # authenticates from a web browser
+ access_token: Optional[str] = None
+ expires_at: Optional[datetime] = None
+ id_token: Optional[str] = None
+ refresh_token: Optional[str] = None
+ refresh_expires_at: Optional[datetime] = None
+ scope: Optional[str] = None
+ session_state: Optional[str] = None
+
+ # User permissions
+ permissions: Set[str]
+
+ class Meta:
+ # TODO: To redefine in subclass of this class
+ # Forced to empty otherwise, django complains about it
+ # "Model class swh.auth.django.OIDCUser doesn't declare an explicit app_label
+ # and isn't in an application in INSTALLED_APPS"
+ app_label = ""
+ proxy = True
+
+ def save(self, **kwargs):
+ """
+ Override django.db.models.Model.save to avoid saving the remote
+ users to web application database.
+ """
+ pass
+
+ def get_group_permissions(self, obj=None) -> Set[str]:
+ """
+ Override django.contrib.auth.models.PermissionsMixin.get_group_permissions
+ to get permissions from OIDC
+ """
+ return self.get_all_permissions(obj)
+
+ def get_all_permissions(self, obj=None) -> Set[str]:
+ """
+ Override django.contrib.auth.models.PermissionsMixin.get_all_permissions
+ to get permissions from OIDC
+ """
+ return self.permissions
+
+ def has_perm(self, perm, obj=None) -> bool:
+ """
+ Override django.contrib.auth.models.PermissionsMixin.has_perm
+ to check permission from OIDC
+ """
+ if self.is_active and self.is_superuser:
+ return True
+
+ return perm in self.permissions
+
+ def has_module_perms(self, app_label) -> bool:
+ """
+ Override django.contrib.auth.models.PermissionsMixin.has_module_perms
+ to check permissions from OIDC.
+ """
+ if self.is_active and self.is_superuser:
+ return True
+
+ return any(perm.startswith(app_label) for perm in self.permissions)
diff --git a/swh/auth/tests/apptest/__init__.py b/swh/auth/tests/apptest/__init__.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/tests/apptest/__init__.py
@@ -0,0 +1 @@
+default_app_config = "swh.auth.tests.apptest.apps.TestApp"
diff --git a/swh/auth/tests/apptest/apps.py b/swh/auth/tests/apptest/apps.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/tests/apptest/apps.py
@@ -0,0 +1,10 @@
+# Copyright (C) 2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from django.apps import AppConfig
+
+
+class TestApp(AppConfig):
+ name = "swh.auth.tests.apptest"
diff --git a/swh/auth/tests/apptest/models.py b/swh/auth/tests/apptest/models.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/tests/apptest/models.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from django.db import models
+
+from swh.auth.django import OIDCUser
+
+
+class MyUser(OIDCUser):
+ """MyUser class to demonstrate the use of the OIDCUser which adds some attributes to
+ serialize in db."""
+
+ url = models.TextField(null=False)
+
+ class meta:
+ db_table = "user_client"
+ app_label = "app-label"
diff --git a/swh/auth/tests/apptest/urls.py b/swh/auth/tests/apptest/urls.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/tests/apptest/urls.py
@@ -0,0 +1,17 @@
+"""apptest URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/2.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+
+urlpatterns = [] # type: ignore
diff --git a/swh/auth/tests/conftest.py b/swh/auth/tests/conftest.py
new file mode 100644
--- /dev/null
+++ b/swh/auth/tests/conftest.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2021 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from django.conf import settings
+
+
+def pytest_configure():
+ # Basic settings to avoid having to define a django module settings all over the
+ # place so django tests pass fine
+ settings.configure(
+ INSTALLED_APPS=[
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "swh.auth.tests.apptest",
+ ],
+ )
diff --git a/swh/auth/tests/test_models.py b/swh/auth/tests/test_models.py
new file mode 100644
diff --git a/tox.ini b/tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -7,6 +7,7 @@
deps =
pytest-cov
dev: pdbpp
+ -r requirements-django.txt
commands =
pytest --doctest-modules \
{envsitepackagesdir}/swh/auth \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Dec 21 2024, 6:31 PM (11 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3221160
Attached To
D5219: swh.auth.django: Expose OIDCUser model object
Event Timeline
Log In to Comment