Skip to content
Browse files

Adding a example using Flask-HTTPAuth; Merge with branch ryepdx

  • Loading branch information...
1 parent f7e7481 commit b89268072d290cc1f7cf66ccd55005b8cc7c14eb @nycholas nycholas committed
View
63 examples/flask-httpauth/README.rst
@@ -0,0 +1,63 @@
+flask-httpauth
+==============
+
+A example using `Flask-HTTPAuth <https://github.com/miguelgrinberg/Flask-HTTPAuth>`_.
+
+
+Testing your service
+********************
+
+1. Installation
+
+::
+
+ $ pip install -r requirements.pip
+
+
+1. Running
+
+::
+
+ $ python auth.py
+ * Running on http://0.0.0.0:5000/
+
+
+2. Testing
+
+::
+
+ $ curl --user john:hello -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "App.index", "id": "1"}' http://localhost:5000/api
+ HTTP/1.0 200 OK
+ Content-Type: application/json
+ Content-Length: 76
+ Server: Werkzeug/0.9.4 Python/3.4.1
+ Date: Mon, 07 Jul 2014 18:03:22 GMT
+
+ {
+ "id": "1",
+ "jsonrpc": "2.0",
+ "result": "Welcome to Flask JSON-RPC"
+ }
+
+
+::
+
+ $ curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "App.index", "id": "1"}' http://localhost:5000/api
+ HTTP/1.0 401 UNAUTHORIZED
+ Content-Type: application/json
+ Content-Length: 503
+ Server: Werkzeug/0.9.4 Python/3.4.1
+ Date: Mon, 07 Jul 2014 18:04:12 GMT
+
+ {
+ "error": {
+ "code": 401,
+ "data": null,
+ "executable": "/usr/bin/python",
+ "message": "InvalidCredentialsError: 401 UNAUTHORIZED",
+ "name": "InvalidCredentialsError",
+ "stack": "Traceback (most recent call last):\n File \"/home/nycholas/project/src/o_lalertom/flask/flask-jsonrpc/examples/../flask_jsonrpc/site.py\", line 216, in response_dict\n raise InvalidCredentialsError(R.status)\nflask_jsonrpc.exceptions.InvalidCredentialsError\n"
+ },
+ "id": "1",
+ "jsonrpc": "2.0"
+ }
View
78 examples/flask-httpauth/auth.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2012-2014, Cenobit Technologies, Inc. http://cenobit.es/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the Cenobit Technologies nor the names of
+# its contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import os
+import sys
+
+from flask import Flask
+
+from flask.ext.httpauth import HTTPBasicAuth
+
+PROJECT_DIR, PROJECT_MODULE_NAME = os.path.split(
+ os.path.dirname(os.path.realpath(__file__))
+)
+
+FLASK_JSONRPC_PROJECT_DIR = os.path.join(PROJECT_DIR, os.pardir)
+if os.path.exists(FLASK_JSONRPC_PROJECT_DIR) \
+ and not FLASK_JSONRPC_PROJECT_DIR in sys.path:
+ sys.path.append(FLASK_JSONRPC_PROJECT_DIR)
+
+from flask_jsonrpc import JSONRPC
+
+app = Flask(__name__)
+auth = HTTPBasicAuth()
+jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True)
+
+users = {
+ 'john': 'hello',
+ 'susan': 'bye'
+}
+
+@auth.get_password
+def get_pw(username):
+ if username in users:
+ return users.get(username)
+ return None
+
+@auth.verify_password
+def verify_pwd(username, password):
+ return users.get(username) == password
+
+@jsonrpc.method('App.index')
+@auth.login_required
+def index():
+ return u'Welcome to Flask JSON-RPC'
+
+@jsonrpc.method('App.hello')
+@auth.login_required
+def hello(name):
+ return u'Hello {0}'.format(name)
+
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', debug=True)
View
7 examples/flask-httpauth/requirements.pip
@@ -0,0 +1,7 @@
+# This file collects all required third-party applications that are needed
+# to run this project. Later you can install all these apps in a row
+# using pip. Example:
+#
+# pip install -r requirements.pip
+Flask==0.10
+Flask-HTTPAuth==2.2.1
View
18 flask_jsonrpc/__init__.py
@@ -156,31 +156,26 @@ def _inject_args(sig, types):
sig = '{0}({1})'.format(sig, ', '.join(types))
return sig
-def _site_api(site, decorators=[]):
+def _site_api(site):
def wrapper(method=''):
response_dict, status_code = site.dispatch(request, method)
if current_app.config['DEBUG']:
logging.debug('request: %s', extract_raw_data_request(request))
logging.debug('response: %s, %s', status_code, response_dict)
-
- response_func = jsonify_status_code
- for decorator in decorators:
- response_func = decorator(response_func)
-
- return response_func(status_code, response_dict), status_code
+ return jsonify_status_code(status_code, response_dict), status_code
return wrapper
class JSONRPC(object):
def __init__(self, app=None, service_url='/api', auth_backend=authenticate, site=default_site,
- enable_web_browsable_api=False, decorators=[]):
+ enable_web_browsable_api=False):
self.service_url = service_url
self.browse_url = self._make_browse_url(service_url)
self.enable_web_browsable_api = enable_web_browsable_api
self.auth_backend = auth_backend
self.site = site
- self.site_api = _site_api(site, decorators=decorators)
+ self.site_api = _site_api(site)
if app is not None:
self.app = app
self.init_app(self.app)
@@ -213,11 +208,11 @@ def _enable_web_browsable_api(self, app, url_prefix=None):
def init_app(self, app):
app.add_url_rule(self.service_url, self._unique_name(), self.site_api, methods=['POST', 'OPTIONS'])
- app.add_url_rule(self.service_url + '/<method>', self._unique_name('/<method>'), self.site_api, methods=['GET'])
+ app.add_url_rule(self.service_url + '/<method>', self._unique_name('/<method>'), self.site_api, methods=['GET', 'OPTIONS'])
def register_blueprint(self, blueprint):
blueprint.add_url_rule(self.service_url, '', self.site_api, methods=['POST', 'OPTIONS'])
- blueprint.add_url_rule(self.service_url + '/<method>', '', self.site_api, methods=['GET'])
+ blueprint.add_url_rule(self.service_url + '/<method>', '', self.site_api, methods=['GET', 'OPTIONS'])
def method(self, name, authenticated=False, safe=False, validate=False, **options):
def decorator(f):
@@ -241,3 +236,4 @@ def decorator(f):
self.site.register(method, _f)
return _f
return decorator
+
View
5 flask_jsonrpc/exceptions.py
@@ -51,11 +51,14 @@ class Error(Exception):
data = None
status = 200
- def __init__(self, message=None):
+ def __init__(self, message=None, code=None):
"""Setup the Exception and overwrite the default message
"""
+ super(Error, self).__init__()
if message is not None:
self.message = message
+ if code is not None:
+ self.code = code
@property
def json_rpc_format(self):
View
2 flask_jsonrpc/helpers.py
@@ -95,4 +95,4 @@ def _f(*args, **kwargs):
return _f
def log_exception(sender, exception, **extra):
- sender.logger.debug('Got exception during processing: %s', exception)
+ sender.logger.debug('Got exception during processing: %s', exception)
View
35 flask_jsonrpc/site.py
@@ -33,8 +33,8 @@
from werkzeug.exceptions import HTTPException
-from flask import json, jsonify, current_app, got_request_exception
from flask.wrappers import Response
+from flask import json, jsonify, current_app
from flask_jsonrpc.types import Object, Array, Any
from flask_jsonrpc.helpers import extract_raw_data_request, log_exception
@@ -211,35 +211,29 @@ def response_dict(self, request, D, is_batch=False, version_hint='1.0'):
if 'id' not in D or ('id' in D and D['id'] is None): # notification
return None, 204
+ if isinstance(R, Response):
+ if R.status_code == 401:
+ raise InvalidCredentialsError(R.status)
+ raise OtherError(R.status, R.status_code)
+
encoder = current_app.json_encoder()
- response['result'] = R
- status = 200
# type of `R` should be one of these or...
- if isinstance(R, Response):
- status = R.status_code
- response['result'] = R.status
-
- elif not sum([isinstance(R, e) for e in \
+ if not sum([isinstance(R, e) for e in \
string_types + integer_types + (dict, list, set, NoneType, bool)]):
try:
rs = encoder.default(R) # ...or something this thing supports
except TypeError as exc:
- raise TypeError("Return type not supported, for {0!r}".format(R))
-
+ raise TypeError('Return type not supported, for {0!r}'.format(R))
+ response['result'] = R
+ status = 200
except Error as e:
- # exception missed by others
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
response['error'] = e.json_rpc_format
if version in ('1.1', '2.0') and 'result' in response:
response.pop('result')
status = e.status
except HTTPException as e:
- # exception missed by others
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
other_error = OtherError(e)
response['error'] = other_error.json_rpc_format
response['error']['code'] = e.code
@@ -247,9 +241,6 @@ def response_dict(self, request, D, is_batch=False, version_hint='1.0'):
response.pop('result')
status = e.code
except Exception as e:
- # exception missed by others
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
other_error = OtherError(e)
response['error'] = other_error.json_rpc_format
status = other_error.status
@@ -293,14 +284,9 @@ def dispatch(self, request, method=''):
response = ''
return response, status
except Error as e:
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
response['error'] = e.json_rpc_format
status = e.status
except Exception as e:
- # exception missed by others
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
other_error = OtherError(e)
response['result'] = None
response['error'] = other_error.json_rpc_format
@@ -338,3 +324,4 @@ def describe(self):
jsonrpc_site = JSONRPCSite()
+

0 comments on commit b892680

Please sign in to comment.
Something went wrong with that request. Please try again.