score.auth

This module provides authentication (“who are you?”) and authorization (“are you allowed to do this?”) for your application. The former is implemented with the use of so-called Authenticators, the latter with a clever workflow for defining and probing rules.

Quickstart

Authentication

Add this module to your initialization list and configure it to make use of the score.session module (assuming you have a User class in your database):

[score.init]
modules =
    score.db
    score.session
    score.auth

[auth]
authenticators =
    score.auth.authenticator.SessionAuthenticator(path.to.User)

You can now store assign a User to your context and the authenticator will store and retrieve that user from your session in subsequent contexts:

>>> with score.ctx.Context() as ctx:
...     assert ctx.actor is None
...     ctx.actor = ctx.db.query(path.to.User).first()
...
>>> with score.ctx.Context() as ctx:
...     assert ctx.actor is ctx.db.query(path.to.User).first()
...

Authorization

Create a rule set for authorization queries and add some rules:

ruleset = score.auth.RuleSet()

@ruleset.rule('confuse', Cat)
def confuse(ctx, cat):
    # decide, whether this cat may be confused given this context
    return isinstance(ctx.actor, Vet)

The module configuration should point to the given rule set for authorization:

[auth]
authenticators =
    score.auth.authenticator.SessionAuthenticator(path.to.User)
ruleset = path.to.ruleset

You can now query your context for permissions:

>>> ctx.permits('confuse', cat)
False

Configuration

score.auth.init(confdict, ctx)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

ruleset RuleSet()
A dotted path to an instance of RuleSet in your project. This module will be initialized without any rules, if this configuration key is omitted, resulting in denial of every operation.
authenticators list()
List of Authenticators capable of determining the current actor.
ctx.member actor

The context member under which the current actor should be made available. Leaving this at its default will allow you to access the current actor as the following:

>>> ctx.actor

Details

Authenticators

When the configured module is requested to determine the currently active user, it will need to forward the request to registered Authenticators. These Authenticators are organized as a chain, each Authenticator asking the next one if it cannot figure out the current user.

A typical chain of Authenticators could look like the following:

  • A login authenticator: Tests, if a form was submitted, that would log the user in. If it was not, it asks the next Authenticator.
  • SessionAuthenticator: Checks the session, if there is a previously stored user. Continues asking the next Authenticator if there is not.
  • NullAuthenticator: Returns None to indicate that the chain has reached its end.

An example login authenticator could look like the following:

from score.auth import Authenticator

class LoginAuthenticator(Authenticator):

    def retrieve(self, ctx):
        user = self._perform_login(ctx)
        if user:
            return user
        else:
            # the login was not successful, ask the next authenticator to
            # retrieve the current user.
            return self.next.retrieve(ctx)

    def _perform_login(self, ctx):
        if not hasattr(ctx, 'http'):
            return None
        if 'username' not in ctx.http.request.POST:
            return None
        if 'password' not in ctx.http.request.POST:
            return None
        username = ctx.http.request.POST['username']
        user = ctx.db.query(db.User).\
            filter(db.User.username == username)
            first()
        if not user:
            return None
        if not user.verify_password(ctx.http.request.POST['password']):
            return None
        # we have a logged in user, so pass it to all subsequent
        # authenticators to allow them storing the value.
        self.next.store(ctx, user)
        return user

RuleSets

The RuleSet is a decorator for defining rules. A rule consists of an operation and any number of type objects, like the following:

ruleset = score.auth.RuleSet()

@ruleset.rule('sing', Song)
def sing(ctx, song):
    return song.performer == ctx.actor and \
        Permission.SING_A_SONG in actor.permissions

Whenever the context is queried whether it is possible to sing a specific Song, this function will be invoked to provide the answer:

if ctx.permits('sing', Song.load('Galaxy Song')):
    # this must be Stephen Hawking!

It is also possible to omit the parameters to the decorator, if the rule does not require any objects. The name of the rule will be the name of the function in this case:

ruleset = score.auth.RuleSet()

@ruleset.rule  # NOTE: no definitions here ..
def sing(ctx):  # .. and no additional function parameters
    pass

API

score.auth.init(confdict, ctx)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

ruleset RuleSet()
A dotted path to an instance of RuleSet in your project. This module will be initialized without any rules, if this configuration key is omitted, resulting in denial of every operation.
authenticators list()
List of Authenticators capable of determining the current actor.
ctx.member actor

The context member under which the current actor should be made available. Leaving this at its default will allow you to access the current actor as the following:

>>> ctx.actor
class score.auth.ConfiguredAuthModule(ruleset, ctx_member)[source]

This module’s configuration class.

ruleset

A configured instance of RuleSet.

permits(ctx, operation, *args, raise_=False)[source]

A proxy for RuleSet.permits() of the configured ruleset instance.

class score.auth.RuleSet[source]

Stores rules and handles operations for classes.

rules

A set of rules added by the rule decorator.

permits(ctx, operation, *args, raise_=False)[source]

Checks if given operation is allowed on given args in given context.

rule(operation, *args)[source]

Decorator for adding a rule to this RuleSet.

The function can be used in two different ways: Either it directly annotates a function …

@ruleset.rule
def sing(ctx):
    return ctx.actor.name == 'Stephen Hawking'

… or it defines an operation and a list of argument types, which must match the types of the arguments of the call to RuleSet.permits():

@ruleset.rule('rewrite', Song)
def rewrite_song(ctx, song):
    return True  # songs may be rewritten at any time
class score.auth.authenticator.Authenticator(conf, next)[source]

An object that can query (and possibly remember) the currently acting user.

class score.auth.authenticator.NullAuthenticator[source]

Always returns None as the current user. This class is used as the last Authenticator in an authentication chain.

class score.auth.authenticator.SessionAuthenticator(conf, next, actor_class=None, session_key='actor')[source]

Makes a lookup in the current session context member.