diff --git a/aiohttp_security/__init__.py b/aiohttp_security/__init__.py index dc1bab8..572237a 100644 --- a/aiohttp_security/__init__.py +++ b/aiohttp_security/__init__.py @@ -1,5 +1,5 @@ from .abc import AbstractIdentityPolicy, AbstractAuthorizationPolicy -from .api import remember, forget, setup, authorize, permits +from .api import remember, forget, setup, authorize, permits, get_user_identity from .cookies_identity import CookiesIdentityPolicy from .session_identity import SessionIdentityPolicy @@ -9,4 +9,5 @@ __version__ = '0.1.0' __all__ = ('AbstractIdentityPolicy', 'AbstractAuthorizationPolicy', 'CookiesIdentityPolicy', 'SessionIdentityPolicy', - 'remember', 'forget', 'authorize', 'permits', 'setup') + 'remember', 'forget', 'authorize', 'permits', + 'get_user_identity', 'setup') diff --git a/aiohttp_security/api.py b/aiohttp_security/api.py index d1c5051..a3607a6 100644 --- a/aiohttp_security/api.py +++ b/aiohttp_security/api.py @@ -1,6 +1,8 @@ import asyncio import functools + from aiohttp import web + from aiohttp_security.abc import (AbstractIdentityPolicy, AbstractAuthorizationPolicy) @@ -16,18 +18,34 @@ def authorize(required=True, redirect_url=None, permission=None): # assuming first argument is request assert isinstance(args[0], web.Request) request = args[0] + + # check if coroutine if asyncio.iscoroutinefunction(f): coro = f else: coro = asyncio.coroutine(f) - identity = yield from get_user_identity(request) - if not identity and required: - raise web.HTTPForbidden() - kwargs['identity'] = identity - return (yield from coro(*args, **kwargs)) - return wrapped - return wrapper + # get identity + identity = yield from get_user_identity(request) + kwargs['identity'] = identity + + if required: + + # check identity + if not identity: + return web.HTTPFound(redirect_url) if redirect_url \ + else web.HTTPForbidden(reason='not authenticated') + + # check permission + allowed = yield from permits(request, permission) + if permission and not allowed: + return web.HTTPForbidden(reason='unauthorized') + + return (yield from coro(*args, **kwargs)) + + return wrapped + + return wrapper @asyncio.coroutine @@ -78,6 +96,7 @@ def get_user_identity(request): identity = yield from identity_policy.identify(request) return identity + ''' @asyncio.coroutine def authorized_userid(request): @@ -98,6 +117,8 @@ def permits(request, permission, context=None): assert isinstance(permission, str), permission assert permission autz_policy = request.app.get(AUTZ_KEY) + if autz_policy is None: + return True identity = yield from get_user_identity(request) access = yield from autz_policy.permits(identity, permission, context) return access diff --git a/demo/session_auth/auth_policy.py b/demo/session_auth/auth_policy.py new file mode 100644 index 0000000..953ebd7 --- /dev/null +++ b/demo/session_auth/auth_policy.py @@ -0,0 +1,11 @@ +import asyncio + +from aiohttp_security.abc import AbstractAuthorizationPolicy + + +class StubAuthorizationPolicy(AbstractAuthorizationPolicy): + @asyncio.coroutine + def permits(self, identity, permission, context=None): + if identity == permission: + return True + return False diff --git a/demo/session_auth/handlers.py b/demo/session_auth/handlers.py new file mode 100644 index 0000000..a6a4fbf --- /dev/null +++ b/demo/session_auth/handlers.py @@ -0,0 +1,34 @@ +from aiohttp import web +from aiohttp_jinja2 import render_template + +from aiohttp_security import authorize, get_user_identity, remember, forget + + +@authorize(required=True, redirect_url='/login', permission='Steve') +async def index(request, identity=None): + context = {'name': identity} + response = render_template('index.html', request, context) + return response + + +async def login(request): + identity = await get_user_identity(request) + if identity: + return web.HTTPFound('/') + response = render_template('login.html', request, {}) + return response + + +async def login_post(request): + post_data = await request.post() + user_id = post_data['username'] + password = post_data['password'] + response = web.Response(body=b'OK') + await remember(request, response, user_id) + return response + + +async def logout(request): + response = web.Response(body=b'OK') + await forget(request, response) + return response diff --git a/demo/session_auth/main.py b/demo/session_auth/main.py new file mode 100644 index 0000000..38e0508 --- /dev/null +++ b/demo/session_auth/main.py @@ -0,0 +1,27 @@ +import asyncio +import os + +import jinja2 +from aiohttp import web +from aiohttp_jinja2 import setup as setup_jinja +from aiohttp_session import setup as setup_session +from aiohttp_session.redis_storage import RedisStorage +from aioredis import create_pool + +from aiohttp_security import setup as setup_security, SessionIdentityPolicy +from .auth_policy import StubAuthorizationPolicy +from .handlers import index, login, login_post, logout + +loop = asyncio.get_event_loop() +redis_pool = loop.run_until_complete(create_pool(('localhost', 6379))) +app = web.Application(loop=loop) +setup_session(app, RedisStorage(redis_pool)) +setup_security(app, + SessionIdentityPolicy(), + StubAuthorizationPolicy()) +setup_jinja(app, loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates'))) +app.router.add_route('GET', '/', index) +app.router.add_route('GET', '/login', login) +app.router.add_route('POST', '/login', login_post) +app.router.add_route('GET', '/logout', logout) +web.run_app(app) diff --git a/demo/session_auth/templates/base.html b/demo/session_auth/templates/base.html new file mode 100644 index 0000000..0e9ee8e --- /dev/null +++ b/demo/session_auth/templates/base.html @@ -0,0 +1,13 @@ + + + +
+ + +