Skip to content

Commit

Permalink
Adds the ability to define how to store a user (#1328)
Browse files Browse the repository at this point in the history
* Update oauth2_validators.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add docs & tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
1vank1n and pre-commit-ci[bot] committed May 13, 2024
1 parent 2ef14c5 commit a34be99
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 3 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Hasan Ramezani
Hiroki Kiyohara
Hossein Shakiba
Islam Kamel
Ivan Lukyanets
Jadiel Teófilo
Jens Timmerman
Jerome Leclanche
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* #1337 Gracefully handle expired or deleted refresh tokens, in `validate_user`.
* #1350 Support Python 3.12 and Django 5.0
* #1249 Add code_challenge_methods_supported property to auto discovery information, per [RFC 8414 section 2](https://www.rfc-editor.org/rfc/rfc8414.html#page-7)
* #1328 Adds the ability to define how to store a user profile


### Fixed
Expand Down
11 changes: 11 additions & 0 deletions docs/oidc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ In the docs below, it assumes that you have mounted the
the URLs accordingly.


Define where to store the profile
=================================

.. py:function:: OAuth2Validator.get_or_create_user_from_content(content)
An optional layer to define where to store the profile in ``UserModel`` or a separate model. For example ``UserOAuth``, where ``user = models.OneToOneField(UserModel)``.

The function is called after checking that the username is present in the content.

:return: An instance of the ``UserModel`` representing the user fetched or created.

ConnectDiscoveryInfoView
~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
15 changes: 12 additions & 3 deletions oauth2_provider/oauth2_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,17 @@ def validate_client_id(self, client_id, request, *args, **kwargs):
def get_default_redirect_uri(self, client_id, request, *args, **kwargs):
return request.client.default_redirect_uri

def get_or_create_user_from_content(self, content):
"""
An optional layer to define where to store the profile in `UserModel` or a separate model. For example `UserOAuth`, where `user = models.OneToOneField(UserModel)` .
The function is called after checking that username is in the content.
Returns an UserModel instance;
"""
user, _ = UserModel.objects.get_or_create(**{UserModel.USERNAME_FIELD: content["username"]})
return user

def _get_token_from_authentication_server(
self, token, introspection_url, introspection_token, introspection_credentials
):
Expand Down Expand Up @@ -383,9 +394,7 @@ def _get_token_from_authentication_server(

if "active" in content and content["active"] is True:
if "username" in content:
user, _created = UserModel.objects.get_or_create(
**{UserModel.USERNAME_FIELD: content["username"]}
)
user = self.get_or_create_user_from_content(content)
else:
user = None

Expand Down
8 changes: 8 additions & 0 deletions tests/test_oauth2_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ def test_save_bearer_token__with_new_token__calls_methods_to_create_access_and_r
assert create_access_token_mock.call_count == 1
assert create_refresh_token_mock.call_count == 1

def test_get_or_create_user_from_content(self):
content = {"username": "test_user"}
UserModel.objects.filter(username=content["username"]).delete()
user = self.validator.get_or_create_user_from_content(content)

self.assertIsNotNone(user)
self.assertEqual(content["username"], user.username)


class TestOAuth2ValidatorProvidesErrorData(TransactionTestCase):
"""These test cases check that the recommended error codes are returned
Expand Down

0 comments on commit a34be99

Please sign in to comment.