Permalink
Browse files

#21 Support py2k

  • Loading branch information...
1 parent b5aae7f commit a518edcbe55c240e9cf0745109d73a52174b1b7a @nycholas nycholas committed Jul 7, 2014
@@ -0,0 +1,64 @@
+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,79 @@
+#!/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
@@ -210,12 +210,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):
@@ -34,32 +34,35 @@
_ = lambda t, *a, **k: t
class Error(Exception):
- """Error class based on the JSON-RPC 2.0 specs
- http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal
-
+ """Error class based on the JSON-RPC 2.0 specs
+ http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal
+
code - number
message - string
data - object
-
+
status - number from http://groups.google.com/group/json-rpc/web/json-rpc-over-http JSON-RPC over HTTP Errors section
"""
-
+
code = 0
message = None
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):
"""Return the Exception data in a format for JSON-RPC
"""
-
+
error = {
'name': str(self.__class__.__name__),
'code': self.code,
@@ -77,41 +80,41 @@ def json_rpc_format(self):
# Exceptions
# from http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal
-# The error-codes -32768 .. -32000 (inclusive) are reserved for pre-defined errors.
+# The error-codes -32768 .. -32000 (inclusive) are reserved for pre-defined errors.
# Any error-code within this range not defined explicitly below is reserved for future use
class ParseError(Error):
"""Invalid JSON. An error occurred on the server while parsing the JSON text.
"""
code = -32700
message = _('Parse error.')
-
+
class InvalidRequestError(Error):
"""The received JSON is not a valid JSON-RPC Request.
"""
code = -32600
message = _('Invalid Request.')
-
+
class MethodNotFoundError(Error):
"""The requested remote-procedure does not exist / is not available.
"""
code = -32601
message = _('Method not found.')
-
+
class InvalidParamsError(Error):
"""Invalid method parameters.
"""
code = -32602
message = _('Invalid params.')
-
+
class ServerError(Error):
"""Internal JSON-RPC error.
"""
code = -32603
message = _('Internal error.')
-
+
# -32099..-32000 Server error.
-# Reserved for implementation-defined server-errors.
+# Reserved for implementation-defined server-errors.
# The remainder of the space is available for application defined errors.
@@ -126,7 +129,7 @@ class InvalidCredentialsError(Error):
code = 401
message = _('Invalid login credentials')
status = 401
-
+
class OtherError(Error):
"""catchall error
"""
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.helpers import extract_raw_data_request, log_exception
from flask_jsonrpc.types import Object, Array, Any
@@ -206,6 +207,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)
+
try:
encoder = current_app.json_encoder()
except AttributeError:
@@ -217,34 +223,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, exc:
- raise TypeError("Return type not supported, for %r" % R)
+ raise TypeError('Return type not supported, for %r' % R)
response['result'] = R
-
status = 200
-
except Error, 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, 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
status = e.code
if version in ('1.1', '2.0') and 'result' in response:
response.pop('result')
except Exception, 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
@@ -288,16 +283,10 @@ def dispatch(self, request, method=''):
response = ''
return response, status
except Error, e:
- #got_request_exception.connect(log_exception, current_app._get_current_object())
-
response['error'] = e.json_rpc_format
status = e.status
except Exception, 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
status = other_error.status

0 comments on commit a518edc

Please sign in to comment.