class WebAuthorizeHandler(tornado.web.RequestHandler): """ oauth 用户授权 """ def initialize(self): # 初始化 oauth2 后端服务 self._authorization_endpoint = WebApplicationServer(WebValidator()) self._error_uri = self.reverse_url('web-error') def get(self): # 解析 request,包装 oauthlib request uri, http_method, body, headers = extract_params(self.request) redirect_uri = self.get_query_argument('redirect_uri', None) try: # 验证client请求,识别client身份 scopes, credentials = self._authorization_endpoint.validate_authorization_request( uri, http_method, body, headers) except errors.FatalClientError as e: logger.error(e.error) self.finish(e.error) self.redirect(self._error_uri) except errors.OAuth2Error as e: e.redirect_uri = redirect_uri logger.error(e.error) self.finish(e.error) self.redirect(e.in_uri(e.redirect_uri)) # 渲染用户认证授权页面 self.write('<h1> Authorize access to %s </h1>' % credentials['client_id']) self.write('<form method="POST" action="">') for scope in scopes or []: self.write('<input type="checkbox" checked="checked" name="scopes" value="%s"/> %s' % (scope, scope)) self.write('<input type="text" name="username" value="username"/>') self.write('<input type="password" name="password" value="password"/>') self.write('<input type="submit" value="Authorize"/>') def post(self, *args, **kwargs): uri, http_method, body, headers = extract_params(self.request) try: headers, body, status = self._token_endpoint.create_token_response(uri, http_method, body, headers) except errors.FatalClientError as e: logger.error(e) print e self.redirect(self._error_uri) except errors.OAuth2Error as e: logger.error(e) print e self.redirect(self._error_uri) self.set_header('Content-Type', 'application/json') self.finish(body)
class OAuthAuthorizeController(object): def __init__(self, request): self.request = request self.user_svc = self.request.find_service(name='user') validator = self.request.find_service(name='oauth_validator') self.oauth = WebApplicationServer(validator) @view_config(request_method='GET', renderer='h:templates/oauth/authorize.html.jinja2') def get(self): scopes, credentials = self.oauth.validate_authorization_request(self.request.url) if self.request.authenticated_userid is None: raise HTTPFound(self.request.route_url('login', _query={ 'next': self.request.url})) client_id = credentials.get('client_id') state = credentials.get('state') user = self.user_svc.fetch(self.request.authenticated_userid) client = self.request.db.query(models.AuthClient).get(client_id) return {'username': user.username, 'client_name': client.name, 'client_id': client.id, 'response_type': client.response_type.value, 'state': state} @view_config(request_method='POST', effective_principals=security.Authenticated) def post(self): # We don't support scopes at the moment, but oauthlib does need a scope, # so we're explicitly overwriting whatever the client provides. scopes = DEFAULT_SCOPES user = self.user_svc.fetch(self.request.authenticated_userid) credentials = {'user': user} headers, _, status = self.oauth.create_authorization_response( self.request.url, scopes=scopes, credentials=credentials) try: return HTTPFound(location=headers['Location']) except KeyError: client_id = self.request.params.get('client_id') raise RuntimeError('created authorisation code for client "{}" but got no redirect location'.format(client_id))
class TestScopeHandling(TestCase): DEFAULT_REDIRECT_URI = 'http://i.b./path' def set_scopes(self, scopes): def set_request_scopes(client_id, code, client, request): request.scopes = scopes return True return set_request_scopes def set_user(self, request): request.user = '******' request.client_id = 'bar' request.client = mock.MagicMock() request.client.client_id = 'mocked' return True def set_client(self, request): request.client = mock.MagicMock() request.client.client_id = 'mocked' return True def setUp(self): self.validator = mock.MagicMock(spec=RequestValidator) self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI self.validator.authenticate_client.side_effect = self.set_client self.web = WebApplicationServer(self.validator) self.mobile = MobileApplicationServer(self.validator) self.legacy = LegacyApplicationServer(self.validator) self.backend = BackendApplicationServer(self.validator) def test_scope_extraction(self): scopes = ( ('images', ['images']), ('images+videos', ['images', 'videos']), ('http%3A%2f%2fa.b%2fvideos', ['http://a.b/videos']), ('http%3A%2f%2fa.b%2fvideos+pics', ['http://a.b/videos', 'pics']), ('pics+http%3A%2f%2fa.b%2fvideos', ['pics', 'http://a.b/videos']), ('http%3A%2f%2fa.b%2fvideos+https%3A%2f%2fc.d%2Fsecret', ['http://a.b/videos', 'https://c.d/secret']), ) uri = 'http://example.com/path?client_id=abc&scope=%s&response_type=%s' for scope, correct_scopes in scopes: scopes, _ = self.web.validate_authorization_request( uri % (scope, 'code')) self.assertItemsEqual(scopes, correct_scopes) scopes, _ = self.mobile.validate_authorization_request( uri % (scope, 'token')) self.assertItemsEqual(scopes, correct_scopes) def test_scope_preservation(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' decoded_scope = 'pics http://a.b/videos' auth_uri = 'http://example.com/path?client_id=abc&response_type=' token_uri = 'http://example.com/path' # authorization grant h, _, s = self.web.create_authorization_response( auth_uri + 'code', scopes=decoded_scope.split(' ')) self.validator.validate_code.side_effect = self.set_scopes( decoded_scope.split(' ')) self.assertEqual(s, 302) self.assertIn('Location', h) code = get_query_credentials(h['Location'])['code'][0] _, body, _ = self.web.create_token_response( token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], decoded_scope) # implicit grant h, _, s = self.mobile.create_authorization_response( auth_uri + 'token', scopes=decoded_scope.split(' ')) self.assertEqual(s, 302) self.assertIn('Location', h) self.assertEqual( get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) # client credentials grant body = 'grant_type=client_credentials&scope=%s' self.validator.authenticate_client.side_effect = self.set_user _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) def test_scope_changed(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' scopes = ['images', 'http://a.b/videos'] decoded_scope = 'images http://a.b/videos' auth_uri = 'http://example.com/path?client_id=abc&response_type=' token_uri = 'http://example.com/path' # authorization grant h, _, s = self.web.create_authorization_response(auth_uri + 'code', scopes=scopes) self.assertEqual(s, 302) self.assertIn('Location', h) code = get_query_credentials(h['Location'])['code'][0] self.validator.validate_code.side_effect = self.set_scopes(scopes) _, body, _ = self.web.create_token_response( token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], decoded_scope) # implicit grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) h, _, s = self.mobile.create_authorization_response(auth_uri + 'token', scopes=scopes) self.assertEqual(s, 302) self.assertIn('Location', h) self.assertEqual( get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) # resource owner password credentials grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) body = 'grant_type=password&username=abc&password=secret&scope=%s' _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) # client credentials grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) def test_invalid_scope(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' auth_uri = 'http://example.com/path?client_id=abc&response_type=' token_uri = 'http://example.com/path' self.validator.validate_scopes.return_value = False # authorization grant h, _, s = self.web.create_authorization_response(auth_uri + 'code', scopes=['invalid']) self.assertEqual(s, 302) self.assertIn('Location', h) error = get_query_credentials(h['Location'])['error'][0] self.assertEqual(error, 'invalid_scope') # implicit grant h, _, s = self.mobile.create_authorization_response(auth_uri + 'token', scopes=['invalid']) self.assertEqual(s, 302) self.assertIn('Location', h) error = get_fragment_credentials(h['Location'])['error'][0] self.assertEqual(error, 'invalid_scope') # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope') # client credentials grant self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope')
class TestScopeHandling(TestCase): DEFAULT_REDIRECT_URI = 'http://i.b./path' def set_scopes(self, scopes): def set_request_scopes(client_id, code, client, request): request.scopes = scopes return True return set_request_scopes def set_user(self, request): request.user = '******' request.client_id = 'bar' request.client = mock.MagicMock() request.client.client_id = 'mocked' return True def set_client(self, request): request.client = mock.MagicMock() request.client.client_id = 'mocked' return True def setUp(self): self.validator = mock.MagicMock(spec=RequestValidator) self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI self.validator.authenticate_client.side_effect = self.set_client self.web = WebApplicationServer(self.validator) self.mobile = MobileApplicationServer(self.validator) self.legacy = LegacyApplicationServer(self.validator) self.backend = BackendApplicationServer(self.validator) def test_scope_extraction(self): scopes = ( ('images', ['images']), ('images+videos', ['images', 'videos']), ('http%3A%2f%2fa.b%2fvideos', ['http://a.b/videos']), ('http%3A%2f%2fa.b%2fvideos+pics', ['http://a.b/videos', 'pics']), ('pics+http%3A%2f%2fa.b%2fvideos', ['pics', 'http://a.b/videos']), ('http%3A%2f%2fa.b%2fvideos+https%3A%2f%2fc.d%2Fsecret', ['http://a.b/videos', 'https://c.d/secret']), ) uri = 'http://example.com/path?client_id=abc&scope=%s&response_type=%s' for scope, correct_scopes in scopes: scopes, _ = self.web.validate_authorization_request( uri % (scope, 'code')) self.assertItemsEqual(scopes, correct_scopes) scopes, _ = self.mobile.validate_authorization_request( uri % (scope, 'token')) self.assertItemsEqual(scopes, correct_scopes) def test_scope_preservation(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' correct_scope = 'pics http%3A%2f%2fa.b%2fvideos' decoded_scope = 'pics http://a.b/videos' scopes = ['pics', 'http%3A%2f%2fa.b%2fvideos'] auth_uri = 'http://example.com/path?client_id=abc&scope=%s&%s' token_uri = 'http://example.com/path' # authorization grant uri, _, _, _ = self.web.create_authorization_response( auth_uri % (scope, 'response_type=code')) self.validator.validate_code.side_effect = self.set_scopes(scopes) code = get_query_credentials(uri)['code'][0] _, _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], correct_scope) # implicit grant uri, _, _, _ = self.mobile.create_authorization_response( auth_uri % (scope, 'response_type=token')) self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' _, _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) # client credentials grant body = 'grant_type=client_credentials&scope=%s' self.validator.authenticate_client.side_effect = self.set_user _, _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) def test_scope_changed(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' scopes = ['images', 'http://a.b/videos'] decoded_scope = 'images http://a.b/videos' auth_uri = 'http://example.com/path?client_id=abc&scope=%s&%s' token_uri = 'http://example.com/path' # authorization grant uri, _, _, _ = self.web.create_authorization_response( auth_uri % (scope, 'response_type=code')) code = get_query_credentials(uri)['code'][0] self.validator.validate_code.side_effect = self.set_scopes(scopes) _, _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], decoded_scope) # implicit grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) uri, _, _, _ = self.mobile.create_authorization_response( auth_uri % (scope, 'response_type=token')) self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) # resource owner password credentials grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) body = 'grant_type=password&username=abc&password=secret&scope=%s' _, _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) # client credentials grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' _, _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) def test_invalid_scope(self): scope = 'pics+http%3A%2f%2fa.b%2fvideos' auth_uri = 'http://example.com/path?client_id=abc&scope=%s&%s' token_uri = 'http://example.com/path' self.validator.validate_scopes.return_value = False # authorization grant uri, _, _, _ = self.web.create_authorization_response( auth_uri % (scope, 'response_type=code')) error = get_query_credentials(uri)['error'][0] self.assertEqual(error, 'invalid_scope') # implicit grant uri, _, _, _ = self.mobile.create_authorization_response( auth_uri % (scope, 'response_type=token')) error = get_fragment_credentials(uri)['error'][0] self.assertEqual(error, 'invalid_scope') # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' _, _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope') # client credentials grant self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' _, _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope')