plain.oauth 0.2.0__tar.gz → 0.4.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.2.0 → plain_oauth-0.4.0}/PKG-INFO +8 -4
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/README.md +3 -3
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/README.md +3 -3
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/migrations/0001_initial.py +0 -1
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/models.py +5 -5
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/providers.py +9 -6
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/views.py +4 -4
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/pyproject.toml +12 -11
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/LICENSE +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/__init__.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/config.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/exceptions.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/migrations/0002_alter_oauthconnection_options_and_more.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/migrations/0003_alter_oauthconnection_access_token_and_more.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/migrations/0004_alter_oauthconnection_access_token_and_more.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/migrations/__init__.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/staff.py +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/templates/oauth/error.html +0 -0
- {plain_oauth-0.2.0 → plain_oauth-0.4.0}/plain/oauth/urls.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: plain.oauth
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: OAuth login and API access for Plain.
|
5
5
|
Home-page: https://plainframework.com
|
6
6
|
License: BSD-3-Clause
|
@@ -11,6 +11,10 @@ Classifier: License :: OSI Approved :: BSD License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
|
+
Requires-Dist: plain (<1.0.0)
|
15
|
+
Requires-Dist: plain.auth (<1.0.0)
|
16
|
+
Requires-Dist: plain.models (<1.0.0)
|
17
|
+
Requires-Dist: requests
|
14
18
|
Project-URL: Documentation, https://plainframework.com/docs/
|
15
19
|
Project-URL: Repository, https://github.com/dropseed/plain
|
16
20
|
Description-Content-Type: text/markdown
|
@@ -108,9 +112,11 @@ class ExampleOAuthProvider(OAuthProvider):
|
|
108
112
|
response.raise_for_status()
|
109
113
|
data = response.json()
|
110
114
|
return OAuthUser(
|
115
|
+
# The provider ID is required
|
111
116
|
id=data["id"],
|
112
|
-
|
117
|
+
# And you can populate any of your User model fields with additional kwargs
|
113
118
|
email=data["email"],
|
119
|
+
username=data["username"],
|
114
120
|
)
|
115
121
|
```
|
116
122
|
|
@@ -223,13 +229,11 @@ Hello {{ request.user }}!
|
|
223
229
|
{% for connection in request.user.oauth_connections.all %}
|
224
230
|
<li>
|
225
231
|
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
|
226
|
-
{% if connection.can_be_disconnected %}
|
227
232
|
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
|
228
233
|
{{ csrf_input }}
|
229
234
|
<input type="hidden" name="provider_user_id" value="{{ connection.provider_user_id }}">
|
230
235
|
<button type="submit">Disconnect</button>
|
231
236
|
</form>
|
232
|
-
{% endif %}
|
233
237
|
</li>
|
234
238
|
{% endfor %}
|
235
239
|
</ul>
|
@@ -91,9 +91,11 @@ class ExampleOAuthProvider(OAuthProvider):
|
|
91
91
|
response.raise_for_status()
|
92
92
|
data = response.json()
|
93
93
|
return OAuthUser(
|
94
|
+
# The provider ID is required
|
94
95
|
id=data["id"],
|
95
|
-
|
96
|
+
# And you can populate any of your User model fields with additional kwargs
|
96
97
|
email=data["email"],
|
98
|
+
username=data["username"],
|
97
99
|
)
|
98
100
|
```
|
99
101
|
|
@@ -206,13 +208,11 @@ Hello {{ request.user }}!
|
|
206
208
|
{% for connection in request.user.oauth_connections.all %}
|
207
209
|
<li>
|
208
210
|
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
|
209
|
-
{% if connection.can_be_disconnected %}
|
210
211
|
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
|
211
212
|
{{ csrf_input }}
|
212
213
|
<input type="hidden" name="provider_user_id" value="{{ connection.provider_user_id }}">
|
213
214
|
<button type="submit">Disconnect</button>
|
214
215
|
</form>
|
215
|
-
{% endif %}
|
216
216
|
</li>
|
217
217
|
{% endfor %}
|
218
218
|
</ul>
|
@@ -89,9 +89,11 @@ class ExampleOAuthProvider(OAuthProvider):
|
|
89
89
|
response.raise_for_status()
|
90
90
|
data = response.json()
|
91
91
|
return OAuthUser(
|
92
|
+
# The provider ID is required
|
92
93
|
id=data["id"],
|
93
|
-
|
94
|
+
# And you can populate any of your User model fields with additional kwargs
|
94
95
|
email=data["email"],
|
96
|
+
username=data["username"],
|
95
97
|
)
|
96
98
|
```
|
97
99
|
|
@@ -204,13 +206,11 @@ Hello {{ request.user }}!
|
|
204
206
|
{% for connection in request.user.oauth_connections.all %}
|
205
207
|
<li>
|
206
208
|
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
|
207
|
-
{% if connection.can_be_disconnected %}
|
208
209
|
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
|
209
210
|
{{ csrf_input }}
|
210
211
|
<input type="hidden" name="provider_user_id" value="{{ connection.provider_user_id }}">
|
211
212
|
<button type="submit">Disconnect</button>
|
212
213
|
</form>
|
213
|
-
{% endif %}
|
214
214
|
</li>
|
215
215
|
{% endfor %}
|
216
216
|
</ul>
|
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|
2
2
|
|
3
3
|
from plain import models
|
4
4
|
from plain.auth import get_user_model
|
5
|
+
from plain.exceptions import ValidationError
|
5
6
|
from plain.models import transaction
|
6
7
|
from plain.models.db import IntegrityError, OperationalError, ProgrammingError
|
7
8
|
from plain.preflight import Error
|
@@ -14,7 +15,7 @@ if TYPE_CHECKING:
|
|
14
15
|
from .providers import OAuthToken, OAuthUser
|
15
16
|
|
16
17
|
|
17
|
-
#
|
18
|
+
# TODO preflight check for deploy that ensures all provider keys in db are also in settings?
|
18
19
|
|
19
20
|
|
20
21
|
class OAuthConnection(models.Model):
|
@@ -84,7 +85,7 @@ class OAuthConnection(models.Model):
|
|
84
85
|
)
|
85
86
|
|
86
87
|
@classmethod
|
87
|
-
def
|
88
|
+
def get_or_create_user(
|
88
89
|
cls, *, provider_key: str, oauth_token: "OAuthToken", oauth_user: "OAuthUser"
|
89
90
|
) -> "OAuthConnection":
|
90
91
|
try:
|
@@ -101,12 +102,11 @@ class OAuthConnection(models.Model):
|
|
101
102
|
# that to be taken care of on the user model itself
|
102
103
|
try:
|
103
104
|
user = get_user_model()(
|
104
|
-
|
105
|
-
email=oauth_user.email,
|
105
|
+
**oauth_user.user_model_fields,
|
106
106
|
)
|
107
107
|
user.full_clean()
|
108
108
|
user.save()
|
109
|
-
except IntegrityError:
|
109
|
+
except (IntegrityError, ValidationError):
|
110
110
|
raise OAuthUserAlreadyExistsError()
|
111
111
|
|
112
112
|
return cls.connect(
|
@@ -33,13 +33,16 @@ class OAuthToken:
|
|
33
33
|
|
34
34
|
|
35
35
|
class OAuthUser:
|
36
|
-
def __init__(self, *, id: str,
|
37
|
-
self.id = id
|
38
|
-
self.
|
39
|
-
self.email = email
|
36
|
+
def __init__(self, *, id: str, **user_model_fields: dict):
|
37
|
+
self.id = id # ID on the provider's system
|
38
|
+
self.user_model_fields = user_model_fields
|
40
39
|
|
41
40
|
def __str__(self):
|
42
|
-
|
41
|
+
if "email" in self.user_model_fields:
|
42
|
+
return self.user_model_fields["email"]
|
43
|
+
if "username" in self.user_model_fields:
|
44
|
+
return self.user_model_fields["username"]
|
45
|
+
return str(self.id)
|
43
46
|
|
44
47
|
|
45
48
|
class OAuthProvider:
|
@@ -157,7 +160,7 @@ class OAuthProvider:
|
|
157
160
|
)
|
158
161
|
user = connection.user
|
159
162
|
else:
|
160
|
-
connection = OAuthConnection.
|
163
|
+
connection = OAuthConnection.get_or_create_user(
|
161
164
|
provider_key=self.provider_key,
|
162
165
|
oauth_token=oauth_token,
|
163
166
|
oauth_user=oauth_user,
|
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
|
3
3
|
from plain.auth.views import AuthViewMixin
|
4
4
|
from plain.http import ResponseBadRequest, ResponseRedirect
|
5
|
-
from plain.templates import
|
5
|
+
from plain.templates import Template
|
6
6
|
from plain.views import View
|
7
7
|
|
8
8
|
from .exceptions import (
|
@@ -38,7 +38,7 @@ class OAuthCallbackView(View):
|
|
38
38
|
try:
|
39
39
|
return provider_instance.handle_callback_request(request=request)
|
40
40
|
except OAuthUserAlreadyExistsError:
|
41
|
-
template =
|
41
|
+
template = Template("oauth/error.html")
|
42
42
|
return ResponseBadRequest(
|
43
43
|
template.render(
|
44
44
|
{
|
@@ -47,7 +47,7 @@ class OAuthCallbackView(View):
|
|
47
47
|
)
|
48
48
|
)
|
49
49
|
except OAuthStateMismatchError:
|
50
|
-
template =
|
50
|
+
template = Template("oauth/error.html")
|
51
51
|
return ResponseBadRequest(
|
52
52
|
template.render(
|
53
53
|
{
|
@@ -57,7 +57,7 @@ class OAuthCallbackView(View):
|
|
57
57
|
)
|
58
58
|
except OAuthError as e:
|
59
59
|
logger.exception("OAuth error")
|
60
|
-
template =
|
60
|
+
template = Template("oauth/error.html")
|
61
61
|
return ResponseBadRequest(template.render({"oauth_error": str(e)}))
|
62
62
|
|
63
63
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "plain.oauth"
|
3
|
-
version = "0.
|
3
|
+
version = "0.4.0"
|
4
4
|
description = "OAuth login and API access for Plain."
|
5
5
|
authors = ["Dave Gaeddert <dave.gaeddert@dropseed.dev>"]
|
6
6
|
license = "BSD-3-Clause"
|
@@ -19,17 +19,18 @@ FAIL_INVALID_TEMPLATE_VARS = true
|
|
19
19
|
|
20
20
|
[tool.poetry.dependencies]
|
21
21
|
python = "^3.11"
|
22
|
+
plain = "<1.0.0"
|
23
|
+
"plain.auth" = "<1.0.0"
|
24
|
+
"plain.models" = "<1.0.0"
|
25
|
+
requests = "*"
|
22
26
|
|
23
|
-
[tool.poetry.dev
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
mypy = "*"
|
31
|
-
isort = "^5.10.1"
|
32
|
-
python-dotenv = "^0.20.0"
|
27
|
+
[tool.poetry.group.dev.dependencies]
|
28
|
+
plain = {path = "../plain", develop = true}
|
29
|
+
"plain.auth" = {path = "../plain-auth", develop = true}
|
30
|
+
"plain.sessions" = {path = "../plain-sessions", develop = true}
|
31
|
+
"plain.models" = {path = "../plain-models", develop = true}
|
32
|
+
"plain.pytest" = {path = "../plain-pytest", develop = true}
|
33
|
+
coverage = "^7.6.0"
|
33
34
|
|
34
35
|
[build-system]
|
35
36
|
requires = ["poetry-core>=1.0.0"]
|
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
|