plain.oauth 0.29.0__tar.gz → 0.29.2__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.29.0 → plain_oauth-0.29.2}/PKG-INFO +3 -3
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/CHANGELOG.md +21 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/README.md +2 -2
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/models.py +6 -6
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/providers.py +1 -1
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/views.py +5 -5
- {plain_oauth-0.29.0/tests/providers → plain_oauth-0.29.2/provider_examples}/bitbucket.py +10 -4
- {plain_oauth-0.29.0/tests/providers → plain_oauth-0.29.2/provider_examples}/github.py +10 -4
- {plain_oauth-0.29.0/tests/providers → plain_oauth-0.29.2/provider_examples}/gitlab.py +11 -4
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/pyproject.toml +1 -1
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/.gitignore +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/LICENSE +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/README.md +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/__init__.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/admin.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/config.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/default_settings.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/exceptions.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/migrations/0001_initial.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/migrations/__init__.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/templates/oauth/callback.html +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/plain/oauth/urls.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/provider_examples/__init__.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/settings.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/templates/base.html +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/templates/index.html +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/templates/login.html +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/urls.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/users/migrations/0001_initial.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/users/migrations/__init__.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/app/users/models.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/provider_tests/__init__.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/provider_tests/test_github.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/providers/__init__.py +0 -0
- {plain_oauth-0.29.0/provider_examples → plain_oauth-0.29.2/tests/providers}/bitbucket.py +0 -0
- {plain_oauth-0.29.0/provider_examples → plain_oauth-0.29.2/tests/providers}/github.py +0 -0
- {plain_oauth-0.29.0/provider_examples → plain_oauth-0.29.2/tests/providers}/gitlab.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/test_backends.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/test_checks.py +0 -0
- {plain_oauth-0.29.0 → plain_oauth-0.29.2}/tests/test_providers.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: plain.oauth
|
3
|
-
Version: 0.29.
|
3
|
+
Version: 0.29.2
|
4
4
|
Summary: Let users log in with OAuth providers.
|
5
5
|
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
6
6
|
License-Expression: BSD-3-Clause
|
@@ -196,11 +196,11 @@ Here's an very basic example:
|
|
196
196
|
{% extends "base.html" %}
|
197
197
|
|
198
198
|
{% block content %}
|
199
|
-
Hello {{
|
199
|
+
Hello {{ get_current_user() }}!
|
200
200
|
|
201
201
|
<h2>Existing connections</h2>
|
202
202
|
<ul>
|
203
|
-
{% for connection in
|
203
|
+
{% for connection in get_current_user().oauth_connections.all %}
|
204
204
|
<li>
|
205
205
|
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
|
206
206
|
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
|
@@ -1,5 +1,26 @@
|
|
1
1
|
# plain-oauth changelog
|
2
2
|
|
3
|
+
## [0.29.2](https://github.com/dropseed/plain/releases/plain-oauth@0.29.2) (2025-10-06)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- Added type annotations to improve IDE and type checker friendliness ([35fb8c4](https://github.com/dropseed/plain/commit/35fb8c4))
|
8
|
+
- Updated provider examples (Bitbucket, GitHub, GitLab) with proper type annotations ([50463b0](https://github.com/dropseed/plain/commit/50463b0))
|
9
|
+
|
10
|
+
### Upgrade instructions
|
11
|
+
|
12
|
+
- No changes required
|
13
|
+
|
14
|
+
## [0.29.1](https://github.com/dropseed/plain/releases/plain-oauth@0.29.1) (2025-10-02)
|
15
|
+
|
16
|
+
### What's changed
|
17
|
+
|
18
|
+
- Updated documentation examples to use `get_current_user()` instead of `request.user` ([f6278d9](https://github.com/dropseed/plain/commit/f6278d9bb4))
|
19
|
+
|
20
|
+
### Upgrade instructions
|
21
|
+
|
22
|
+
- No changes required
|
23
|
+
|
3
24
|
## [0.29.0](https://github.com/dropseed/plain/releases/plain-oauth@0.29.0) (2025-10-02)
|
4
25
|
|
5
26
|
### What's changed
|
@@ -182,11 +182,11 @@ Here's an very basic example:
|
|
182
182
|
{% extends "base.html" %}
|
183
183
|
|
184
184
|
{% block content %}
|
185
|
-
Hello {{
|
185
|
+
Hello {{ get_current_user() }}!
|
186
186
|
|
187
187
|
<h2>Existing connections</h2>
|
188
188
|
<ul>
|
189
|
-
{% for connection in
|
189
|
+
{% for connection in get_current_user().oauth_connections.all %}
|
190
190
|
<li>
|
191
191
|
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
|
192
192
|
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
1
|
+
from typing import TYPE_CHECKING, Any
|
2
2
|
|
3
3
|
from plain import models
|
4
4
|
from plain.auth import get_user_model
|
@@ -50,7 +50,7 @@ class OAuthConnection(models.Model):
|
|
50
50
|
]
|
51
51
|
ordering = ("provider_key",)
|
52
52
|
|
53
|
-
def __str__(self):
|
53
|
+
def __str__(self) -> str:
|
54
54
|
return f"{self.provider_key}[{self.user}:{self.provider_user_id}]"
|
55
55
|
|
56
56
|
def refresh_access_token(self) -> None:
|
@@ -69,13 +69,13 @@ class OAuthConnection(models.Model):
|
|
69
69
|
self.set_token_fields(refreshed_oauth_token)
|
70
70
|
self.save()
|
71
71
|
|
72
|
-
def set_token_fields(self, oauth_token: "OAuthToken"):
|
72
|
+
def set_token_fields(self, oauth_token: "OAuthToken") -> None:
|
73
73
|
self.access_token = oauth_token.access_token
|
74
74
|
self.refresh_token = oauth_token.refresh_token
|
75
75
|
self.access_token_expires_at = oauth_token.access_token_expires_at
|
76
76
|
self.refresh_token_expires_at = oauth_token.refresh_token_expires_at
|
77
77
|
|
78
|
-
def set_user_fields(self, oauth_user: "OAuthUser"):
|
78
|
+
def set_user_fields(self, oauth_user: "OAuthUser") -> None:
|
79
79
|
self.provider_user_id = oauth_user.provider_id
|
80
80
|
|
81
81
|
def access_token_expired(self) -> bool:
|
@@ -125,7 +125,7 @@ class OAuthConnection(models.Model):
|
|
125
125
|
def connect(
|
126
126
|
cls,
|
127
127
|
*,
|
128
|
-
user,
|
128
|
+
user: Any,
|
129
129
|
provider_key: str,
|
130
130
|
oauth_token: "OAuthToken",
|
131
131
|
oauth_user: "OAuthUser",
|
@@ -155,7 +155,7 @@ class OAuthConnection(models.Model):
|
|
155
155
|
return connection
|
156
156
|
|
157
157
|
@classmethod
|
158
|
-
def preflight(cls):
|
158
|
+
def preflight(cls) -> list[PreflightResult]:
|
159
159
|
"""
|
160
160
|
A system check for ensuring that provider_keys in the database are also present in settings.
|
161
161
|
"""
|
@@ -40,7 +40,7 @@ class OAuthUser:
|
|
40
40
|
self.provider_id = provider_id # ID on the provider's system
|
41
41
|
self.user_model_fields = user_model_fields or {}
|
42
42
|
|
43
|
-
def __str__(self):
|
43
|
+
def __str__(self) -> str:
|
44
44
|
if "email" in self.user_model_fields:
|
45
45
|
return self.user_model_fields["email"]
|
46
46
|
if "username" in self.user_model_fields:
|
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
|
3
3
|
from plain.auth.requests import get_request_user
|
4
4
|
from plain.auth.views import AuthViewMixin
|
5
|
-
from plain.http import ResponseRedirect
|
5
|
+
from plain.http import Response, ResponseRedirect
|
6
6
|
from plain.views import TemplateView, View
|
7
7
|
|
8
8
|
from .exceptions import (
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
14
14
|
|
15
15
|
|
16
16
|
class OAuthLoginView(View):
|
17
|
-
def post(self):
|
17
|
+
def post(self) -> Response:
|
18
18
|
request = self.request
|
19
19
|
provider = self.url_kwargs["provider"]
|
20
20
|
if get_request_user(request):
|
@@ -31,7 +31,7 @@ class OAuthCallbackView(TemplateView):
|
|
31
31
|
|
32
32
|
template_name = "oauth/callback.html"
|
33
33
|
|
34
|
-
def get(self):
|
34
|
+
def get(self) -> Response:
|
35
35
|
provider = self.url_kwargs["provider"]
|
36
36
|
provider_instance = get_oauth_provider_instance(provider_key=provider)
|
37
37
|
try:
|
@@ -51,7 +51,7 @@ class OAuthCallbackView(TemplateView):
|
|
51
51
|
|
52
52
|
|
53
53
|
class OAuthConnectView(AuthViewMixin, View):
|
54
|
-
def post(self):
|
54
|
+
def post(self) -> Response:
|
55
55
|
request = self.request
|
56
56
|
provider = self.url_kwargs["provider"]
|
57
57
|
provider_instance = get_oauth_provider_instance(provider_key=provider)
|
@@ -59,7 +59,7 @@ class OAuthConnectView(AuthViewMixin, View):
|
|
59
59
|
|
60
60
|
|
61
61
|
class OAuthDisconnectView(AuthViewMixin, View):
|
62
|
-
def post(self):
|
62
|
+
def post(self) -> Response:
|
63
63
|
request = self.request
|
64
64
|
provider = self.url_kwargs["provider"]
|
65
65
|
provider_instance = get_oauth_provider_instance(provider_key=provider)
|
@@ -1,15 +1,21 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import datetime
|
4
|
+
from typing import TYPE_CHECKING, Any
|
2
5
|
|
3
6
|
import requests
|
4
7
|
|
5
8
|
from plain.oauth.providers import OAuthProvider, OAuthToken, OAuthUser
|
6
9
|
from plain.utils import timezone
|
7
10
|
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from plain.http import Request
|
13
|
+
|
8
14
|
|
9
15
|
class BitbucketOAuthProvider(OAuthProvider):
|
10
16
|
authorization_url = "https://bitbucket.org/site/oauth2/authorize"
|
11
17
|
|
12
|
-
def _get_token(self, request_data):
|
18
|
+
def _get_token(self, request_data: dict[str, Any]) -> OAuthToken:
|
13
19
|
response = requests.post(
|
14
20
|
"https://bitbucket.org/site/oauth2/access_token",
|
15
21
|
auth=(self.get_client_id(), self.get_client_secret()),
|
@@ -27,7 +33,7 @@ class BitbucketOAuthProvider(OAuthProvider):
|
|
27
33
|
+ datetime.timedelta(seconds=data["expires_in"]),
|
28
34
|
)
|
29
35
|
|
30
|
-
def get_oauth_token(self, *, code, request):
|
36
|
+
def get_oauth_token(self, *, code: str, request: Request) -> OAuthToken:
|
31
37
|
return self._get_token(
|
32
38
|
{
|
33
39
|
"grant_type": "authorization_code",
|
@@ -36,7 +42,7 @@ class BitbucketOAuthProvider(OAuthProvider):
|
|
36
42
|
}
|
37
43
|
)
|
38
44
|
|
39
|
-
def refresh_oauth_token(self, *, oauth_token):
|
45
|
+
def refresh_oauth_token(self, *, oauth_token: OAuthToken) -> OAuthToken:
|
40
46
|
return self._get_token(
|
41
47
|
{
|
42
48
|
"grant_type": "refresh_token",
|
@@ -44,7 +50,7 @@ class BitbucketOAuthProvider(OAuthProvider):
|
|
44
50
|
}
|
45
51
|
)
|
46
52
|
|
47
|
-
def get_oauth_user(self, *, oauth_token):
|
53
|
+
def get_oauth_user(self, *, oauth_token: OAuthToken) -> OAuthUser:
|
48
54
|
response = requests.get(
|
49
55
|
"https://api.bitbucket.org/2.0/user",
|
50
56
|
headers={
|
@@ -1,4 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import datetime
|
4
|
+
from typing import TYPE_CHECKING, Any
|
2
5
|
|
3
6
|
import requests
|
4
7
|
|
@@ -6,6 +9,9 @@ from plain.oauth.exceptions import OAuthError
|
|
6
9
|
from plain.oauth.providers import OAuthProvider, OAuthToken, OAuthUser
|
7
10
|
from plain.utils import timezone
|
8
11
|
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from plain.http import Request
|
14
|
+
|
9
15
|
|
10
16
|
class GitHubOAuthProvider(OAuthProvider):
|
11
17
|
authorization_url = "https://github.com/login/oauth/authorize"
|
@@ -14,7 +20,7 @@ class GitHubOAuthProvider(OAuthProvider):
|
|
14
20
|
github_user_url = "https://api.github.com/user"
|
15
21
|
github_emails_url = "https://api.github.com/user/emails"
|
16
22
|
|
17
|
-
def _get_token(self, request_data):
|
23
|
+
def _get_token(self, request_data: dict[str, Any]) -> OAuthToken:
|
18
24
|
response = requests.post(
|
19
25
|
self.github_token_url,
|
20
26
|
headers={
|
@@ -45,7 +51,7 @@ class GitHubOAuthProvider(OAuthProvider):
|
|
45
51
|
|
46
52
|
return oauth_token
|
47
53
|
|
48
|
-
def get_oauth_token(self, *, code, request):
|
54
|
+
def get_oauth_token(self, *, code: str, request: Request) -> OAuthToken:
|
49
55
|
return self._get_token(
|
50
56
|
{
|
51
57
|
"client_id": self.get_client_id(),
|
@@ -54,7 +60,7 @@ class GitHubOAuthProvider(OAuthProvider):
|
|
54
60
|
}
|
55
61
|
)
|
56
62
|
|
57
|
-
def refresh_oauth_token(self, *, oauth_token):
|
63
|
+
def refresh_oauth_token(self, *, oauth_token: OAuthToken) -> OAuthToken:
|
58
64
|
return self._get_token(
|
59
65
|
{
|
60
66
|
"client_id": self.get_client_id(),
|
@@ -64,7 +70,7 @@ class GitHubOAuthProvider(OAuthProvider):
|
|
64
70
|
}
|
65
71
|
)
|
66
72
|
|
67
|
-
def get_oauth_user(self, *, oauth_token):
|
73
|
+
def get_oauth_user(self, *, oauth_token: OAuthToken) -> OAuthUser:
|
68
74
|
response = requests.get(
|
69
75
|
self.github_user_url,
|
70
76
|
headers={
|
@@ -1,12 +1,19 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Any
|
4
|
+
|
1
5
|
import requests
|
2
6
|
|
3
7
|
from plain.oauth.providers import OAuthProvider, OAuthToken, OAuthUser
|
4
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from plain.http import Request
|
11
|
+
|
5
12
|
|
6
13
|
class GitLabOAuthProvider(OAuthProvider):
|
7
14
|
authorization_url = "https://gitlab.com/oauth/authorize"
|
8
15
|
|
9
|
-
def _get_token(self, request_data):
|
16
|
+
def _get_token(self, request_data: dict[str, Any]) -> OAuthToken:
|
10
17
|
request_data["client_id"] = self.get_client_id()
|
11
18
|
request_data["client_secret"] = self.get_client_secret()
|
12
19
|
response = requests.post(
|
@@ -24,7 +31,7 @@ class GitLabOAuthProvider(OAuthProvider):
|
|
24
31
|
# expires_in is missing in response?
|
25
32
|
)
|
26
33
|
|
27
|
-
def get_oauth_token(self, *, code, request):
|
34
|
+
def get_oauth_token(self, *, code: str, request: Request) -> OAuthToken:
|
28
35
|
return self._get_token(
|
29
36
|
{
|
30
37
|
"grant_type": "authorization_code",
|
@@ -33,7 +40,7 @@ class GitLabOAuthProvider(OAuthProvider):
|
|
33
40
|
}
|
34
41
|
)
|
35
42
|
|
36
|
-
def refresh_oauth_token(self, *, oauth_token):
|
43
|
+
def refresh_oauth_token(self, *, oauth_token: OAuthToken) -> OAuthToken:
|
37
44
|
return self._get_token(
|
38
45
|
{
|
39
46
|
"grant_type": "refresh_token",
|
@@ -41,7 +48,7 @@ class GitLabOAuthProvider(OAuthProvider):
|
|
41
48
|
}
|
42
49
|
)
|
43
50
|
|
44
|
-
def get_oauth_user(self, *, oauth_token):
|
51
|
+
def get_oauth_user(self, *, oauth_token: OAuthToken) -> OAuthUser:
|
45
52
|
response = requests.get(
|
46
53
|
"https://gitlab.com/api/v4/user",
|
47
54
|
headers={
|
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
|