def test_storage_delete(self): # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. response = self.app.get("/foo_path") self.assertTrue(response.status.startswith("302")) m = mox.Mox() m.StubOutWithMock(appengine, "_parse_state_value") appengine._parse_state_value("foo_path:xsrfkey123", mox.IgnoreArg()).AndReturn("foo_path") m.ReplayAll() # Now simulate the callback to /oauth2callback. response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "foo_path:xsrfkey123"}) self.assertEqual("http://localhost/foo_path", response.headers["Location"]) self.assertEqual(None, self.decorator.credentials) # Now requesting the decorated path should work. response = self.app.get("/foo_path") self.assertTrue(self.had_credentials) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Invalidate the stored Credentials. self.found_credentials.store.delete() # Invalid Credentials should start the OAuth dance again. response = self.app.get("/foo_path") self.assertTrue(response.status.startswith("302")) m.UnsetStubs() m.VerifyAll()
def test_storage_delete(self): # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) m = mox.Mox() m.StubOutWithMock(appengine, '_parse_state_value') appengine._parse_state_value('foo_path:xsrfkey123', mox.IgnoreArg()).AndReturn('foo_path') m.ReplayAll() # Now simulate the callback to /oauth2callback. response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'foo_path:xsrfkey123', }) self.assertEqual('http://localhost/foo_path', response.headers['Location']) self.assertEqual(None, self.decorator.credentials) # Now requesting the decorated path should work. response = self.app.get('/foo_path') # Invalidate the stored Credentials. self.decorator.credentials.store.delete() # Invalid Credentials should start the OAuth dance again. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) m.UnsetStubs() m.VerifyAll()
def test_aware(self): # An initial request to an oauth_aware decorated path should not redirect. response = self.app.get('http://localhost/bar_path/2012/01') self.assertEqual('Hello World!', response.body) self.assertEqual('200 OK', response.status) self.assertEqual(False, self.decorator.has_credentials()) url = self.decorator.authorize_url() q = urlparse.parse_qs(url.split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) self.assertEqual('foo_client_id', q['client_id'][0]) self.assertEqual('foo_scope bar_scope', q['scope'][0]) self.assertEqual('http://localhost/bar_path/2012/01', q['state'][0].rsplit(':', 1)[0]) self.assertEqual('code', q['response_type'][0]) m = mox.Mox() m.StubOutWithMock(appengine, '_parse_state_value') appengine._parse_state_value('bar_path:xsrfkey456', mox.IgnoreArg()).AndReturn('bar_path') m.ReplayAll() # Now simulate the callback to /oauth2callback. url = self.decorator.authorize_url() response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'bar_path:xsrfkey456', }) self.assertEqual('http://localhost/bar_path', response.headers['Location']) self.assertEqual(False, self.decorator.has_credentials()) m.UnsetStubs() m.VerifyAll() # Now requesting the decorated path will have credentials. response = self.app.get('/bar_path/2012/01') self.assertEqual('200 OK', response.status) self.assertEqual('Hello World!', response.body) self.assertEqual(True, self.had_credentials) self.assertEqual('foo_refresh_token', self.found_credentials.refresh_token) self.assertEqual('foo_access_token', self.found_credentials.access_token) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = True try: response = self.app.get('/bar_path/2012/01') self.fail('Should have raised an exception.') except Exception: pass self.assertEqual(None, self.decorator.credentials) self.should_raise = False
def test_aware(self): # An initial request to an oauth_aware decorated path should not redirect. response = self.app.get('http://localhost/bar_path/2012/01') self.assertEqual('Hello World!', response.body) self.assertEqual('200 OK', response.status) self.assertEqual(False, self.decorator.has_credentials()) url = self.decorator.authorize_url() q = parse_qs(url.split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) self.assertEqual('foo_client_id', q['client_id'][0]) self.assertEqual('foo_scope bar_scope', q['scope'][0]) self.assertEqual('http://localhost/bar_path/2012/01', q['state'][0].rsplit(':', 1)[0]) self.assertEqual('code', q['response_type'][0]) m = mox.Mox() m.StubOutWithMock(appengine, '_parse_state_value') appengine._parse_state_value('bar_path:xsrfkey456', mox.IgnoreArg()).AndReturn('bar_path') m.ReplayAll() # Now simulate the callback to /oauth2callback. url = self.decorator.authorize_url() response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'bar_path:xsrfkey456', }) self.assertEqual('http://localhost/bar_path', response.headers['Location']) self.assertEqual(False, self.decorator.has_credentials()) m.UnsetStubs() m.VerifyAll() # Now requesting the decorated path will have credentials. response = self.app.get('/bar_path/2012/01') self.assertEqual('200 OK', response.status) self.assertEqual('Hello World!', response.body) self.assertEqual(True, self.had_credentials) self.assertEqual('foo_refresh_token', self.found_credentials.refresh_token) self.assertEqual('foo_access_token', self.found_credentials.access_token) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = True try: response = self.app.get('/bar_path/2012/01') self.fail('Should have raised an exception.') except Exception: pass self.assertEqual(None, self.decorator.credentials) self.should_raise = False
def test_required(self): # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. response = self.app.get('http://localhost/foo_path') self.assertTrue(response.status.startswith('302')) q = parse_qs(response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) self.assertEqual('foo_client_id', q['client_id'][0]) self.assertEqual('foo_scope bar_scope', q['scope'][0]) self.assertEqual('http://localhost/foo_path', q['state'][0].rsplit(':', 1)[0]) self.assertEqual('code', q['response_type'][0]) self.assertEqual(False, self.decorator.has_credentials()) m = mox.Mox() m.StubOutWithMock(appengine, '_parse_state_value') appengine._parse_state_value('foo_path:xsrfkey123', mox.IgnoreArg()).AndReturn('foo_path') m.ReplayAll() # Now simulate the callback to /oauth2callback. response = self.app.get('/oauth2callback', { 'code': 'foo_access_code', 'state': 'foo_path:xsrfkey123', }) parts = response.headers['Location'].split('?', 1) self.assertEqual('http://localhost/foo_path', parts[0]) self.assertEqual(None, self.decorator.credentials) if self.decorator._token_response_param: response = parse_qs(parts[1])[self.decorator._token_response_param][0] self.assertEqual(Http2Mock.content, simplejson.loads(urllib.unquote(response))) m.UnsetStubs() m.VerifyAll() # Now requesting the decorated path should work. response = self.app.get('/foo_path') self.assertEqual('200 OK', response.status) self.assertEqual(True, self.decorator.has_credentials()) self.assertEqual('foo_refresh_token', self.decorator.credentials.refresh_token) self.assertEqual('foo_access_token', self.decorator.credentials.access_token) # Invalidate the stored Credentials. self.decorator.credentials.invalid = True self.decorator.credentials.store.put(self.decorator.credentials) # Invalid Credentials should start the OAuth dance again. response = self.app.get('/foo_path') self.assertTrue(response.status.startswith('302')) q = parse_qs(response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
def test_aware(self): # An initial request to an oauth_aware decorated path should not redirect. response = self.app.get("http://localhost/bar_path/2012/01") self.assertEqual("Hello World!", response.body) self.assertEqual("200 OK", response.status) self.assertEqual(False, self.decorator.has_credentials()) url = self.decorator.authorize_url() q = parse_qs(url.split("?", 1)[1]) self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0]) self.assertEqual("foo_client_id", q["client_id"][0]) self.assertEqual("foo_scope bar_scope", q["scope"][0]) self.assertEqual("http://localhost/bar_path/2012/01", q["state"][0].rsplit(":", 1)[0]) self.assertEqual("code", q["response_type"][0]) m = mox.Mox() m.StubOutWithMock(appengine, "_parse_state_value") appengine._parse_state_value("bar_path:xsrfkey456", mox.IgnoreArg()).AndReturn("bar_path") m.ReplayAll() # Now simulate the callback to /oauth2callback. url = self.decorator.authorize_url() response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "bar_path:xsrfkey456"}) self.assertEqual("http://localhost/bar_path", response.headers["Location"]) self.assertEqual(False, self.decorator.has_credentials()) m.UnsetStubs() m.VerifyAll() # Now requesting the decorated path will have credentials. response = self.app.get("/bar_path/2012/01") self.assertEqual("200 OK", response.status) self.assertEqual("Hello World!", response.body) self.assertEqual(True, self.had_credentials) self.assertEqual("foo_refresh_token", self.found_credentials.refresh_token) self.assertEqual("foo_access_token", self.found_credentials.access_token) # Credentials should be cleared after each call. self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = True try: response = self.app.get("/bar_path/2012/01") self.fail("Should have raised an exception.") except Exception: pass self.assertEqual(None, self.decorator.credentials) self.should_raise = False
def test_build_and_parse_state(self): state = appengine._build_state_value(MockRequestHandler(), UserMock()) self.assertEqual( 'https://example.org', appengine._parse_state_value(state, UserMock())) self.assertRaises(appengine.InvalidXsrfTokenError, appengine._parse_state_value, state[1:], UserMock())
def get(self): # In order to use our own User class and webapp2 sessions # for user management instead of the App Engine Users API (which requires # showing a very ugly sign in page and requires the user to authorize # Google twice, essentially), we've created our own version of oauth2client's # OAuth2CallbackHandler. error = self.request.get('error') if error: message = self.request.get('error_description', error) logging.error(message) self.response.out.write('Authorization request failed.') return # Resume the oauth flow. self.decorator._create_flow(self) credentials = self.decorator.flow.step2_exchange(self.request.params) # Get a Google Account ID for the user that just OAuthed in. http = credentials.authorize(httplib2.Http(memcache)) service = discovery.build('oauth2', 'v2', http=httplib2.Http(memcache)) # Keys are: name, email, given_name, family_name, link, locale, id, # gender, verified_email (which is a bool), picture (url). data = service.userinfo().v2().me().get().execute(http=http) auth_id = 'google:{}'.format(data['id']) # If the user is returning, try and find an existing User. # If the user is signing in for the first time, create a User. user = self.user_model.get_by_auth_id(auth_id) if user is None: nickname = data['email'] data.pop('id', None) unique_properties = ['nickname', 'email'] ok, user = self.user_model.create_user( auth_id, unique_properties=unique_properties, nickname=nickname, **data) if not ok: logging.exception('Invalid values: {}'.format(user)) self.error(500, 'Error creating user.') return # Store the user in the session. self.auth.set_session({'user_id': auth_id}, remember=True) session_user = users.UserStub(self.session['sid']) redirect_uri = appengine._parse_state_value( str(self.request.get('state')), session_user) # Store the user's credentials for later possible use. storage = self.decorator._storage_class( model=self.decorator._credentials_class, key_name='user:{}'.format(user.user_id()), property_name=self.decorator._credentials_property_name) storage.put(credentials) # Adjust the redirect uri in case this callback occurred as part of an # authenticated request to get some data. if self.decorator._token_response_param and credentials.token_response: resp = json.dumps(credentials.token_response) redirect_uri = appengine.util._add_query_parameter( redirect_uri, self.decorator._token_response_param, resp) self.redirect(redirect_uri)
def test_required(self): # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. self.assertEqual(self.decorator.flow, None) self.assertEqual(self.decorator.credentials, None) response = self.app.get("http://localhost/foo_path") self.assertTrue(response.status.startswith("302")) q = parse_qs(response.headers["Location"].split("?", 1)[1]) self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0]) self.assertEqual("foo_client_id", q["client_id"][0]) self.assertEqual("foo_scope bar_scope", q["scope"][0]) self.assertEqual("http://localhost/foo_path", q["state"][0].rsplit(":", 1)[0]) self.assertEqual("code", q["response_type"][0]) self.assertEqual(False, self.decorator.has_credentials()) m = mox.Mox() m.StubOutWithMock(appengine, "_parse_state_value") appengine._parse_state_value("foo_path:xsrfkey123", mox.IgnoreArg()).AndReturn("foo_path") m.ReplayAll() # Now simulate the callback to /oauth2callback. response = self.app.get("/oauth2callback", {"code": "foo_access_code", "state": "foo_path:xsrfkey123"}) parts = response.headers["Location"].split("?", 1) self.assertEqual("http://localhost/foo_path", parts[0]) self.assertEqual(None, self.decorator.credentials) if self.decorator._token_response_param: response = parse_qs(parts[1])[self.decorator._token_response_param][0] self.assertEqual(Http2Mock.content, simplejson.loads(urllib.unquote(response))) self.assertEqual(self.decorator.flow, self.decorator._tls.flow) self.assertEqual(self.decorator.credentials, self.decorator._tls.credentials) m.UnsetStubs() m.VerifyAll() # Now requesting the decorated path should work. response = self.app.get("/foo_path") self.assertEqual("200 OK", response.status) self.assertEqual(True, self.had_credentials) self.assertEqual("foo_refresh_token", self.found_credentials.refresh_token) self.assertEqual("foo_access_token", self.found_credentials.access_token) self.assertEqual(None, self.decorator.credentials) # Raising an exception still clears the Credentials. self.should_raise = True try: response = self.app.get("/foo_path") self.fail("Should have raised an exception.") except Exception: pass self.assertEqual(None, self.decorator.credentials) self.should_raise = False # Invalidate the stored Credentials. self.found_credentials.invalid = True self.found_credentials.store.put(self.found_credentials) # Invalid Credentials should start the OAuth dance again. response = self.app.get("/foo_path") self.assertTrue(response.status.startswith("302")) q = parse_qs(response.headers["Location"].split("?", 1)[1]) self.assertEqual("http://localhost/oauth2callback", q["redirect_uri"][0])