added simplistic dictionary_auth example (#105)
This commit is contained in:
committed by
Andrew Svetlov
parent
b0895806af
commit
1a9ab6424e
34
demo/database_auth/db.py
Normal file
34
demo/database_auth/db.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
metadata = sa.MetaData()
|
||||
|
||||
|
||||
users = sa.Table(
|
||||
'users', metadata,
|
||||
sa.Column('id', sa.Integer, nullable=False),
|
||||
sa.Column('login', sa.String(256), nullable=False),
|
||||
sa.Column('passwd', sa.String(256), nullable=False),
|
||||
sa.Column('is_superuser', sa.Boolean, nullable=False,
|
||||
server_default='FALSE'),
|
||||
sa.Column('disabled', sa.Boolean, nullable=False,
|
||||
server_default='FALSE'),
|
||||
|
||||
# indices
|
||||
sa.PrimaryKeyConstraint('id', name='user_pkey'),
|
||||
sa.UniqueConstraint('login', name='user_login_key'),
|
||||
)
|
||||
|
||||
|
||||
permissions = sa.Table(
|
||||
'permissions', metadata,
|
||||
sa.Column('id', sa.Integer, nullable=False),
|
||||
sa.Column('user_id', sa.Integer, nullable=False),
|
||||
sa.Column('perm_name', sa.String(64), nullable=False),
|
||||
|
||||
# indices
|
||||
sa.PrimaryKeyConstraint('id', name='permission_pkey'),
|
||||
sa.ForeignKeyConstraint(['user_id'], [users.c.id],
|
||||
name='user_permission_fkey',
|
||||
ondelete='CASCADE'),
|
||||
)
|
67
demo/database_auth/db_auth.py
Normal file
67
demo/database_auth/db_auth.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import asyncio
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from aiohttp_security.abc import AbstractAuthorizationPolicy
|
||||
from passlib.hash import sha256_crypt
|
||||
|
||||
from . import db
|
||||
|
||||
|
||||
class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
|
||||
def __init__(self, dbengine):
|
||||
self.dbengine = dbengine
|
||||
|
||||
@asyncio.coroutine
|
||||
def authorized_userid(self, identity):
|
||||
with (yield from self.dbengine) as conn:
|
||||
where = sa.and_(db.users.c.login == identity,
|
||||
sa.not_(db.users.c.disabled))
|
||||
query = db.users.count().where(where)
|
||||
ret = yield from conn.scalar(query)
|
||||
if ret:
|
||||
return identity
|
||||
else:
|
||||
return None
|
||||
|
||||
@asyncio.coroutine
|
||||
def permits(self, identity, permission, context=None):
|
||||
if identity is None:
|
||||
return False
|
||||
|
||||
with (yield from self.dbengine) as conn:
|
||||
where = sa.and_(db.users.c.login == identity,
|
||||
sa.not_(db.users.c.disabled))
|
||||
query = db.users.select().where(where)
|
||||
ret = yield from conn.execute(query)
|
||||
user = yield from ret.fetchone()
|
||||
if user is not None:
|
||||
user_id = user[0]
|
||||
is_superuser = user[3]
|
||||
if is_superuser:
|
||||
return True
|
||||
|
||||
where = db.permissions.c.user_id == user_id
|
||||
query = db.permissions.select().where(where)
|
||||
ret = yield from conn.execute(query)
|
||||
result = yield from ret.fetchall()
|
||||
if ret is not None:
|
||||
for record in result:
|
||||
if record.perm_name == permission:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_credentials(db_engine, username, password):
|
||||
with (yield from db_engine) as conn:
|
||||
where = sa.and_(db.users.c.login == username,
|
||||
sa.not_(db.users.c.disabled))
|
||||
query = db.users.select().where(where)
|
||||
ret = yield from conn.execute(query)
|
||||
user = yield from ret.fetchone()
|
||||
if user is not None:
|
||||
hash = user[2]
|
||||
return sha256_crypt.verify(password, hash)
|
||||
return False
|
95
demo/database_auth/handlers.py
Normal file
95
demo/database_auth/handlers.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from aiohttp_security import remember, forget, authorized_userid, permits
|
||||
|
||||
from .db_auth import check_credentials
|
||||
|
||||
|
||||
def require(permission):
|
||||
def wrapper(f):
|
||||
@asyncio.coroutine
|
||||
@functools.wraps(f)
|
||||
def wrapped(self, request):
|
||||
has_perm = yield from permits(request, permission)
|
||||
if not has_perm:
|
||||
message = 'User has no permission {}'.format(permission)
|
||||
raise web.HTTPForbidden(body=message.encode())
|
||||
return (yield from f(self, request))
|
||||
return wrapped
|
||||
return wrapper
|
||||
|
||||
|
||||
class Web(object):
|
||||
index_template = """
|
||||
<!doctype html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<p>{message}</p>
|
||||
<form action="/login" method="post">
|
||||
Login:
|
||||
<input type="text" name="login">
|
||||
Password:
|
||||
<input type="password" name="password">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
||||
<a href="/logout">Logout</a>
|
||||
</body>
|
||||
"""
|
||||
|
||||
@asyncio.coroutine
|
||||
def index(self, request):
|
||||
username = yield from authorized_userid(request)
|
||||
if username:
|
||||
template = self.index_template.format(
|
||||
message='Hello, {username}!'.format(username=username))
|
||||
else:
|
||||
template = self.index_template.format(message='You need to login')
|
||||
response = web.Response(body=template.encode())
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def login(self, request):
|
||||
response = web.HTTPFound('/')
|
||||
form = yield from request.post()
|
||||
login = form.get('login')
|
||||
password = form.get('password')
|
||||
db_engine = request.app.db_engine
|
||||
if (yield from check_credentials(db_engine, login, password)):
|
||||
yield from remember(request, response, login)
|
||||
return response
|
||||
|
||||
return web.HTTPUnauthorized(
|
||||
body=b'Invalid username/password combination')
|
||||
|
||||
@require('public')
|
||||
@asyncio.coroutine
|
||||
def logout(self, request):
|
||||
response = web.Response(body=b'You have been logged out')
|
||||
yield from forget(request, response)
|
||||
return response
|
||||
|
||||
@require('public')
|
||||
@asyncio.coroutine
|
||||
def internal_page(self, request):
|
||||
response = web.Response(
|
||||
body=b'This page is visible for all registered users')
|
||||
return response
|
||||
|
||||
@require('protected')
|
||||
@asyncio.coroutine
|
||||
def protected_page(self, request):
|
||||
response = web.Response(body=b'You are on protected page')
|
||||
return response
|
||||
|
||||
def configure(self, app):
|
||||
router = app.router
|
||||
router.add_route('GET', '/', self.index, name='index')
|
||||
router.add_route('POST', '/login', self.login, name='login')
|
||||
router.add_route('GET', '/logout', self.logout, name='logout')
|
||||
router.add_route('GET', '/public', self.internal_page, name='public')
|
||||
router.add_route('GET', '/protected', self.protected_page,
|
||||
name='protected')
|
61
demo/database_auth/main.py
Normal file
61
demo/database_auth/main.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import asyncio
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp_session import setup as setup_session
|
||||
from aiohttp_session.redis_storage import RedisStorage
|
||||
from aiohttp_security import setup as setup_security
|
||||
from aiohttp_security import SessionIdentityPolicy
|
||||
from aiopg.sa import create_engine
|
||||
from aioredis import create_pool
|
||||
|
||||
|
||||
from demo.db_auth import DBAuthorizationPolicy
|
||||
from demo.handlers import Web
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def init(loop):
|
||||
redis_pool = yield from create_pool(('localhost', 6379))
|
||||
db_engine = yield from create_engine(user='aiohttp_security',
|
||||
password='aiohttp_security',
|
||||
database='aiohttp_security',
|
||||
host='127.0.0.1')
|
||||
app = web.Application(loop=loop)
|
||||
app.db_engine = db_engine
|
||||
setup_session(app, RedisStorage(redis_pool))
|
||||
setup_security(app,
|
||||
SessionIdentityPolicy(),
|
||||
DBAuthorizationPolicy(db_engine))
|
||||
|
||||
web_handlers = Web()
|
||||
web_handlers.configure(app)
|
||||
|
||||
handler = app.make_handler()
|
||||
srv = yield from loop.create_server(handler, '127.0.0.1', 8080)
|
||||
print('Server started at http://127.0.0.1:8080')
|
||||
return srv, app, handler
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def finalize(srv, app, handler):
|
||||
sock = srv.sockets[0]
|
||||
app.loop.remove_reader(sock.fileno())
|
||||
sock.close()
|
||||
|
||||
yield from handler.finish_connections(1.0)
|
||||
srv.close()
|
||||
yield from srv.wait_closed()
|
||||
yield from app.finish()
|
||||
|
||||
|
||||
def main():
|
||||
loop = asyncio.get_event_loop()
|
||||
srv, app, handler = loop.run_until_complete(init(loop))
|
||||
try:
|
||||
loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
loop.run_until_complete((finalize(srv, app, handler)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
5
demo/database_auth/sql/init_db.sql
Normal file
5
demo/database_auth/sql/init_db.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE USER aiohttp_security WITH PASSWORD 'aiohttp_security';
|
||||
DROP DATABASE IF EXISTS aiohttp_security;
|
||||
CREATE DATABASE aiohttp_security;
|
||||
ALTER DATABASE aiohttp_security OWNER TO aiohttp_security;
|
||||
GRANT ALL PRIVILEGES ON DATABASE aiohttp_security TO aiohttp_security;
|
38
demo/database_auth/sql/sample_data.sql
Normal file
38
demo/database_auth/sql/sample_data.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- create users table
|
||||
CREATE TABLE IF NOT EXISTS users
|
||||
(
|
||||
id integer NOT NULL,
|
||||
login character varying(256) NOT NULL,
|
||||
passwd character varying(256) NOT NULL,
|
||||
is_superuser boolean NOT NULL DEFAULT false,
|
||||
disabled boolean NOT NULL DEFAULT false,
|
||||
CONSTRAINT user_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT user_login_key UNIQUE (login)
|
||||
);
|
||||
|
||||
-- and permissions for them
|
||||
CREATE TABLE IF NOT EXISTS permissions
|
||||
(
|
||||
id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
perm_name character varying(64) NOT NULL,
|
||||
CONSTRAINT permission_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT user_permission_fkey FOREIGN KEY (user_id)
|
||||
REFERENCES users (id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- insert some data
|
||||
INSERT INTO users(id, login, passwd, is_superuser, disabled)
|
||||
VALUES (1, 'admin', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', TRUE, FALSE);
|
||||
INSERT INTO users(id, login, passwd, is_superuser, disabled)
|
||||
VALUES (2, 'moderator', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', FALSE, FALSE);
|
||||
INSERT INTO users(id, login, passwd, is_superuser, disabled)
|
||||
VALUES (3, 'user', '$5$rounds=535000$2kqN9fxCY6Xt5/pi$tVnh0xX87g/IsnOSuorZG608CZDFbWIWBr58ay6S4pD', FALSE, FALSE);
|
||||
|
||||
INSERT INTO permissions(id, user_id, perm_name)
|
||||
VALUES (1, 2, 'protected');
|
||||
INSERT INTO permissions(id, user_id, perm_name)
|
||||
VALUES (2, 2, 'public');
|
||||
INSERT INTO permissions(id, user_id, perm_name)
|
||||
VALUES (3, 3, 'public');
|
Reference in New Issue
Block a user