Skip to content

Commit

Permalink
Add some more docs, license/manifest, etc
Browse files Browse the repository at this point in the history
  • Loading branch information
gaker committed May 13, 2016
1 parent 7898def commit 4d7953a
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 13 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Greg Aker, Vinli Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include README.rst
include LICENSE
42 changes: 40 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ Installation

pip install vinli-tornado-auth

---------------------
Settings Requirements
---------------------

Register and create an app at `https://dev.vin.li <dev.vin.li>`_

In the app, create a ``web`` client type and take note of the
following values:

* ``vinli_client_id`` - App Client Id
* ``vinli_client_secret`` - App Client Secret
* ``vinli_redirect_uri`` - A valid URL to redirect to. eg: ``http://localhost:8000/auth/login``

Add these values to application ``settings``.

-------------
Example Usage
-------------
Expand All @@ -23,7 +38,7 @@ Example Usage
import tornado.gen
import tornado.web

from vinli_tornado_auth import VinliAuthLoginMixin
from vinli_tornado_auth.auth import VinliAuthLoginMixin

class LoginHandler(tornado.web.RequestHandler, VinliAuthLoginMixin):
"""
Expand All @@ -32,7 +47,7 @@ Example Usage
@tornado.gen.coroutine
def get(self):
code = self.get_argument('code', None)
if note code:
if not code:
yield self.authorize_redirect(
redirect_uri=self.settings['vinli_redirect_uri'],
client_id=self.settings['vinli_client_id'],
Expand Down Expand Up @@ -96,3 +111,26 @@ Example Usage
app.listen(8000)
tornado.ioloop.IOLoop.instance().start()


-----------------------------
Making Authenticated Requests
-----------------------------

Use the ``vinli_request`` method to make authenticated requests to
the platform after initial authentication has been completed.

Get Trips for a device
^^^^^^^^^^^^^^^^^^^^^^

As with the `following example <http://docs.vin.li/en/latest/web/trip-services/index.html>`_
from the Vinli API Documentation, a list of trips for device id
``fe4bbc20-cc90-11e3-8e05-f3abac5b6b58`` can be retrieved with the following::

@tornado.web.authenticated
@tornado.gen.coroutine
def get(self):
trips = yield self.vinli_request(
'trips', '/api/v1/devices/fe4bbc20-cc90-11e3-8e05-f3abac5b6b58/trips',
access_token=self.current_user.get('token')
)
self.write(trips)
15 changes: 11 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""
"""

