diff --git a/lms/envs/common.py b/lms/envs/common.py index 8f1f7f6c459bc378158b524f8ca3420aea7976cc..7c9aacee30214ab873e0e09ac821f35f5f10d16e 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -650,9 +650,7 @@ derived_collection_entry('DEFAULT_TEMPLATE_ENGINE', 'DIRS') ############################################################################################### -AUTHENTICATION_BACKENDS = [ - 'openedx.core.djangoapps.oauth_dispatch.dot_overrides.backends.EdxRateLimitedAllowAllUsersModelBackend' -] +AUTHENTICATION_BACKENDS = ['openedx.core.djangoapps.oauth_dispatch.dot_overrides.validators.EdxRateLimitedAllowAllUsersModelBackend'] STUDENT_FILEUPLOAD_MAX_SIZE = 4 * 1000 * 1000 # 4 MB MAX_FILEUPLOADS_PER_INPUT = 20 diff --git a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/backends.py b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/backends.py deleted file mode 100644 index 664d0d79168e962f10ad4d8eb52a4f6928267710..0000000000000000000000000000000000000000 --- a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/backends.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Custom authentication backends. -""" -from django.contrib.auth.backends import AllowAllUsersModelBackend as UserModelBackend -from ratelimitbackend.backends import RateLimitMixin - - -class EdxRateLimitedAllowAllUsersModelBackend(RateLimitMixin, UserModelBackend): - """ - Authentication backend needed to incorporate rate limiting of login attempts - but also - enabling users with is_active of False in the Django auth_user model to still authenticate. - This is necessary for mobile users using 3rd party auth who have not activated their accounts, - Inactive users who use 1st party auth (username/password auth) will still fail login attempts, - just at a higher layer, in the login_user view. - - See: https://openedx.atlassian.net/browse/TNL-4516 - """ - pass diff --git a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/validators.py b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/validators.py index bc265a035cdec311ecdd31904bd50467c8f80f0f..5214622995fdf25d53e82e25ab5e92d75b0c4b1d 100644 --- a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/validators.py +++ b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/validators.py @@ -6,12 +6,14 @@ from __future__ import unicode_literals from datetime import datetime from django.contrib.auth import authenticate, get_user_model +from django.contrib.auth.backends import AllowAllUsersModelBackend as UserModelBackend from django.db.models.signals import pre_save from django.dispatch import receiver from oauth2_provider.models import AccessToken from oauth2_provider.oauth2_validators import OAuth2Validator from oauth2_provider.scopes import get_scopes_backend from pytz import utc +from ratelimitbackend.backends import RateLimitMixin from ..models import RestrictedApplication @@ -25,6 +27,19 @@ def on_access_token_presave(sender, instance, *args, **kwargs): # pylint: disab instance.expires = datetime(1970, 1, 1, tzinfo=utc) +class EdxRateLimitedAllowAllUsersModelBackend(RateLimitMixin, UserModelBackend): + """ + Authentication backend needed to incorporate rate limiting of login attempts - but also + enabling users with is_active of False in the Django auth_user model to still authenticate. + This is necessary for mobile users using 3rd party auth who have not activated their accounts, + Inactive users who use 1st party auth (username/password auth) will still fail login attempts, + just at a higher layer, in the login_user view. + + See: https://openedx.atlassian.net/browse/TNL-4516 + """ + pass + + class EdxOAuth2Validator(OAuth2Validator): """ Validator class that implements edX-specific custom behavior: diff --git a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py index ba238b8a0bd47e2fa4e8e4c80b0544bbf2424f6c..0485f1c665480dafadb6e260358e5d05cde0a044 100644 --- a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py +++ b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from oauth2_provider.exceptions import OAuthToolkitError from oauth2_provider.http import HttpResponseUriRedirect -from oauth2_provider.models import get_access_token_model, get_application_model +from oauth2_provider.models import get_application_model from oauth2_provider.scopes import get_scopes_backend from oauth2_provider.settings import oauth2_settings from oauth2_provider.views import AuthorizationView @@ -69,12 +69,11 @@ class EdxOAuth2AuthorizationView(AuthorizationView): uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) - return HttpResponseUriRedirect(uri, application.get_allowed_schemes()) + return HttpResponseUriRedirect(uri) # *** Changed the if statement that checked for require_approval to an assert. assert require_approval == 'auto_even_if_expired' - tokens = get_access_token_model().objects.filter( - user=request.user, + tokens = request.user.accesstoken_set.filter( application=kwargs['application'], # *** Purposefully keeping this commented out code to highlight that # our version of the implementation does NOT filter by expiration date. @@ -87,7 +86,7 @@ class EdxOAuth2AuthorizationView(AuthorizationView): uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), credentials=credentials, allow=True) - return HttpResponseUriRedirect(uri, application.get_allowed_schemes()) + return HttpResponseUriRedirect(uri) # render an authorization prompt so the user can approve # the application's requested scopes diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py b/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py index 5ea6412073eda9dd4daa3c86980ffb28c9acd073..635eda8af6f29dbc68436cd22fafbccf878abf52 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py @@ -613,39 +613,36 @@ class TestRevokeTokenView(AccessTokenLoginMixin, _DispatchingViewTestCase): # p 'token': token, } - def assert_refresh_token_status_code(self, refresh_token, expected_status_code): + def _assert_refresh_token_invalidated(self): """ - Asserts the status code using oauth assigned refresh_token + Asserts that oauth assigned refresh_token is not valid """ response = self.client.post( self.access_token_url, - self.access_token_post_body_with_refresh_token(refresh_token) + self.access_token_post_body_with_refresh_token(self.refresh_token) ) - self.assertEqual(response.status_code, expected_status_code) + self.assertEqual(response.status_code, 401) - def revoke_token(self, token): + def verify_revoke_token(self, token): """ - Revokes the passed access or refresh token + Verifies access of token before and after revoking """ + self._assert_access_token_is_valid() + response = self.client.post(self.revoke_token_url, self.revoke_token_post_body(token)) self.assertEqual(response.status_code, 200) + self._assert_access_token_invalidated() + self._assert_refresh_token_invalidated() + def test_revoke_refresh_token_dot(self): """ - Tests invalidation/revoke of refresh token for django-oauth-toolkit + Tests invalidation/revoke of user tokens against refresh token for django-oauth-toolkit """ - self.assert_refresh_token_status_code(self.refresh_token, expected_status_code=200) - - self.revoke_token(self.refresh_token) - - self.assert_refresh_token_status_code(self.refresh_token, expected_status_code=401) + self.verify_revoke_token(self.refresh_token) def test_revoke_access_token_dot(self): """ Tests invalidation/revoke of user access token for django-oauth-toolkit """ - self._assert_access_token_is_valid(self.access_token) - - self.revoke_token(self.access_token) - - self._assert_access_token_invalidated(self.access_token) + self.verify_revoke_token(self.access_token) diff --git a/requirements/edx/base.in b/requirements/edx/base.in index 0d9da968fe1a234cda00f47456989bd69504b03a..d5b00a1ea9bb54b756e837887ad01cc919f917c7 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -47,7 +47,7 @@ django-method-override==0.1.0 django-model-utils==3.0.0 django-mptt>=0.8.6,<0.9 django-mysql -django-oauth-toolkit<1.2 # Provides oAuth2 capabilities for Django. 1.2+ requires Django 2 and Python 3.5 +django-oauth-toolkit==0.12.0 django-pyfs django-ratelimit django-ratelimit-backend==1.1.1 @@ -104,11 +104,11 @@ MySQL-python # Driver for the default production relation newrelic # New Relic agent for performance monitoring nodeenv==1.1.1 # Utility for managing Node.js environments; we use this for deployments and testing numpy==1.6.2 # Fast numeric array computation, used in some problem types -oauthlib # OAuth specification support for authenticating via LTI or other Open edX services +oauthlib==2.0.1 # OAuth specification support for authenticating via LTI or other Open edX services pdfminer # Used in shoppingcart for extracting/parsing pdf text piexif==1.0.2 # Exif image metadata manipulation, used in the profile_images app Pillow==3.4 # Image manipulation library; used for course assets, profile images, invoice PDFs, etc. -py2neo<4.0.0 # Used to communicate with Neo4j, which is used internally for modulestore inspection +py2neo<4.0.0 # Used to communicate with Neo4j, which is used internally for modulestore inspection PyContracts==1.7.1 pycountry==1.20 pycryptodomex==3.4.7 @@ -133,7 +133,7 @@ pysrt==0.4.7 # Support for SubRip subtitle files, used in pytz==2016.10 # Time zone information database PyYAML # Used to parse XModule resource templates redis==2.10.6 # celery task broker -requests-oauthlib # Simplifies use of OAuth via the requests library, used for CCX and LTI +requests-oauthlib==0.6.1 # Simplifies use of OAuth via the requests library, used for CCX and LTI rules # Django extension for rules-based authorization checks sailthru-client==2.2.3 # For Sailthru integration Shapely==1.2.16 # Geometry library, used for image click regions in capa diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index eb22b53cd06873be8d7500f0ad073e893860e964..92ba7a6e96cb357e8d7d38fe96fcaceca5d2a005 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -56,10 +56,8 @@ boto3==1.4.8 boto==2.39.0 botocore==1.8.17 celery==3.1.25 -certifi==2018.4.16 cffi==1.11.5 charade==1.0.3 # via pysrt -chardet==3.0.4 click==6.7 # via user-util coreapi==2.3.3 # via django-rest-swagger, openapi-codec coreschema==0.0.4 # via coreapi @@ -71,6 +69,7 @@ defusedxml==0.4.1 django-appconf==1.0.2 # via django-statici18n django-babel-underscore==0.5.2 django-babel==0.6.2 # via django-babel-underscore +django-braces==1.13.0 # via django-oauth-toolkit django-classy-tags==0.8.0 # via django-sekizai django-config-models==0.1.8 django-cors-headers==2.1.0 @@ -85,7 +84,7 @@ django-model-utils==3.0.0 django-mptt==0.8.7 django-multi-email-field==0.5.1 # via edx-enterprise django-mysql==2.3.0 -django-oauth-toolkit==1.1.2 +django-oauth-toolkit==0.12.0 django-object-actions==0.10.0 # via edx-enterprise django-pyfs==2.0 django-ratelimit-backend==1.1.1 @@ -173,7 +172,7 @@ nltk==3.3.0 nodeenv==1.1.1 numpy==1.6.2 oauth2==1.9.0.post1 -oauthlib==2.1.0 +oauthlib==2.0.1 openapi-codec==1.3.2 # via django-rest-swagger path.py==8.2.1 pathtools==0.1.2 @@ -208,8 +207,8 @@ pyuca==1.1 pyyaml==3.13 redis==2.10.6 reportlab==3.1.44 -requests-oauthlib==1.0.0 -requests==2.19.1 +requests-oauthlib==0.6.1 +requests==2.9.1 rest-condition==1.0.3 rfc6266-parser==0.0.5.post2 rules==1.3 @@ -230,7 +229,7 @@ stevedore==1.10.0 sympy==0.7.1 unicodecsv==0.14.1 uritemplate==3.0.0 # via coreapi -urllib3==1.23 +urllib3==1.23 # via elasticsearch user-util==0.1.3 voluptuous==0.11.1 watchdog==0.8.3 diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 414090b628d2ee6e99cca0d019528697fa5e97b1..5dc4228a42eed2e919de0a5fbb912f366bf4b19d 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -67,10 +67,8 @@ boto3==1.4.8 boto==2.39.0 botocore==1.8.17 celery==3.1.25 -certifi==2018.4.16 cffi==1.11.5 charade==1.0.3 -chardet==3.0.4 click-log==0.1.8 click==6.7 colorama==0.3.9 @@ -90,6 +88,7 @@ diff-cover==0.9.8 django-appconf==1.0.2 django-babel-underscore==0.5.2 django-babel==0.6.2 +django-braces==1.13.0 django-classy-tags==0.8.0 django-config-models==0.1.8 django-cors-headers==2.1.0 @@ -105,7 +104,7 @@ django-model-utils==3.0.0 django-mptt==0.8.7 django-multi-email-field==0.5.1 django-mysql==2.3.0 -django-oauth-toolkit==1.1.2 +django-oauth-toolkit==0.12.0 django-object-actions==0.10.0 django-pyfs==2.0 django-ratelimit-backend==1.1.1 @@ -224,7 +223,7 @@ nodeenv==1.1.1 nose==1.3.7 numpy==1.6.2 oauth2==1.9.0.post1 -oauthlib==2.1.0 +oauthlib==2.0.1 openapi-codec==1.3.2 pa11ycrawler==1.6.2 packaging==17.1 @@ -290,8 +289,8 @@ queuelib==1.5.0 radon==2.2.0 redis==2.10.6 reportlab==3.1.44 -requests-oauthlib==1.0.0 -requests==2.19.1 +requests-oauthlib==0.6.1 +requests==2.9.1 rest-condition==1.0.3 rfc6266-parser==0.0.5.post2 rules==1.3 diff --git a/requirements/edx/paver.in b/requirements/edx/paver.in index 4be620eed2fd3113e97c9e60f002858f227593e9..e51f84e944d3982985b01c3bdb02c1d19641bad0 100644 --- a/requirements/edx/paver.in +++ b/requirements/edx/paver.in @@ -17,7 +17,7 @@ paver # Build, distribution and deployment scripti psutil==1.2.1 # Library for retrieving information on running processes and system utilization pymongo==2.9.1 # via edx-opaque-keys python-memcached==1.48 # Python interface to the memcached memory cache daemon -requests # Simple interface for making HTTP requests +requests==2.9.1 # Simple interface for making HTTP requests stevedore==1.10.0 # via edx-opaque-keys watchdog # Used in paver watch_assets wrapt==1.10.5 # Decorator utilities used in the @timed paver task decorator diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt index feeeba3611d4d83f7843c02e9623aa397b86bc0c..ebaa7f91c5e1a9fe65573bcb9ef53874551a59cb 100644 --- a/requirements/edx/paver.txt +++ b/requirements/edx/paver.txt @@ -6,10 +6,7 @@ # argh==0.26.2 # via watchdog argparse==1.4.0 # via stevedore -certifi==2018.4.16 # via requests -chardet==3.0.4 # via requests edx-opaque-keys==0.4.4 -idna==2.7 # via requests lazy==1.1 libsass==0.10.0 markupsafe==1.0 @@ -22,9 +19,8 @@ psutil==1.2.1 pymongo==2.9.1 python-memcached==1.48 pyyaml==3.13 # via watchdog -requests==2.19.1 +requests==2.9.1 six==1.11.0 # via edx-opaque-keys, libsass, paver, stevedore stevedore==1.10.0 -urllib3==1.23 # via requests watchdog==0.8.3 wrapt==1.10.5 diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index ed4d74352d618047f25e66a1fbd7b4c862ee6e90..1c8e96c992d6b2c68034009939def3d537a864f2 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -64,10 +64,8 @@ boto3==1.4.8 boto==2.39.0 botocore==1.8.17 celery==3.1.25 -certifi==2018.4.16 cffi==1.11.5 charade==1.0.3 -chardet==3.0.4 click-log==0.1.8 # via edx-lint click==6.7 colorama==0.3.9 # via radon @@ -87,6 +85,7 @@ diff-cover==0.9.8 django-appconf==1.0.2 django-babel-underscore==0.5.2 django-babel==0.6.2 +django-braces==1.13.0 django-classy-tags==0.8.0 django-config-models==0.1.8 django-cors-headers==2.1.0 @@ -101,7 +100,7 @@ django-model-utils==3.0.0 django-mptt==0.8.7 django-multi-email-field==0.5.1 django-mysql==2.3.0 -django-oauth-toolkit==1.1.2 +django-oauth-toolkit==0.12.0 django-object-actions==0.10.0 django-pyfs==2.0 django-ratelimit-backend==1.1.1 @@ -215,7 +214,7 @@ nodeenv==1.1.1 nose==1.3.7 numpy==1.6.2 oauth2==1.9.0.post1 -oauthlib==2.1.0 +oauthlib==2.0.1 openapi-codec==1.3.2 pa11ycrawler==1.6.2 packaging==17.1 # via tox @@ -279,8 +278,8 @@ queuelib==1.5.0 # via scrapy radon==2.2.0 redis==2.10.6 reportlab==3.1.44 -requests-oauthlib==1.0.0 -requests==2.19.1 +requests-oauthlib==0.6.1 +requests==2.9.1 rest-condition==1.0.3 rfc6266-parser==0.0.5.post2 rules==1.3