Flask Principal

“I am that I am”

Introduction

Flask-Principal provides a very loose framework to tie in providers of two types of service, often located in different parts of a web application:

  1. Authentication providers
  2. User information providers

For example, an authentication provider may be oauth, using Flask-OAuth and the user information may be stored in a relational database. Looseness of the framework is provided by using signals as the interface.

The major components are the Identity, Needs, Permission, and the Principal.

  1. The Identity represents the user, and is stored/loaded from the cookie sessions.
  2. A need is a pair of (method, value) where method is used to specify common things such as “role”, “user”, etc. And the value is the value. An example of such is (‘role’, ‘admin’). Which would be a Need for a admin role.
  1. A permission is a set of requirements, any of which should be present for access to a resource.
  2. A Principal is the context of a certain identity against a certain Permission.

Protecting access to resources

For users of Flask-Principal (not authentication providers), access restriction is easy to define as both a decorator and a context manager. A simple quickstart example is presented with commenting:

from flask import Flask, Response
from flaskext.principal import Permission, RoleNeed

app = Flask(__name__)

# Create a permission with a single Need, in this case a RoleNeed.
admin_permission = Permission(RoleNeed('admin'))

# protect a view with a principal for that need
@app.route('/admin')
@admin_permission.require()
def do_admin_index():
    return Response('Only if you are an admin')

# this time protect with a context manager
@app.route('/articles')
def do_articles():
    with admin_permission.require():
        return Response('Only if you are admin')

API

flaskext.principal

Identity management for Flask.

copyright:
  1. 2010 by Ali Afshar.
license:

MIT, see LICENSE for more details.

Starting the extension

flaskext.principal.init_principal(app)

Start the principal extension

For example:

from flask import Flask
from flaskext.principal import init_principal

app = Flask(__name__)
init_principal(app)

Main Types

class flaskext.principal.Permission(*needs)

Represents needs, any of which must be present to access a resource

Parameters:
  • needs – The needs for this permission
issubset(other)

Whether this permission needs are a subset of another

Parameters:
  • other – The other permission
needs

A set of needs, any of which must be present in an identity to have access.

require()

Create a principal for this permission.

The principal may be used as a context manager, or a decroator.

union(other)

Create a new permission with the requirements of the union of this and other.

Parameters:
  • other – The other permission
class flaskext.principal.Identity(name, auth_type='')

Represent the user’s identity.

Parameters:
  • name – The username
  • auth_type – The authentication type used to confirm the user’s identity.

The identity is used to represent the user’s identity in the system. This object is created on login, or on the start of the request as loaded from the user’s session.

Once loaded it is sent using the identity-loaded signal, and should be populated with additional required information.

Needs that are provided by this identity should be added to the provides set after loading.

can(permission)

Whether the identity has access to the permission.

Parameters:
  • permission – The permission to test provision for.
provides

A set of needs provided by this user

Provisions can be added using the add method, for example:

identity = Identity('ali')
identity.provides.add(('role', 'admin'))
class flaskext.principal.AnonymousIdentity

An anonymous identity

Attr name:“anon”
class flaskext.principal.Principal(permission)

The context of an identity for a permission.

Note

The principal is usually created by the flaskext.Permission.require method call for normal use-cases.

The principal behaves as either a context manager or a decorator. The permission is checked for provision in the identity, and if available the flow is continued (context manager) or the function is executed (decorator).

can()

Whether the identity has access to the permission

identity

The identity of this principal

permission

The permission of this principal

flaskext.principal.set_identity(identity)

Set the identity in the session

Predefined Need Types

class flaskext.principal.Need

A required need

This is just a named tuple, and practically any tuple will do.

The method attribute can be used to look up element 0, and the value attribute can be used to look up element 1.

principal.RoleNeed

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

principal.UserNeed

partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.

Relevant Signals

principal.identity_changed

Signal sent when the identity for a request has been changed.

Actual name: identity-changed

Authentication providers should send this signal when authentication has been successfully performed. Flask-Principal connects to this signal and causes the identity to be saved in the session.

For example:

from flaskext.principal import Identity, identity_changed

def login_view(req):
    username = req.form.get('username')
    # check the credentials
    identity_changed.send('login-view', identity=Identity(username))
principal.identity_loaded

Signal sent when the identity has been initialised for a request.

Actual name:: identity-loaded

Identity information providers should connect to this signal to perform two major activities:

  1. Populate the identity object with the necessary authorization provisions.
  2. Load any additional user information.

For example:

from flaskext.principal import indentity_loaded, RoleNeed, UserNeed

@identity_loaded.connect
def on_identity_loaded(sender, identity):
    # Get the user information from the db
    user = db.get(identity.name)
    # Update the roles that a user can provide
    for role in user.roles:
        identity.provides.add(RoleNeed(role.name))
    # Save the user somewhere so we only look it up once
    identity.user = user

Indices and tables

Fork me on GitHub