Welcome | Get started | Dive into Lino | Contribute | Reference | More
Introduction to permissions¶
As soon as a database application is used by more than one user, we usually need to speak about permissions. For example, a site manager can see certain resources that a simple end user should not get to see. The application must check whether a given user has permission to see a given resource or to execute a given action. An application framework must provide a system for managing these permissions. Lino replaces Django's user management and permission system (see Lino has its own user management if you wonder why).
This is a tested document. The following instructions are used for initialization:
>>> from lino import startup
>>> startup('lino_book.projects.min9.settings')
>>> from lino.api.doctest import *
User roles¶
A user role is a role of a user in our application. It is used as the basic unit for defining permissions.
Every user has a set of roles.
Every resource (table or action) has a set of required roles.
User roles are class objects that represent conditions for getting permission to
access the functionalities of the application. Lino comes with a few built-in
user roles that are defined in lino.core.roles
. Every plugin may define
its own user roles which must be subclasses of these builtin roles. A role can
inherit from one or several other roles.
A real-world application can define many user roles. For example here is an inheritance diagram of the roles used by Lino Così:

And if you think this diagram is complex, then don't look at the following one (that of Lino Noi)...

As the application developer you must specify for each resource the role(s) that are required to use it.
User types¶
When creating a new user, the site manager needs to assign these roles to every user.
But imagine they would get, for each user group, a multiple-choice combobox with all available roles from above examples! They would get crazy.
That's why we have user types. The application developer defines
a meaningful subset of all available roles for their application.
This is done by populating the UserTypes
choicelist.
User types are a way to classify end users in order to grant them different sets of permissions.
Each user type is basically not much more than a user-friendly name
and a storable value given to a selected user role. Here is the
default list of user types, defined in lino.core.user_types
:
>>> rt.show(users.UserTypes)
======= =========== ===============
value name text
------- ----------- ---------------
000 anonymous Anonymous
100 user User
900 admin Administrator
======= =========== ===============
Actually a user type contains a bit more information than a user role. It has the following fields:
role
, the role given to users of this typetext
, a translatable namevalue
, a value for storing it in the databasereadonly
defines a user type which shows everything that a given user role can see, but unlike the original user role it cannot change any data.hidden_languages
(experimental), a set of languages to not show to users of this type. This is used on sites with more than three or fourlanguages
.
The user type of a user is stored in a field whose internal name
is user_type
. This is
because at the beginnings of Lino we called them "user profiles". Now
we prefer to call them user types. The web interface already calls
them "types", but it will take some time to change all internal names
from "profile" to "type".
>>> rt.show('users.AllUsers', column_names="username user_type")
========== =====================
Username User type
---------- ---------------------
robin 900 (Administrator)
rolf 900 (Administrator)
romain 900 (Administrator)
========== =====================
Relation between user roles and user types¶
There is a built-in virtual table that shows an overview of which roles are contained for each user type. This table can be helpful for documenting the permissions granted to each user type.
>>> rt.show(users.UserRoles)
======================== ===== ===== =====
Name 000 100 900
------------------------ ----- ----- -----
cal.GuestOperator ☑ ☑
comments.CommentsStaff ☑
comments.CommentsUser ☑ ☑
contacts.ContactsStaff ☑
contacts.ContactsUser ☑ ☑
excerpts.ExcerptsStaff ☑
excerpts.ExcerptsUser ☑ ☑
notes.NotesStaff ☑
notes.NotesUser ☑ ☑
office.OfficeStaff ☑
office.OfficeUser ☑ ☑
polls.PollsAdmin ☑
polls.PollsUser ☑ ☑
xl.SiteAdmin ☑
xl.SiteUser ☑
======================== ===== ===== =====
Accessing permissions from within your code¶
Just some examples...
>>> UserTypes = rt.models.users.UserTypes
>>> UserTypes.admin
<users.UserTypes.admin:900>
>>> UserTypes.admin.role
<lino_xl.lib.xl.user_types.SiteAdmin object at ...>
>>> UserTypes.admin.readonly
False
>>> UserTypes.admin.hidden_languages
>>> robin = users.User.objects.get(username='robin')
>>> robin.user_type
<users.UserTypes.admin:900>
>>> robin.user_type.role
<lino_xl.lib.xl.user_types.SiteAdmin object at ...>
Defining required roles¶
The application developer specifies which roles are required for a given resource.
Where "resource" is one of the following:
an actor (a subclass of
lino.core.actors.Actor
)an action (an instance of
lino.core.actions.Action
or a subclass thereof)a panel (an instance of
lino.core.layouts.Panel
)
These objects have a required_roles
attribute which
must be a set()
of the user roles required for getting
permission to access this resource.
This set of user roles can be specified using the
login_required
utility
function. You can also specify it manually. But it must stisfy some
conditions described in check_required_roles
.
See lino.modlib.users.UserType.has_required_role()
For example, the list of all users (the users.AllUsers
table) is visible only for users
who have the SiteAdmin
role:
>>> sixprint(rt.models.users.AllUsers.required_roles)
{<class 'lino.core.roles.SiteAdmin'>}
>>> from lino.core.roles import SiteUser, SiteAdmin
>>> user = SiteUser()
>>> admin = SiteAdmin()
>>> user.has_required_roles(rt.models.users.AllUsers.required_roles)
False
>>> admin.has_required_roles(rt.models.users.AllUsers.required_roles)
True
Local customizations¶
You may have noted that UserTypes
is a choicelist, not a
database table. This is because it depends on the application and is
usually not locally modified.
Local site managers may nevertheless decide to change the set of available user types.
The user types module¶
The roles_required
attribute is being
ignored when user_types_module
is empty.
- roles.py¶
- user_types.py¶
The roles.py
is used for both defining roles
A user_types.py
module is used for defining the user roles
that we want to make available in a given application. Every user
type is assigned to one and only one user role. But not every user
role is made available for selection in that list.
Permission debug messages¶
Sometimes you want to know why a given action is available (or not available) on an actor where you would not (or would) have expected it to be.
In this situation you can temporarily set the debug_permissions
attributes on both the Actor
and
the Action
to True.
This will cause Lino to log an info message for each invocation of a handler on this action.
Since you probably don't want to have this feature accidentally
activated on a production server, Lino will raise an Exception if this
happens when DEBUG
is False.