pirates-auth 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pirates_auth-0.8.0/LICENSE +9 -0
- pirates_auth-0.8.0/PKG-INFO +140 -0
- pirates_auth-0.8.0/README.md +109 -0
- pirates_auth-0.8.0/pirates/__init__.py +0 -0
- pirates_auth-0.8.0/pirates/apps.py +13 -0
- pirates_auth-0.8.0/pirates/auth.py +49 -0
- pirates_auth-0.8.0/pirates/models.py +129 -0
- pirates_auth-0.8.0/pirates/signals.py +3 -0
- pirates_auth-0.8.0/pirates/urls.py +5 -0
- pirates_auth-0.8.0/pyproject.toml +63 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020, Jan Bednařík
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pirates-auth
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Passwordless SSO (OpenID Connect) authentication backend and abstract user, team and group models for Django. Used in the Czech Pirate Party.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: django,openid,sso
|
|
8
|
+
Author: Jan Bednařík
|
|
9
|
+
Author-email: jan.bednarik@gmail.com
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Classifier: Framework :: Django
|
|
22
|
+
Classifier: Framework :: Django :: 5.2
|
|
23
|
+
Classifier: Framework :: Django :: 6.0
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Dist: Django (>=5.2,<7.0)
|
|
26
|
+
Requires-Dist: mozilla-django-oidc (>=3.0,<5.0)
|
|
27
|
+
Project-URL: Homepage, https://gitlab.pirati.cz/to/pirates
|
|
28
|
+
Project-URL: Repository, https://gitlab.pirati.cz/to/pirates
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Pirates
|
|
32
|
+
|
|
33
|
+
Django app na uživatele, týmy a skupiny, s napojením na SSO.
|
|
34
|
+
|
|
35
|
+
[](https://github.com/psf/black)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
Bezheslová SSO autentizace (OpenID Connect) a abstraktní modely uživatele, týmu
|
|
41
|
+
a skupiny pro Django.
|
|
42
|
+
|
|
43
|
+
## Požadavky
|
|
44
|
+
|
|
45
|
+
* Python 3.11+
|
|
46
|
+
* Django 5.2–6.x
|
|
47
|
+
|
|
48
|
+
## Instalace
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install pirates-auth
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Na PyPI se balíček jmenuje **`pirates-auth`**, ale importuje se jako `pirates` –
|
|
55
|
+
pod tímto názvem se také přidává do `INSTALLED_APPS`.
|
|
56
|
+
|
|
57
|
+
## Použití
|
|
58
|
+
|
|
59
|
+
### Settings
|
|
60
|
+
|
|
61
|
+
Přidat `pirates` do `INSTALLED_APPS`.
|
|
62
|
+
|
|
63
|
+
### Modely
|
|
64
|
+
|
|
65
|
+
Jsou k dipozici abstraktní modely pro uživatele, tým a organizační skupinu. Ty
|
|
66
|
+
lze doplnit o další fieldy specifické pro aplikaci. Příklad:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from django.db import models
|
|
70
|
+
from pirates.models import AbstractUser
|
|
71
|
+
|
|
72
|
+
class CustomUser(AbstractUser):
|
|
73
|
+
is_friendly = models.BooleanField(default=True)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A nezapomenout model pro uživatele nastavit v settings:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
AUTH_USER_MODEL = "myapp.CustomUser"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Uživatel je bezheslový – autentizuje se přes SSO (OpenID Connect), takže
|
|
83
|
+
`AbstractUser` nedědí z `AbstractBaseUser` a nemá pole `password` ani
|
|
84
|
+
`last_login`. `get_session_auth_hash()` je proto odvozen z `sso_id`, aby
|
|
85
|
+
`django.contrib.auth.login()` fungoval i na Djangu 6+.
|
|
86
|
+
|
|
87
|
+
### URLs
|
|
88
|
+
|
|
89
|
+
URL patterns (v současné době pouze pro OpenID Connect) jsou definovány v
|
|
90
|
+
`pirates.urls`. Stačí je připojit k URL patterns projektu:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from pirates.urls import urlpatterns as pirates_urlpatterns
|
|
94
|
+
|
|
95
|
+
urlpatterns = [
|
|
96
|
+
# URL patterns projektu
|
|
97
|
+
# ...
|
|
98
|
+
] + pirates_urlpatterns
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### SSO přes OpenID Connect
|
|
103
|
+
|
|
104
|
+
Implementaci OpenID zajišťuje knihovna
|
|
105
|
+
[mozilla-django-oidc](https://github.com/mozilla/mozilla-django-oidc).
|
|
106
|
+
|
|
107
|
+
V settings projektu je třeba nastavit několik konfiguračních konstant a
|
|
108
|
+
autentifikační backend. Příklad settings (s využitím
|
|
109
|
+
[django-environ](https://github.com/joke2k/django-environ)):
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from os.path import join
|
|
113
|
+
import environ
|
|
114
|
+
|
|
115
|
+
env = environ.Env()
|
|
116
|
+
|
|
117
|
+
AUTHENTICATION_BACKENDS = ["pirates.auth.PiratesOIDCAuthenticationBackend"]
|
|
118
|
+
|
|
119
|
+
OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID")
|
|
120
|
+
OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
|
|
121
|
+
OIDC_RP_REALM_URL = env.str("OIDC_RP_REALM_URL")
|
|
122
|
+
OIDC_RP_SIGN_ALGO = "RS256"
|
|
123
|
+
OIDC_OP_JWKS_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/certs")
|
|
124
|
+
OIDC_OP_AUTHORIZATION_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/auth")
|
|
125
|
+
OIDC_OP_TOKEN_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/token")
|
|
126
|
+
OIDC_OP_USER_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/userinfo")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
URL patterns pro OpenID Connect už jsou součástí `pirates.urls` (viz výše).
|
|
130
|
+
|
|
131
|
+
#### Signál po přihlášení
|
|
132
|
+
|
|
133
|
+
Po přihlášení uživatele je poslán signál `pirates.signals.post_login` s
|
|
134
|
+
parametry:
|
|
135
|
+
|
|
136
|
+
* `sender` - `PiratesOIDCAuthenticationBackend`
|
|
137
|
+
* `user` - přihlášený uživatel (instance `AUTH_USER_MODEL`)
|
|
138
|
+
* `created` - `True`/`False` zda-li byl vytvořen nový uživatel
|
|
139
|
+
* `request` - instance `HttpRequest`
|
|
140
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Pirates
|
|
2
|
+
|
|
3
|
+
Django app na uživatele, týmy a skupiny, s napojením na SSO.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/psf/black)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
Bezheslová SSO autentizace (OpenID Connect) a abstraktní modely uživatele, týmu
|
|
11
|
+
a skupiny pro Django.
|
|
12
|
+
|
|
13
|
+
## Požadavky
|
|
14
|
+
|
|
15
|
+
* Python 3.11+
|
|
16
|
+
* Django 5.2–6.x
|
|
17
|
+
|
|
18
|
+
## Instalace
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
pip install pirates-auth
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Na PyPI se balíček jmenuje **`pirates-auth`**, ale importuje se jako `pirates` –
|
|
25
|
+
pod tímto názvem se také přidává do `INSTALLED_APPS`.
|
|
26
|
+
|
|
27
|
+
## Použití
|
|
28
|
+
|
|
29
|
+
### Settings
|
|
30
|
+
|
|
31
|
+
Přidat `pirates` do `INSTALLED_APPS`.
|
|
32
|
+
|
|
33
|
+
### Modely
|
|
34
|
+
|
|
35
|
+
Jsou k dipozici abstraktní modely pro uživatele, tým a organizační skupinu. Ty
|
|
36
|
+
lze doplnit o další fieldy specifické pro aplikaci. Příklad:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from django.db import models
|
|
40
|
+
from pirates.models import AbstractUser
|
|
41
|
+
|
|
42
|
+
class CustomUser(AbstractUser):
|
|
43
|
+
is_friendly = models.BooleanField(default=True)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
A nezapomenout model pro uživatele nastavit v settings:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
AUTH_USER_MODEL = "myapp.CustomUser"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Uživatel je bezheslový – autentizuje se přes SSO (OpenID Connect), takže
|
|
53
|
+
`AbstractUser` nedědí z `AbstractBaseUser` a nemá pole `password` ani
|
|
54
|
+
`last_login`. `get_session_auth_hash()` je proto odvozen z `sso_id`, aby
|
|
55
|
+
`django.contrib.auth.login()` fungoval i na Djangu 6+.
|
|
56
|
+
|
|
57
|
+
### URLs
|
|
58
|
+
|
|
59
|
+
URL patterns (v současné době pouze pro OpenID Connect) jsou definovány v
|
|
60
|
+
`pirates.urls`. Stačí je připojit k URL patterns projektu:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from pirates.urls import urlpatterns as pirates_urlpatterns
|
|
64
|
+
|
|
65
|
+
urlpatterns = [
|
|
66
|
+
# URL patterns projektu
|
|
67
|
+
# ...
|
|
68
|
+
] + pirates_urlpatterns
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### SSO přes OpenID Connect
|
|
73
|
+
|
|
74
|
+
Implementaci OpenID zajišťuje knihovna
|
|
75
|
+
[mozilla-django-oidc](https://github.com/mozilla/mozilla-django-oidc).
|
|
76
|
+
|
|
77
|
+
V settings projektu je třeba nastavit několik konfiguračních konstant a
|
|
78
|
+
autentifikační backend. Příklad settings (s využitím
|
|
79
|
+
[django-environ](https://github.com/joke2k/django-environ)):
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from os.path import join
|
|
83
|
+
import environ
|
|
84
|
+
|
|
85
|
+
env = environ.Env()
|
|
86
|
+
|
|
87
|
+
AUTHENTICATION_BACKENDS = ["pirates.auth.PiratesOIDCAuthenticationBackend"]
|
|
88
|
+
|
|
89
|
+
OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID")
|
|
90
|
+
OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
|
|
91
|
+
OIDC_RP_REALM_URL = env.str("OIDC_RP_REALM_URL")
|
|
92
|
+
OIDC_RP_SIGN_ALGO = "RS256"
|
|
93
|
+
OIDC_OP_JWKS_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/certs")
|
|
94
|
+
OIDC_OP_AUTHORIZATION_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/auth")
|
|
95
|
+
OIDC_OP_TOKEN_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/token")
|
|
96
|
+
OIDC_OP_USER_ENDPOINT = join(OIDC_RP_REALM_URL, "protocol/openid-connect/userinfo")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
URL patterns pro OpenID Connect už jsou součástí `pirates.urls` (viz výše).
|
|
100
|
+
|
|
101
|
+
#### Signál po přihlášení
|
|
102
|
+
|
|
103
|
+
Po přihlášení uživatele je poslán signál `pirates.signals.post_login` s
|
|
104
|
+
parametry:
|
|
105
|
+
|
|
106
|
+
* `sender` - `PiratesOIDCAuthenticationBackend`
|
|
107
|
+
* `user` - přihlášený uživatel (instance `AUTH_USER_MODEL`)
|
|
108
|
+
* `created` - `True`/`False` zda-li byl vytvořen nový uživatel
|
|
109
|
+
* `request` - instance `HttpRequest`
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PiratesConfig(AppConfig):
|
|
6
|
+
name = "pirates"
|
|
7
|
+
verbose_name = _("Pirates")
|
|
8
|
+
|
|
9
|
+
def ready(self):
|
|
10
|
+
try:
|
|
11
|
+
import pirates.signals
|
|
12
|
+
except ImportError:
|
|
13
|
+
pass
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
|
|
2
|
+
|
|
3
|
+
from .signals import post_login
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PiratesOIDCAuthenticationBackend(OIDCAuthenticationBackend):
|
|
7
|
+
"""
|
|
8
|
+
Pirates OIDC Authentication Backend.
|
|
9
|
+
|
|
10
|
+
Instead of `email` uses claim `sub` (as `sso_id`) to identify users. Which
|
|
11
|
+
allows for email change.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def get_sso_id(self, claims):
|
|
15
|
+
return claims.get("sub")
|
|
16
|
+
|
|
17
|
+
def filter_users_by_claims(self, claims):
|
|
18
|
+
sso_id = self.get_sso_id(claims)
|
|
19
|
+
if not sso_id:
|
|
20
|
+
return self.UserModel.objects.none()
|
|
21
|
+
return self.UserModel.objects.filter(sso_id=sso_id)
|
|
22
|
+
|
|
23
|
+
def create_user(self, claims):
|
|
24
|
+
sso_id = self.get_sso_id(claims)
|
|
25
|
+
first_name = claims.get("given_name", "")
|
|
26
|
+
last_name = claims.get("family_name", "")
|
|
27
|
+
email = claims.get("email", "")
|
|
28
|
+
user = self.UserModel.objects.create(
|
|
29
|
+
sso_id=sso_id, first_name=first_name, last_name=last_name, email=email
|
|
30
|
+
)
|
|
31
|
+
self.send_post_login_signal(user, True, claims)
|
|
32
|
+
return user
|
|
33
|
+
|
|
34
|
+
def update_user(self, user, claims):
|
|
35
|
+
user.first_name = claims.get("given_name", "")
|
|
36
|
+
user.last_name = claims.get("family_name", "")
|
|
37
|
+
user.email = claims.get("email", "")
|
|
38
|
+
user.save()
|
|
39
|
+
self.send_post_login_signal(user, False, claims)
|
|
40
|
+
return user
|
|
41
|
+
|
|
42
|
+
def send_post_login_signal(self, user, created, claims):
|
|
43
|
+
post_login.send(
|
|
44
|
+
sender=self.__class__,
|
|
45
|
+
user=user,
|
|
46
|
+
created=created,
|
|
47
|
+
claims=claims,
|
|
48
|
+
request=self.request,
|
|
49
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.auth.models import PermissionsMixin
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
from django.utils.crypto import salted_hmac
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AbstractUser(PermissionsMixin):
|
|
10
|
+
"""
|
|
11
|
+
An abstract base class implementing a fully featured User model with
|
|
12
|
+
admin-compliant permissions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
sso_id = models.CharField(
|
|
16
|
+
_("SSO ID"),
|
|
17
|
+
max_length=150,
|
|
18
|
+
unique=True,
|
|
19
|
+
error_messages={"unique": _("A user with that SSO ID already exists.")},
|
|
20
|
+
)
|
|
21
|
+
first_name = models.CharField(_("first name"), max_length=150, blank=True)
|
|
22
|
+
last_name = models.CharField(_("last name"), max_length=150, blank=True)
|
|
23
|
+
email = models.EmailField(_("email address"), blank=True)
|
|
24
|
+
is_staff = models.BooleanField(
|
|
25
|
+
_("staff status"),
|
|
26
|
+
default=False,
|
|
27
|
+
help_text=_("Designates whether the user can log into this admin site."),
|
|
28
|
+
)
|
|
29
|
+
is_active = models.BooleanField(
|
|
30
|
+
_("active"),
|
|
31
|
+
default=True,
|
|
32
|
+
help_text=_(
|
|
33
|
+
"Designates whether this user should be treated as active. "
|
|
34
|
+
"Unselect this instead of deleting accounts."
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
|
|
38
|
+
|
|
39
|
+
USERNAME_FIELD = "sso_id"
|
|
40
|
+
REQUIRED_FIELDS = []
|
|
41
|
+
|
|
42
|
+
class Meta:
|
|
43
|
+
abstract = True
|
|
44
|
+
verbose_name = _("user")
|
|
45
|
+
verbose_name_plural = _("users")
|
|
46
|
+
|
|
47
|
+
def get_full_name(self):
|
|
48
|
+
"""Return the first_name plus the last_name, with a space in between."""
|
|
49
|
+
full_name = f"{self.first_name} {self.last_name}"
|
|
50
|
+
return full_name.strip()
|
|
51
|
+
|
|
52
|
+
def get_short_name(self):
|
|
53
|
+
"""Return the short name for the user."""
|
|
54
|
+
return self.first_name
|
|
55
|
+
|
|
56
|
+
def get_session_auth_hash(self):
|
|
57
|
+
"""
|
|
58
|
+
Return an HMAC of the ``sso_id`` field.
|
|
59
|
+
|
|
60
|
+
This account is passwordless -- it authenticates through SSO and does
|
|
61
|
+
not inherit ``AbstractBaseUser``, so there is no ``password`` to hash
|
|
62
|
+
(the mechanism Django's own ``AbstractBaseUser`` uses). Django still
|
|
63
|
+
requires the method: since Django 6.0 ``django.contrib.auth.login()``
|
|
64
|
+
calls it unconditionally, so without it every login raises
|
|
65
|
+
``AttributeError``. We derive the hash from the immutable ``sso_id``
|
|
66
|
+
instead. It must be non-empty and stable -- an empty value would make
|
|
67
|
+
Django flush the session on the next request and log the user out.
|
|
68
|
+
"""
|
|
69
|
+
return self._get_session_auth_hash()
|
|
70
|
+
|
|
71
|
+
def get_session_auth_fallback_hash(self):
|
|
72
|
+
for fallback_secret in settings.SECRET_KEY_FALLBACKS:
|
|
73
|
+
yield self._get_session_auth_hash(secret=fallback_secret)
|
|
74
|
+
|
|
75
|
+
def _get_session_auth_hash(self, secret=None):
|
|
76
|
+
key_salt = "pirates.models.AbstractUser.get_session_auth_hash"
|
|
77
|
+
return salted_hmac(
|
|
78
|
+
key_salt,
|
|
79
|
+
self.sso_id,
|
|
80
|
+
secret=secret,
|
|
81
|
+
algorithm="sha256",
|
|
82
|
+
).hexdigest()
|
|
83
|
+
|
|
84
|
+
def __str__(self):
|
|
85
|
+
return self.get_full_name() or self.email or self.sso_id
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def is_anonymous(self):
|
|
89
|
+
"""
|
|
90
|
+
Always return False. This is a way of comparing User objects to
|
|
91
|
+
anonymous users.
|
|
92
|
+
"""
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def is_authenticated(self):
|
|
97
|
+
"""
|
|
98
|
+
Always return True. This is a way to tell if the user has been
|
|
99
|
+
authenticated in templates.
|
|
100
|
+
"""
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class AbstractTeam(models.Model):
|
|
105
|
+
name = models.CharField(max_length=250, unique=True)
|
|
106
|
+
members = models.ManyToManyField(settings.AUTH_USER_MODEL)
|
|
107
|
+
|
|
108
|
+
class Meta:
|
|
109
|
+
abstract = True
|
|
110
|
+
ordering = ["name"]
|
|
111
|
+
verbose_name = _("team")
|
|
112
|
+
verbose_name_plural = _("teams")
|
|
113
|
+
|
|
114
|
+
def __str__(self):
|
|
115
|
+
return self.name
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class AbstractOrgGroup(models.Model):
|
|
119
|
+
name = models.CharField(max_length=250, unique=True)
|
|
120
|
+
members = models.ManyToManyField(settings.AUTH_USER_MODEL)
|
|
121
|
+
|
|
122
|
+
class Meta:
|
|
123
|
+
abstract = True
|
|
124
|
+
ordering = ["name"]
|
|
125
|
+
verbose_name = _("group")
|
|
126
|
+
verbose_name_plural = _("groups")
|
|
127
|
+
|
|
128
|
+
def __str__(self):
|
|
129
|
+
return self.name
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pirates-auth"
|
|
3
|
+
version = "0.8.0"
|
|
4
|
+
description = "Passwordless SSO (OpenID Connect) authentication backend and abstract user, team and group models for Django. Used in the Czech Pirate Party."
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Jan Bednařík", email = "jan.bednarik@gmail.com" },
|
|
7
|
+
{ name = "Alexandra Ťapková", email = "git@ouppy.space" }
|
|
8
|
+
]
|
|
9
|
+
license = "MIT"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = ["django", "openid", "sso"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 5 - Production/Stable",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
24
|
+
"Framework :: Django",
|
|
25
|
+
"Framework :: Django :: 5.2",
|
|
26
|
+
"Framework :: Django :: 6.0",
|
|
27
|
+
"Topic :: Utilities",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"Django>=5.2,<7.0",
|
|
31
|
+
"mozilla-django-oidc>=3.0,<5.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
homepage = "https://gitlab.pirati.cz/to/pirates"
|
|
36
|
+
repository = "https://gitlab.pirati.cz/to/pirates"
|
|
37
|
+
|
|
38
|
+
[tool.poetry]
|
|
39
|
+
packages = [{ include = "pirates" }]
|
|
40
|
+
|
|
41
|
+
[tool.poetry.group.dev.dependencies]
|
|
42
|
+
tox = ">=4.15"
|
|
43
|
+
pytest = ">=8"
|
|
44
|
+
pytest-cov = ">=5"
|
|
45
|
+
pytest-factoryboy = ">=2.7"
|
|
46
|
+
pytest-django = ">=4.8"
|
|
47
|
+
pytest-mock = ">=3.14"
|
|
48
|
+
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["poetry-core>=2.0.0"]
|
|
51
|
+
build-backend = "poetry.core.masonry.api"
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
python_files = "test_*.py"
|
|
55
|
+
addopts = "--ds=tests.settings"
|
|
56
|
+
|
|
57
|
+
[tool.isort]
|
|
58
|
+
# config compatible with Black
|
|
59
|
+
line_length = 88
|
|
60
|
+
multi_line_output = 3
|
|
61
|
+
default_section = "THIRDPARTY"
|
|
62
|
+
include_trailing_comma = true
|
|
63
|
+
known_third_party = ["django", "factory", "faker", "mozilla_django_oidc", "pytest", "pytest_factoryboy"]
|