From 3fcc0448b344564a21c2c95f56958bc77ee35522 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Wed, 17 Oct 2018 09:59:43 +0100 Subject: [PATCH 01/10] document force migration sequence forr application model --- docs/advanced_topics.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/advanced_topics.rst b/docs/advanced_topics.rst index ea65cbe50..3fa1519b1 100644 --- a/docs/advanced_topics.rst +++ b/docs/advanced_topics.rst @@ -48,6 +48,15 @@ Be aware that, when you intend to swap the application model, you should create migration defining the swapped application model prior to setting OAUTH2_PROVIDER_APPLICATION_MODEL. You'll run into models.E022 in Core system checks if you don't get the order right. +You can force your migration providing the custom model to run in the right order by +adding:: + + run_before = [ + ('oauth2_provider', '0001_initial'), + ] + +to the migration class. + That's all, now Django OAuth Toolkit will use your model wherever an Application instance is needed. **Notice:** `OAUTH2_PROVIDER_APPLICATION_MODEL` is the only setting variable that is not namespaced, this From 8df25232d6a76e81229e8b1b5fbcc07a6358bc28 Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Thu, 13 Dec 2018 15:36:31 +0000 Subject: [PATCH 02/10] add tests for clear_expired --- tests/test_models.py | 61 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 45533d1a7..02966c75b 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,11 +1,14 @@ +from datetime import datetime as dt + +import pytest from django.contrib.auth import get_user_model -from django.core.exceptions import ValidationError +from django.core.exceptions import ImproperlyConfigured, ValidationError from django.test import TestCase from django.test.utils import override_settings from django.utils import timezone from oauth2_provider.models import ( - get_access_token_model, get_application_model, + clear_expired, get_access_token_model, get_application_model, get_grant_model, get_refresh_token_model ) from oauth2_provider.settings import oauth2_settings @@ -19,6 +22,7 @@ class TestModels(TestCase): + def setUp(self): self.user = UserModel.objects.create_user("test_user", "test@example.com", "123456") @@ -118,6 +122,7 @@ def test_scopes_property(self): OAUTH2_PROVIDER_GRANT_MODEL="tests.SampleGrant" ) class TestCustomModels(TestCase): + def setUp(self): self.user = UserModel.objects.create_user("test_user", "test@example.com", "123456") @@ -260,6 +265,7 @@ def test_expires_can_be_none(self): class TestAccessTokenModel(TestCase): + def setUp(self): self.user = UserModel.objects.create_user("test_user", "test@example.com", "123456") @@ -289,3 +295,54 @@ class TestRefreshTokenModel(TestCase): def test_str(self): refresh_token = RefreshToken(token="test_token") self.assertEqual("%s" % refresh_token, refresh_token.token) + + +class TestClearExpired(TestCase): + + def setUp(self): + self.user = UserModel.objects.create_user("test_user", "test@example.com", "123456") + """Insert two tokens on database.""" + AccessToken.objects.create( + id=1, + token='555', + expires=dt.now(), + scope=2, + application_id=3, + user_id=1, + created=dt.now(), + updated=dt.now(), + source_refresh_token_id='0') + AccessToken.objects.create( + id=2, + token='666', + expires=dt.now(), + scope=2, + application_id=3, + user_id=1, + created=dt.now(), + updated=dt.now(), + source_refresh_token_id='1') + + def test_clear_expired_tokens(self): + oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 60 + assert clear_expired() is None + + def test_clear_expired_tokens_incorect_timetype(self): + oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 'A' + with pytest.raises(ImproperlyConfigured) as excinfo: + clear_expired() + result = excinfo.value.__class__.__name__ + assert result == 'ImproperlyConfigured' + + def test_clear_expired_tokens_with_tokens(self): + + self.client.login(username="test_user", password="123456") + oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 1 + ttokens = AccessToken.objects.count() + expiredt = AccessToken.objects.filter(expires__lte=dt.now()).count() + assert ttokens == 2 + assert expiredt == 2 + clear_expired() + expiredt = AccessToken.objects.filter(expires__lte=dt.now()).count() + + assert expiredt == 0 From 85356b728b8cbaad525b5d19e5f460a74fd08ad1 Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Thu, 13 Dec 2018 15:47:23 +0000 Subject: [PATCH 03/10] add tests for clear_tokens --- tests/test_models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index 02966c75b..4ac682cd9 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -344,5 +344,4 @@ def test_clear_expired_tokens_with_tokens(self): assert expiredt == 2 clear_expired() expiredt = AccessToken.objects.filter(expires__lte=dt.now()).count() - assert expiredt == 0 From 5e33ab146c6c3de42e1a5c0c1421a2dbe30d8685 Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Thu, 13 Dec 2018 16:06:37 +0000 Subject: [PATCH 04/10] add tests for clear_expired --- tests/test_models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 4ac682cd9..739a95bd3 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -13,7 +13,6 @@ ) from oauth2_provider.settings import oauth2_settings - Application = get_application_model() Grant = get_grant_model() AccessToken = get_access_token_model() @@ -335,7 +334,6 @@ def test_clear_expired_tokens_incorect_timetype(self): assert result == 'ImproperlyConfigured' def test_clear_expired_tokens_with_tokens(self): - self.client.login(username="test_user", password="123456") oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 1 ttokens = AccessToken.objects.count() From 442ea14dbd77590e073967505ded8423706ab6da Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Thu, 13 Dec 2018 16:11:13 +0000 Subject: [PATCH 05/10] fix isort --- tests/test_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_models.py b/tests/test_models.py index 739a95bd3..c003c4e3a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -13,6 +13,7 @@ ) from oauth2_provider.settings import oauth2_settings + Application = get_application_model() Grant = get_grant_model() AccessToken = get_access_token_model() From 0e3cf14903917203bada81ab71740c0089709f46 Mon Sep 17 00:00:00 2001 From: Mike Hurt Date: Fri, 14 Dec 2018 09:47:42 +0000 Subject: [PATCH 06/10] Update tests/test_models.py Co-Authored-By: ecilveks --- tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index c003c4e3a..e6bc95774 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -301,7 +301,7 @@ class TestClearExpired(TestCase): def setUp(self): self.user = UserModel.objects.create_user("test_user", "test@example.com", "123456") - """Insert two tokens on database.""" + # Insert two tokens on database. AccessToken.objects.create( id=1, token='555', From f7c5bb11a87c7831399128fc3b209f08f050bbc8 Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Fri, 14 Dec 2018 09:54:30 +0000 Subject: [PATCH 07/10] CR changes --- tests/test_models.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index c003c4e3a..9e1c2c878 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -311,7 +311,8 @@ def setUp(self): user_id=1, created=dt.now(), updated=dt.now(), - source_refresh_token_id='0') + source_refresh_token_id='0', + ) AccessToken.objects.create( id=2, token='666', @@ -321,7 +322,8 @@ def setUp(self): user_id=1, created=dt.now(), updated=dt.now(), - source_refresh_token_id='1') + source_refresh_token_id='1', + ) def test_clear_expired_tokens(self): oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 60 @@ -336,7 +338,7 @@ def test_clear_expired_tokens_incorect_timetype(self): def test_clear_expired_tokens_with_tokens(self): self.client.login(username="test_user", password="123456") - oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 1 + oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 0 ttokens = AccessToken.objects.count() expiredt = AccessToken.objects.filter(expires__lte=dt.now()).count() assert ttokens == 2 From 4fcb78c86c41768cbe77c4701652fff5fd366ed3 Mon Sep 17 00:00:00 2001 From: armandsvijups Date: Mon, 17 Dec 2018 15:20:05 +0000 Subject: [PATCH 08/10] fix isort for upstream --- tests/test_models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 16ef0d33a..8adc3b099 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -304,25 +304,25 @@ def setUp(self): # Insert two tokens on database. AccessToken.objects.create( id=1, - token='555', + token="555", expires=dt.now(), scope=2, application_id=3, user_id=1, created=dt.now(), updated=dt.now(), - source_refresh_token_id='0', + source_refresh_token_id="0", ) AccessToken.objects.create( id=2, - token='666', + token="666", expires=dt.now(), scope=2, application_id=3, user_id=1, created=dt.now(), updated=dt.now(), - source_refresh_token_id='1', + source_refresh_token_id="1", ) def test_clear_expired_tokens(self): @@ -330,11 +330,11 @@ def test_clear_expired_tokens(self): assert clear_expired() is None def test_clear_expired_tokens_incorect_timetype(self): - oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = 'A' + oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS = "A" with pytest.raises(ImproperlyConfigured) as excinfo: clear_expired() result = excinfo.value.__class__.__name__ - assert result == 'ImproperlyConfigured' + assert result == "ImproperlyConfigured" def test_clear_expired_tokens_with_tokens(self): self.client.login(username="test_user", password="123456") From 88b9a46a53b90e5ce7aeb68a8b3981b7b854b2d9 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Wed, 23 Jan 2019 11:52:18 +0000 Subject: [PATCH 09/10] add logging about what is to be deleted --- oauth2_provider/models.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 6f7cc096e..98362ec69 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -1,5 +1,6 @@ from datetime import timedelta from urllib.parse import parse_qsl, urlparse +import logging from django.apps import apps from django.conf import settings @@ -14,6 +15,8 @@ from .settings import oauth2_settings from .validators import RedirectURIValidator, WildcardSet +logger = logging.getLogger(__name__) + class AbstractApplication(models.Model): """ @@ -436,7 +439,27 @@ def clear_expired(): with transaction.atomic(): if refresh_expire_at: - refresh_token_model.objects.filter(revoked__lt=refresh_expire_at).delete() - refresh_token_model.objects.filter(access_token__expires__lt=refresh_expire_at).delete() - access_token_model.objects.filter(refresh_token__isnull=True, expires__lt=now).delete() - grant_model.objects.filter(expires__lt=now).delete() + revoked = refresh_token_model.objects.filter( + revoked__lt=refresh_expire_at, + ) + expired = refresh_token_model.objects.filter( + access_token__expires__lt=refresh_expire_at, + ) + + logger.info('%s Revoked refresh tokens to be deleted', revoked.count()) + logger.info('%s Expired refresh tokens to be deleted', expired.count()) + + revoked.delete() + expired.delete() + + access_tokens = access_token_model.objects.filter( + refresh_token__isnull=True, + expires__lt=now + ) + grants = grant_model.objects.filter(expires__lt=now) + + logger.info('%s Expired access tokens to be deleted', access_tokens.count()) + logger.info('%s Expired grant tokens to be deleted', grants.count()) + + access_tokens.delete() + grants.delete() From f1e9bc288ee1a879a0828618824e07f27d8f3f84 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Wed, 23 Jan 2019 12:39:01 +0000 Subject: [PATCH 10/10] also log when no refresh tokens are to be deleted --- oauth2_provider/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 98362ec69..7d08f88fb 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -451,6 +451,9 @@ def clear_expired(): revoked.delete() expired.delete() + else: + logger.info('refresh_expire_at is %s. No refresh tokens deleted.', + refresh_expire_at) access_tokens = access_token_model.objects.filter( refresh_token__isnull=True,