Moya Auth (users and permissions)

The Moya Auth library provides the ability for users to log in to your web application, and manages what they can do (or see) when they are there. Most sites will require some kind of system of users, even if it is a single admin user who may make changes to the site.

Installation

The Moya Auth library is built in to Moya and may be imported via its Python path as follows:

<import py="moya.libs.forms" />

You can install the library with the following:

<install name="auth" lib="moya.auth" mount="/auth/"/>
<mount app="auth" mountpoint="middleware" url="/"/>

This will mount Auth views and middleware which does the work of managing permissions.

Init Command

The Auth library has a command to create the default user and some other required object in the database. To run it, enter the following at the command line:

$ moya moya.auth#cmd.init

The command prompt will ask you for the credentials (username and password) and email address of the super user. A super user is the first user created and will have the ability to perform any administration tasks.

If you run this on a production server, be sure to use a strong password

Views

When mounted, the Auth library supplies a few views to allow visitors to login / logout and do other user account tasks, such as reseting their password.

This section lists the available views by their URL name. You can use the URL name to retrieve the actual URL with something like the following:

<get-url from="moya.auth" name="login" dst="url" />

Or in template…

<a href="{% url 'login' from 'moya.auth' %}">Login</a>
The URLs given here assume the Auth library was mounted on /auth/.

login

/auth/login/

Displays a form with username and password form. If the login was successful, the user is forwarded to either / or a path defined in the query value called fwd. For example, /auth/login/?fwd=/dashboard/.

logout

/auth/logout

Logs the user out and forwards them to / or a path given in a query value called fwd.

user_edit

/auth/edit/

A form to allow a user to edit basic details; their first / last name and timezone.

forgot_password

/auth/forgot-password/

Displays a form requesting the user's email address. If the email is recognized then Moya Auth will send the user a password reset email which contains a link. Clicking the link in the email will let them enter a new password.

A link to this page is displayed in the login view.

reset_password

/auth/reset/{token}/

This is the view to handle password resets.

User Model

When a user logs in, Moya looks up a user object from the database and stores it in the context as .user. If no user has logged in, the value of .user will be missing – which allows you to write code such as this:

<if test=".user">
    <echo>User is logged in </echo>
</if>

Or the equivalent in template:

{% if .user %}
<h1>Welcome, ${.user.username}!</h1>
{% endif %}
username
This is the user's identity in the site. It will be a maximum of 30 characters with no spaces.
first_name
The user's first name (forename).
last_name
The user's last name (surname).
email
The user's email address.
mail_validated
A boolean which indicates if the user's email has been validated.
password
A hash of the user's password. Moya follows the best practice of not storing the password has a hash rather than plain-text.
timezone
The user's timezone. If set, Moya can use this information to display times and dates in the user's timezone.
last_login
The time of the user's most recent login. This will be the start of the current session if the user is logged in.
previous_login
The time the user logged in prior to the most recent login. Use this value in preference to last_login when working out what has changed since the user's last logged in.
notes
A text field for admin to store notes regarding a user.
groups
A many to many of the groups a user belongs to. Including the user's personal group.

Permissions and Groups

Moya Auth supports a flexible permissions system that you can user to manage different types of users and define what they can do or see.

Permissions

A permission is a label associated with a particular capability on the site. The default permissions (created with the Init Command) are 'admin' which gives you access to the admin site, and 'superuser' which identifies you as the owner of the site.

When a user is logged in, Moya stores the user's permissions in the context as a dict called .permissions, which maps the permission label to a permission object (consisting of it's name and description). For example, if a user has the admin permission, then .permission.admin would contain the following:

{
    'name': 'admin',
    'description': 'User may perform administration tasks'
}

You can use the permissions object to test if a user has a particular permission as follows:

<if test=".permissions.admin">
    <echo>The user is an Administrator!</echo>
    <echo>${.permissions.admin.description}</echo>
</if>

Alternatively, there is a modifier, permission: which checks the .permissions value. Here's the equivalent check to the above code, written with the modifier:

<if test="permission:'admin'">
    <echo>The user is an Administrator!</echo>
</if>

The permission: modifier also accept a list of permissions, and will return True only if the user has all of them.

<if test="permission:['admin', 'superuser']">
    <echo>User is an administrator and a superuser</echo>
</if>

The modifier has the advantage that it is easier to define the permissions check in a setting. You might want to do this in a library if you don't want to hard-code the required permissions. Here's an example:

<if test="not permission:.settings.post_permssions.list">
    <echo>Sorry, you are not permitted to post!</echo>
