# Copyright 2016 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit test for django_util views""" import copy import json import django from django import http import django.conf from django.contrib.auth.models import AnonymousUser, User import mock from six.moves import reload_module from tests.contrib.django_util import TestWithDjangoEnvironment from tests.contrib.django_util.models import CredentialsModel from oauth2client.client import FlowExchangeError, OAuth2WebServerFlow import oauth2client.contrib.django_util from oauth2client.contrib.django_util import views from oauth2client.contrib.django_util.models import CredentialsField class OAuth2AuthorizeTest(TestWithDjangoEnvironment): def setUp(self): super(OAuth2AuthorizeTest, self).setUp() self.save_settings = copy.deepcopy(django.conf.settings) reload_module(oauth2client.contrib.django_util) self.user = User.objects.create_user( username='bill', email='bill@example.com', password='hunter2') def tearDown(self): django.conf.settings = copy.deepcopy(self.save_settings) def test_authorize_works(self): request = self.factory.get('oauth2/oauth2authorize') request.session = self.session request.user = self.user response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) def test_authorize_anonymous_user(self): request = self.factory.get('oauth2/oauth2authorize') request.session = self.session request.user = AnonymousUser() response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) def test_authorize_works_explicit_return_url(self): request = self.factory.get('oauth2/oauth2authorize', data={'return_url': '/return_endpoint'}) request.session = self.session request.user = self.user response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) class Oauth2AuthorizeStorageModelTest(TestWithDjangoEnvironment): def setUp(self): super(Oauth2AuthorizeStorageModelTest, self).setUp() self.save_settings = copy.deepcopy(django.conf.settings) STORAGE_MODEL = { 'model': 'tests.contrib.django_util.models.CredentialsModel', 'user_property': 'user_id', 'credentials_property': 'credentials' } django.conf.settings.GOOGLE_OAUTH2_STORAGE_MODEL = STORAGE_MODEL # OAuth2 Settings gets configured based on Django settings # at import time, so in order for us to reload the settings # we need to reload the module reload_module(oauth2client.contrib.django_util) self.user = User.objects.create_user( username='bill', email='bill@example.com', password='hunter2') def tearDown(self): django.conf.settings = copy.deepcopy(self.save_settings) def test_authorize_works(self): request = self.factory.get('oauth2/oauth2authorize') request.session = self.session request.user = self.user response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) # redirects to Google oauth self.assertIn('accounts.google.com', response.url) def test_authorize_anonymous_user_redirects_login(self): request = self.factory.get('oauth2/oauth2authorize') request.session = self.session request.user = AnonymousUser() response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) # redirects to Django login self.assertIn(django.conf.settings.LOGIN_URL, response.url) def test_authorize_works_explicit_return_url(self): request = self.factory.get('oauth2/oauth2authorize', data={'return_url': '/return_endpoint'}) request.session = self.session request.user = self.user response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) def test_authorized_user_not_logged_in_redirects(self): request = self.factory.get('oauth2/oauth2authorize', data={'return_url': '/return_endpoint'}) request.session = self.session authorized_user = User.objects.create_user( username='bill2', email='bill@example.com', password='hunter2') credentials = CredentialsField() CredentialsModel.objects.create( user_id=authorized_user, credentials=credentials) request.user = authorized_user response = views.oauth2_authorize(request) self.assertIsInstance(response, http.HttpResponseRedirect) class Oauth2CallbackTest(TestWithDjangoEnvironment): def setUp(self): super(Oauth2CallbackTest, self).setUp() self.save_settings = copy.deepcopy(django.conf.settings) reload_module(oauth2client.contrib.django_util) self.CSRF_TOKEN = 'token' self.RETURN_URL = 'http://return-url.com' self.fake_state = { 'csrf_token': self.CSRF_TOKEN, 'return_url': self.RETURN_URL, 'scopes': django.conf.settings.GOOGLE_OAUTH2_SCOPES } self.user = User.objects.create_user( username='bill', email='bill@example.com', password='hunter2') @mock.patch('oauth2client.contrib.django_util.views.pickle') def test_callback_works(self, pickle): request = self.factory.get('oauth2/oauth2callback', data={ 'state': json.dumps(self.fake_state), 'code': 123 }) self.session['google_oauth2_csrf_token'] = self.CSRF_TOKEN flow = OAuth2WebServerFlow( client_id='clientid', client_secret='clientsecret', scope=['email'], state=json.dumps(self.fake_state), redirect_uri=request.build_absolute_uri("oauth2/oauth2callback")) name = 'google_oauth2_flow_{0}'.format(self.CSRF_TOKEN) self.session[name] = pickle.dumps(flow) flow.step2_exchange = mock.Mock() pickle.loads.return_value = flow request.session = self.session request.user = self.user response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseRedirect) self.assertEqual( response.status_code, django.http.HttpResponseRedirect.status_code) self.assertEqual(response['Location'], self.RETURN_URL) @mock.patch('oauth2client.contrib.django_util.views.pickle') def test_callback_handles_bad_flow_exchange(self, pickle): request = self.factory.get('oauth2/oauth2callback', data={ "state": json.dumps(self.fake_state), "code": 123 }) self.session['google_oauth2_csrf_token'] = self.CSRF_TOKEN flow = OAuth2WebServerFlow( client_id='clientid', client_secret='clientsecret', scope=['email'], state=json.dumps(self.fake_state), redirect_uri=request.build_absolute_uri('oauth2/oauth2callback')) self.session['google_oauth2_flow_{0}'.format(self.CSRF_TOKEN)] \ = pickle.dumps(flow) def local_throws(code): raise FlowExchangeError('test') flow.step2_exchange = local_throws pickle.loads.return_value = flow request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) def test_error_returns_bad_request(self): request = self.factory.get('oauth2/oauth2callback', data={ 'error': 'There was an error in your authorization.', }) response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) self.assertIn(b'Authorization failed', response.content) def test_no_session(self): request = self.factory.get('oauth2/oauth2callback', data={ 'code': 123, 'state': json.dumps(self.fake_state) }) request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) self.assertEqual( response.content, b'No existing session for this flow.') def test_missing_state_returns_bad_request(self): request = self.factory.get('oauth2/oauth2callback', data={ 'code': 123 }) self.session['google_oauth2_csrf_token'] = "token" request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) def test_bad_state(self): request = self.factory.get('oauth2/oauth2callback', data={ 'code': 123, 'state': json.dumps({'wrong': 'state'}) }) self.session['google_oauth2_csrf_token'] = 'token' request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) self.assertEqual(response.content, b'Invalid state parameter.') def test_bad_csrf(self): request = self.factory.get('oauth2/oauth2callback', data={ "state": json.dumps(self.fake_state), "code": 123 }) self.session['google_oauth2_csrf_token'] = 'WRONG TOKEN' request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) self.assertEqual(response.content, b'Invalid CSRF token.') def test_no_saved_flow(self): request = self.factory.get('oauth2/oauth2callback', data={ 'state': json.dumps(self.fake_state), 'code': 123 }) self.session['google_oauth2_csrf_token'] = self.CSRF_TOKEN self.session['google_oauth2_flow_{0}'.format(self.CSRF_TOKEN)] = None request.session = self.session response = views.oauth2_callback(request) self.assertIsInstance(response, http.HttpResponseBadRequest) self.assertEqual(response.content, b'Missing Oauth2 flow.')