class DOTAdapterMixin(object): """ Mixin to rewire existing tests to use django-oauth-toolkit (DOT) backend Overwrites self.client_id, self.access_token, self.oauth2_adapter """ client_id = 'dot_test_client_id' access_token = 'dot_test_access_token' oauth2_adapter = adapters.DOTAdapter() def create_public_client(self, user, client_id=None): """ Create an oauth client application that is public. """ return self.oauth2_adapter.create_public_client( name='Test Public Application', user=user, client_id=client_id, redirect_uri=DUMMY_REDIRECT_URL, ) def create_confidential_client(self, user, client_id=None): """ Create an oauth client application that is confidential. """ return self.oauth2_adapter.create_confidential_client( name='Test Confidential Application', user=user, client_id=client_id, redirect_uri=DUMMY_REDIRECT_URL, ) def get_token_response_keys(self): """ Return the set of keys provided when requesting an access token """ return { 'access_token', 'refresh_token', 'token_type', 'expires_in', 'scope' } def test_get_method(self): # Dispatch routes all get methods to DOP, so we test this on the view request_factory = RequestFactory() request = request_factory.get('/oauth2/exchange_access_token/') request.session = {} view = DOTAccessTokenExchangeView.as_view() response = view(request, backend='facebook') self.assertEqual(response.status_code, 400) @expectedFailure def test_single_access_token(self): # TODO: Single access tokens not supported yet for DOT (See MA-2122) super(DOTAdapterMixin, self).test_single_access_token() @skip("Not supported yet (See MA-2123)") def test_scopes(self): super(DOTAdapterMixin, self).test_scopes()
def setUp(self): super(OAuth2Tests, self).setUp() self.dop_adapter = adapters.DOPAdapter() self.dot_adapter = adapters.DOTAdapter() self.csrf_client = APIClient(enforce_csrf_checks=True) self.username = '******' self.email = '*****@*****.**' self.password = '******' self.user = User.objects.create_user(self.username, self.email, self.password) self.CLIENT_ID = 'client_key' # pylint: disable=invalid-name self.CLIENT_SECRET = 'client_secret' # pylint: disable=invalid-name self.ACCESS_TOKEN = 'access_token' # pylint: disable=invalid-name self.REFRESH_TOKEN = 'refresh_token' # pylint: disable=invalid-name self.dop_oauth2_client = self.dop_adapter.create_public_client( name='example', user=self.user, client_id=self.CLIENT_ID, redirect_uri='https://example.edx/redirect', ) self.access_token = oauth2_provider.oauth2.models.AccessToken.objects.create( token=self.ACCESS_TOKEN, client=self.dop_oauth2_client, user=self.user, ) self.refresh_token = oauth2_provider.oauth2.models.RefreshToken.objects.create( user=self.user, access_token=self.access_token, client=self.dop_oauth2_client, ) self.dot_oauth2_client = self.dot_adapter.create_public_client( name='example', user=self.user, client_id='dot-client-id', redirect_uri='https://example.edx/redirect', ) self.dot_access_token = dot_models.AccessToken.objects.create( user=self.user, token='dot-access-token', application=self.dot_oauth2_client, expires=datetime.now() + timedelta(days=30), ) # This is the a change we've made from the django-rest-framework-oauth version # of these tests. self.user.is_active = False self.user.save() # This is the a change we've made from the django-rest-framework-oauth version # of these tests. # Override the SCOPE_NAME_DICT setting for tests for oauth2-with-scope-test. This is # needed to support READ and WRITE scopes as they currently aren't supported by the # edx-auth2-provider, and their scope values collide with other scopes defined in the # edx-auth2-provider. scope.SCOPE_NAME_DICT = {'read': constants.READ, 'write': constants.WRITE}
class DOTAccessTokenExchangeView(AccessTokenExchangeBase, DOTAccessTokenView): """ View for token exchange from 3rd party OAuth access token to 1st party OAuth access token. Uses django-oauth-toolkit (DOT) to manage access tokens. """ oauth2_adapter = adapters.DOTAdapter() def get(self, request, _backend): return Response(status=400, data={ 'error': 'invalid_request', 'error_description': 'Only POST requests allowed.', }) def get_access_token(self, request, user, scope, client): """ TODO: MA-2122: Reusing access tokens is not yet supported for DOT. Just return a new access token. """ return self.create_access_token(request, user, scope, client) def create_access_token(self, request, user, scope, client): """ Create and return a new access token. """ _days = 24 * 60 * 60 token_generator = BearerToken( expires_in=settings.OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS * _days, request_validator=oauth2_settings.OAUTH2_VALIDATOR_CLASS(), ) self._populate_create_access_token_request(request, user, scope, client) return token_generator.create_token(request, refresh_token=True) def access_token_response(self, token): """ Wrap an access token in an appropriate response """ return Response(data=token) def _populate_create_access_token_request(self, request, user, scope, client): """ django-oauth-toolkit expects certain non-standard attributes to be present on the request object. This function modifies the request object to match these expectations """ request.user = user request.scopes = [SCOPE_VALUE_DICT[scope]] request.client = client request.state = None request.refresh_token = None request.extra_credentials = None request.grant_type = client.authorization_grant_type def error_response(self, form_errors): """ Return an error response consisting of the errors in the form """ return Response(status=400, data=form_errors)