dj-jwt-auth 1.2.0__tar.gz → 1.3.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.
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/PKG-INFO +51 -21
- dj-jwt-auth-1.3.0/README.md +99 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/dj_jwt_auth.egg-info/PKG-INFO +51 -21
- dj-jwt-auth-1.3.0/django_jwt/exceptions.py +10 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/settings.py +6 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/user.py +46 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/views.py +14 -8
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/setup.cfg +1 -1
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/tests/test.py +46 -7
- dj-jwt-auth-1.2.0/README.md +0 -65
- dj-jwt-auth-1.2.0/django_jwt/exceptions.py +0 -4
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/dj_jwt_auth.egg-info/SOURCES.txt +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/dj_jwt_auth.egg-info/dependency_links.txt +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/dj_jwt_auth.egg-info/requires.txt +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/dj_jwt_auth.egg-info/top_level.txt +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/__init__.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/config.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/middleware.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/pkce.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/urls.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/django_jwt/utils.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/pyproject.toml +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/setup.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/tests/__init__.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/tests/models.py +0 -0
- {dj-jwt-auth-1.2.0 → dj-jwt-auth-1.3.0}/tests/urls.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dj-jwt-auth
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A Django package for JSON Web Token validation and verification. Using PyJWT.
|
|
5
5
|
Home-page: https://www.example.com/
|
|
6
6
|
Author: Konstantin Seleznev
|
|
@@ -21,23 +21,22 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
21
21
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
22
22
|
Requires-Python: >=3.8
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
-
Requires-Dist: Django>=3.0
|
|
25
|
-
Requires-Dist: pyjwt>=2.5.0
|
|
26
|
-
Requires-Dist: requests>=2.28.1
|
|
27
|
-
Requires-Dist: cryptography>=36.0.2
|
|
28
24
|
|
|
29
25
|
# Django-JWT
|
|
30
26
|
|
|
31
27
|
This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
32
28
|
|
|
33
29
|
### Installation
|
|
34
|
-
1. Install the package using pip
|
|
30
|
+
1. Install the package using pip:
|
|
31
|
+
```bash
|
|
32
|
+
pip install dj-jwt-auth
|
|
33
|
+
```
|
|
35
34
|
|
|
36
35
|
2. Add "django_jwt" to your INSTALLED_APPS setting like this::
|
|
37
36
|
```
|
|
38
37
|
INSTALLED_APPS = [
|
|
39
38
|
...
|
|
40
|
-
|
|
39
|
+
"django_jwt",
|
|
41
40
|
]
|
|
42
41
|
```
|
|
43
42
|
|
|
@@ -45,33 +44,39 @@ This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
|
45
44
|
```
|
|
46
45
|
MIDDLEWARE = [
|
|
47
46
|
...
|
|
48
|
-
|
|
47
|
+
"django_jwt.middleware.JWTAuthMiddleware",
|
|
49
48
|
]
|
|
50
49
|
```
|
|
51
50
|
|
|
52
51
|
### Configuration:
|
|
53
52
|
Required variables:
|
|
54
|
-
-
|
|
55
|
-
|
|
53
|
+
- OIDC_CONFIG_ROUTES - dict of "algorithm": "config_url". Required for using JWTAuthMiddleware. Example:
|
|
54
|
+
```
|
|
55
|
+
OIDC_CONFIG_ROUTES = {
|
|
56
|
+
"RS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
57
|
+
"HS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
58
|
+
}
|
|
59
|
+
```
|
|
56
60
|
Optional variables:
|
|
57
61
|
- OIDC_AUDIENCE - by default ["account", "broker"]
|
|
62
|
+
|
|
58
63
|
User retated variables:
|
|
59
64
|
- OIDC_USER_UPDATE - if True, user model will be updated from userinfo endpoint if MODIFIED date has changed, by default True
|
|
60
65
|
- OIDC_USER_MODIFIED_FIELD - user model field to store last modified date, by default `modified_timestamp`
|
|
61
66
|
- OIDC_TOKEN_MODIFIED_FIELD - access token field to store last modified date, by default `updated_at`
|
|
62
|
-
- OIDC_USER_UID - User model
|
|
67
|
+
- OIDC_USER_UID - User model" unique identifier, by default `kc_id`
|
|
63
68
|
- OIDC_USER_MAPPING - mapping between JWT claims and user model fields, by default:
|
|
64
69
|
```
|
|
65
70
|
OIDC_USER_MAPPING = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
"first_name": "first_name",
|
|
72
|
+
"last_name": "last_name",
|
|
73
|
+
"username": "username",
|
|
69
74
|
}
|
|
70
75
|
```
|
|
71
76
|
- OIDC_USER_DEFAULTS - default values for user model fields, by default:
|
|
72
77
|
```
|
|
73
78
|
OIDC_USER_DEFAULTS = {
|
|
74
|
-
|
|
79
|
+
"is_active": True,
|
|
75
80
|
}
|
|
76
81
|
```
|
|
77
82
|
|
|
@@ -80,14 +85,39 @@ User retated variables:
|
|
|
80
85
|
OIDC_USER_ON_CREATE = None
|
|
81
86
|
OIDC_USER_ON_UPDATE = None
|
|
82
87
|
```
|
|
83
|
-
|
|
88
|
+
These functions should accept two arguments: user and request.
|
|
89
|
+
|
|
90
|
+
### Admin panel integration:
|
|
91
|
+
To integrate admin panel with OIDC, add OIDC_ADMIN_ISSUER and OIDC_ADMIN_CLIENT_ID to settings.
|
|
92
|
+
- OIDC_ADMIN_ISSUER - required for admin-panel access through OIDC. Example:
|
|
84
93
|
```
|
|
85
|
-
|
|
86
|
-
'RS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
87
|
-
'HS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
88
|
-
}
|
|
94
|
+
OIDC_ADMIN_ISSUER = "https://keyCloak/realms/h/.well-known/openid-configuration"
|
|
89
95
|
```
|
|
90
|
-
|
|
96
|
+
- OIDC_ADMIN_CLIENT_ID - by default "complete-anatomy"
|
|
97
|
+
To mapping roles to admin panel permissions, use OIDC_ADMIN_ROLES. Example:
|
|
98
|
+
```python
|
|
99
|
+
from django_jwt.user import ROLE
|
|
100
|
+
|
|
101
|
+
OIDC_ADMIN_ROLES = [
|
|
102
|
+
ROLE(
|
|
103
|
+
name="admin", # name from token
|
|
104
|
+
is_superuser=True,
|
|
105
|
+
),
|
|
106
|
+
ROLE(
|
|
107
|
+
name="staff",
|
|
108
|
+
groups=["LMS (Full)", "Organizations (Full)", "Customer Support (Full)"],
|
|
109
|
+
permissions=["Can add user"],
|
|
110
|
+
),
|
|
111
|
+
]
|
|
112
|
+
```
|
|
113
|
+
And add login view to urls.py:
|
|
114
|
+
```python
|
|
115
|
+
urlpatterns = [
|
|
116
|
+
path("admin/", include("django_jwt.urls")),
|
|
117
|
+
...
|
|
118
|
+
]
|
|
119
|
+
```
|
|
120
|
+
Login URL will be available at `/admin/oidc/`.
|
|
91
121
|
|
|
92
122
|
### Testing:
|
|
93
123
|
Run command `python runtests.py` to run tests.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Django-JWT
|
|
2
|
+
|
|
3
|
+
This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
1. Install the package using pip:
|
|
7
|
+
```bash
|
|
8
|
+
pip install dj-jwt-auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. Add "django_jwt" to your INSTALLED_APPS setting like this::
|
|
12
|
+
```
|
|
13
|
+
INSTALLED_APPS = [
|
|
14
|
+
...
|
|
15
|
+
"django_jwt",
|
|
16
|
+
]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3. Add "django_jwt.middleware.JWTAuthMiddleware" to your MIDDLEWARE setting like this::
|
|
20
|
+
```
|
|
21
|
+
MIDDLEWARE = [
|
|
22
|
+
...
|
|
23
|
+
"django_jwt.middleware.JWTAuthMiddleware",
|
|
24
|
+
]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Configuration:
|
|
28
|
+
Required variables:
|
|
29
|
+
- OIDC_CONFIG_ROUTES - dict of "algorithm": "config_url". Required for using JWTAuthMiddleware. Example:
|
|
30
|
+
```
|
|
31
|
+
OIDC_CONFIG_ROUTES = {
|
|
32
|
+
"RS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
33
|
+
"HS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
Optional variables:
|
|
37
|
+
- OIDC_AUDIENCE - by default ["account", "broker"]
|
|
38
|
+
|
|
39
|
+
User retated variables:
|
|
40
|
+
- OIDC_USER_UPDATE - if True, user model will be updated from userinfo endpoint if MODIFIED date has changed, by default True
|
|
41
|
+
- OIDC_USER_MODIFIED_FIELD - user model field to store last modified date, by default `modified_timestamp`
|
|
42
|
+
- OIDC_TOKEN_MODIFIED_FIELD - access token field to store last modified date, by default `updated_at`
|
|
43
|
+
- OIDC_USER_UID - User model" unique identifier, by default `kc_id`
|
|
44
|
+
- OIDC_USER_MAPPING - mapping between JWT claims and user model fields, by default:
|
|
45
|
+
```
|
|
46
|
+
OIDC_USER_MAPPING = {
|
|
47
|
+
"first_name": "first_name",
|
|
48
|
+
"last_name": "last_name",
|
|
49
|
+
"username": "username",
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
- OIDC_USER_DEFAULTS - default values for user model fields, by default:
|
|
53
|
+
```
|
|
54
|
+
OIDC_USER_DEFAULTS = {
|
|
55
|
+
"is_active": True,
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- OIDC_USER_ON_CREATE and OIDC_USER_ON_UPDATE - functions to be called on user creation and update, by default:
|
|
60
|
+
```
|
|
61
|
+
OIDC_USER_ON_CREATE = None
|
|
62
|
+
OIDC_USER_ON_UPDATE = None
|
|
63
|
+
```
|
|
64
|
+
These functions should accept two arguments: user and request.
|
|
65
|
+
|
|
66
|
+
### Admin panel integration:
|
|
67
|
+
To integrate admin panel with OIDC, add OIDC_ADMIN_ISSUER and OIDC_ADMIN_CLIENT_ID to settings.
|
|
68
|
+
- OIDC_ADMIN_ISSUER - required for admin-panel access through OIDC. Example:
|
|
69
|
+
```
|
|
70
|
+
OIDC_ADMIN_ISSUER = "https://keyCloak/realms/h/.well-known/openid-configuration"
|
|
71
|
+
```
|
|
72
|
+
- OIDC_ADMIN_CLIENT_ID - by default "complete-anatomy"
|
|
73
|
+
To mapping roles to admin panel permissions, use OIDC_ADMIN_ROLES. Example:
|
|
74
|
+
```python
|
|
75
|
+
from django_jwt.user import ROLE
|
|
76
|
+
|
|
77
|
+
OIDC_ADMIN_ROLES = [
|
|
78
|
+
ROLE(
|
|
79
|
+
name="admin", # name from token
|
|
80
|
+
is_superuser=True,
|
|
81
|
+
),
|
|
82
|
+
ROLE(
|
|
83
|
+
name="staff",
|
|
84
|
+
groups=["LMS (Full)", "Organizations (Full)", "Customer Support (Full)"],
|
|
85
|
+
permissions=["Can add user"],
|
|
86
|
+
),
|
|
87
|
+
]
|
|
88
|
+
```
|
|
89
|
+
And add login view to urls.py:
|
|
90
|
+
```python
|
|
91
|
+
urlpatterns = [
|
|
92
|
+
path("admin/", include("django_jwt.urls")),
|
|
93
|
+
...
|
|
94
|
+
]
|
|
95
|
+
```
|
|
96
|
+
Login URL will be available at `/admin/oidc/`.
|
|
97
|
+
|
|
98
|
+
### Testing:
|
|
99
|
+
Run command `python runtests.py` to run tests.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dj-jwt-auth
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A Django package for JSON Web Token validation and verification. Using PyJWT.
|
|
5
5
|
Home-page: https://www.example.com/
|
|
6
6
|
Author: Konstantin Seleznev
|
|
@@ -21,23 +21,22 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
21
21
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
22
22
|
Requires-Python: >=3.8
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
|
-
Requires-Dist: Django>=3.0
|
|
25
|
-
Requires-Dist: pyjwt>=2.5.0
|
|
26
|
-
Requires-Dist: requests>=2.28.1
|
|
27
|
-
Requires-Dist: cryptography>=36.0.2
|
|
28
24
|
|
|
29
25
|
# Django-JWT
|
|
30
26
|
|
|
31
27
|
This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
32
28
|
|
|
33
29
|
### Installation
|
|
34
|
-
1. Install the package using pip
|
|
30
|
+
1. Install the package using pip:
|
|
31
|
+
```bash
|
|
32
|
+
pip install dj-jwt-auth
|
|
33
|
+
```
|
|
35
34
|
|
|
36
35
|
2. Add "django_jwt" to your INSTALLED_APPS setting like this::
|
|
37
36
|
```
|
|
38
37
|
INSTALLED_APPS = [
|
|
39
38
|
...
|
|
40
|
-
|
|
39
|
+
"django_jwt",
|
|
41
40
|
]
|
|
42
41
|
```
|
|
43
42
|
|
|
@@ -45,33 +44,39 @@ This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
|
45
44
|
```
|
|
46
45
|
MIDDLEWARE = [
|
|
47
46
|
...
|
|
48
|
-
|
|
47
|
+
"django_jwt.middleware.JWTAuthMiddleware",
|
|
49
48
|
]
|
|
50
49
|
```
|
|
51
50
|
|
|
52
51
|
### Configuration:
|
|
53
52
|
Required variables:
|
|
54
|
-
-
|
|
55
|
-
|
|
53
|
+
- OIDC_CONFIG_ROUTES - dict of "algorithm": "config_url". Required for using JWTAuthMiddleware. Example:
|
|
54
|
+
```
|
|
55
|
+
OIDC_CONFIG_ROUTES = {
|
|
56
|
+
"RS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
57
|
+
"HS256": "https://keyCloak/realms/h/.well-known/openid-configuration",
|
|
58
|
+
}
|
|
59
|
+
```
|
|
56
60
|
Optional variables:
|
|
57
61
|
- OIDC_AUDIENCE - by default ["account", "broker"]
|
|
62
|
+
|
|
58
63
|
User retated variables:
|
|
59
64
|
- OIDC_USER_UPDATE - if True, user model will be updated from userinfo endpoint if MODIFIED date has changed, by default True
|
|
60
65
|
- OIDC_USER_MODIFIED_FIELD - user model field to store last modified date, by default `modified_timestamp`
|
|
61
66
|
- OIDC_TOKEN_MODIFIED_FIELD - access token field to store last modified date, by default `updated_at`
|
|
62
|
-
- OIDC_USER_UID - User model
|
|
67
|
+
- OIDC_USER_UID - User model" unique identifier, by default `kc_id`
|
|
63
68
|
- OIDC_USER_MAPPING - mapping between JWT claims and user model fields, by default:
|
|
64
69
|
```
|
|
65
70
|
OIDC_USER_MAPPING = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
"first_name": "first_name",
|
|
72
|
+
"last_name": "last_name",
|
|
73
|
+
"username": "username",
|
|
69
74
|
}
|
|
70
75
|
```
|
|
71
76
|
- OIDC_USER_DEFAULTS - default values for user model fields, by default:
|
|
72
77
|
```
|
|
73
78
|
OIDC_USER_DEFAULTS = {
|
|
74
|
-
|
|
79
|
+
"is_active": True,
|
|
75
80
|
}
|
|
76
81
|
```
|
|
77
82
|
|
|
@@ -80,14 +85,39 @@ User retated variables:
|
|
|
80
85
|
OIDC_USER_ON_CREATE = None
|
|
81
86
|
OIDC_USER_ON_UPDATE = None
|
|
82
87
|
```
|
|
83
|
-
|
|
88
|
+
These functions should accept two arguments: user and request.
|
|
89
|
+
|
|
90
|
+
### Admin panel integration:
|
|
91
|
+
To integrate admin panel with OIDC, add OIDC_ADMIN_ISSUER and OIDC_ADMIN_CLIENT_ID to settings.
|
|
92
|
+
- OIDC_ADMIN_ISSUER - required for admin-panel access through OIDC. Example:
|
|
84
93
|
```
|
|
85
|
-
|
|
86
|
-
'RS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
87
|
-
'HS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
88
|
-
}
|
|
94
|
+
OIDC_ADMIN_ISSUER = "https://keyCloak/realms/h/.well-known/openid-configuration"
|
|
89
95
|
```
|
|
90
|
-
|
|
96
|
+
- OIDC_ADMIN_CLIENT_ID - by default "complete-anatomy"
|
|
97
|
+
To mapping roles to admin panel permissions, use OIDC_ADMIN_ROLES. Example:
|
|
98
|
+
```python
|
|
99
|
+
from django_jwt.user import ROLE
|
|
100
|
+
|
|
101
|
+
OIDC_ADMIN_ROLES = [
|
|
102
|
+
ROLE(
|
|
103
|
+
name="admin", # name from token
|
|
104
|
+
is_superuser=True,
|
|
105
|
+
),
|
|
106
|
+
ROLE(
|
|
107
|
+
name="staff",
|
|
108
|
+
groups=["LMS (Full)", "Organizations (Full)", "Customer Support (Full)"],
|
|
109
|
+
permissions=["Can add user"],
|
|
110
|
+
),
|
|
111
|
+
]
|
|
112
|
+
```
|
|
113
|
+
And add login view to urls.py:
|
|
114
|
+
```python
|
|
115
|
+
urlpatterns = [
|
|
116
|
+
path("admin/", include("django_jwt.urls")),
|
|
117
|
+
...
|
|
118
|
+
]
|
|
119
|
+
```
|
|
120
|
+
Login URL will be available at `/admin/oidc/`.
|
|
91
121
|
|
|
92
122
|
### Testing:
|
|
93
123
|
Run command `python runtests.py` to run tests.
|
|
@@ -39,3 +39,9 @@ OIDC_CONFIG_ROUTES = getattr(settings, "OIDC_CONFIG_ROUTES", None)
|
|
|
39
39
|
OIDC_ADMIN_ISSUER = getattr(settings, "OIDC_ADMIN_ISSUER", None)
|
|
40
40
|
OIDC_ADMIN_CLIENT_ID = getattr(settings, "OIDC_ADMIN_CLIENT_ID", "complete-anatomy")
|
|
41
41
|
OIDC_ADMIN_SCOPE = getattr(settings, "OIDC_ADMIN_SCOPE", "openid")
|
|
42
|
+
OIDC_ADMIN_ROLES = getattr(settings, "OIDC_ADMIN_ROLES", [])
|
|
43
|
+
|
|
44
|
+
for role in OIDC_ADMIN_ROLES:
|
|
45
|
+
from django_jwt.user import ROLE
|
|
46
|
+
|
|
47
|
+
assert isinstance(role, ROLE), f"Role must be a namedtuple, got {type(role)}"
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from collections import namedtuple
|
|
1
2
|
from datetime import datetime
|
|
3
|
+
from functools import cache
|
|
2
4
|
from logging import getLogger
|
|
3
5
|
|
|
4
6
|
import pytz
|
|
5
7
|
from django.contrib.auth import get_user_model
|
|
8
|
+
from django.contrib.auth.models import Group, Permission
|
|
6
9
|
from django.http.request import HttpRequest
|
|
7
10
|
|
|
8
11
|
from django_jwt import settings
|
|
@@ -12,6 +15,7 @@ utc = pytz.UTC
|
|
|
12
15
|
log = getLogger(__name__)
|
|
13
16
|
|
|
14
17
|
model = get_user_model()
|
|
18
|
+
ROLE = namedtuple("Role", ["name", "is_superuser", "groups", "permissions"], defaults=["", False, [], []])
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
class UserHandler:
|
|
@@ -100,3 +104,45 @@ class UserHandler:
|
|
|
100
104
|
return self._get_by_email()
|
|
101
105
|
except model.DoesNotExist:
|
|
102
106
|
return self._create_new_user()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class RoleHandler:
|
|
110
|
+
"""
|
|
111
|
+
Process user roles and permissions from access token.
|
|
112
|
+
Token be like:
|
|
113
|
+
...
|
|
114
|
+
"resource_access": {
|
|
115
|
+
"complete_anatomy": {
|
|
116
|
+
"roles": [
|
|
117
|
+
"admin"
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def roles(self) -> dict:
|
|
125
|
+
return {role.name: role for role in settings.OIDC_ADMIN_ROLES}
|
|
126
|
+
|
|
127
|
+
@cache
|
|
128
|
+
def get_permissions(self, role_name: str) -> Permission:
|
|
129
|
+
return Permission.objects.filter(codename__in=self.roles[role_name].permissions)
|
|
130
|
+
|
|
131
|
+
@cache
|
|
132
|
+
def get_groups(self, role_name: str) -> Group:
|
|
133
|
+
return Group.objects.filter(name__in=self.roles[role_name].groups)
|
|
134
|
+
|
|
135
|
+
def apply(self, user: model, access_token: dict):
|
|
136
|
+
token_roles = access_token.get("resource_access", {}).get(settings.OIDC_ADMIN_CLIENT_ID, {}).get("roles", [])
|
|
137
|
+
for role_name in token_roles:
|
|
138
|
+
if role_name in self.roles:
|
|
139
|
+
role = self.roles[role_name]
|
|
140
|
+
user.groups.add(*self.get_groups(role_name))
|
|
141
|
+
user.user_permissions.add(*self.get_permissions(role_name))
|
|
142
|
+
if role.is_superuser != user.is_superuser:
|
|
143
|
+
user.is_superuser = role.is_superuser
|
|
144
|
+
user.save(update_fields=["is_superuser"])
|
|
145
|
+
break
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
role_handler = RoleHandler()
|
|
@@ -15,9 +15,9 @@ from requests.exceptions import HTTPError
|
|
|
15
15
|
|
|
16
16
|
from django_jwt import settings as jwt_settings
|
|
17
17
|
from django_jwt.config import config
|
|
18
|
-
from django_jwt.exceptions import ConfigException
|
|
18
|
+
from django_jwt.exceptions import BadRequestException, ConfigException
|
|
19
19
|
from django_jwt.pkce import PKCESecret
|
|
20
|
-
from django_jwt.user import UserHandler
|
|
20
|
+
from django_jwt.user import UserHandler, role_handler
|
|
21
21
|
from django_jwt.utils import get_access_token, oidc_handler
|
|
22
22
|
|
|
23
23
|
log = getLogger(__name__)
|
|
@@ -36,6 +36,8 @@ class AbsView(View):
|
|
|
36
36
|
return HttpResponse(status=exc.response.status_code, content=exc.response.text)
|
|
37
37
|
except ConfigException as exc:
|
|
38
38
|
return HttpResponse(content=str(exc), status=500)
|
|
39
|
+
except BadRequestException as exc:
|
|
40
|
+
return HttpResponse(content=str(exc), status=400)
|
|
39
41
|
except Exception:
|
|
40
42
|
return redirect("start_oidc_auth")
|
|
41
43
|
|
|
@@ -61,6 +63,7 @@ class StartOIDCAuthView(AbsView):
|
|
|
61
63
|
"nonce": random_nonce,
|
|
62
64
|
}
|
|
63
65
|
cache.set(state, str(pkce_secret), timeout=600)
|
|
66
|
+
log.info(f"OIDC Admin login: {authorization_endpoint}?{urlencode(params)}")
|
|
64
67
|
return redirect(f"{authorization_endpoint}?{urlencode(params)}")
|
|
65
68
|
|
|
66
69
|
|
|
@@ -70,15 +73,18 @@ class ReceiveRedirectView(AbsView):
|
|
|
70
73
|
state = request.GET.get("state")
|
|
71
74
|
if not code or not state:
|
|
72
75
|
log.warning(f"No code or state in the request {request.GET}")
|
|
73
|
-
|
|
76
|
+
raise BadRequestException("No code or state in the request")
|
|
74
77
|
|
|
75
78
|
redirect_uri = request.build_absolute_uri(reverse("receive_redirect_view"))
|
|
76
79
|
if state := cache.get(state):
|
|
77
80
|
token = get_access_token(code, redirect_uri, state)
|
|
78
81
|
data = oidc_handler.decode_token(token)
|
|
79
82
|
user = UserHandler(data, request, token).get_user()
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
log.info(f"OIDC Admin login: {user}", extra={"data": data})
|
|
84
|
+
role_handler.apply(user, data)
|
|
85
|
+
if not user.is_staff:
|
|
86
|
+
raise BadRequestException("User is not staff")
|
|
87
|
+
login(request, user, backend=settings.DEFAULT_AUTHENTICATION_BACKEND)
|
|
88
|
+
return redirect("admin:index")
|
|
89
|
+
|
|
90
|
+
raise BadRequestException("No PKCE secret found in cache")
|
|
@@ -2,12 +2,14 @@ from http import HTTPStatus
|
|
|
2
2
|
from unittest.mock import Mock, patch
|
|
3
3
|
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
|
+
from django.contrib.auth.models import Group, Permission
|
|
5
6
|
from django.test import TestCase
|
|
6
7
|
from django.urls import reverse
|
|
7
8
|
from jwt.api_jwt import ExpiredSignatureError
|
|
8
9
|
|
|
9
10
|
from django_jwt import settings
|
|
10
11
|
from django_jwt.middleware import JWTAuthMiddleware
|
|
12
|
+
from django_jwt.user import ROLE, role_handler
|
|
11
13
|
|
|
12
14
|
access_token_payload = {
|
|
13
15
|
"sub": "12345",
|
|
@@ -102,13 +104,6 @@ class OIDCHandlerTest(TestCase):
|
|
|
102
104
|
# fields are updated if they are changed in KeyCloak
|
|
103
105
|
self.assertUserWithPayload()
|
|
104
106
|
|
|
105
|
-
# def test_roles(self, decode_token):
|
|
106
|
-
# """User has admin and staff roles"""
|
|
107
|
-
# decode_token.return_value["realm_access"]["roles"] = ["admin", "staff"]
|
|
108
|
-
# self.middleware.process_request(self.request)
|
|
109
|
-
# self.assertTrue(self.request.user.is_staff)
|
|
110
|
-
# self.assertTrue(self.request.user.is_superuser)
|
|
111
|
-
|
|
112
107
|
def test_profile_info(self, *_):
|
|
113
108
|
"""User has profile info"""
|
|
114
109
|
|
|
@@ -140,3 +135,47 @@ class OIDCHandlerTest(TestCase):
|
|
|
140
135
|
User.objects.create(kc_id="1234", first_name="", last_name="", username="")
|
|
141
136
|
self.middleware.process_request(self.request)
|
|
142
137
|
self.assertEqual(self.request.user.username, "on_update")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class RolesTest(TestCase):
|
|
141
|
+
def setUp(self) -> None:
|
|
142
|
+
self.user = User.objects.create(username="user")
|
|
143
|
+
settings.OIDC_ADMIN_ROLES = [
|
|
144
|
+
ROLE(
|
|
145
|
+
name="admin",
|
|
146
|
+
is_superuser=True,
|
|
147
|
+
),
|
|
148
|
+
ROLE(
|
|
149
|
+
name="staff",
|
|
150
|
+
groups=["staff group"],
|
|
151
|
+
permissions=["add_user", "change_user", "delete_user"],
|
|
152
|
+
),
|
|
153
|
+
]
|
|
154
|
+
self.group = Group.objects.create(name="staff group")
|
|
155
|
+
self.permission = Permission.objects.get(name="Can add user")
|
|
156
|
+
self.access_token = {"resource_access": {settings.OIDC_ADMIN_CLIENT_ID: {"roles": ["staff"]}}}
|
|
157
|
+
|
|
158
|
+
def test_staff_role(self):
|
|
159
|
+
self.access_token["resource_access"][settings.OIDC_ADMIN_CLIENT_ID]["roles"] = ["staff"]
|
|
160
|
+
role_handler.apply(self.user, self.access_token)
|
|
161
|
+
self.assertTrue(self.user.groups.filter(name="staff group").exists())
|
|
162
|
+
self.assertTrue(self.user.user_permissions.filter(codename="add_user").exists())
|
|
163
|
+
self.assertFalse(self.user.is_superuser)
|
|
164
|
+
|
|
165
|
+
def test_admin_role(self):
|
|
166
|
+
self.access_token["resource_access"][settings.OIDC_ADMIN_CLIENT_ID]["roles"] = ["admin"]
|
|
167
|
+
role_handler.apply(self.user, self.access_token)
|
|
168
|
+
self.assertTrue(self.user.is_superuser)
|
|
169
|
+
|
|
170
|
+
def test_apply_staff_then_admin_role(self):
|
|
171
|
+
self.access_token["resource_access"][settings.OIDC_ADMIN_CLIENT_ID]["roles"] = ["staff"]
|
|
172
|
+
role_handler.apply(self.user, self.access_token)
|
|
173
|
+
self.assertFalse(self.user.is_superuser)
|
|
174
|
+
self.assertTrue(self.user.groups.filter(name="staff group").exists())
|
|
175
|
+
self.assertTrue(self.user.user_permissions.filter(codename="add_user").exists())
|
|
176
|
+
|
|
177
|
+
self.access_token["resource_access"][settings.OIDC_ADMIN_CLIENT_ID]["roles"] = ["admin"]
|
|
178
|
+
role_handler.apply(self.user, self.access_token)
|
|
179
|
+
self.assertTrue(self.user.is_superuser)
|
|
180
|
+
self.assertTrue(self.user.groups.filter(name="staff group").exists())
|
|
181
|
+
self.assertTrue(self.user.user_permissions.filter(codename="add_user").exists())
|
dj-jwt-auth-1.2.0/README.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Django-JWT
|
|
2
|
-
|
|
3
|
-
This is a package to verify and validate JSON Web Tokens (JWT) in Django.
|
|
4
|
-
|
|
5
|
-
### Installation
|
|
6
|
-
1. Install the package using pip.
|
|
7
|
-
|
|
8
|
-
2. Add "django_jwt" to your INSTALLED_APPS setting like this::
|
|
9
|
-
```
|
|
10
|
-
INSTALLED_APPS = [
|
|
11
|
-
...
|
|
12
|
-
'django_jwt',
|
|
13
|
-
]
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
3. Add "django_jwt.middleware.JWTAuthMiddleware" to your MIDDLEWARE setting like this::
|
|
17
|
-
```
|
|
18
|
-
MIDDLEWARE = [
|
|
19
|
-
...
|
|
20
|
-
'django_jwt.middleware.JWTAuthMiddleware',
|
|
21
|
-
]
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Configuration:
|
|
25
|
-
Required variables:
|
|
26
|
-
- OIDC_CERTS_URL - certificate endpoint, like `https://keyCloak/realms/h/.well-known/openid-configuration`
|
|
27
|
-
|
|
28
|
-
Optional variables:
|
|
29
|
-
- OIDC_AUDIENCE - by default ["account", "broker"]
|
|
30
|
-
User retated variables:
|
|
31
|
-
- OIDC_USER_UPDATE - if True, user model will be updated from userinfo endpoint if MODIFIED date has changed, by default True
|
|
32
|
-
- OIDC_USER_MODIFIED_FIELD - user model field to store last modified date, by default `modified_timestamp`
|
|
33
|
-
- OIDC_TOKEN_MODIFIED_FIELD - access token field to store last modified date, by default `updated_at`
|
|
34
|
-
- OIDC_USER_UID - User model' unique identifier, by default `kc_id`
|
|
35
|
-
- OIDC_USER_MAPPING - mapping between JWT claims and user model fields, by default:
|
|
36
|
-
```
|
|
37
|
-
OIDC_USER_MAPPING = {
|
|
38
|
-
'first_name': 'first_name',
|
|
39
|
-
'last_name': 'last_name',
|
|
40
|
-
'username': 'username',
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
- OIDC_USER_DEFAULTS - default values for user model fields, by default:
|
|
44
|
-
```
|
|
45
|
-
OIDC_USER_DEFAULTS = {
|
|
46
|
-
'is_active': True,
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
- OIDC_USER_ON_CREATE and OIDC_USER_ON_UPDATE - functions to be called on user creation and update, by default:
|
|
51
|
-
```
|
|
52
|
-
OIDC_USER_ON_CREATE = None
|
|
53
|
-
OIDC_USER_ON_UPDATE = None
|
|
54
|
-
```
|
|
55
|
-
- OIDC_CONFIG_ROUTES - dict of 'algorithm': 'config_url', by default is empty. If filled will be used instead of OIDC_CERTS_URL
|
|
56
|
-
```
|
|
57
|
-
OIDC_CONFIG_ROUTES = {
|
|
58
|
-
'RS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
59
|
-
'HS256': 'https://keyCloak/realms/h/.well-known/openid-configuration',
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
These functions should accept two arguments: user and request.
|
|
63
|
-
|
|
64
|
-
### Testing:
|
|
65
|
-
Run command `python runtests.py` to run tests.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|