plain.oauth 0.26.0__tar.gz → 0.28.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.
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/PKG-INFO +1 -1
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/CHANGELOG.md +24 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/models.py +6 -13
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/providers.py +13 -13
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/pyproject.toml +1 -1
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/test_checks.py +4 -4
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/.gitignore +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/LICENSE +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/README.md +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/README.md +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/admin.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/config.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/default_settings.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/exceptions.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/migrations/0001_initial.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/migrations/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/templates/oauth/callback.html +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/urls.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/plain/oauth/views.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/provider_examples/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/provider_examples/bitbucket.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/provider_examples/github.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/provider_examples/gitlab.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/settings.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/templates/base.html +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/templates/index.html +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/templates/login.html +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/urls.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/users/migrations/0001_initial.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/users/migrations/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/app/users/models.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/provider_tests/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/provider_tests/test_github.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/providers/__init__.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/providers/bitbucket.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/providers/github.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/providers/gitlab.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/test_backends.py +0 -0
- {plain_oauth-0.26.0 → plain_oauth-0.28.0}/tests/test_providers.py +0 -0
@@ -1,5 +1,29 @@
|
|
1
1
|
# plain-oauth changelog
|
2
2
|
|
3
|
+
## [0.28.0](https://github.com/dropseed/plain/releases/plain-oauth@0.28.0) (2025-09-30)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- `HttpRequest` has been renamed to `Request` throughout the OAuth provider classes ([cd46ff2](https://github.com/dropseed/plain/commit/cd46ff2003))
|
8
|
+
|
9
|
+
### Upgrade instructions
|
10
|
+
|
11
|
+
- If you have custom OAuth providers that override methods like `get_authorization_url_params`, `get_oauth_token`, `get_callback_url`, or any other methods that accept a request parameter, update the type hint from `HttpRequest` to `Request`
|
12
|
+
- Update any imports of `HttpRequest` in custom OAuth provider code to import `Request` instead from `plain.http`
|
13
|
+
|
14
|
+
## [0.27.0](https://github.com/dropseed/plain/releases/plain-oauth@0.27.0) (2025-09-25)
|
15
|
+
|
16
|
+
### What's changed
|
17
|
+
|
18
|
+
- The `OAuthConnection.check()` method has been replaced with `OAuthConnection.preflight()` as part of the new preflight system ([b0b610d](https://github.com/dropseed/plain/commit/b0b610d461))
|
19
|
+
- Preflight check IDs have been renamed from numeric format (e.g., `plain.oauth.E001`) to descriptive names (e.g., `oauth.provider_in_db_not_in_settings`) ([cd96c97](https://github.com/dropseed/plain/commit/cd96c97b25))
|
20
|
+
- Preflight messages now provide clearer fix instructions directly in the `fix` attribute ([c7cde12](https://github.com/dropseed/plain/commit/c7cde12149))
|
21
|
+
|
22
|
+
### Upgrade instructions
|
23
|
+
|
24
|
+
- If you have custom code that calls `OAuthConnection.check()`, update it to use `OAuthConnection.preflight()` instead
|
25
|
+
- If you have code that references specific preflight check IDs (e.g., `plain.oauth.E001`), update them to use the new descriptive format (e.g., `oauth.provider_in_db_not_in_settings`)
|
26
|
+
|
3
27
|
## [0.26.0](https://github.com/dropseed/plain/releases/plain-oauth@0.26.0) (2025-09-12)
|
4
28
|
|
5
29
|
### What's changed
|
@@ -5,7 +5,7 @@ from plain.auth import get_user_model
|
|
5
5
|
from plain.exceptions import ValidationError
|
6
6
|
from plain.models import transaction
|
7
7
|
from plain.models.db import IntegrityError, OperationalError, ProgrammingError
|
8
|
-
from plain.preflight import
|
8
|
+
from plain.preflight import PreflightResult
|
9
9
|
from plain.runtime import SettingsReference
|
10
10
|
from plain.utils import timezone
|
11
11
|
|
@@ -155,18 +155,11 @@ class OAuthConnection(models.Model):
|
|
155
155
|
return connection
|
156
156
|
|
157
157
|
@classmethod
|
158
|
-
def
|
158
|
+
def preflight(cls):
|
159
159
|
"""
|
160
160
|
A system check for ensuring that provider_keys in the database are also present in settings.
|
161
|
-
|
162
|
-
Note that the --database flag is required for this to work:
|
163
|
-
plain check --database default
|
164
161
|
"""
|
165
|
-
errors = super().
|
166
|
-
|
167
|
-
database = kwargs.get("database", False)
|
168
|
-
if not database:
|
169
|
-
return errors
|
162
|
+
errors = super().preflight()
|
170
163
|
|
171
164
|
from .providers import get_provider_keys
|
172
165
|
|
@@ -183,11 +176,11 @@ class OAuthConnection(models.Model):
|
|
183
176
|
|
184
177
|
if keys_in_db - keys_in_settings:
|
185
178
|
errors.append(
|
186
|
-
|
187
|
-
"The following OAuth providers are in the database but not in the settings: {}".format(
|
179
|
+
PreflightResult(
|
180
|
+
fix="The following OAuth providers are in the database but not in the settings: {}. Add these providers to your OAUTH_LOGIN_PROVIDERS setting or remove the corresponding OAuthConnection records.".format(
|
188
181
|
", ".join(keys_in_db - keys_in_settings)
|
189
182
|
),
|
190
|
-
id="
|
183
|
+
id="oauth.provider_in_db_not_in_settings",
|
191
184
|
)
|
192
185
|
)
|
193
186
|
|
@@ -4,7 +4,7 @@ from typing import Any
|
|
4
4
|
from urllib.parse import urlencode
|
5
5
|
|
6
6
|
from plain.auth import login as auth_login
|
7
|
-
from plain.http import
|
7
|
+
from plain.http import Request, Response, ResponseRedirect
|
8
8
|
from plain.runtime import settings
|
9
9
|
from plain.urls import reverse
|
10
10
|
from plain.utils.cache import add_never_cache_headers
|
@@ -65,7 +65,7 @@ class OAuthProvider:
|
|
65
65
|
self.client_secret = client_secret
|
66
66
|
self.scope = scope
|
67
67
|
|
68
|
-
def get_authorization_url_params(self, *, request:
|
68
|
+
def get_authorization_url_params(self, *, request: Request) -> dict:
|
69
69
|
return {
|
70
70
|
"redirect_uri": self.get_callback_url(request=request),
|
71
71
|
"client_id": self.get_client_id(),
|
@@ -77,13 +77,13 @@ class OAuthProvider:
|
|
77
77
|
def refresh_oauth_token(self, *, oauth_token: OAuthToken) -> OAuthToken:
|
78
78
|
raise NotImplementedError()
|
79
79
|
|
80
|
-
def get_oauth_token(self, *, code: str, request:
|
80
|
+
def get_oauth_token(self, *, code: str, request: Request) -> OAuthToken:
|
81
81
|
raise NotImplementedError()
|
82
82
|
|
83
83
|
def get_oauth_user(self, *, oauth_token: OAuthToken) -> OAuthUser:
|
84
84
|
raise NotImplementedError()
|
85
85
|
|
86
|
-
def get_authorization_url(self, *, request:
|
86
|
+
def get_authorization_url(self, *, request: Request) -> str:
|
87
87
|
return self.authorization_url
|
88
88
|
|
89
89
|
def get_client_id(self) -> str:
|
@@ -95,14 +95,14 @@ class OAuthProvider:
|
|
95
95
|
def get_scope(self) -> str:
|
96
96
|
return self.scope
|
97
97
|
|
98
|
-
def get_callback_url(self, *, request:
|
98
|
+
def get_callback_url(self, *, request: Request) -> str:
|
99
99
|
url = reverse("oauth:callback", provider=self.provider_key)
|
100
100
|
return request.build_absolute_uri(url)
|
101
101
|
|
102
102
|
def generate_state(self) -> str:
|
103
103
|
return get_random_string(length=32)
|
104
104
|
|
105
|
-
def check_request_state(self, *, request:
|
105
|
+
def check_request_state(self, *, request: Request) -> None:
|
106
106
|
if error := request.query_params.get("error"):
|
107
107
|
raise OAuthError(error)
|
108
108
|
|
@@ -117,7 +117,7 @@ class OAuthProvider:
|
|
117
117
|
raise OAuthStateMismatchError()
|
118
118
|
|
119
119
|
def handle_login_request(
|
120
|
-
self, *, request:
|
120
|
+
self, *, request: Request, redirect_to: str = ""
|
121
121
|
) -> Response:
|
122
122
|
authorization_url = self.get_authorization_url(request=request)
|
123
123
|
authorization_params = self.get_authorization_url_params(request=request)
|
@@ -138,11 +138,11 @@ class OAuthProvider:
|
|
138
138
|
return self.get_redirect_response(redirect_url)
|
139
139
|
|
140
140
|
def handle_connect_request(
|
141
|
-
self, *, request:
|
141
|
+
self, *, request: Request, redirect_to: str = ""
|
142
142
|
) -> Response:
|
143
143
|
return self.handle_login_request(request=request, redirect_to=redirect_to)
|
144
144
|
|
145
|
-
def handle_disconnect_request(self, *, request:
|
145
|
+
def handle_disconnect_request(self, *, request: Request) -> Response:
|
146
146
|
provider_user_id = request.data["provider_user_id"]
|
147
147
|
connection = OAuthConnection.query.get(
|
148
148
|
provider_key=self.provider_key, provider_user_id=provider_user_id
|
@@ -151,7 +151,7 @@ class OAuthProvider:
|
|
151
151
|
redirect_url = self.get_disconnect_redirect_url(request=request)
|
152
152
|
return self.get_redirect_response(redirect_url)
|
153
153
|
|
154
|
-
def handle_callback_request(self, *, request:
|
154
|
+
def handle_callback_request(self, *, request: Request) -> Response:
|
155
155
|
self.check_request_state(request=request)
|
156
156
|
|
157
157
|
oauth_token = self.get_oauth_token(
|
@@ -181,13 +181,13 @@ class OAuthProvider:
|
|
181
181
|
redirect_url = self.get_login_redirect_url(request=request)
|
182
182
|
return self.get_redirect_response(redirect_url)
|
183
183
|
|
184
|
-
def login(self, *, request:
|
184
|
+
def login(self, *, request: Request, user: Any) -> None:
|
185
185
|
auth_login(request=request, user=user)
|
186
186
|
|
187
|
-
def get_login_redirect_url(self, *, request:
|
187
|
+
def get_login_redirect_url(self, *, request: Request) -> str:
|
188
188
|
return request.session.pop(SESSION_NEXT_KEY, "/")
|
189
189
|
|
190
|
-
def get_disconnect_redirect_url(self, *, request:
|
190
|
+
def get_disconnect_redirect_url(self, *, request: Request) -> str:
|
191
191
|
return request.data.get("next", "/")
|
192
192
|
|
193
193
|
def get_redirect_response(self, redirect_url: str) -> Response:
|
@@ -23,7 +23,7 @@ def test_oauth_provider_keys_check_pass(db, settings):
|
|
23
23
|
access_token="test",
|
24
24
|
)
|
25
25
|
|
26
|
-
errors = OAuthConnection.
|
26
|
+
errors = OAuthConnection.preflight()
|
27
27
|
assert len(errors) == 0
|
28
28
|
|
29
29
|
|
@@ -54,9 +54,9 @@ def test_oauth_provider_keys_check_fail(db, settings):
|
|
54
54
|
access_token="test",
|
55
55
|
)
|
56
56
|
|
57
|
-
errors = OAuthConnection.
|
57
|
+
errors = OAuthConnection.preflight()
|
58
58
|
assert len(errors) == 1
|
59
59
|
assert (
|
60
|
-
errors[0].
|
61
|
-
== "The following OAuth providers are in the database but not in the settings: bar"
|
60
|
+
errors[0].fix
|
61
|
+
== "The following OAuth providers are in the database but not in the settings: bar. Add these providers to your OAUTH_LOGIN_PROVIDERS setting or remove the corresponding OAuthConnection records."
|
62
62
|
)
|
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
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|