Skip to main content

Simple Declarative Authentication based on Ryan Bates excellent cancan library

Project description

Simple Declarative Authentication DSL inspired by Ryan Bates’ excellent cancan library

https://travis-ci.org/jtushman/bouncer.svg?branch=docs:target:https://travis-ci.org/jtushman/bouncer

Introduction

Meet bouncer.

bouncer is your faithful servant. Big, burly, trustworthy – smarter than he looks, but very effective in what he does. You just have to talk simply to him. For example:

from bouncer import authorization_method
from bouncer.constants import *

@authorization_method
def authorize(user, they):

    if user.is_admin:
        they.can(MANAGE, ALL)
    else:
        they.can(READ, ALL)
        they.cannot(READ, 'TopSecretDocs')

        def if_author(article):
            return article.author == user

        they.can(EDIT, 'Article', if_author)

And once you have that setup, you can ask questions like:

jonathan = User(name='jonathan',admin=False)
marc = User(name='marc',admin=False)

article = Article(author=jonathan)

print can(jonathan, EDIT, article)   # True
print can(marc, EDIT, article)       # False

# Can Marc view articles in general?
print can(marc, VIEW, Article)       # True

Installation

pip install bouncer

Defining Abilities

User permissions are defined in an method decorated with @authorize_method

A simple setup looks like so …

@authorization_method
def authorize(user, they):

    if user.is_admin:
        they.can(MANAGE, ALL)
    else:
        they.can(READ, ALL)

        def if_author(article):
            return article.author == user

        they.can(EDIT, Article, if_author)
  • If you do not think the “they.can” is pythonic enough you can use the append syntax

@authorization_method
def authorize(user, abilities):

    if user.is_admin:
        abilities.append(MANAGE, ALL)
    else:
        abilities.append(READ, ALL)

        # See I am using a string here
        abilities.append(EDIT, 'Article', author=user)

Alternative syntax

dict syntax

  • You can also use an alternative dict syntax. The following is equivalent to above:

@authorization_method
def authorize(user, they):

    if user.is_admin:
        they.can(MANAGE, ALL)
    else:
        they.can(READ, ALL)
        they.can(EDIT, Article, author=user)
  • You can add multiple conditions to the dict:

they.can(READ, Article, published=True, active=True)

Strings instead of classes

Use can use Strings instead of classes (so you do not need to import a bunch of files you are not using in initialization

@authorization_method
def authorize(user, they):

    if user.is_admin:
        they.can(MANAGE, ALL)
    else:
        they.can(READ, ALL)

        # Notice that I am using a string here
        they.can(EDIT, 'Article', author=user)

You can (are encouraged to) combine similar rules on a single line:

they.can((EDIT,READ,DELETE),(Article,Photo))

Combining Abilities

It is possible to define multiple abilites for the same resource. This is particularly useful in combination with the cannot method

they.can(MANAGE, ALL)
then.cannot(DELETE, ('USER', 'ACCOUNT')

Check Abilities & Authorization

There are two main way for checking for authorization. can (and its brother cannot) and ensure

  • can returns a boolean

  • while ensure will raise an AccessDenied Exception

from bouncer import can, ensure
from bouncer.constants import *

jonathan = User(name='jonathan',admin=False)

# can jonathan edit articles in general
can(jonathan, EDIT, Article)

# ensure jonathan edit articles in general -- otherwise we are going to throw an exception
ensure(jonathan, EDIT, Article)

article = Article(author=jonathan)

# can jonathan delete this specific article
can(jonathan, EDIT, article)

Decorating your User Model

Helper methods are mixed into your User model (once it is decorated with the @authorization_target)

For example:

from bouncer import authorization_target

@authorization_target
class User(object):

def __init__(self, **kwargs):
    self.id = kwargs.get('id', 1)
    self.name = kwargs.get('name', '')
    self.admin = kwargs.get('name', False)
    pass

@property
def is_admin(self):
    return self.admin

jonathan = User(name='jonathan',admin=False)
marc = User(name='marc',admin=False)

article = Article(author=jonathan)

print jonathan.can(EDIT,article)   # True
print marc.can(EDIT,article)       # False

Flask

If you use Flask, I am currently working on a Flask extension – follow its progress here: flask-bouncer.

Questions / Issues

Feel free to ping me on twitter: @tushman or add issues or PRs at https://github.com/jtushman/bouncer

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bouncer-0.1.10.tar.gz (5.4 kB view details)

Uploaded Source

File details

Details for the file bouncer-0.1.10.tar.gz.

File metadata

  • Download URL: bouncer-0.1.10.tar.gz
  • Upload date:
  • Size: 5.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for bouncer-0.1.10.tar.gz
Algorithm Hash digest
SHA256 92c49a438edcf307c6ca593c5144e01433e60118c5ed2252af017dc112d57353
MD5 7166beb8bb1b010049aa99b3d16b5521
BLAKE2b-256 6672ac759492cbb443f258dcf9ae142eefbebae61fbd02be12c1260fe6837a42

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page