Example using SQL backends¶
Authorization¶
The following example is a walk-through for a simple web application that uses SQLAlchemy for its backend.
First, we start with a simple model:
from alcohol.rbac.sqlalchemy import SQLAlchemyRBAC
from alcohol.mixins.sqlalchemy import SQLAlchemyEmailMixin,SQLAlchemyPasswordMixin
from sqlalchemy import Column, Integer, Unicode, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base, SQLAlchemyEmailMixin, SQLAlchemyPasswordMixin):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True)
class Role(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
class Permission(Base):
__tablename__ = 'permissions'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
acl = SQLAlchemyRBAC(User, Role, Permission)
Note that alcohol supports non-Integer
primary keys just fine and will
create the appropriate columns.
A script to create our database is required; for simplicity, we will use a very simple version:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///dev.db', echo=True)
Base.metadata.create_all(engine)
# we need a session
Session = sessionmaker(bind=engine)
s = Session()
# create a few users
alice = User(name='Alice')
bob = User(name='Bob')
cecille = User(name='Cecille')
# and a few roles
admin = Role(name='admin')
user = Role(name='user')
# now we can create permissions as well
view_article = Permission(name='view_article')
publish_article = Permission(name='publish_article')
s.add_all([alice, bob, cecille, admin, user, view_article, publish_article])
s.commit()
# later on , we can assign permissions to roles (and users to permissions):
acl.assign(alice, admin)
acl.assign(bob, user)
acl.permit(admin, publish_article)
acl.permit(admin, view_article)
acl.permit(user, view_article)
s.add_all([alice, bob, admin, user])
s.commit()
Note that in a more real-world situation, it would depend on the application whether or not users, roles and permissions are created upfront upon DB initialization or while the app is running. For example, users are usually created by having them sign up, but smaller applications may have a fixed set of roles and permissions. Larger, more configurable applications may customize these as well.
Of course, we will want to check these later on:
alice = s.query(User).filter_by(name='Alice').one()
bob = s.query(User).filter_by(name='Bob').one()
read_article = s.query(Permission).filter_by(name='publish_article').one()
print acl.allowed(alice, publish_article)
print acl.allowed(bob, publish_article)
It is somewhat cumbersome to look up the Permissions
object each time, how
to alleviate this is up to the application. A simple-yet-effective way is
caching the Permissions known to the application.
Passwords and emails¶
Notice that due to the fact that we added
SQLAlchemyPasswordMixin
and
SQLAlchemyEmailMixin
, we have additional
functionality on users unrelated to authoziation:
SECRET_KEY='my-apps-secret-key'
pw_reset_token = alice.create_password_reset_token(SECRET_KEY)
# later on, we can validate this token
if alice.check_password_reset_token(SECRET_KEY, pw_reset_token):
alice.password = 'new-pw'
# to activate emails, a similar functionality exists:
mail_token = alice.create_email_activation_token(SECRET_KEY, 'new@mail.com')
alice.activate_email(SECRET_KEY, mail_token)
Calling alcohol.mixins.sqlalchemy.SQLAlchemyEmailMixin.activate_email
will automatically update the email address of Alice here, provided the token
has not been tampered with and is not older than a day.