.. doctest docs/dev/users.rst
.. _dev.users:

=========================
User management à la Lino
=========================

This document explains how to get started with Lino's user management system.
See :doc:`/specs/users` for a more detailed documentation of the
:mod:`lino.modlib.users` plugin.

.. contents::
    :depth: 1
    :local:

.. include:: /../docs/shared/include/tested.rst

>>> from lino import startup
>>> startup('lino_book.projects.min1.settings')
>>> from lino.api.doctest import *
>>> from atelier.sheller import Sheller
>>> shell = Sheller("lino_book/projects/min1")

Creating a root user
====================

The most Linoish way to create a root user (or a set of demo users) is to run
:manage:`prep`.  This will reset the database to a virgin state and then load
the :fixture:`demo` fixture, which will create well-known demo users Robin,
Rolf, Romain, Rando, Rik, Ronaldo ... depending on your
:attr:`lino.core.site.Site.languages`.

Once you have a superuser, you can sign in via the web interface and work as
described in  :doc:`/guide/users/index`.


Managing users from the command line
====================================

Plain Django projects have only a limited :manage:`createsuperuser` admin
command, but Lino sites have a more useful admin command :manage:`passwd`.

.. management_command:: passwd

.. program:: passwd

Update or optionally create password, name and type of a user. The default
action displays and optionally edits the user. Specify :option:`-c` to create a
new user.

Usage: go to your project directory and say::

  $ python manage.py passwd [options] USERNAME

Where USERNAME is the username of the user to process.
Default value for ``USERNAME`` is your system username.


.. rubric:: Options


.. option:: -c, --create

  Create the given user. Fail if that username exists already.

.. option:: --batch

    Run in batch mode, i.e. without asking any questions.
    Assume yes to all questions.

.. # tidy up
  >>> try:
  ...     users.User.objects.get(username="test").delete()
  ... except users.User.DoesNotExist:
  ...    pass


>>> shell("python manage.py show users.AllUsers")
... #doctest: +ELLIPSIS
========== ===================== ============ ===========
 Username   User type             First name   Last name
---------- --------------------- ------------ -----------
 robin      900 (Administrator)   Robin        Rood
 rolf       900 (Administrator)   Rolf         Rompen
 romain     900 (Administrator)   Romain       Raffault
========== ===================== ============ ===========

>>> shell("python manage.py passwd -c test --batch")
Creating new user
User test has been saved.

>>> shell("python manage.py show users.AllUsers")
... #doctest: +ELLIPSIS
========== ===================== ============ ===========
 Username   User type             First name   Last name
---------- --------------------- ------------ -----------
 robin      900 (Administrator)   Robin        Rood
 rolf       900 (Administrator)   Rolf         Rompen
 romain     900 (Administrator)   Romain       Raffault
 test
========== ===================== ============ ===========

>>> u = users.User.objects.get(username="test")
>>> u.has_usable_password()
False



Managing users programmatically
====================================

If you don't want to reset your database, then you can write a script
and run it with :manage:`run`. For example::

    from lino.api.shell import users
    obj = users.User(username="root")
    obj.set_password("1234!")
    obj.full_clean()
    obj.save()




Passwords of new users
======================

The `password` field of a newly created user is empty, and the account therefore
cannot be used to sign in.  When you created a new user manually using the web
interface, you must click their :class:`ChangePassword` action and set their
password.

.. # tidy up
  >>> try:
  ...     users.User.objects.get(username="test").delete()
  ... except users.User.DoesNotExist:
  ...    pass

>>> u = users.User(username="test")
>>> u.full_clean()
>>> u.save()

Since we didn't set a `password`, Django stores a "non usable" password, and the
:meth:`User.check_password` method returns `False`:

>>> u.password  #doctest: +ELLIPSIS
'!...'
>>> u.check_password('')
False

>>> u.has_usable_password()
False


When setting the password for a newly created user, leave the
field :guilabel:`Current password` empty.

>>> ses = rt.login('robin')
>>> values = dict(current="", new1="1234", new2="1234")
>>> rv = ses.run(u.change_password, action_param_values=values)
>>> print(rv['message'])
New password has been set for test.

Note that a site administrator (a user having the
:class:`lino.core.roles.SiteAdmin` role) never needs to specify the current
password when setting a new password for any user.