Skip to content

Commit 1a9ab64

Browse files
dfeeasvetlov
authored andcommitted
added simplistic dictionary_auth example (#105)
1 parent b089580 commit 1a9ab64

File tree

10 files changed

+184
-0
lines changed

10 files changed

+184
-0
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

demo/dictionary_auth/authz.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from aiohttp_security.abc import AbstractAuthorizationPolicy
2+
3+
4+
class DictionaryAuthorizationPolicy(AbstractAuthorizationPolicy):
5+
def __init__(self, user_map):
6+
super().__init__()
7+
self.user_map = user_map
8+
9+
async def authorized_userid(self, identity):
10+
"""Retrieve authorized user id.
11+
Return the user_id of the user identified by the identity
12+
or 'None' if no user exists related to the identity.
13+
"""
14+
if identity in self.user_map:
15+
return identity
16+
17+
async def permits(self, identity, permission, context=None):
18+
"""Check user permissions.
19+
Return True if the identity is allowed the permission in the
20+
current context, else return False.
21+
"""
22+
# pylint: disable=unused-argument
23+
user = self.user_map.get(identity)
24+
if not user:
25+
return False
26+
return permission in user.permissions
27+
28+
29+
async def check_credentials(user_map, username, password):
30+
user = user_map.get(username)
31+
if not user:
32+
return False
33+
34+
return user.password == password

demo/dictionary_auth/handlers.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import asyncio
2+
import functools
3+
from textwrap import dedent
4+
5+
from aiohttp import web
6+
7+
from aiohttp_security import remember, forget, authorized_userid, permits
8+
9+
from .authz import check_credentials
10+
11+
12+
def require(permission):
13+
def wrapper(f):
14+
@asyncio.coroutine
15+
@functools.wraps(f)
16+
def wrapped(request):
17+
has_perm = yield from permits(request, permission)
18+
if not has_perm:
19+
message = 'User has no permission {}'.format(permission)
20+
raise web.HTTPForbidden(body=message.encode())
21+
return (yield from f(request))
22+
return wrapped
23+
return wrapper
24+
25+
26+
index_template = dedent("""
27+
<!doctype html>
28+
<head>
29+
</head>
30+
<body>
31+
<p>{message}</p>
32+
<form action="/login" method="post">
33+
Login:
34+
<input type="text" name="username">
35+
Password:
36+
<input type="password" name="password">
37+
<input type="submit" value="Login">
38+
</form>
39+
<a href="/logout">Logout</a>
40+
</body>
41+
""")
42+
43+
44+
async def index(request):
45+
username = await authorized_userid(request)
46+
if username:
47+
template = index_template.format(
48+
message='Hello, {username}!'.format(username=username))
49+
else:
50+
template = index_template.format(message='You need to login')
51+
return web.Response(
52+
text=template,
53+
content_type='text/html',
54+
)
55+
56+
57+
async def login(request):
58+
response = web.HTTPFound('/')
59+
form = await request.post()
60+
username = form.get('username')
61+
password = form.get('password')
62+
63+
verified = await check_credentials(request.app.user_map, username, password)
64+
if verified:
65+
await remember(request, response, username)
66+
return response
67+
68+
return web.HTTPUnauthorized(body='Invalid username / password combination')
69+
70+
71+
@require('public')
72+
async def logout(request):
73+
response = web.Response(
74+
text='You have been logged out',
75+
content_type='text/html',
76+
)
77+
await forget(request, response)
78+
return response
79+
80+
81+
@require('public')
82+
async def internal_page(request):
83+
# pylint: disable=unused-argument
84+
response = web.Response(
85+
text='This page is visible for all registered users',
86+
content_type='text/html',
87+
)
88+
return response
89+
90+
91+
@require('protected')
92+
async def protected_page(request):
93+
# pylint: disable=unused-argument
94+
response = web.Response(
95+
text='You are on protected page',
96+
content_type='text/html',
97+
)
98+
return response
99+
100+
101+
def configure_handlers(app):
102+
router = app.router
103+
router.add_get('/', index, name='index')
104+
router.add_post('/login', login, name='login')
105+
router.add_get('/logout', logout, name='logout')
106+
router.add_get('/public', internal_page, name='public')
107+
router.add_get('/protected', protected_page, name='protected')

demo/dictionary_auth/main.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import base64
2+
from cryptography import fernet
3+
from aiohttp import web
4+
from aiohttp_session import setup as setup_session
5+
from aiohttp_session.cookie_storage import EncryptedCookieStorage
6+
from aiohttp_security import setup as setup_security
7+
from aiohttp_security import SessionIdentityPolicy
8+
9+
from .authz import DictionaryAuthorizationPolicy
10+
from .handlers import configure_handlers
11+
from .users import user_map
12+
13+
14+
def make_app():
15+
app = web.Application()
16+
app.user_map = user_map
17+
configure_handlers(app)
18+
19+
# secret_key must be 32 url-safe base64-encoded bytes
20+
fernet_key = fernet.Fernet.generate_key()
21+
secret_key = base64.urlsafe_b64decode(fernet_key)
22+
23+
storage = EncryptedCookieStorage(secret_key, cookie_name='API_SESSION')
24+
setup_session(app, storage)
25+
26+
policy = SessionIdentityPolicy()
27+
setup_security(app, policy, DictionaryAuthorizationPolicy(user_map))
28+
29+
return app
30+
31+
32+
if __name__ == '__main__':
33+
web.run_app(make_app(), port=9000)

demo/dictionary_auth/users.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from collections import namedtuple
2+
3+
User = namedtuple('User', ['username', 'password', 'permissions'])
4+
5+
user_map = {
6+
user.username: user for user in [
7+
User('devin', 'password', ('public',)),
8+
User('jack', 'password', ('public', 'protected',)),
9+
]
10+
}

0 commit comments

Comments
 (0)