Skip to content

Commit 3e1e968

Browse files
committed
Make user fields optional in OAuth, set up tests
1 parent c0992b1 commit 3e1e968

24 files changed

+459
-856
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
matrix:
1111
python-version: ["3.11", "3.12"]
12-
package: ["plain", "plain-worker", "plain-flags", "plain-sessions", "plain-staff"]
12+
package: ["plain", "plain-worker", "plain-flags", "plain-sessions", "plain-staff", "plain-oauth"]
1313

1414
steps:
1515
- uses: actions/checkout@v3

plain-oauth/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ class ExampleOAuthProvider(OAuthProvider):
9191
response.raise_for_status()
9292
data = response.json()
9393
return OAuthUser(
94+
# The provider ID is required
9495
id=data["id"],
95-
username=data["username"],
96+
# And you can populate any of your User model fields with additional kwargs
9697
email=data["email"],
98+
username=data["username"],
9799
)
98100
```
99101

@@ -206,13 +208,11 @@ Hello {{ request.user }}!
206208
{% for connection in request.user.oauth_connections.all %}
207209
<li>
208210
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
209-
{% if connection.can_be_disconnected %}
210211
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
211212
{{ csrf_input }}
212213
<input type="hidden" name="provider_user_id" value="{{ connection.provider_user_id }}">
213214
<button type="submit">Disconnect</button>
214215
</form>
215-
{% endif %}
216216
</li>
217217
{% endfor %}
218218
</ul>

plain-oauth/plain/oauth/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ class ExampleOAuthProvider(OAuthProvider):
8989
response.raise_for_status()
9090
data = response.json()
9191
return OAuthUser(
92+
# The provider ID is required
9293
id=data["id"],
93-
username=data["username"],
94+
# And you can populate any of your User model fields with additional kwargs
9495
email=data["email"],
96+
username=data["username"],
9597
)
9698
```
9799

@@ -204,13 +206,11 @@ Hello {{ request.user }}!
204206
{% for connection in request.user.oauth_connections.all %}
205207
<li>
206208
{{ connection.provider_key }} [ID: {{ connection.provider_user_id }}]
207-
{% if connection.can_be_disconnected %}
208209
<form action="{% url 'oauth:disconnect' connection.provider_key %}" method="post">
209210
{{ csrf_input }}
210211
<input type="hidden" name="provider_user_id" value="{{ connection.provider_user_id }}">
211212
<button type="submit">Disconnect</button>
212213
</form>
213-
{% endif %}
214214
</li>
215215
{% endfor %}
216216
</ul>

plain-oauth/plain/oauth/models.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from plain import models
44
from plain.auth import get_user_model
5+
from plain.exceptions import ValidationError
56
from plain.models import transaction
67
from plain.models.db import IntegrityError, OperationalError, ProgrammingError
78
from plain.preflight import Error
@@ -14,7 +15,7 @@
1415
from .providers import OAuthToken, OAuthUser
1516

1617

17-
# django check for deploy that ensures all provider keys in db are also in settings?
18+
# TODO preflight check for deploy that ensures all provider keys in db are also in settings?
1819

1920

2021
class OAuthConnection(models.Model):
@@ -84,7 +85,7 @@ def refresh_token_expired(self) -> bool:
8485
)
8586

8687
@classmethod
87-
def get_or_createuser(
88+
def get_or_create_user(
8889
cls, *, provider_key: str, oauth_token: "OAuthToken", oauth_user: "OAuthUser"
8990
) -> "OAuthConnection":
9091
try:
@@ -101,12 +102,11 @@ def get_or_createuser(
101102
# that to be taken care of on the user model itself
102103
try:
103104
user = get_user_model()(
104-
username=oauth_user.username,
105-
email=oauth_user.email,
105+
**oauth_user.user_model_fields,
106106
)
107107
user.full_clean()
108108
user.save()
109-
except IntegrityError:
109+
except (IntegrityError, ValidationError):
110110
raise OAuthUserAlreadyExistsError()
111111

112112
return cls.connect(

plain-oauth/plain/oauth/providers.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ def __init__(
3333

3434

3535
class OAuthUser:
36-
def __init__(self, *, id: str, email: str, username: str = ""):
37-
self.id = id
38-
self.username = username
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
4039

4140
def __str__(self):
42-
return self.email
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)
4346

4447

4548
class OAuthProvider:
@@ -157,7 +160,7 @@ def handle_callback_request(self, *, request: HttpRequest) -> Response:
157160
)
158161
user = connection.user
159162
else:
160-
connection = OAuthConnection.get_or_createuser(
163+
connection = OAuthConnection.get_or_create_user(
161164
provider_key=self.provider_key,
162165
oauth_token=oauth_token,
163166
oauth_user=oauth_user,

0 commit comments

Comments
 (0)