diff --git a/cms/envs/test.py b/cms/envs/test.py index 1f84dbe7d3..5074c48d98 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -331,3 +331,7 @@ FEATURES['CUSTOM_COURSES_EDX'] = True # API access management -- needed for simple-history to run. INSTALLED_APPS += ('openedx.core.djangoapps.api_admin',) + +# Set the default Oauth2 Provider Model so that migrations can run in +# verbose mode +OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' diff --git a/lms/envs/common.py b/lms/envs/common.py index 2069002f24..602d20987a 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -450,14 +450,9 @@ OAUTH2_PROVIDER = { 'read': 'Read scope', 'write': 'Write scope', 'email': 'Email scope', - # conform profile scope message that is presented to end-user - # to lms/templates/provider/authorize.html. This may be revised later. - 'profile': 'Read your user profile', - }, + 'profile': 'Profile scope', + } } -# This is required for the migrations in oauth_dispatch.models -# otherwise it fails saying this attribute is not present in Settings -OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' ################################## TEMPLATE CONFIGURATION ##################################### # Mako templating diff --git a/lms/envs/test.py b/lms/envs/test.py index 6e2e61fac5..6c0e373ae5 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -582,6 +582,10 @@ JWT_AUTH.update({ 'JWT_AUDIENCE': 'test-key', }) +# Set the default Oauth2 Provider Model so that migrations can run in +# verbose mode +OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' + COURSE_CATALOG_API_URL = 'https://catalog.example.com/api/v1' COMPREHENSIVE_THEME_DIRS = [REPO_ROOT / "themes", REPO_ROOT / "common/test"] diff --git a/lms/static/sass/views/_oauth2.scss b/lms/static/sass/views/_oauth2.scss index dd6431c192..ae3c6fc682 100644 --- a/lms/static/sass/views/_oauth2.scss +++ b/lms/static/sass/views/_oauth2.scss @@ -15,41 +15,3 @@ } } - -// For the django-oauth-toolkit Authorization view -.wrapper-authorize { - background: $white; - width: 60%; - padding-right: 140px; - padding-left: 140px; - - font-family: $sans-serif; - - h1 { - @extend %t-title4; - margin-bottom: 0; - margin-left: 0; - padding: $baseline; - padding-left: 0px; - @include text-align(left); - } - - p { - @extend %t-copy-base; - margin: $baseline/2 0; - } - - .control-group { - float: right; - } - - .btn-authorization-allow { - @extend %btn-primary-blue; - margin-left: 20px; - line-height: 0.7em; - } - - .btn-authorization-cancel { - @extend %btn-secondary-blue-outline; - } -} diff --git a/lms/templates/oauth2_provider/authorize.html b/lms/templates/oauth2_provider/authorize.html deleted file mode 100644 index 75146eeaa4..0000000000 --- a/lms/templates/oauth2_provider/authorize.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "main_django.html" %} -{% load i18n configuration %} - -{% block title %} - {% trans "Authorize" %} | {% platform_name %} -{% endblock %} - -{% block body %} - - - - - {% if not error %} - - {% trans "Authorize" %} {{ application.name }}? - {% csrf_token %} - - {% for field in form %} - {% if field.is_hidden %} - {{ field }} - {% endif %} - {% endfor %} - - {% trans "The above application requests the following permissions from your account:" %} - - {% for scope in scopes_descriptions %} - {{ scope }} - {% endfor %} - - {% trans "Please click the 'Allow' button to grant these permissions to the above application. Otherwise, to withhold these permissions, please click the 'Cancel' button." %} - - - {{ form.errors }} - {{ form.non_field_errors }} - - - - {% trans "Cancel" %}{% trans "Allow" %} - - - - - {% else %} - {% trans "Error" %}: {{ error.error }} - {{ error.description }} - {% endif %} - - - - -{% endblock %} diff --git a/openedx/core/djangoapps/oauth_dispatch/admin.py b/openedx/core/djangoapps/oauth_dispatch/admin.py index 2c01b289d1..f26ea13aec 100644 --- a/openedx/core/djangoapps/oauth_dispatch/admin.py +++ b/openedx/core/djangoapps/oauth_dispatch/admin.py @@ -5,8 +5,6 @@ Override admin configuration for django-oauth-toolkit from django.contrib.admin import ModelAdmin, site from oauth2_provider import models -from .models import RestrictedApplication - def reregister(model_class): """ @@ -73,13 +71,3 @@ class DOTGrantAdmin(ModelAdmin): list_filter = [u'application'] raw_id_fields = [u'user'] search_fields = [u'code', u'user__username'] - - -class RestrictedApplicationAdmin(ModelAdmin): - """ - ModelAdmin for the Restricted Application - """ - list_display = [u'application'] - - -site.register(RestrictedApplication, RestrictedApplicationAdmin) diff --git a/openedx/core/djangoapps/oauth_dispatch/dot_overrides.py b/openedx/core/djangoapps/oauth_dispatch/dot_overrides.py index 69db994dcb..2e71bc316b 100644 --- a/openedx/core/djangoapps/oauth_dispatch/dot_overrides.py +++ b/openedx/core/djangoapps/oauth_dispatch/dot_overrides.py @@ -3,32 +3,10 @@ Classes that override default django-oauth-toolkit behavior """ from __future__ import unicode_literals -from .models import RestrictedApplication - -from datetime import datetime from django.contrib.auth import authenticate, get_user_model -from django.db.models.signals import pre_save -from django.dispatch import receiver -from pytz import utc - -from oauth2_provider.models import AccessToken from oauth2_provider.oauth2_validators import OAuth2Validator -@receiver(pre_save, sender=AccessToken) -def on_access_token_presave(sender, instance, *args, **kwargs): # pylint: disable=unused-argument - """ - A hook on the AccessToken. Since we do not have protected scopes, we must mark all - AccessTokens as expired for 'restricted applications'. - - We do this as a pre-save hook on the ORM - """ - - is_application_restricted = RestrictedApplication.objects.filter(application=instance.application).exists() - if is_application_restricted: - RestrictedApplication.set_access_token_as_expired(instance) - - class EdxOAuth2Validator(OAuth2Validator): """ Validator class that implements edX-specific custom behavior: @@ -83,23 +61,6 @@ class EdxOAuth2Validator(OAuth2Validator): super(EdxOAuth2Validator, self).save_bearer_token(token, request, *args, **kwargs) - is_application_restricted = RestrictedApplication.objects.filter(application=request.client).exists() - if is_application_restricted: - # Since RestrictedApplications will override the DOT defined expiry, so that access_tokens - # are always expired, we need to re-read the token from the database and then calculate the - # expires_in (in seconds) from what we stored in the database. This value should be a negative - #value, meaning that it is already expired - - access_token = AccessToken.objects.get(token=token['access_token']) - utc_now = datetime.utcnow().replace(tzinfo=utc) - expires_in = (access_token.expires - utc_now).total_seconds() - - # assert that RestriectedApplications only issue expired tokens - # blow up processing if we see otherwise - assert expires_in < 0 - - token['expires_in'] = expires_in - # Restore the original request attributes request.grant_type = grant_type request.user = user diff --git a/openedx/core/djangoapps/oauth_dispatch/migrations/0001_initial.py b/openedx/core/djangoapps/oauth_dispatch/migrations/0001_initial.py deleted file mode 100644 index 39ea4aaa18..0000000000 --- a/openedx/core/djangoapps/oauth_dispatch/migrations/0001_initial.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='RestrictedApplication', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('application', models.ForeignKey(to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), - ], - ), - ] diff --git a/openedx/core/djangoapps/oauth_dispatch/migrations/__init__.py b/openedx/core/djangoapps/oauth_dispatch/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openedx/core/djangoapps/oauth_dispatch/models.py b/openedx/core/djangoapps/oauth_dispatch/models.py deleted file mode 100644 index 45236c5196..0000000000 --- a/openedx/core/djangoapps/oauth_dispatch/models.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Specialized models for oauth_dispatch djangoapp -""" - -from datetime import datetime -from django.db import models -from pytz import utc - -from oauth2_provider.settings import oauth2_settings - - -class RestrictedApplication(models.Model): - """ - This model lists which django-oauth-toolkit Applications are considered 'restricted' - and thus have a limited ability to use various APIs. - - A restricted Application will only get expired token/JWT payloads - so that they cannot be used to call into APIs. - """ - - application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, null=False) - - def __unicode__(self): - """ - Return a unicode representation of this object - """ - return u"".format( - name=self.application.name - ) - - @classmethod - def set_access_token_as_expired(cls, access_token): - """ - For access_tokens for RestrictedApplications, put the expire timestamp into the beginning of the epoch - which is Jan. 1, 1970 - """ - access_token.expires = datetime(1970, 1, 1, tzinfo=utc) - - @classmethod - def verify_access_token_as_expired(cls, access_token): - """ - For access_tokens for RestrictedApplications, make sure that the expiry date - is set at the beginning of the epoch which is Jan. 1, 1970 - """ - return access_token.expires == datetime(1970, 1, 1, tzinfo=utc) diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/constants.py b/openedx/core/djangoapps/oauth_dispatch/tests/constants.py index 598e7278cc..b3f19be8df 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/constants.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/constants.py @@ -3,4 +3,3 @@ Constants for testing purposes """ DUMMY_REDIRECT_URL = u'https://example.com/edx/redirect' -DUMMY_REDIRECT_URL2 = u'https://example.com/edx/other-redirect' diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/mixins.py b/openedx/core/djangoapps/oauth_dispatch/tests/mixins.py index fd65945add..f51524c148 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/mixins.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/mixins.py @@ -1,11 +1,8 @@ """ OAuth Dispatch test mixins """ - -from django.conf import settings - import jwt -from jwt.exceptions import ExpiredSignatureError +from django.conf import settings from student.models import UserProfile, anonymous_id_for_user @@ -13,14 +10,13 @@ from student.models import UserProfile, anonymous_id_for_user class AccessTokenMixin(object): """ Mixin for tests dealing with OAuth 2 access tokens. """ - def assert_valid_jwt_access_token(self, access_token, user, scopes=None, should_be_expired=False): + def assert_valid_jwt_access_token(self, access_token, user, scopes=None): """ Verify the specified JWT access token is valid, and belongs to the specified user. Args: access_token (str): JWT user (User): User whose information is contained in the JWT payload. - (optional) should_be_expired: indicates if the passed in JWT token is expected to be expired Returns: dict: Decoded JWT payload @@ -28,27 +24,13 @@ class AccessTokenMixin(object): scopes = scopes or [] audience = settings.JWT_AUTH['JWT_AUDIENCE'] issuer = settings.JWT_AUTH['JWT_ISSUER'] - - def _decode_jwt(verify_expiration): - """ - Helper method to decode a JWT with the ability to - verify the expiration of said token - """ - return jwt.decode( - access_token, - settings.JWT_AUTH['JWT_SECRET_KEY'], - algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']], - audience=audience, - issuer=issuer, - verify_expiration=verify_expiration - ) - - # Note that if we expect the claims to have expired - # then we ask the JWT library not to verify expiration - # as that would throw a ExpiredSignatureError and - # halt other verifications steps. We'll do a manual - # expiry verification later on - payload = _decode_jwt(verify_expiration=not should_be_expired) + payload = jwt.decode( + access_token, + settings.JWT_AUTH['JWT_SECRET_KEY'], + algorithms=[settings.JWT_AUTH['JWT_ALGORITHM']], + audience=audience, + issuer=issuer + ) expected = { 'aud': audience, @@ -72,13 +54,4 @@ class AccessTokenMixin(object): self.assertDictContainsSubset(expected, payload) - # Since we suppressed checking of expiry - # in the claim in the above check, because we want - # to fully examine the claims outside of the expiry, - # now we should assert that the claim is indeed - # expired - if should_be_expired: - with self.assertRaises(ExpiredSignatureError): - _decode_jwt(verify_expiration=True) - return payload diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/test_dot_adapter.py b/openedx/core/djangoapps/oauth_dispatch/tests/test_dot_adapter.py index 057867fa86..a623e8cc6d 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/test_dot_adapter.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/test_dot_adapter.py @@ -5,21 +5,17 @@ Tests for DOT Adapter from datetime import timedelta import ddt -from django.conf import settings from django.test import TestCase from django.utils.timezone import now from oauth2_provider import models -import unittest from student.tests.factories import UserFactory from ..adapters import DOTAdapter -from .constants import DUMMY_REDIRECT_URL, DUMMY_REDIRECT_URL2 -from ..models import RestrictedApplication +from .constants import DUMMY_REDIRECT_URL @ddt.ddt -@unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") class DOTAdapterTestCase(TestCase): """ Test class for DOTAdapter. @@ -42,21 +38,6 @@ class DOTAdapterTestCase(TestCase): redirect_uri=DUMMY_REDIRECT_URL, client_id='confidential-client-id', ) - self.restricted_client = self.adapter.create_confidential_client( - name='restricted app', - user=self.user, - redirect_uri=DUMMY_REDIRECT_URL2, - client_id='restricted-client-id', - ) - self.restricted_app = RestrictedApplication.objects.create(application=self.restricted_client) - - def test_restricted_app_unicode(self): - """ - Make sure unicode representation of RestrictedApplication is correct - """ - self.assertEqual(unicode(self.restricted_app), u"".format( - name=self.restricted_client.name - )) @ddt.data( ('confidential', models.Application.CLIENT_CONFIDENTIAL), @@ -70,14 +51,7 @@ class DOTAdapterTestCase(TestCase): self.assertEqual(client.client_type, client_type) def test_get_client(self): - """ - Read back one of the confidential clients (there are two) - and verify that we get back what we expected - """ - client = self.adapter.get_client( - redirect_uris=DUMMY_REDIRECT_URL, - client_type=models.Application.CLIENT_CONFIDENTIAL - ) + client = self.adapter.get_client(client_type=models.Application.CLIENT_CONFIDENTIAL) self.assertIsInstance(client, models.Application) self.assertEqual(client.client_type, models.Application.CLIENT_CONFIDENTIAL) @@ -100,18 +74,3 @@ class DOTAdapterTestCase(TestCase): expires=now() + timedelta(days=30), ) self.assertEqual(self.adapter.get_access_token(token_string='token-id'), token) - - def test_get_restricted_access_token(self): - """ - Make sure when generating an access_token for a restricted client - that the token is immediately expired - """ - models.AccessToken.objects.create( - token='expired-token-id', - application=self.restricted_client, - user=self.user, - expires=now() + timedelta(days=30), - ) - - readback_token = self.adapter.get_access_token(token_string='expired-token-id') - self.assertTrue(RestrictedApplication.verify_access_token_as_expired(readback_token)) diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/test_factories.py b/openedx/core/djangoapps/oauth_dispatch/tests/test_factories.py index ebf530a78c..b6553f0528 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/test_factories.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/test_factories.py @@ -1,15 +1,12 @@ # pylint: disable=missing-docstring -from django.conf import settings from django.test import TestCase from oauth2_provider.models import Application, AccessToken, RefreshToken -import unittest from openedx.core.djangoapps.oauth_dispatch.tests import factories from student.tests.factories import UserFactory -@unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") class TestClientFactory(TestCase): def setUp(self): super(TestClientFactory, self).setUp() @@ -21,7 +18,6 @@ class TestClientFactory(TestCase): self.assertEqual(actual_application, expected_application) -@unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") class TestAccessTokenFactory(TestCase): def setUp(self): super(TestAccessTokenFactory, self).setUp() @@ -34,7 +30,6 @@ class TestAccessTokenFactory(TestCase): self.assertEqual(actual_access_token, expected_access_token) -@unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") class TestRefreshTokenFactory(TestCase): def setUp(self): super(TestRefreshTokenFactory, self).setUp() diff --git a/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py b/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py index 5332b0441c..57d83b9597 100644 --- a/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py +++ b/openedx/core/djangoapps/oauth_dispatch/tests/test_views.py @@ -9,7 +9,6 @@ from django.conf import settings from django.test import RequestFactory, TestCase from django.core.urlresolvers import reverse import httpretty -from oauth2_provider import models as dot_models from provider import constants import unittest @@ -17,49 +16,10 @@ from student.tests.factories import UserFactory from third_party_auth.tests.utils import ThirdPartyOAuthTestMixin, ThirdPartyOAuthTestMixinGoogle from .constants import DUMMY_REDIRECT_URL -from . import mixins from .. import adapters -from .. import models - if settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"): from .. import views - - -class AccessTokenLoginMixin(object): - """ - Shared helper class to assert proper access levels when using access_tokens - """ - - def setUp(self): - """ - Initialize mixin - """ - super(AccessTokenLoginMixin, self).setUp() - self.login_with_access_token_url = reverse("login_with_access_token") - - def login_with_access_token(self, access_token=None): - """ - Login with access token and return response. - You can optionally send in an accss_token to override - the object's attribute - """ - - return self.client.post( - self.login_with_access_token_url, - HTTP_AUTHORIZATION="Bearer {0}".format(access_token if access_token else self.access_token) - ) - - def _assert_access_token_is_valid(self, access_token=None): - """ - Asserts that oauth assigned access_token is valid and usable - """ - self.assertEqual(self.login_with_access_token(access_token=access_token).status_code, 204) - - def _assert_access_token_invalidated(self, access_token=None): - """ - Asserts that oauth assigned access_token is not valid - """ - self.assertEqual(self.login_with_access_token(access_token=access_token).status_code, 401) +from . import mixins @unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") @@ -88,16 +48,6 @@ class _DispatchingViewTestCase(TestCase): client_id='dop-app-client-id', ) - # Create a "restricted" DOT Application which means any AccessToken/JWT - # generated for this application will be immediately expired - self.restricted_dot_app = self.dot_adapter.create_public_client( - name='test restricted dot application', - user=self.user, - redirect_uri=DUMMY_REDIRECT_URL, - client_id='dot-restricted-app-client-id', - ) - models.RestrictedApplication.objects.create(application=self.restricted_dot_app) - def _post_request(self, user, client, token_type=None): """ Call the view with a POST request objectwith the appropriate format, @@ -113,14 +63,14 @@ class _DispatchingViewTestCase(TestCase): @ddt.ddt -class TestAccessTokenView(AccessTokenLoginMixin, mixins.AccessTokenMixin, _DispatchingViewTestCase): +class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase): """ Test class for AccessTokenView """ def setUp(self): - super(TestAccessTokenView, self).setUp() self.url = reverse('access_token') self.view_class = views.AccessTokenView + super(TestAccessTokenView, self).setUp() def _post_body(self, user, client, token_type=None): """ @@ -149,22 +99,6 @@ class TestAccessTokenView(AccessTokenLoginMixin, mixins.AccessTokenMixin, _Dispa self.assertIn('scope', data) self.assertIn('token_type', data) - def test_restricted_access_token_fields(self): - response = self._post_request(self.user, self.restricted_dot_app) - self.assertEqual(response.status_code, 200) - data = json.loads(response.content) - self.assertIn('access_token', data) - self.assertIn('expires_in', data) - self.assertIn('scope', data) - self.assertIn('token_type', data) - - # Restricted applications have immediately expired tokens - self.assertLess(data['expires_in'], 0) - - # double check that the token stored in the DB is marked as expired - access_token = dot_models.AccessToken.objects.get(token=data['access_token']) - self.assertTrue(models.RestrictedApplication.verify_access_token_as_expired(access_token)) - @ddt.data('dop_app', 'dot_app') def test_jwt_access_token(self, client_attr): client = getattr(self, client_attr) @@ -175,47 +109,6 @@ class TestAccessTokenView(AccessTokenLoginMixin, mixins.AccessTokenMixin, _Dispa self.assertEqual(data['token_type'], 'JWT') self.assert_valid_jwt_access_token(data['access_token'], self.user, data['scope'].split(' ')) - def test_restricted_jwt_access_token(self): - """ - Verify that when requesting a JWT token from a restricted Application - within the DOT subsystem, that our claims is marked as already expired - (i.e. expiry set to Jan 1, 1970) - """ - response = self._post_request(self.user, self.restricted_dot_app, token_type='jwt') - self.assertEqual(response.status_code, 200) - data = json.loads(response.content) - self.assertIn('expires_in', data) - - # jwt must indicate that it is already expired - self.assertLess(data['expires_in'], 0) - self.assertEqual(data['token_type'], 'JWT') - self.assert_valid_jwt_access_token( - data['access_token'], - self.user, - data['scope'].split(' '), - should_be_expired=True - ) - - def test_restricted_access_token(self): - """ - Verify that an access_token generated for a RestrictedApplication fails when - submitted to an API endpoint - """ - - response = self._post_request(self.user, self.restricted_dot_app) - self.assertEqual(response.status_code, 200) - data = json.loads(response.content) - - self.assertIn('expires_in', data) - self.assertIn('access_token', data) - - # the payload should indicate that the token is expired - self.assertLess(data['expires_in'], 0) - - # try submitting this expired access_token to an API, - # and assert that it fails - self._assert_access_token_invalidated(data['access_token']) - def test_dot_access_token_provides_refresh_token(self): response = self._post_request(self.user, self.dot_app) self.assertEqual(response.status_code, 200) @@ -304,47 +197,6 @@ class TestAuthorizationView(_DispatchingViewTestCase): check_response = getattr(self, '_check_{}_response'.format(client_type)) check_response(response) - def test_check_dot_authorization_page_get(self): - """ - Make sure we get the overridden Authorization page - not - the default django-oauth-toolkit when we perform a page load - """ - self.client.login(username=self.user.username, password='test') - response = self.client.get( - '/oauth2/authorize/', - { - 'client_id': self.dot_app.client_id, - 'response_type': 'code', - 'state': 'random_state_string', - 'redirect_uri': DUMMY_REDIRECT_URL, - 'scope': 'profile' - }, - follow=True, - ) - - # are the requested scopes on the page? We only requested 'profile', lets make - # sure the page only lists that one - self.assertContains(response, settings.OAUTH2_PROVIDER['SCOPES']['profile']) - self.assertNotContains(response, settings.OAUTH2_PROVIDER['SCOPES']['read']) - self.assertNotContains(response, settings.OAUTH2_PROVIDER['SCOPES']['write']) - self.assertNotContains(response, settings.OAUTH2_PROVIDER['SCOPES']['email']) - - # is the application name specified? - self.assertContains( - response, - "Authorize {name}".format(name=self.dot_app.name) - ) - - # are the cancel and allow buttons on the page? - self.assertContains( - response, - 'Cancel' - ) - self.assertContains( - response, - 'Allow' - ) - def _check_dot_response(self, response): """ Check that django-oauth-toolkit gives an appropriate authorization response. @@ -475,11 +327,12 @@ class TestViewDispatch(TestCase): self.assertRaises(KeyError, view_object.get_view_for_backend, None) -class TestRevokeTokenView(AccessTokenLoginMixin, _DispatchingViewTestCase): # pylint: disable=abstract-method +class TestRevokeTokenView(_DispatchingViewTestCase): # pylint: disable=abstract-method """ Test class for RevokeTokenView """ def setUp(self): + self.login_with_access_token_url = reverse("login_with_access_token") self.revoke_token_url = reverse('revoke_token') self.access_token_url = reverse('access_token') @@ -521,6 +374,27 @@ class TestRevokeTokenView(AccessTokenLoginMixin, _DispatchingViewTestCase): # p 'token': token, } + def login_with_access_token(self): + """ + Login with access token and return response + """ + return self.client.post( + self.login_with_access_token_url, + HTTP_AUTHORIZATION="Bearer {0}".format(self.access_token) + ) + + def _assert_access_token_is_valid(self): + """ + Asserts that oauth assigned access_token is valid and usable + """ + self.assertEqual(self.login_with_access_token().status_code, 204) + + def _assert_access_token_invalidated(self): + """ + Asserts that oauth assigned access_token is not valid + """ + self.assertEqual(self.login_with_access_token().status_code, 401) + def _assert_refresh_token_invalidated(self): """ Asserts that oauth assigned refresh_token is not valid diff --git a/openedx/core/lib/api/tests/test_authentication.py b/openedx/core/lib/api/tests/test_authentication.py index 5a92cd2905..17370706ab 100644 --- a/openedx/core/lib/api/tests/test_authentication.py +++ b/openedx/core/lib/api/tests/test_authentication.py @@ -11,7 +11,6 @@ from collections import namedtuple import ddt from datetime import datetime, timedelta -from django.conf import settings from django.conf.urls import patterns, url, include from django.contrib.auth.models import User from django.http import HttpResponse @@ -27,7 +26,6 @@ from rest_framework.test import APIRequestFactory, APIClient from rest_framework.views import APIView from rest_framework_oauth import permissions from rest_framework_oauth.compat import oauth2_provider, oauth2_provider_scope -import unittest from openedx.core.djangoapps.oauth_dispatch import adapters from openedx.core.lib.api import authentication @@ -75,7 +73,6 @@ urlpatterns = patterns( @attr(shard=2) @ddt.ddt -@unittest.skipUnless(settings.FEATURES.get("ENABLE_OAUTH2_PROVIDER"), "OAuth2 not enabled") class OAuth2Tests(TestCase): """OAuth 2.0 authentication""" urls = 'openedx.core.lib.api.tests.test_authentication'
{% trans "The above application requests the following permissions from your account:" %}
{% trans "Please click the 'Allow' button to grant these permissions to the above application. Otherwise, to withhold these permissions, please click the 'Cancel' button." %} -
{{ error.description }}