Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
users
: user management¶
This document describes the lino.modlib.users
plugin, which in Lino
replaces django.contrib.auth
. See also Introduction to user management for getting
started with user management. If you wonder why Lino replaces Django’s user
management and permission system, see Lino has its own user management.
Side note: Code snippets (lines starting with >>>
) in this document get
tested as part of our development workflow. The following
initialization snippet tells you which demo project is being used in
this document.
>>> from lino import startup
>>> startup('lino_book.projects.noi1r.settings')
>>> from lino.api.doctest import *
Users¶
- class lino.modlib.users.User¶
The Django model used to represent a user account.
Database fields that must be edited by a site manager:
- username¶
Must be either unique or empty. User accounts having this field empty cannot be used to sign in.
- user_type¶
The user type assigned to this user. User accounts having this field empty cannot be used to sign in.
See also Introduction to permissions.
- partner¶
The partner record with additional contact information about this user account.
This field is used to optionally link a user account to a partner. Some applications provide functionalities that aren’t available unless this field is given.
A same partner can have more than one user accounts.
This is a pointer to
lino_xl.lib.contacts.Partner
. This is aDummyField
whenlino_xl.lib.contacts
is not installed or when User is a subclass ofPartner
.
Database fields that can be edited by the user as their user settings:
- nickname¶
The nickname used to refer to you when speaking to other site users. Does not need to be unique but should be reasonably identifying.
This field is hidden unless
users.with_nickname
is True. See also__str__()
andget_full_name()
.
- initials¶
The initials used to refer to you when speaking to business partners. Does not need to be unique but should be reasonably identifying.
- date_format¶
The date format to use when parsing or displaying dates.
Database fields that aren’t directly editable:
- verification_code¶
A random string that has been sent to the user via email in order to verify their email address.
When this is non empty,
is_verified()
returns False and theverify_me
action is available.
- verification_password¶
A not-yet-active password given by anonymous when they asked for a password reset. This will become the active password when the user verifies.
- person¶
A virtual read-only field that returns the
Person
MTI child of thepartner
(if it exists) and otherwise None.
- company¶
A virtual read-only field that returns the
Company
MTI child of thepartner
(if it exists) and otherwise None.
- last_login¶
Not used in Lino.
- end_date¶
- start_date¶
If
start_date
is given, then the user cannot sign in before that date. Ifend_date
is given, then the user cannot sign in after that date.These fields are also used for userstats: User statistics.
Instance attributes:
- authenticated¶
No longer used. See as
is_authenticated
.
- is_authenticated¶
This is always True. Compare with
AnonymousUser.is_authenticated
.
Querying users:
- classmethod get_active_users(cls, required_roles=None)¶
Return a queryset of users that satisfy the specified criteria.
Usage examples see Querying users below.
Other methods
- __str__(self)¶
Returns
nickname
if this field is not empty, otherwiseget_full_name()
unless
- get_full_name(self)¶
Return the user’s full name, i.e.
first_name
followed bylast_name
. If both fields are empty, return theinitials
or theusername
.
- get_row_permission(self, ar, state, ba)¶
Only system managers may edit other users. See also
disabled_fields()
.One exception is when AnonymousUser is not readonly. This means that we want to enable online registration. In this case everybody can modify an unsaved user.
Actions
- change_password¶
Ask for a new password to be used for authentication.
See
ChangePassword
.
- verify_me¶
Ask for the verification code you have received by email and mark your email address as verified.
Data tables¶
- class lino.modlib.users.AllUsers¶
Shows the list of all users on this site.
- class lino.modlib.users.UsersOverview¶
A variant of
Users
showing only active users and only some fields. This is used on demo sites inadmin_main.html
to display the list of available users.
User types¶
>>> rt.show('users.UserTypes', language="en")
======= =============== ===============
value name text
------- --------------- ---------------
000 anonymous Anonymous
100 customer user Customer
200 contributor Contributor
400 developer Developer
900 admin Administrator
======= =============== ===============
- class lino.modlib.users.UserTypes¶
The list of user types available in this application.
Every application should define at least three named user types:
- anonymous¶
- user¶
- admin¶
Class attributes:
- user_role¶
The user role of users having this type.
Default value for the
hidden_languages
of newly added choice items.
- classmethod get_anonymous_user(cls)¶
Return an instance of
AnonymousUser
.
- class lino.modlib.users.UserType¶
An item of the
UserTypes
list. Every instance of this represents a user type.- readonly¶
Whether users of this type get only write-proteced access.
A subset of
languages
which should be hidden for users of this type. Default value ishidden_languages
. This is used on multilingual sites with more than 4 or 5 languages.
- context(self)¶
Return a context manager so you can write code to be run with this as the current user type:
with UserTypes.admin.context(): # some code
- mask_message_types¶
A set of notification message types to be masked for users of this type.
Notifications will not be forwarded to users whose user type filters them away.
- mask_notifications(self, *args)¶
Add the given notification message types to the notification mask.
- has_required_roles(self, required_roles)¶
Return True if this user type’s
role
satisfies the specified requirements.
Find the item of the main menu for the specified bound action.
User roles and their usage¶
- class lino.modlib.users.UserRoles¶
Shows a list of the user roles used in this application together with the user type that have them.
This table can help when designing the list of user types and what permissions each of them should have.
Example:
>>> rt.show(users.UserRoles)
...
================================ ===== ===== ===== ===== =====
Name 000 100 200 400 900
-------------------------------- ----- ----- ----- ----- -----
accounting.LedgerStaff ☑
cal.CalendarReader ☑
checkdata.CheckdataUser ☑ ☑ ☑
comments.CommentsReader ☑ ☑ ☑ ☑ ☑
comments.CommentsStaff ☑ ☑
comments.CommentsUser ☑ ☑ ☑ ☑
comments.PrivateCommentsReader ☑ ☑
contacts.ContactsStaff ☑
contacts.ContactsUser ☑ ☑
core.DataExporter ☑ ☑ ☑ ☑
core.Expert ☑ ☑
core.SiteUser ☑ ☑ ☑ ☑
courses.CoursesUser ☑ ☑ ☑
excerpts.ExcerptsStaff ☑ ☑
excerpts.ExcerptsUser ☑ ☑ ☑
invoicing.InvoicingStaff ☑
invoicing.InvoicingUser ☑
noi.Anonymous ☑
noi.Contributor ☑ ☑ ☑
noi.Customer ☑ ☑ ☑ ☑
noi.Developer ☑ ☑
noi.SiteAdmin ☑
office.OfficeStaff ☑
office.OfficeUser ☑ ☑ ☑ ☑
products.ProductsStaff ☑
storage.StorageStaff ☑
storage.StorageUser ☑
tickets.Reporter ☑ ☑ ☑ ☑
tickets.Searcher ☑ ☑ ☑ ☑ ☑
tickets.TicketsStaff ☑ ☑
tickets.Triager ☑ ☑
topics.TopicsUser ☑ ☑ ☑ ☑
votes.VotesStaff ☑
votes.VotesUser ☑ ☑ ☑ ☑
working.Worker ☑ ☑ ☑
================================ ===== ===== ===== ===== =====
The table doesn’t show all user roles, only those that are “meaningful”.
Where meaningful means: those which are mentioned (either imported or defined)
in the global context of the user_types_module
. We tried more “intelligent”
approaches, but it is not trivial for Lino to guess which roles are
“meaningful”.
Querying users¶
All users:
>>> users.User.get_active_users()
<QuerySet [User #7 ('Jean'), User #6 ('Luc'), User #4 ('Marc'), User #5 ('Mathieu'), User #3 ('Romain Raffault'), User #2 ('Rolf Rompen'), User #1 ('Robin Rood')]>
Only site administrators:
>>> from lino.core.roles import SiteAdmin
>>> users.User.get_active_users([SiteAdmin])
<QuerySet [User #3 ('Romain Raffault'), User #2 ('Rolf Rompen'), User #1 ('Robin Rood')]>
All except site administrators:
>>> users.User.get_active_users(unwanted_roles=[SiteAdmin])
<QuerySet [User #7 ('Jean'), User #6 ('Luc'), User #4 ('Marc'), User #5 ('Mathieu')]>
All who speak English:
>>> users.User.get_active_users(language="en")
<QuerySet [User #7 ('Jean'), User #6 ('Luc'), User #4 ('Marc'), User #5 ('Mathieu'), User #1 ('Robin Rood')]>
User sessions¶
- class lino.modlib.users.Sessions¶
Show a list of all user sessions.
See User sessions.
The current user type¶
This is used by lino.utils.jsgen
, i.e. when generating the
linoweb.js
file for a given user type.
Site configuration¶
This plugin adds the following plugin settings:
- users.third_party_authentication¶
Whether this site provides third party authentication.
This should be activated only when there is at least one authentication provider.
This plugin has the following site settings:
- users.allow_online_registration¶
Whether users can register online. When this is set to True, a create_account action will be available under
About
actor. This also activates the verification system, and on account creation new users are asked to enter a verification code to verify their email address.
- active_sessions_limit¶
The sessions limit for this site. The default value -1 means that there is no limitation. Setting this to 0 will prevent any new login attempt and might be useful as a temporary value before shutting down a site.
- users.verification_code_expires = 5¶
Number of minutes after which a user verification code becomes invalid. Used to invalidate a user verification code after the elapsed minutes.
- users.user_type_new = 'user'¶
The default user_type for an unverified user.
- users.user_type_verified = 'user'¶
The default user_type for a verified user.
- users.with_nickname¶
Whether to add the
User.nickname
field.
Roles¶
This plugin defines the following user roles.
- class lino.modlib.users.Helper¶
Somebody who can help others by running
AssignToMe
action.
- class lino.modlib.users.AuthorshipTaker¶
Somebody who can help others by running
TakeAuthorship
action.
Actions¶
- class lino.modlib.users.SendWelcomeMail¶
Send a welcome mail to this user.
- class lino.modlib.users.ChangePassword¶
Change the password of this user.
- current¶
The current password. Leave empty if the user has no password yet. A site manager doesn’t need to specify this at all.
- new1¶
The new password.
- new2¶
The new password a second time. Both passwords must match.
- class lino.modlib.users.SignIn¶
Open a window that asks for username and password and authenticates as this user when submitted.
- class lino.modlib.users.SignOut¶
Sign out the current user and return to the welcome screen for anonymous visitors.
Model mixins¶
- class lino.modlib.users.Authored¶
- manager_roles_required¶
The list of required roles for getting permission to edit other users’ work.
By default, only
SiteStaff
users can edit other users’ work.An application can set
manager_roles_required
to some other user role class or a tuple of such classes.Setting
manager_roles_required
to[]
will disable this behaviour (i.e. everybody can edit the work of other users).This is going to be passed to
has_required_roles
of the requesting user’s profile.Usage examples see
lino_xl.lib.notes.models.Note
orlino_xl.lib.cal.Component
.
- author_field_name¶
No longer used. The name of the field that defines the author of this object.
- class lino.modlib.users.UserAuthored¶
Inherits from
Authored
.Mixin for models with a
user
field that points to the “author” of this object. The default user of new instances is automatically set to the requesting user.- user¶
The author of this database object.
A pointer to
lino.modlib.users.models.User
.
- class lino.modlib.users.StartPlan¶
The action to start a user plan.
This is the default implementation for
UserPlan.start_plan
, but it may be subclassed. For exampleStartInvoicing
extends this because there may be more than one types of invoicing plans.- update_after_start¶
Whether to run
Plan.update_plan()
after starting the plan.
- class lino.modlib.users.UserPlan¶
Mixin for anything that represents a “plan” of a given user on a given day.
What a “plan” means, depends on the inheriting child. Examples are an invoicing plan (
lino_xl.lib.invoicing.Plan
), a shopping cart (lino_xl.lib.shopping.Cart
) or an accounting report (lino_xl.lib.sheets.Report
).The mixin makes sure that there is only one database instance per user. A plan is considered a low value database object to be reused frequently.
Inherits from
UserAuthored
.- user¶
The user who owns and uses this plan.
- today¶
This date of this plan. This is automatically set to today each time the plan is called or updated.
- update_plan_button¶
- create_user_plan(self, user)¶
Return the database object for this plan and user. or create
- update_plan(self, ar)¶
Implementing models should provide this method.
- class lino.modlib.users.UpdatePlan¶
Build a new list of suggestions. This will remove all current suggestions.
doctests¶
Verify whether the help_text of the change_password action is set:
>>> ba = rt.models.users.AllUsers.get_action_by_name('change_password')
>>> print(ba.action.help_text)
Ask for a new password to be used for authentication.
Verify whether #3766 is fixed:
>>> show_choices('robin', '/choices/users/Users/partner')
...
Altenberg Hans
Arens Andreas
...
Ärgerlich Erna
Õunapuu Õie
Östges Otto
>>> show_choices('robin', '/choices/users/Users/user_type')
000 (Anonymous)
100 (Customer)
200 (Contributor)
400 (Developer)
900 (Administrator)
Reset password and verify email¶
The following actions are available also to anonymous users.
- class lino.modlib.about.About
- sign_in¶
Ask for your username and password in order to authenticate.
If
users.third_party_authentication
is enabled, this also shows alternative authentication methods.
- reset_password¶
Ask for your email address and send a verification code.
- verify_user¶
Ask for the verification code you have received by email and mark your email address as verified.
User types module¶
The default value for Site.user_types_module
is None, meaning that
permission control is inactive: everything is permitted. But note that
Site.set_user_model()
sets it to lino.core.user_types
.
This must be set if you want to enable permission control based on
user roles defined in Permittable.required_roles
and
UserType.role
.
If set, Lino will import the named module during site startup. It is expected to
define application-specific user roles (if necessary) and to populate the
UserTypes
choicelist.
Examples of such user types modules are
lino.core.user_types
and
lino_noi.lib.noi.user_types
.
The welcome message¶
- users/welcome_email.eml¶
The template used to generate the welcome email to new users.
Here are several ways for generating the verify link in the
users/welcome_email.eml
.
A first series used the instance action:
>>> ar = rt.login("robin", renderer=settings.SITE.kernel.default_renderer)
>>> obj = ar.get_user()
>>> ar.permalink_uris = True
>>> print(tostring(ar.instance_action_button(
... obj.verify_me, request_kwargs=dict(action_param_values=dict(
... email="foo@example.com", verification_code="123")))))
<a href="/api/users/AllUsers/1?fv=123&an=verify_me" title="Ask ... verified." style="text-decoration:none">Verify</a>
>>> # ba = users.Users.get_action_by_name('verify')
>>> url = ar.get_permalink(obj.verify_me.bound_action, obj, email="foo@example.com", verification_code="123")
>>> print(url)
/api/users/AllUsers/1?fv=123&an=verify_me
But this wasn’t good because it uses the lino.modlib.users.AllUsers
data table, which is visible only to SiteAdmin
.
>>> ba = users.Me.get_action_by_name('verify_me')
>>> pv = dict(email="foo@example.com", verification_code="123")
>>> from lino.api import _
>>> print(tostring(ar.row_action_button(
... obj, ba, _("Click here to verify"),
... request_kwargs=dict(action_param_values=pv))))
<a href="/api/users/Me/1?fv=123&an=verify_me" title="Ask ... mark your email address as verified." style="text-decoration:none">Click here to verify</a>
But even this had the disadvantage that the user had to sign in before being able to verify.
>>> ba = about.About.get_action_by_name('verify_user')
>>> pv = dict(email="foo@example.com", verification_code="123")
>>> from lino.api import _
>>> print(tostring(ar.row_action_button(
... obj, ba, _("Click here to verify"),
... request_kwargs=dict(action_param_values=pv))))
...
<a href="/api/about/About?fv=foo%40example.com&fv=123&an=verify_user"
title="Ask ... verified." style="text-decoration:none">Click here to verify</a>
Note how lino.modlib.users.User.verify_me
inherits from
lino.modlib.about.About.verify_user
. They do the same, but in different
contexts.
TODO: Is there a more intuitive syntax for rendering our button?
Here is a first attempt, which works only for row actions:
>>> sar = obj.verify_me.request_from(ar, action_param_values=dict(
... email="foo@example.com", verification_code="123"), permalink_uris=True)
>>> sar.permalink_uris = True
>>> print(tostring(sar.row_action_button_ar(obj)))
...
<a href="javascript:window.App.runAction(...)" title="..."
style="text-decoration:none">Verify</a>
Here is a second attempt (used since 20240407):
>>> print(ar.action_link("about.About.verify_user",
... text=_("click here to verify"),
... pv=dict(email=obj.email, verification_code="123")))
...
<a href="/api/about/About?fv=demo%40example.com&fv=123&an=verify_user"
style="text-decoration:none">click here to verify</a>
In the body of the welcome_message.eml
, we use
lino.core.site.Site.server_url
as a base href
header field:
<html><head><base href="{{settings.SITE.server_url}}" target="_blank"></head><body>
>>> print(settings.SITE.server_url)
http://127.0.0.1:8000/
Utility functions¶
- lino.core.site.with_user_profile(profile, func, *args, **kwargs)¶
Run the given callable func with the given user type activated. Optional args and kwargs are forwarded to the callable, and the return value is returned.
This might get deprecated some day since we now have the
UserType.context()
method