FLASK-PRINCIPAL(1) Flask Principal FLASK-PRINCIPAL(1) (NAME) flask-principal - Flask-Principal " " (INTRODUCTION) Flask-Principal (loose) : 1. (Authentication providers) 2. (User information providers) OAuth Flask-OAuth . . Identity Needs Permission IdentityContext. 1. Identity ( session) . Identity . 2. Need : << >> ("has the admin role") << >> ("can edit blog posts"). Need (tuple) . Need ( ) (method, value) method "role" "user" value . ('role', 'admin') Need admin (Triples) << >> ('article', 'edit', 46) 46 / (ID) / . Needs Need . Need Identity (Needs) . 3. Permission (requirements) . 4. IdentityContext (context) (Identity) Permission . context manager (decorator) . [] (LINKS) o o o <> (PROTECTING ACCESS TO RESOURCES) Flask-Principal ( ) (decorator) (context manager) . : from flask import Flask, Response from flask.ext.principal import Principal, Permission, RoleNeed app = Flask(__name__) # load the extension principals = Principal(app) # 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') identity-changed . Flask-Login Flask-Principal : from flask import Flask, current_app, request, session from flask.ext.login import LoginManager, login_user, logout_user, \ login_required, current_user from flask.ext.wtf import Form, TextField, PasswordField, Required, Email from flask.ext.principal import Principal, Identity, AnonymousIdentity, \ identity_changed app = Flask(__name__) Principal(app) login_manager = LoginManager(app) @login_manager.user_loader def load_user(userid): # Return an instance of the User model return datastore.find_user(id=userid) class LoginForm(Form): email = TextField() password = PasswordField() @app.route('/login', methods=['GET', 'POST']) def login(): # A hypothetical login form that uses Flask-WTF form = LoginForm() # Validate form input if form.validate_on_submit(): # Retrieve the user from the hypothetical datastore user = datastore.find_user(email=form.email.data) # Compare passwords (use password hashing production) if form.password.data == user.password: # Keep the user info in the session using Flask-Login login_user(user) # Tell Flask-Principal the identity changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return redirect(request.args.get('next') or '/') return render_template('login.html', form=form) @app.route('/logout') @login_required def logout(): # Remove the user information from the session logout_user() # Remove session keys set by Flask-Principal for key in ('identity.name', 'identity.auth_type'): session.pop(key, None) # Tell Flask-Principal the user is anonymous identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) return redirect(request.args.get('next') or '/') (USER INFORMATION PROVIDERS) identity-loaded (roles) Identity . Flask-Login . : from flask.ext.login import current_user from flask.ext.principal import identity_loaded, RoleNeed, UserNeed @identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): # Set the identity user object identity.user = current_user # Add the UserNeed to the identity if hasattr(current_user, 'id'): identity.provides.add(UserNeed(current_user.id)) # Assuming the User model has a list of roles, update the # identity with the roles that the user provides if hasattr(current_user, 'roles'): for role in current_user.roles: identity.provides.add(RoleNeed(role.name)) (GRANULAR RESOURCE PROTECTION) . Need Permission identity_loaded . : from collections import namedtuple from functools import partial from flask.ext.login import current_user from flask.ext.principal import identity_loaded, Permission, RoleNeed, \ UserNeed BlogPostNeed = namedtuple('blog_post', ['method', 'value']) EditBlogPostNeed = partial(BlogPostNeed, 'edit') class EditBlogPostPermission(Permission): def __init__(self, post_id): need = EditBlogPostNeed(unicode(post_id)) super(EditBlogPostPermission, self).__init__(need) @identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): # Set the identity user object identity.user = current_user # Add the UserNeed to the identity if hasattr(current_user, 'id'): identity.provides.add(UserNeed(current_user.id)) # Assuming the User model has a list of roles, update the # identity with the roles that the user provides if hasattr(current_user, 'roles'): for role in current_user.roles: identity.provides.add(RoleNeed(role.name)) # Assuming the User model has a list of posts the user # has authored, add the needs to the identity if hasattr(current_user, 'posts'): for post in current_user.posts: identity.provides.add(EditBlogPostNeed(unicode(post.id))) (endpoint) . (ID) ( ) : @app.route('/posts/', methods=['PUT', 'PATCH']) def edit_post(post_id): permission = EditBlogPostPermission(post_id) if permission.can(): # Save the edits ... return render_template('edit_post.html') abort(403) # HTTP Forbidden class flask_principal.Principal(app=None, use_sessions=True, skip_static=False) Principal. (Parameters) o app -- flask . o use_sessions -- (sessions) . o skip_static -- (endpoints) . identity_loader(f) (identity loader). . . : app = Flask(__name__) principals = Principal(app) @principals.identity_loader def load_identity_from_weird_usecase(): return Identity('ali') identity_saver(f) (identity saver). () . : app = Flask(__name__) principals = Principal(app) @principals.identity_saver def save_identity_to_weird_usecase(identity): my_special_cookie['identity'] = identity set_identity(identity) . (Parameters) identity -- . (MAIN TYPES) class flask_principal.Permission(*needs) (needs) . (Parameters) needs -- . allows(identity) . (Parameters) identity -- . can() (context) . . difference(other) (). issubset(other) . (Parameters) other -- . require(http_exception=None) principal . principal (context manager) . http_exception abort() HTTP . PermissionDenied . (Parameters) http_exception -- HTTP ( 403 401 ). reverse() (needs->excludes excludes->needs). test(http_exception=None) . ()require . : with permission.require(): pass union(other) . (Parameters) other -- . class flask_principal.Identity(id, auth_type=None) . (Parameters) o id -- (user id). o auth_type -- . . (login) (session) . identity-loaded . (Needs) provides . can(permission) . (Parameters) permission -- . class flask_principal.AnonymousIdentity class flask_principal.IdentityContext(permission, http_exception=None) (context) . : principal flaskext.Permission.require . principal (context manager) (decorator) . (context manager) (decorator). can() http_exception principal property identity principal NEED (PREDEFINED NEED TYPES) class flask_principal.Need(method, value) (need) (named tuple) . method value . flask_principal.RoleNeed (alias) functools.partial(, 'role') flask_principal.UserNeed (alias) functools.partial(, 'id') class flask_principal.ItemNeed(method, value, type) (item need) item need . Need (type) : ItemNeed('update', 27, 'posts') ('update', 27, 'posts') # or like this . . (SIGNALS) identity_changed . identity_loaded . FLASK-PRINCIPAL (FLASK-PRINCIPAL CHANGELOG) Flask-Principal . 0.4.0 (Version 0.4.0) o Python 3 o Python 2.5 0.3.5 (Version 0.3.5) o "anon" AnonymousIdentity o name Identity id 0.3.4 (Version 0.3.4) o __repr__ Identity Permission o _is_static_resource o init_app (factory pattern). 0.3.2 o static_url_path Flask 0.9 0.3.1 o (re-raising) / (tracebacks) 0.3 o Python 2.5/GAE o o ignore_static o 0.2 Ali Asfshar. o <> o <> o <> (AUTHORS) Matt Wright (COPYRIGHT) 2012, Ali Afshar 0.4.0 December 15, 2025 FLASK-PRINCIPAL(1)