from setuptools import setup, find_packages
Expand All @@ -12,14 +11,22 @@
setup(
name='vinli_tornado_auth',
version='0.1.0',
description='',
description='Tornado web authentication for the Vinli Platform.',
long_description=readme,
author='Greg Aker',
author_email='[email protected]',
url='https://github.com/gaker/vinli-tornado-auth',
download_url='https://github.com/gaker/vinli-tornado-auth/archive/0.1.0.tar.gz',
packages=find_packages(),
install_requires=[],
classifiers=[

'License :: OSI Approved :: MIT License',
'Environment :: Web Environment',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
]
)
103 changes: 103 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""
Based on tests at
https://github.com/tornadoweb/tornado/blob/master/tornado/test/auth_test.py
As is mentioned there, this is merely meant to catch potential syntax
errors, or python 2/3 errors.
"""


import tornado.gen

from tornado.escape import json_decode
from tornado.httputil import url_concat
from tornado.web import Application, RequestHandler
from tornado.testing import AsyncHTTPTestCase


from vinli_tornado_auth.auth import VinliAuthLoginMixin


class VinliLoginHandler(RequestHandler, VinliAuthLoginMixin):

def initialize(self, test):
self.test = test
self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth/authorization/new')
self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/oauth/token')
self._OAUTH_USERINFO_URL = test.get_url('/api/v1/users/_current')

@tornado.gen.coroutine
def get(self):
code = self.get_argument('code', None)
if not code:
yield self.authorize_redirect(
redirect_uri=self.request.full_url(),
client_id=self.settings['vinli_client_id'],
response_type='code'
)
else:
access = yield self.get_authenticated_user(
redirect_uri=self.request.full_url(),
code=code
)
user = yield self.oauth2_request(
self._OAUTH_USERINFO_URL,
access_token=access['access_token']
)
self.write(user)


class VinliOAuth2AuthorizeHandler(RequestHandler):
def get(self):
self.redirect(
url_concat(self.get_argument('redirect_uri'),
dict(code='some-fake-code')))


class VinliOAuth2TokenHandler(RequestHandler):
def post(self):
assert self.get_argument('code') == 'some-fake-code'
self.finish({
'access_token': 'abc123',
'expires': 'nope'
})


class VinliOAuth2UserinfoHandler(RequestHandler):
def get(self):
assert self.request.headers['authorization'] == 'Bearer abc123'
self.finish({
'user': {
'name': 'foobar'
}
})


class VinliAuthTestsCase(AsyncHTTPTestCase):
def get_app(self):
settings = {
'vinli_redirect_uri': 'http://example.com/auth/login',
'vinli_client_id': 'abc123',
'vinli_client_secret': 'secret!'
}

return Application([
('/vinli/auth/login', VinliLoginHandler, dict(test=self)),

# mocked handlers
('/oauth/authorization/new', VinliOAuth2AuthorizeHandler),
('/oauth/token', VinliOAuth2TokenHandler),
('/api/v1/users/_current', VinliOAuth2UserinfoHandler),

],
**settings)


def test_vinli_login(self):
response = self.fetch('/vinli/auth/login')
self.assertEqual(response.code, 200)
self.assertDictEqual(
{"user": {"name": "foobar"}},
json_decode(response.body)
)
6 changes: 6 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tox]
envlist = py{27,33,34,35}

[testenv]
deps = tornado
commands = python -m tornado.testing tests
File renamed without changes.
39 changes: 32 additions & 7 deletions vinli-tornado-auth/auth.py → vinli_tornado_auth/auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
"""
a
Vinli Authentication for tornado
The following is heavily based on Google/Facebook/Twitter/etc
authentication in the core Tornado auth module.
https://github.com/tornadoweb/tornado/blob/master/tornado/auth.py
"""
import base64
import functools
import sys
import tornado.auth

from tornado.concurrent import chain_future
from tornado.util import PY3

if PY3:
if sys.version_info >= (3, 0):
import urllib.parse as urlparse
import urllib.parse as urllib_parse
else:
Expand All @@ -17,7 +23,8 @@

class VinliAuthLoginMixin(tornado.auth.OAuth2Mixin):
"""
Mixin to authenticate and perform authenticated requests to the
Vinli platform
"""
_OAUTH_AUTHORIZE_URL = 'https://auth.vin.li/oauth/authorization/new'
_OAUTH_ACCESS_TOKEN_URL = 'https://auth.vin.li/oauth/token'
Expand All @@ -29,7 +36,7 @@ class VinliAuthLoginMixin(tornado.auth.OAuth2Mixin):
@tornado.auth._auth_return_future
def get_authenticated_user(self, redirect_uri, code, callback):
"""
Do the oauth dance with the Vinli platform.
"""
http = self.get_auth_http_client()
args = urllib_parse.urlencode({
Expand All @@ -45,7 +52,8 @@ def get_authenticated_user(self, redirect_uri, code, callback):

headers = {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic {}'.format(base64.b64encode(creds))
'Authorization': 'Basic {}'.format(
base64.b64encode(creds.encode('ascii')))
}

http.fetch(
Expand All @@ -55,6 +63,21 @@ def get_authenticated_user(self, redirect_uri, code, callback):
headers=headers
)

def _on_access_token(self, future, response):
"""
Callback after the access token is obtained
"""
if response.error:
future.set_exception(
tornado.auth.AuthError(
"Vinli Auth Error: {}".format(response)
)
)
return

args = tornado.escape.json_decode(response.body)
future.set_result(args)

@tornado.auth._auth_return_future
def oauth2_request(self, url, callback, access_token=None,
post_args=None, **kwargs):
Expand Down Expand Up @@ -82,7 +105,9 @@ def oauth2_request(self, url, callback, access_token=None,
@tornado.auth._auth_return_future
def vinli_request(self, service, path, callback,
access_token=None, post_args=None, **kwargs):

"""
Make an authenticated request to the vinli platform.
"""
url = 'https://{}{}{}'.format(
service, self._VINLI_BASE_URL, path
)
Expand Down

0 comments on commit 4d7953a

Please sign in to comment.