From e08d306f438c61320292c190b72c20b68a11ecaf Mon Sep 17 00:00:00 2001 From: Jae Date: Tue, 17 Nov 2020 14:40:51 -0800 Subject: [PATCH 01/14] add token expiration time fields to Application model --- oauth2_provider/models.py | 6 ++++++ oauth2_provider/settings.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 5676bc0c5..097fb7c5f 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -83,6 +83,12 @@ class AbstractApplication(models.Model): created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) + access_token_expire_seconds = models.IntegerField( + default=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS + ) + refresh_token_expire_seconds = models.IntegerField( + default=oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS + ) class Meta: abstract = True diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index 0135da8b7..bfff6f6b0 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -44,8 +44,8 @@ "READ_SCOPE": "read", "WRITE_SCOPE": "write", "AUTHORIZATION_CODE_EXPIRE_SECONDS": 60, - "ACCESS_TOKEN_EXPIRE_SECONDS": 36000, - "REFRESH_TOKEN_EXPIRE_SECONDS": None, + "ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds + "REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds "REFRESH_TOKEN_GRACE_PERIOD_SECONDS": 0, "ROTATE_REFRESH_TOKEN": True, "ERROR_RESPONSE_WITH_SCOPES": False, From ef937d227f527140240c94034f8c324dd939191c Mon Sep 17 00:00:00 2001 From: Jae Date: Thu, 19 Nov 2020 23:14:12 -0800 Subject: [PATCH 02/14] add new field to migration --- oauth2_provider/migrations/0001_initial.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oauth2_provider/migrations/0001_initial.py b/oauth2_provider/migrations/0001_initial.py index 1d1a38e0e..5663eb76d 100644 --- a/oauth2_provider/migrations/0001_initial.py +++ b/oauth2_provider/migrations/0001_initial.py @@ -36,6 +36,8 @@ class Migration(migrations.Migration): ('skip_authorization', models.BooleanField(default=False)), ('created', models.DateTimeField(auto_now_add=True)), ('updated', models.DateTimeField(auto_now=True)), + ('access_token_expire_seconds', models.IntegerField(default=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS)), + ('refresh_token_expire_seconds', models.IntegerField(default=oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS)), ], options={ 'abstract': False, From c35755a7ac737b713d8664aa839472b45130b72d Mon Sep 17 00:00:00 2001 From: Jae Date: Thu, 19 Nov 2020 23:14:51 -0800 Subject: [PATCH 03/14] check refresh token expires on validation --- oauth2_provider/oauth2_validators.py | 38 ++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 515353d6f..5bd82d567 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -475,11 +475,15 @@ def save_bearer_token(self, token, request, *args, **kwargs): if "scope" not in token: raise FatalClientError("Failed to renew access token: missing scope") + # "authenticate_client" sets the client (Application) on request + application = request.client + access_token_expire_seconds = application.access_token_expire_seconds + # expires_in is passed to Server on initialization # custom server class can have logic to override this - expires = timezone.now() + timedelta(seconds=token.get( - "expires_in", oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS, - )) + expires = ( + timezone.now() + timedelta(seconds=access_token_expire_seconds) + ) if request.grant_type == "client_credentials": request.user = None @@ -648,14 +652,38 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs ) ) rt = RefreshToken.objects.filter(null_or_recent, token=refresh_token).select_related( - "access_token" + "access_token", + "application", ).first() if not rt: return False + # Access and refresh token expiration is configurable by Application + # Determine refresh token expiration datetime by adding the timedelta + # of "refresh_token_expire_seconds" to the "created" datetime + expire_seconds = rt.application.refresh_token_expire_seconds + expires = rt.created + timedelta(seconds=expire_seconds) + + is_expired = timezone.now() >= expires + + # Revoke token if expired + if is_expired: + try: + rt.revoke() + # Catch exception in case access or refresh token do not exist + except (AccessToken.DoesNotExist, RefreshToken.DoesNotExist): + pass + request.user = rt.user request.refresh_token = rt.token # Temporary store RefreshToken instance to be reused by get_original_scopes and save_bearer_token. request.refresh_token_instance = rt - return rt.application == client + + # Token is valid if it refers to the right client AND is not expired + is_valid = ( + rt.application == client and + not is_expired + ) + + return is_valid From 5fb1e593e658cdfb654f36e9c1c0a3dbb37d4eb5 Mon Sep 17 00:00:00 2001 From: Jae Date: Fri, 20 Nov 2020 14:35:43 -0800 Subject: [PATCH 04/14] refactor: do not check user.is_active on user validation --- oauth2_provider/oauth2_validators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 5bd82d567..9262d4eae 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -626,7 +626,10 @@ def validate_user(self, username, password, client, request, *args, **kwargs): Check username and password correspond to a valid and active User """ u = authenticate(username=username, password=password) - if u is not None and u.is_active: + + # NOTE: [11/20/2020] Removed check for u.is_active because the check + # will be made *before* calling DOT (Django OAuth Toolkit) + if u is not None: request.user = u return True return False From b559c35bd8d60d93fd841a92a06601f1339efdc1 Mon Sep 17 00:00:00 2001 From: Jae Date: Fri, 8 Jan 2021 08:56:51 -0800 Subject: [PATCH 05/14] refactor: support legacy tokens --- oauth2_provider/models.py | 19 +++++++++++++ oauth2_provider/oauth2_validators.py | 40 +++++++++++++++------------- oauth2_provider/settings.py | 8 ++++-- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 097fb7c5f..284cd96c3 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -387,6 +387,25 @@ class AbstractRefreshToken(models.Model): updated = models.DateTimeField(auto_now=True) revoked = models.DateTimeField(null=True) + @property + def is_expired(self): + """Determine if RefreshToken is expired.""" + expire_seconds = self.application.refresh_token_expire_seconds + expires = self.created + timedelta(seconds=expire_seconds) + + now = timezone.now() + is_refresh_token_expired = now >= expires + + # RefreshToken should not outlive AccessToken. + # NOTE: Check AccessToken expiration for backwards compatibility with + # long-lived tokens. + access_token_expires = self.access_token.expires + is_access_token_expired = now >= access_token_expires + + # RefreshToken expired if and only if both refresh and access tokens + # are expired. + return is_refresh_token_expired and is_access_token_expired + def revoke(self): """ Mark this refresh token revoked and revoke related access token diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 9262d4eae..217ebedd4 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -475,9 +475,19 @@ def save_bearer_token(self, token, request, *args, **kwargs): if "scope" not in token: raise FatalClientError("Failed to renew access token: missing scope") - # "authenticate_client" sets the client (Application) on request - application = request.client - access_token_expire_seconds = application.access_token_expire_seconds + # "authenticate_client" sets the client (Application) on request. + app = request.client + + # Users on older app versions should get long-lived tokens for + # backwards compatibility. + is_legacy_token = request.POST.get('is_legacy_token', False) + + if is_legacy_token: + access_token_expire_seconds = ( + settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS, + ) + else: + access_token_expire_seconds = app.access_token_expire_seconds # expires_in is passed to Server on initialization # custom server class can have logic to override this @@ -654,24 +664,18 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs seconds=oauth2_settings.REFRESH_TOKEN_GRACE_PERIOD_SECONDS ) ) - rt = RefreshToken.objects.filter(null_or_recent, token=refresh_token).select_related( - "access_token", - "application", - ).first() + rt = ( + RefreshToken.objects + .filter(null_or_recent, token=refresh_token) + .select_related("user", "access_token", "application") + .first() + ) if not rt: return False - # Access and refresh token expiration is configurable by Application - # Determine refresh token expiration datetime by adding the timedelta - # of "refresh_token_expire_seconds" to the "created" datetime - expire_seconds = rt.application.refresh_token_expire_seconds - expires = rt.created + timedelta(seconds=expire_seconds) - - is_expired = timezone.now() >= expires - - # Revoke token if expired - if is_expired: + # Revoke token if expired. + if rt.is_expired: try: rt.revoke() # Catch exception in case access or refresh token do not exist @@ -686,7 +690,7 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs # Token is valid if it refers to the right client AND is not expired is_valid = ( rt.application == client and - not is_expired + not rt.is_expired ) return is_valid diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index bfff6f6b0..d39cf9af6 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -44,8 +44,12 @@ "READ_SCOPE": "read", "WRITE_SCOPE": "write", "AUTHORIZATION_CODE_EXPIRE_SECONDS": 60, - "ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds - "REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds + "ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds + "REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds + + # Older app versions should get long-lived auth tokens. + "LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS": 315569520, # 10 years + "REFRESH_TOKEN_GRACE_PERIOD_SECONDS": 0, "ROTATE_REFRESH_TOKEN": True, "ERROR_RESPONSE_WITH_SCOPES": False, From 622f2516c16f62e6041a658d9a16edf5fb8e4f33 Mon Sep 17 00:00:00 2001 From: Jae Date: Mon, 11 Jan 2021 22:06:04 -0800 Subject: [PATCH 06/14] chore: get is_legacy_token from request --- oauth2_provider/oauth2_validators.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 217ebedd4..ea918627f 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -480,20 +480,16 @@ def save_bearer_token(self, token, request, *args, **kwargs): # Users on older app versions should get long-lived tokens for # backwards compatibility. - is_legacy_token = request.POST.get('is_legacy_token', False) + is_legacy_token = getattr(request, 'is_legacy_token', False) if is_legacy_token: - access_token_expire_seconds = ( - settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS, - ) + expire_seconds = oauth2_settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS else: - access_token_expire_seconds = app.access_token_expire_seconds + expire_seconds = app.access_token_expire_seconds # expires_in is passed to Server on initialization # custom server class can have logic to override this - expires = ( - timezone.now() + timedelta(seconds=access_token_expire_seconds) - ) + expires = timezone.now() + timedelta(seconds=expire_seconds) if request.grant_type == "client_credentials": request.user = None From 8e85d41244f1d0f43285506b892f908a01a428fd Mon Sep 17 00:00:00 2001 From: Jae Date: Tue, 12 Jan 2021 11:40:00 -0800 Subject: [PATCH 07/14] fix: check access token expiry only if exists --- oauth2_provider/models.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 284cd96c3..d997b7099 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -396,11 +396,15 @@ def is_expired(self): now = timezone.now() is_refresh_token_expired = now >= expires + # Access token assumed to be expired, by default. + is_access_token_expired = True + # RefreshToken should not outlive AccessToken. # NOTE: Check AccessToken expiration for backwards compatibility with # long-lived tokens. - access_token_expires = self.access_token.expires - is_access_token_expired = now >= access_token_expires + if self.access_token: + access_token_expires = self.access_token.expires + is_access_token_expired = now >= access_token_expires # RefreshToken expired if and only if both refresh and access tokens # are expired. From d37bed233080d7c27eccc61d2e8576b011cc520b Mon Sep 17 00:00:00 2001 From: Jae Date: Mon, 25 Jan 2021 16:54:03 -0800 Subject: [PATCH 08/14] refactor: change default access token expiry from 10 hours to 24 hours --- oauth2_provider/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index d39cf9af6..f38df3a86 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -44,8 +44,8 @@ "READ_SCOPE": "read", "WRITE_SCOPE": "write", "AUTHORIZATION_CODE_EXPIRE_SECONDS": 60, - "ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds - "REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds + "ACCESS_TOKEN_EXPIRE_SECONDS": 86400, # 24 hours in seconds. + "REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds. # Older app versions should get long-lived auth tokens. "LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS": 315569520, # 10 years From ba4fbf15bf486fea1515a9a065871270da6fff4e Mon Sep 17 00:00:00 2001 From: Jae Date: Wed, 17 Feb 2021 11:10:32 -0800 Subject: [PATCH 09/14] chore: display token expiration for Application table in admin --- oauth2_provider/admin.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/admin.py b/oauth2_provider/admin.py index 8b963d981..1eef601c5 100644 --- a/oauth2_provider/admin.py +++ b/oauth2_provider/admin.py @@ -7,7 +7,15 @@ class ApplicationAdmin(admin.ModelAdmin): - list_display = ("id", "name", "user", "client_type", "authorization_grant_type") + list_display = ( + "id", + "name", + "user", + "access_token_expire_seconds", + "refresh_token_expire_seconds", + "client_type", + "authorization_grant_type", + ) list_filter = ("client_type", "authorization_grant_type", "skip_authorization") radio_fields = { "client_type": admin.HORIZONTAL, From e967c32d9458971f90ee162e04635b3d6b0dfc85 Mon Sep 17 00:00:00 2001 From: Jae Date: Thu, 4 Mar 2021 17:48:00 -0800 Subject: [PATCH 10/14] refactor: fix is_legacy_token --- oauth2_provider/oauth2_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index ea918627f..5af0749a6 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -480,7 +480,7 @@ def save_bearer_token(self, token, request, *args, **kwargs): # Users on older app versions should get long-lived tokens for # backwards compatibility. - is_legacy_token = getattr(request, 'is_legacy_token', False) + is_legacy_token = getattr(request, 'is_legacy_token') == 'True' if is_legacy_token: expire_seconds = oauth2_settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS From d3435fe4d4c47c09ca8c113f9892cef8200b84b4 Mon Sep 17 00:00:00 2001 From: Jae Date: Thu, 4 Mar 2021 17:50:40 -0800 Subject: [PATCH 11/14] refactor: broaden check for True values --- oauth2_provider/oauth2_validators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 5af0749a6..2d437a510 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -480,7 +480,8 @@ def save_bearer_token(self, token, request, *args, **kwargs): # Users on older app versions should get long-lived tokens for # backwards compatibility. - is_legacy_token = getattr(request, 'is_legacy_token') == 'True' + TRUE_VALUES = [True, 'True', 'true'] + is_legacy_token = getattr(request, 'is_legacy_token') in TRUE_VALUES if is_legacy_token: expire_seconds = oauth2_settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS From 4b373b42b1d5f120e7bea2b62d57d588ecd4b288 Mon Sep 17 00:00:00 2001 From: Jae Date: Thu, 4 Mar 2021 18:26:46 -0800 Subject: [PATCH 12/14] fix: is_legacy_token default --- oauth2_provider/oauth2_validators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 2d437a510..8bb73a0e6 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -481,9 +481,9 @@ def save_bearer_token(self, token, request, *args, **kwargs): # Users on older app versions should get long-lived tokens for # backwards compatibility. TRUE_VALUES = [True, 'True', 'true'] - is_legacy_token = getattr(request, 'is_legacy_token') in TRUE_VALUES + is_legacy_token = getattr(request, 'is_legacy_token', False) - if is_legacy_token: + if is_legacy_token in TRUE_VALUES: expire_seconds = oauth2_settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS else: expire_seconds = app.access_token_expire_seconds From db684e15483b3690e7ffd754e8e933265d30a469 Mon Sep 17 00:00:00 2001 From: Nathan Hopper Date: Tue, 20 Jul 2021 11:40:38 -0700 Subject: [PATCH 13/14] Add Poetry package manager support (#1) * chore: add poetry support * hack: try name change * chore: specify packages * chore: fix typo oauth_provider -> oauth2_provider * Update pyproject.toml --- .python-version | 1 + poetry.lock | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 23 +++++++ 3 files changed, 185 insertions(+) create mode 100644 .python-version create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..1635d0f5a --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.6 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000..85fd44ece --- /dev/null +++ b/poetry.lock @@ -0,0 +1,161 @@ +[[package]] +name = "asgiref" +version = "3.4.1" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] + +[[package]] +name = "certifi" +version = "2021.5.30" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.3" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "django" +version = "3.2.5" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "idna" +version = "3.2" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "oauthlib" +version = "3.1.1" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +rsa = ["cryptography (>=3.0.0,<4)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "pytz" +version = "2021.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "sqlparse" +version = "0.4.1" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "urllib3" +version = "1.26.6" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[metadata] +lock-version = "1.1" +python-versions = "==3.9.6" +content-hash = "1bab1c646c5a5e2b1bfab8df5308fed84590194278ef3c2b303e70ecee919476" + +[metadata.files] +asgiref = [ + {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, + {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, +] +certifi = [ + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.3.tar.gz", hash = "sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"}, + {file = "charset_normalizer-2.0.3-py3-none-any.whl", hash = "sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1"}, +] +django = [ + {file = "Django-3.2.5-py3-none-any.whl", hash = "sha256:c58b5f19c5ae0afe6d75cbdd7df561e6eb929339985dbbda2565e1cabb19a62e"}, + {file = "Django-3.2.5.tar.gz", hash = "sha256:3da05fea54fdec2315b54a563d5b59f3b4e2b1e69c3a5841dda35019c01855cd"}, +] +idna = [ + {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, + {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, +] +oauthlib = [ + {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, + {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, +] +pytz = [ + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, +] +requests = [ + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, +] +sqlparse = [ + {file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"}, + {file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"}, +] +urllib3 = [ + {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, + {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..8d045d163 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "django_oauth_toolkit" +version = "1.3.3" +description = "Common packages" +authors = [] +packages = [ + { include = "oauth2_provider" }, +] +exclude = [ + "tests", +] + +[tool.poetry.dependencies] +django = ">= 2.2" +requests = ">= 2.13.0" +oauthlib = ">= 3.1.0" +python = "==3.9.6" + +[tool.poetry.dev-dependencies] From 7a140bc4a0240f8cb55cdab41ec0db2591ee87fd Mon Sep 17 00:00:00 2001 From: Nathan Hopper Date: Tue, 20 Jul 2021 18:40:53 -0700 Subject: [PATCH 14/14] Revert "Add Poetry package manager support (#1)" This reverts commit db684e15483b3690e7ffd754e8e933265d30a469. --- .python-version | 1 - poetry.lock | 161 ------------------------------------------------ pyproject.toml | 23 ------- 3 files changed, 185 deletions(-) delete mode 100644 .python-version delete mode 100644 poetry.lock delete mode 100644 pyproject.toml diff --git a/.python-version b/.python-version deleted file mode 100644 index 1635d0f5a..000000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.9.6 diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 85fd44ece..000000000 --- a/poetry.lock +++ /dev/null @@ -1,161 +0,0 @@ -[[package]] -name = "asgiref" -version = "3.4.1" -description = "ASGI specs, helper code, and adapters" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] - -[[package]] -name = "certifi" -version = "2021.5.30" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "charset-normalizer" -version = "2.0.3" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "django" -version = "3.2.5" -description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -asgiref = ">=3.3.2,<4" -pytz = "*" -sqlparse = ">=0.2.2" - -[package.extras] -argon2 = ["argon2-cffi (>=19.1.0)"] -bcrypt = ["bcrypt"] - -[[package]] -name = "idna" -version = "3.2" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "oauthlib" -version = "3.1.1" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -rsa = ["cryptography (>=3.0.0,<4)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "pytz" -version = "2021.1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "requests" -version = "2.26.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "sqlparse" -version = "0.4.1" -description = "A non-validating SQL parser." -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "urllib3" -version = "1.26.6" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[metadata] -lock-version = "1.1" -python-versions = "==3.9.6" -content-hash = "1bab1c646c5a5e2b1bfab8df5308fed84590194278ef3c2b303e70ecee919476" - -[metadata.files] -asgiref = [ - {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, - {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, -] -certifi = [ - {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, - {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.3.tar.gz", hash = "sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"}, - {file = "charset_normalizer-2.0.3-py3-none-any.whl", hash = "sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1"}, -] -django = [ - {file = "Django-3.2.5-py3-none-any.whl", hash = "sha256:c58b5f19c5ae0afe6d75cbdd7df561e6eb929339985dbbda2565e1cabb19a62e"}, - {file = "Django-3.2.5.tar.gz", hash = "sha256:3da05fea54fdec2315b54a563d5b59f3b4e2b1e69c3a5841dda35019c01855cd"}, -] -idna = [ - {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, - {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, -] -oauthlib = [ - {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, - {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, -] -pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, -] -requests = [ - {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, - {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, -] -sqlparse = [ - {file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"}, - {file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"}, -] -urllib3 = [ - {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, - {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, -] diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 8d045d163..000000000 --- a/pyproject.toml +++ /dev/null @@ -1,23 +0,0 @@ -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry] -name = "django_oauth_toolkit" -version = "1.3.3" -description = "Common packages" -authors = [] -packages = [ - { include = "oauth2_provider" }, -] -exclude = [ - "tests", -] - -[tool.poetry.dependencies] -django = ">= 2.2" -requests = ">= 2.13.0" -oauthlib = ">= 3.1.0" -python = "==3.9.6" - -[tool.poetry.dev-dependencies]