</if>

View Permissions

You may want to restrict a URL to a users with a particular permission (for example, admin users). One way of doing this is to apply a permission check to the <forbidden> tag which generates a 403 Forbidden response. The Moya Auth library handles this special response by forwarding the user to the login page.

Here's how you might do a permission check in a view:

<forbidden if="not permission:'admin'"/>

Because this is such a common pattern, the <view> has a requires attribute which will have the same effect. Here's how you would do the same permission check with the requires attribute:

<view libname="view.admin" requires="permission:'admin'">
    <!-- admin only view -->
</view>

URL Permissions

It's often a requirement to have many views restricted to users with a particular permission, often under a single top level URL. It would be somewhat tedious to add a permissions check to all the views, and the consequences of a missing check could potentially be serious. In these situations you might want to add a <url> to apply a permissions check.

Here's how you would check for admin permissions for any URL under /admin-only/:

<url route="/admin-only/*">
    <forbidden if="not permission:'admin'" />
</url>

Groups

Permissions are associated with groups which may contain any number of users. Users in a group acquire all the permissions of that group. For example, there is a default group called 'administrators' which gives users the 'admin' permission, i.e. if you are in the 'administrators' group you will have the 'admin' permission.

The user object has a many to many of groups the user belongs to. Here's how you could list the groups for the logged in user:

<for src=".user.groups" dst="group">
    <echo>User belongs to ${group}</echo>
</form>

You will rarely need to look at a users groups directly, as the permissions will already have been determined.

Personal Groups

Permissions are not strictly speaking associated with a user, but users can have a personal group which is a special group unique to them. Adding a permission to this group will apply to that user and that user only. You can use this to tailor individual permissions for a user where there is no appropriate group to put them in.

Default Groups and Permissions

The init command creates a small number of default groups and permission. These are the minimum requirements for most sites.

The init command creates a single group called administrators which supplies a permission called admin. Users with the admin permission may log in to the admin site. The admin permission also enables the editing of site content in some applications, e.g. editing pages, blog posts, FAQs etc.

The first user created is a member of the administrator group, and also has a permission called super assigned to their personal group. You should use the super permission to guard any potentially destructive operations that you wouldn't want to risk an administrator doing by accident.

Managing Groups & Permissions

Generally speaking, groups and permissions are relatively static in that once created you will rarely need to change them – probably only when you roll out features to your site. Creating groups and permissions can be done with either the auth command or via the admin site.

Adding a user to a group can be done with the <add-user-to-group> tag, which takes a user object and the group to add to. For example, the following adds a user with username 'john' to the administrators group.

<db:get model="#User" let:username="john" dst="john"/>
<db:add-user-to-group let:user="john" let:group="administrators" />

To add a permission to a group (or a users's personal group) you can use <add-permission> which takes either a user or group, and a permission name. For example, here is how you make a user a superuser user by adding the super permission to their personal group:

<db:get model="#User" let:username="john" dst="john"/>
<db:add-permission user="john" permission="super" />

Creating Users

You can create a new user manually with <new-user>. Here's an example:

<auth:new-user username="john"
    email="john@moya.com"
    first_name="John"
    last_name="Crichton"
    password="aeryn1234"
    dst="john"/>

This will create a new user with username john. If that username is taken, Moya will raise a moya.auth.duplicate-user exception.

Note that the password will be hashed automatically.

Logging In a User

You can login a user for the current session with <login> which takes a username and password. If the username exists and the password matches, then the user object will be returned (and stored in .user). Here's an example:

<auth:login username="'john'" password="'aeryn1234'" dst="user" />

A few exception may be thrown by <login> if the login was unsuccessful; the exception "moya.auth.no-user" indicates the username was incorrect, "moya.auth.password-fail" indicates the password was incorrect, and "moya.auth.not-active" indicates the login was disabled.

Logging Out a User

The <logout> will logout a given user, or the currently logged in user. For example, the following will log out the current user:

<auth:logout/>

Setting / Checking Passwords

The <set-password> can set (and hash) a password for the given user. Here's an example:

<auth:set-password user="john" password="'ayom74'" />

You can check a password with the <check-password> tag. Here's an example:

<auth:check-password user="john" password="'ayom74'" dst="passed" />
<if test="passed">
    <echo>Password is correct!</echo>
</if>
<else>
    <echo>Password failed!</echo>
</else>

Auth Commands

Moya Auth has a number of commands you can invoke from the command line, including commands to create / edit permissions, groups and users.

Enter the following from a project directory to view all the commands available:

$ moya auth#