Permalink
Browse files

Merge branch 'ryepdx-master'

  • Loading branch information...
2 parents feaafb7 + b892680 commit 87526fe66737eee285a124d148e78ea9ff077c7e @nycholas nycholas committed Jul 7, 2014
@@ -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"
+ }
@@ -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)
@@ -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
@@ -207,12 +207,12 @@ def _enable_web_browsable_api(self, app, url_prefix=None):
jsonrpc_site_name=self._unique_name(), jsonrpc_site=self.site)
def init_app(self, app):
- app.add_url_rule(self.service_url, self._unique_name(), self.site_api, methods=['POST'])
- app.add_url_rule(self.service_url + '/<method>', self._unique_name('/<method>'), self.site_api, methods=['GET'])
+ 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', 'OPTIONS'])
def register_blueprint(self, blueprint):
- blueprint.add_url_rule(self.service_url, '', self.site_api, methods=['POST'])
- blueprint.add_url_rule(self.service_url + '/<method>', '', self.site_api, methods=['GET'])
+ 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', 'OPTIONS'])
def method(self, name, authenticated=False, safe=False, validate=False, **options):
def decorator(f):
@@ -236,3 +236,4 @@ def decorator(f):
self.site.register(method, _f)
return _f
return decorator
+
@@ -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
@@ -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
@@ -33,7 +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
@@ -210,6 +211,11 @@ 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()
# type of `R` should be one of these or...
@@ -218,34 +224,23 @@ def response_dict(self, request, D, is_batch=False, version_hint='1.0'):
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
if version in ('1.1', '2.0') and 'result' in response:
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
@@ -289,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
@@ -334,3 +324,4 @@ def describe(self):
jsonrpc_site = JSONRPCSite()
+

0 comments on commit 87526fe

Please sign in to comment.