From 507de82aba94e1bb1adc5aa17aa389b4cfcae60a Mon Sep 17 00:00:00 2001 From: Matt Wright Date: Fri, 13 Jul 2012 16:06:10 -0400 Subject: [PATCH] Update docs --- docs/index.rst | 367 +++++++++++++++++------------------ flask_security/decorators.py | 12 +- 2 files changed, 186 insertions(+), 193 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index fd587fa..7987894 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,32 +3,20 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Flask-Security -============== +Extending Flask-Security +======================== .. module:: flask_security -Simple security for Flask applications combining -`Flask-Login `_, -`Flask-Principal `_, -`Flask-WTF `_, -`passlib `_, and your choice of datastore. -Currently `SQLAlchemy `_ via -`Flask-SQLAlchemy `_ and -`MongoEngine `_ via -`Flask-MongoEngine `_ are supported -out of the box. You will need to install the necessary Flask extensions that -you'll be using on your own. Additionally, you may need to install an encryption -library such as `py-bcrypt `_ (if -you plan to use bcrypt) for your desired encryption method. +Quick and simple security for Flask applications. Contents ========= * :ref:`overview` * :ref:`installation` -* :ref:`getting-started` -* :ref:`additional-user-fields` +* :ref:`quick-start` +* :ref:`models` * :ref:`flask-script-commands` * :ref:`api` * :doc:`Changelog ` @@ -39,17 +27,34 @@ Contents Overview ======== -Flask-Security does a few things that Flask-Login and Flask-Principal don't -provide out of the box. They are: +Flask-Security allows you to quickly add common user and security mechanisms to +your Flask application. They include: -1. Setting up login and logout endpoints -2. Authenticating users based on username or email -3. Limiting access based on user 'roles' -4. User and role creation -5. Password encryption +1. Session based authentication +2. Role management +3. Password encryption +4. Basic HTTP authentication +5. Token based authentication +6. Token based account activation (optional) +7. Token based password recovery/resetting (optional) +8. User registration (optional) +9. Login tracking (optional) +10. Basic user management commands -That being said, you can still hook into things such as the Flask-Login and -Flask-Principal signals if need be. +Many of these features are made possible by integrating various Flask extensions +and libraries. They include: + +1. Flask-Login +2. Flask-Mail +3. Flask-Principal +4. Flask-Script +5. Flask-WTF +6. itsdangerous +7. passlib + +Additionally, it assumes you'll be using a common library for your database +connections and model definitions. Flask-Security thus supports SQLAlchemy and +MongoEngine out of the box and additional libraries can easily be supported. .. _installation: @@ -66,109 +71,93 @@ Then install your datastore requirement. **SQLAlchemy**:: - $ pip install Flask-SQLAlchemy + $ pip install flask-sqlalchemy **MongoEngine**:: - $ pip install https://github.com/sbook/flask-mongoengine/tarball/master + $ pip install flask-mongoengine + +And lastly install any password encryption library that you may need. For +example:: + + $ pip install py-bcrypt -.. _getting-started: +.. _quick-start: -Getting Started -=============== +Quick Start Example +=================== -The following code samples will illustrate how to get started using SQLAlchemy. -First thing you'll want to do is setup your application and datastore:: +The following code sample illustrates how to get started as quickly as possible +using SQLAlchemy.:: - from flask import Flask, render_template + from flask import Flask, render_template, url_for from flask.ext.sqlalchemy import SQLAlchemy - from flask.ext.security import (User, Security, LoginForm, login_required, - roles_accepted, user_datastore) - from flask.ext.security.datastore.sqlalchemy import SQLAlchemyUserDatastore - + from flask.ext.security import Security, UserMixin, RoleMixin, \ + login_required + from flask.ext.security.datastore import SQLAlchemyUserDatastore + from flask.ext.security.forms import LoginForm + app = Flask(__name__) + app.debug = True app.config['SECRET_KEY'] = 'secret' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' - - db = SQLAlchemy(app) - Security(app, SQLAlchemyUserDatastore(db)) + app.config['SECURITY_POST_LOGIN_VIEW'] = '/protected' -You'll probably want to at least one user to the database to test this out. -There are many ways to do this, but this is a quick and dirty way to do it:: + db = SQLAlchemy(app) + + roles_users = db.Table('roles_users', + db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), + db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) + + class Role(db.Model, RoleMixin): + id = db.Column(db.Integer(), primary_key=True) + name = db.Column(db.String(80), unique=True) + description = db.Column(db.String(255)) + + class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(255), unique=True) + password = db.Column(db.String(120)) + remember_token = db.Column(db.String(255)) + active = db.Column(db.Boolean()) + authentication_token = db.Column(db.String(255)) + roles = db.relationship('Role', secondary=roles_users, + backref=db.backref('users', lazy='dynamic')) + + datastore = SQLAlchemyUserDatastore(db, User, Role) + + Security(app, datastore) @app.before_first_request - def before_first_request(): - user_datastore.create_role(name='admin') - user_datastore.create_user(username='matt', email='matt@something.com', - password='password', roles=['admin']) - -Next you'll want to setup your login screen. Setup your view:: + def add_user(): + db.create_all() + datastore.create_user(email='matt@matt.com', + password='password') - @app.route("/login") + @app.route('/') + @app.route('/login') def login(): - return render_template('login.html', form=LoginForm()) - -And corresponding template:: + return render_template('security/logins/new.html', + login_form=LoginForm()) -
- {{ form.hidden_tag() }} - {{ form.username.label }} {{ form.username }}
- {{ form.password.label }} {{ form.password }}
- {{ form.remember.label }} {{ form.remember }}
- {{ form.submit }} -
- -By default, Flask-Security will redirect a user to `/profile` after logging in. -You can set this page up yourself or set the `SECURITY_POST_LOGIN` config -value to change this behavior. Regardless, setup a protected view as such:: - - @app.route('/profile') + @app.route('/protected') @login_required - def profile(): - return render_template('profile.html') - -Now you have an application with basic authentication. If you run the local -development server you can visit `http://localhost:5000/login `_ -to login. + def protected(): + return """

You are logged in

+

Log out""" % ( + url_for('flask_security.logout')) -The last thing you'll want to do is add a logout link to your templates. This -can be achieved with:: - - Logout - -Now, for instance, say you want to protect an admin area to users that are -administrators. You can use the `roles_accepted` decorator to prevent access. -The corresponding view would look like such:: - - @app.route('/admin') - @roles_accepted('admin') - def admin(): - return render_template('admin/index.html') - -And lastly, maybe you only want to show something in a template if a user has a -specific role:: - - {% if current_user.has_role('admin') %} - Admin Panel - {$ endif %} + if __name__ == '__main__': + app.run() -.. _additional-user-fields: +.. _models: -Additional User Fields ----------------------- -If you'd like to add additional fields to the user model you can use a mixin -class that specifies your additional fields. The following is an example of -how you might do this:: +Data Models +----------- - db = SQLAlchemy(app) - - class UserAccountMixin(): - first_name = db.Column(db.String(120)) - last_name = db.Column(db.String(120)) - - Security(app, SQLAlchemyUserDatastore(db, UserAccountMixin)) +TODO: Describe models and required fields for various features .. _flask-script-commands: @@ -176,12 +165,14 @@ Flask-Script Commands --------------------- Flask-Security comes packed with a few Flask-Script commands. They are: -* :class:`flask.ext.security.script.CreateUserCommand` -* :class:`flask.ext.security.script.CreateRoleCommand` -* :class:`flask.ext.security.script.AddRoleCommand` -* :class:`flask.ext.security.script.RemoveRoleCommand` -* :class:`flask.ext.security.script.DeactivateUserCommand` -* :class:`flask.ext.security.script.ActivateUserCommand` +* :class:`flask_security.script.CreateUserCommand` +* :class:`flask_security.script.CreateRoleCommand` +* :class:`flask_security.script.AddRoleCommand` +* :class:`flask_security.script.RemoveRoleCommand` +* :class:`flask_security.script.DeactivateUserCommand` +* :class:`flask_security.script.ActivateUserCommand` +* :class:`flask_security.script.ActivateUserCommand` +* :class:`flask_security.script.GenerateBlueprintCommand` Register these on your script manager for pure convenience. @@ -192,25 +183,63 @@ Configuration Values ==================== * :attr:`SECURITY_URL_PREFIX`: Specifies the URL prefix for the Security - blueprint -* :attr:`SECURITY_AUTH_PROVIDER`: Specifies the class to use as the - authentication provider. Such as `flask.ext.security.AuthenticationProvider` -* :attr:`SECURITY_PASSWORD_HASH`: Specifies the encryption method to use. e.g.: - plaintext, bcrypt, etc -* :attr:`SECURITY_USER_DATASTORE`: Specifies the property name to use for the - user datastore on the application instance -* :attr:`SECURITY_LOGIN_FORM`: Specifies the form class to use when processing - an authentication request -* :attr:`SECURITY_AUTH_URL`: Specifies the URL to to handle authentication -* :attr:`SECURITY_LOGOUT_URL`: Specifies the URL to process a logout request -* :attr:`SECURITY_LOGIN_VIEW`: Specifies the URL to redirect to when - authentication is required -* :attr:`SECURITY_POST_LOGIN`: Specifies the URL to redirect to after a user is - authenticated -* :attr:`SECURITY_POST_LOGOUT`: Specifies the URL to redirect to after a user - logs out + blueprint. * :attr:`SECURITY_FLASH_MESSAGES`: Specifies wether or not to flash messages - during authentication request + during security mechanisms. +* :attr:`SECURITY_PASSWORD_HASH`: Specifies the encryption method to use. e.g.: + plaintext, bcrypt, etc. +* :attr:`SECURITY_AUTH_URL`: Specifies the URL to to handle authentication. +* :attr:`SECURITY_LOGOUT_URL`: Specifies the URL to process a logout request. +* :attr:`SECURITY_REGISTER_URL`: Specifies the URL for user registrations. +* :attr:`SECURITY_RESET_URL`: Specifies the URL for password resets. +* :attr:`SECURITY_CONFIRM_URL`: Specifies the URL for account confirmations. +* :attr:`SECURITY_LOGIN_VIEW`: Specifies the URL to redirect to when + authentication is required. +* :attr:`SECURITY_CONFIRM_ERROR_VIEW`: Specifies the URL to redirect to when + an confirmation error occurs. +* :attr:`SECURITY_POST_LOGIN_VIEW`: Specifies the URL to redirect to after a + user logins in. +* :attr:`SECURITY_POST_LOGOUT_VIEW`: Specifies the URL to redirect to after a + user logs out. +* :attr:`SECURITY_POST_FORGOT_VIEW`: Specifies the URL to redirect to after a + user requests password reset instructions. +* :attr:`SECURITY_RESET_PASSWORD_ERROR_VIEW`: Specifies the URL to redirect to + after an error occurs during the password reset process. +* :attr:`SECURITY_POST_REGISTER_VIEW`: Specifies the URL to redirect to after a + user successfully registers. +* :attr:`SECURITY_POST_CONFIRM_VIEW`: Specifies the URL to redirect to after a + user successfully confirms their account. +* :attr:`SECURITY_UNAUTHORIZED_VIEW`: Specifies the URL to redirect to when a + user attempts to access a view they don't have permission to view. +* :attr:`SECURITY_DEFAULT_ROLES`: The default roles any new users should have. +* :attr:`SECURITY_CONFIRMABLE`: Enables confirmation features. Defaults to + `False`. +* :attr:`SECURITY_REGISTERABLE`: Enables user registration features. Defaults to + `False`. +* :attr:`SECURITY_RECOVERABLE`: Enables password reset/recovery features. + Defaults to `False`. +* :attr:`SECURITY_TRACKABLE`: Enables login tracking features. Defaults to + `False`. +* :attr:`SECURITY_CONFIRM_EMAIL_WITHIN`: Specifies the amount of time a user + has to confirm their account/email. Default is `5 days`. +* :attr:`SECURITY_RESET_PASSWORD_WITHIN`: Specifies the amount of time a user + has to reset their password. Default is `5 days`. +* :attr:`SECURITY_LOGIN_WITHOUT_CONFIRMATION`: Specifies if users can login + without first confirming their accounts. Defaults to `False` +* :attr:`SECURITY_EMAIL_SENDER`: Specifies the email address to send emails on + behalf of. Defaults to `no-reply@localhost`. +* :attr:`SECURITY_TOKEN_AUTHENTICATION_KEY`: Specifies the query string argument + to use during token authentication. Defaults to `auth_token`. +* :attr:`SECURITY_TOKEN_AUTHENTICATION_HEADER`: Specifies the header name to use + during token authentication. Defaults to `X-Auth-Token`. +* :attr:`SECURITY_CONFIRM_SALT`: Specifies the salt value to use for account + confirmation tokens. Defaults to `confirm-salt`. +* :attr:`SECURITY_RESET_SALT`: Specifies the salt value to use for password + reset tokens. Defaults to `reset-salt`. +* :attr:`SECURITY_AUTH_SALT`: Specifies the salt value to use for token based + authentication tokens. Defaults to `auth-salt`. +* :attr:`SECURITY_DEFAULT_HTTP_AUTH_REALM`: Specifies the default basic HTTP + authentication realm. Defaults to `Login Required`. .. _api: @@ -218,32 +247,36 @@ Configuration Values API === -.. autoclass:: flask_security.Security +.. autoclass:: flask_security.core.Security :members: -.. data:: flask_security.current_user +.. data:: flask_security.core.current_user A proxy for the current user. Protecting Views ---------------- -.. autofunction:: flask_security.login_required +.. autofunction:: flask_security.decorators.login_required -.. autofunction:: flask_security.roles_required +.. autofunction:: flask_security.decorators.roles_required -.. autofunction:: flask_security.roles_accepted +.. autofunction:: flask_security.decorators.roles_accepted + +.. autofunction:: flask_security.decorators.http_auth_required + +.. autofunction:: flask_security.decorators.auth_token_required User Object Helpers ------------------- -.. autoclass:: flask_security.UserMixin +.. autoclass:: flask_security.core.UserMixin :members: -.. autoclass:: flask_security.RoleMixin +.. autoclass:: flask_security.core.RoleMixin :members: -.. autoclass:: flask_security.AnonymousUser +.. autoclass:: flask_security.core.AnonymousUser :members: @@ -252,65 +285,13 @@ Datastores .. autoclass:: flask_security.datastore.UserDatastore :members: -.. autoclass:: flask_security.datastore.sqlalchemy.SQLAlchemyUserDatastore +.. autoclass:: flask_security.datastore.SQLAlchemyUserDatastore :members: :inherited-members: -.. autoclass:: flask_security.datastore.mongoengine.MongoEngineUserDatastore +.. autoclass:: flask_security.datastore.MongoEngineUserDatastore :members: :inherited-members: - - -Models ------- -.. autoclass:: flask_security.User - - .. attribute:: id - - User ID - - .. attribute:: username - - Username - - .. attribute:: email - - Email address - - .. attribute:: password - - Password - - .. attribute:: active - - Active state - - .. attribute:: roles - - User roles - - .. attribute:: created_at - - Created date - - .. attribute:: modified_at - - Modified date - - -.. autoclass:: flask_security.Role - - .. attribute:: id - - Role ID - - .. attribute:: name - - Role name - - .. attribute:: description - - Role description Exceptions @@ -331,6 +312,10 @@ Exceptions .. autoexception:: flask_security.RoleCreationError +.. autoexception:: flask_security.ConfirmationError + +.. autoexception:: flask_security.ResetPasswordError + Signals ------- diff --git a/flask_security/decorators.py b/flask_security/decorators.py index cba8658..5406e4f 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -87,7 +87,10 @@ def _check_http_auth(): def http_auth_required(realm): - """Decorator that protects endpoints using Basic HTTP authentication.""" + """Decorator that protects endpoints using Basic HTTP authentication. + The username should be set to the user's email address. + + :param realm: optional realm name""" def decorator(fn): @wraps(fn) @@ -109,7 +112,12 @@ def http_auth_required(realm): def auth_token_required(fn): - """Decorator that protects endpoints using token authentication.""" + """Decorator that protects endpoints using token authentication. The token + should be added to the request by the client by using a query string + variable with a name equal to the configuration value of + `SECURITY_TOKEN_AUTHENTICATION_KEY` or in a request header named that of + the configuration value of `SECURITY_TOKEN_AUTHENTICATION_HEADER` + """ @wraps(fn) def decorated(*args, **kwargs):