def test_token_authorization(self): r"""Test how token_authorization behaves in GET and POST requests""" env = self._make_environ() p = token_authorization(engine=self.engine) session = p.manager.DBSession # First try an empty environment self.eval_unmet_predicate(p, env, 'No valid matching OAuth token found') # Then try a non-existing token env['QUERY_STRING'] = urlencode(dict(oauth_token='some-token')) self.eval_unmet_predicate(p, env, 'No valid matching OAuth token found') # There is no token in the environment self.assertFalse(env['repoze.what.oauth'].get('token')) # Now create a consumer and the token and try again consumer = Consumer(key='some-consumer', secret='some-secret') token = RequestToken.create( consumer, session=session, key='some-token', callback=u'http://www.test.com/some/path?x=1&y=%20a') session.add(consumer) session.flush() # This time we are passed through self.eval_met_predicate(p, env) # Environment now contains a token which was found according to the # query string parameters self.assertEquals(env['repoze.what.oauth']['token'], token) # Now construct a POST query and expect to find a callback function to # authorize the request token env = self._make_environ() env['REQUEST_METHOD'] = 'POST' self.eval_met_predicate(p, env) callback_maker = env['repoze.what.oauth']['make_callback'] self.assertTrue(callback_maker) # We must provide a request token key and a userid to authorize a # request callback callback = callback_maker('some-token', u'some-user') self.assertEquals(len(callback['verifier']), 6) self.assertTrue(callback['verifier'] in callback['url']) # If the token callback url was provided as 'oob' (out of band) then the # callback['url'] should also specify oob token.callback = u'oob' callback = callback_maker('some-token', u'some-user') self.assertEquals(callback['url'], 'oob')
def test_token_authorization(self): r"""Test how token_authorization behaves in GET and POST requests""" env = self._make_environ() p = token_authorization(engine=self.engine) session = p.manager.DBSession # First try an empty environment self.eval_unmet_predicate(p, env, 'No valid matching OAuth token found') # Then try a non-existing token env['QUERY_STRING'] = urlencode(dict(oauth_token='some-token')) self.eval_unmet_predicate(p, env, 'No valid matching OAuth token found') # There is no token in the environment self.assertFalse(env['repoze.what.oauth'].get('token')) # Now create a consumer and the token and try again consumer = Consumer(key='some-consumer', secret='some-secret') token = RequestToken.create(consumer, session=session, key='some-token', callback=u'http://www.test.com/some/path?x=1&y=%20a') session.add(consumer) session.flush() # This time we are passed through self.eval_met_predicate(p, env) # Environment now contains a token which was found according to the # query string parameters self.assertEquals(env['repoze.what.oauth']['token'], token) # Now construct a POST query and expect to find a callback function to # authorize the request token env = self._make_environ() env['REQUEST_METHOD'] = 'POST' self.eval_met_predicate(p, env) callback_maker = env['repoze.what.oauth']['make_callback'] self.assertTrue(callback_maker) # We must provide a request token key and a userid to authorize a # request callback callback = callback_maker('some-token', u'some-user') self.assertEquals(len(callback['verifier']), 6) self.assertTrue(callback['verifier'] in callback['url']) # If the token callback url was provided as 'oob' (out of band) then the # callback['url'] should also specify oob token.callback = u'oob' callback = callback_maker('some-token', u'some-user') self.assertEquals(callback['url'], 'oob')
def test_3_legged_flow(self): r"""Test a 2-legged flow end-to-end""" # The OAuth spec allows the access token to be the same as request # token. Let's try this here plugin = self._makeOne(access_token_path='/oauth/request_token') std_env_params = { 'wsgi.url_scheme': 'http', 'SERVER_NAME': 'www.example.com', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'POST', 'QUERY_STRING': '', 'wsgi.input': '', } # Create one consumer in our DB from repoze.who.plugins.oauth.model import (Consumer, RequestToken, AccessToken) self.session.add(Consumer(key='cons1', secret='secret1')) self.session.flush() # Construct a nice request token request and try to pass the # authenticator check. We do not have any tokens yet consumer = oauth2.Consumer('cons1', 'secret1') req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=None, http_method='POST', http_url='http://www.example.com/oauth/request_token', parameters=dict(oauth_callback='http://test.com/?x=2')) req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=None) # Pass the oauth parameters in the Authorization header env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # While userid now contains the key of the consumer we don't care much # about it as the downstream application will never execute. What is # more important, `authenticate` replaced the downstream app with a # custom one. self.assertEquals(userid, 'consumer:%s' % consumer.key) app = environ['repoze.who.application'] def assertUrlEncoded(code, headers, *args): self.assertEquals( dict(headers)['Content-Type'], 'application/x-www-form-urlencoded') # The custom app will return a new request token for this consumer enc_token = ''.join(app(environ, assertUrlEncoded)) # Decode the token dec_token = parse_qs(enc_token) # And create an oauth equivalent rtoken = oauth2.Token(key=dec_token['oauth_token'][0], secret=dec_token['oauth_token_secret'][0]) # Check token attributes # Autogenerated key and secret should be 40 chars long self.assertEquals(len(rtoken.key), 40) self.assertEquals(len(rtoken.secret), 40) # The plugin supports the updated OAuth specification self.assertEquals(dec_token['oauth_callback_confirmed'][0], 'true') # Such a token really exists dbtoken = plugin.manager.get_request_token(key=rtoken.key) self.assertEquals(dbtoken.secret, rtoken.secret) # And it really belongs to our consumer self.assertEquals(dbtoken.consumer.key, consumer.key) # And the callback url was set correctly self.assertEquals(dbtoken.callback, u'http://test.com/?x=2') # Now that we have the request token we should ask the user to authorize # it env_params = {} env_params.update(std_env_params) env_params.update({ 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/oauth/authorize', 'QUERY_STRING': urlencode(dict(oauth_token=rtoken.key)) }) environ = self._makeEnviron(env_params) # The token_authorization predicate is supposed to protect the token # authorization method from repoze.what.plugins.oauth import token_authorization authorizer = token_authorization(engine=self.engine) authorizer.check_authorization(environ) # environ now stores the same token taken from the DB. And we can use # the information associated with that token self.assertEquals(environ['repoze.what.oauth']['token'].key, rtoken.key) self.assertEquals(environ['repoze.what.oauth']['token'].consumer.key, consumer.key) # Suppose a user confirms that the consumer is legitimate and gives # permission to the user resources. Usually it will happen through a # form POSTed to 'authorize'. The predicate will intercept that and add # a method to environ which will mark the request token as validated and # create a verification key environ['REQUEST_METHOD'] = 'POST' authorizer.check_authorization(environ) # The request token validator method got added to the environ callback_maker = environ['repoze.what.oauth']['make_callback'] # Call it providing the request token key and a userid - a callback dict # is returned callback = callback_maker(rtoken.key, u'some-user') # The autogenerated verifier should be 6 chars long self.assertEquals(len(callback['verifier']), 6) # And normally it is included in the callback url (except for # out-of-band callbacks) self.assertTrue(callback['verifier'] in callback['url']) # The request token is now attached to the userid self.assertEquals(environ['repoze.what.oauth']['token'].userid, u'some-user') # Now that we have the request token verified we can convert it to an # access token # Set the token verifier - a wrong one first rtoken.set_verifier('-wrong-') # Create a new request using the new request token and verifier req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=rtoken, http_method='POST', http_url='http://www.example.com/oauth/request_token') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=rtoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], # This url handles both request and access tokens 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) # Force manager to reload all the objects as they might be out of date plugin.manager.DBSession.expire_all() # As we are providing a request token and a verifier we should get an # access token in exchange. identity = plugin.identify(environ) self.assertEquals(plugin.authenticate(environ, identity), None) # ... and we failed with the wrong verification code # Check that the plugin returned an Unauthorized response def start_401_response(code, *args): self.assertTrue(code.startswith('401')) environ['repoze.who.application'](environ, start_401_response) # Now create a request with the correct verifier rtoken.set_verifier(callback['verifier']) # Create a new request using the new request token and verifier req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=rtoken, http_method='POST', http_url='http://www.example.com/oauth/request_token') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=rtoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) # As we are providing a request token and a verifier we should get an # access token in exchange identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # The repoze.who.application now contains an app that will remove the # request token, create a new access token and return it as a parameter. # Let's call it and see. app = environ['repoze.who.application'] enc_token = ''.join(app(environ, assertUrlEncoded)) # Decode the access token dec_token = parse_qs(enc_token) # Create a local oauth equivalent of the access token atoken = oauth2.Token(key=dec_token['oauth_token'][0], secret=dec_token['oauth_token_secret'][0]) # If we repeat the request we get a 401 again identity = plugin.identify(environ) self.assertEquals(plugin.authenticate(environ, identity), None) environ['repoze.who.application'](environ, start_401_response) # So now we have a valid access token. Let's try an authorized request req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=atoken, http_method='GET', http_url='http://www.example.com/app') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=atoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/app', } env_params.update(std_env_params) env_params['REQUEST_METHOD'] = 'GET' environ = self._makeEnviron(env_params) identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # We got a *user* id self.assertEquals(userid, 'some-user') # Identity also contains the consumer and consumer key self.assertEquals(identity['repoze.who.consumerkey'], consumer.key) # And we're good to go with the downstream app self.assertEquals(environ.get('repoze.who.application'), None) # Cleanup consumers self.session.execute(Consumer.__table__.delete())
def test_3_legged_flow(self): r"""Test a 2-legged flow end-to-end""" # The OAuth spec allows the access token to be the same as request # token. Let's try this here plugin = self._makeOne(access_token_path='/oauth/request_token') std_env_params = { 'wsgi.url_scheme': 'http', 'SERVER_NAME': 'www.example.com', 'SERVER_PORT': '80', 'REQUEST_METHOD': 'POST', 'QUERY_STRING': '', 'wsgi.input': '', } # Create one consumer in our DB from repoze.who.plugins.oauth.model import (Consumer, RequestToken, AccessToken) self.session.add(Consumer(key='cons1', secret='secret1')) self.session.flush() # Construct a nice request token request and try to pass the # authenticator check. We do not have any tokens yet consumer = oauth2.Consumer('cons1', 'secret1') req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=None, http_method='POST', http_url='http://www.example.com/oauth/request_token', parameters=dict(oauth_callback='http://test.com/?x=2')) req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=None) # Pass the oauth parameters in the Authorization header env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # While userid now contains the key of the consumer we don't care much # about it as the downstream application will never execute. What is # more important, `authenticate` replaced the downstream app with a # custom one. self.assertEquals(userid, 'consumer:%s' % consumer.key) app = environ['repoze.who.application'] def assertUrlEncoded(code, headers, *args): self.assertEquals(dict(headers)['Content-Type'], 'application/x-www-form-urlencoded') # The custom app will return a new request token for this consumer enc_token = ''.join(app(environ, assertUrlEncoded)) # Decode the token dec_token = parse_qs(enc_token) # And create an oauth equivalent rtoken = oauth2.Token(key=dec_token['oauth_token'][0], secret=dec_token['oauth_token_secret'][0]) # Check token attributes # Autogenerated key and secret should be 40 chars long self.assertEquals(len(rtoken.key), 40) self.assertEquals(len(rtoken.secret), 40) # The plugin supports the updated OAuth specification self.assertEquals(dec_token['oauth_callback_confirmed'][0], 'true') # Such a token really exists dbtoken = plugin.manager.get_request_token(key=rtoken.key) self.assertEquals(dbtoken.secret, rtoken.secret) # And it really belongs to our consumer self.assertEquals(dbtoken.consumer.key, consumer.key) # And the callback url was set correctly self.assertEquals(dbtoken.callback, u'http://test.com/?x=2') # Now that we have the request token we should ask the user to authorize # it env_params = {} env_params.update(std_env_params) env_params.update({ 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/oauth/authorize', 'QUERY_STRING': urlencode(dict(oauth_token=rtoken.key)) }) environ = self._makeEnviron(env_params) # The token_authorization predicate is supposed to protect the token # authorization method from repoze.what.plugins.oauth import token_authorization authorizer = token_authorization(engine=self.engine) authorizer.check_authorization(environ) # environ now stores the same token taken from the DB. And we can use # the information associated with that token self.assertEquals(environ['repoze.what.oauth']['token'].key, rtoken.key) self.assertEquals(environ['repoze.what.oauth']['token'].consumer.key, consumer.key) # Suppose a user confirms that the consumer is legitimate and gives # permission to the user resources. Usually it will happen through a # form POSTed to 'authorize'. The predicate will intercept that and add # a method to environ which will mark the request token as validated and # create a verification key environ['REQUEST_METHOD'] = 'POST' authorizer.check_authorization(environ) # The request token validator method got added to the environ callback_maker = environ['repoze.what.oauth']['make_callback'] # Call it providing the request token key and a userid - a callback dict # is returned callback = callback_maker(rtoken.key, u'some-user') # The autogenerated verifier should be 6 chars long self.assertEquals(len(callback['verifier']), 6) # And normally it is included in the callback url (except for # out-of-band callbacks) self.assertTrue(callback['verifier'] in callback['url']) # The request token is now attached to the userid self.assertEquals(environ['repoze.what.oauth']['token'].userid, u'some-user') # Now that we have the request token verified we can convert it to an # access token # Set the token verifier - a wrong one first rtoken.set_verifier('-wrong-') # Create a new request using the new request token and verifier req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=rtoken, http_method='POST', http_url='http://www.example.com/oauth/request_token') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=rtoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], # This url handles both request and access tokens 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) # Force manager to reload all the objects as they might be out of date plugin.manager.DBSession.expire_all() # As we are providing a request token and a verifier we should get an # access token in exchange. identity = plugin.identify(environ) self.assertEquals(plugin.authenticate(environ, identity), None) # ... and we failed with the wrong verification code # Check that the plugin returned an Unauthorized response def start_401_response(code, *args): self.assertTrue(code.startswith('401')) environ['repoze.who.application'](environ, start_401_response) # Now create a request with the correct verifier rtoken.set_verifier(callback['verifier']) # Create a new request using the new request token and verifier req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=rtoken, http_method='POST', http_url='http://www.example.com/oauth/request_token') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=rtoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/oauth/request_token', } env_params.update(std_env_params) environ = self._makeEnviron(env_params) # As we are providing a request token and a verifier we should get an # access token in exchange identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # The repoze.who.application now contains an app that will remove the # request token, create a new access token and return it as a parameter. # Let's call it and see. app = environ['repoze.who.application'] enc_token = ''.join(app(environ, assertUrlEncoded)) # Decode the access token dec_token = parse_qs(enc_token) # Create a local oauth equivalent of the access token atoken = oauth2.Token(key=dec_token['oauth_token'][0], secret=dec_token['oauth_token_secret'][0]) # If we repeat the request we get a 401 again identity = plugin.identify(environ) self.assertEquals(plugin.authenticate(environ, identity), None) environ['repoze.who.application'](environ, start_401_response) # So now we have a valid access token. Let's try an authorized request req = oauth2.Request.from_consumer_and_token( consumer=consumer, token=atoken, http_method='GET', http_url='http://www.example.com/app') req.sign_request(signature_method=oauth2.SignatureMethod_HMAC_SHA1(), consumer=consumer, token=atoken) env_params = { 'HTTP_AUTHORIZATION': req.to_header()['Authorization'], 'PATH_INFO': '/app', } env_params.update(std_env_params) env_params['REQUEST_METHOD'] = 'GET' environ = self._makeEnviron(env_params) identity = plugin.identify(environ) userid = plugin.authenticate(environ, identity) # We got a *user* id self.assertEquals(userid, 'some-user') # Identity also contains the consumer and consumer key self.assertEquals(identity['repoze.who.consumerkey'], consumer.key) # And we're good to go with the downstream app self.assertEquals(environ.get('repoze.who.application'), None) # Cleanup consumers self.session.execute(Consumer.__table__.delete())