===========================================
How to authenticate from custom users table
===========================================

By default, all user information is stored in a table in the admin.sqlite 
database. This table has a fixed structure that cannot be changed.

In this section, we describe how to authenticate a user using data from the 
custom users table.

First, we create an item group **Authentication** select it and add an item 
**Users** that has the following fields:

.. image:: /how_to/_images/users_fields.png
	:align: center
	:alt: users_fields.png


We won't store in the table the user password and use this field in the interface.
We will store the password salted hash in the password_hash field.

We also created the 
:doc:`lookup list </admin/lookup_lists>`
"Roles" that we used in the "Roles" field definition.

We added to it the same roles (ids and names) as in the table
:doc:`Roles </admin/roles>`
We 'll have to sycronize this roles in the future.

.. image:: /how_to/_images/roles_lookup_list.png
	:align: center
	:alt: roles_lookup_list.png

In the 
:doc:`Roles </admin/roles>`
it is necessary to allow view the **Users** item only people that will be 
responsible for it

We removed password_hash field from field lists in the
:doc:`View Form Dialog </admin/items/view_form_dialog>`
and
:doc:`Edit Form Dialog </admin/items/edit_form_dialog>`

In the User server module we define the following
:doc:`on_apply </refs/server/item/on_apply>`
event handler:


.. code-block:: py

  def on_apply(item, delta, params, connection):
      for d in delta:
          if not (d.rec_deleted() or d.rec_modified() and d.login.value == d.login.old_value):
              users = d.task.users.copy(handlers=False)
              users.set_where(login=d.login.value)
              users.open(fields=['login'])
              if users.rec_count:
                  raise Exception('There is a user with this login - %s' % d.login.value)
          if d.password.value:
              d.edit();
              d.password_hash.value = delta.task.generate_password_hash(d.password.value)
              d.password.value = None
              d.post();

In this event handler we check if there is a users with the same login and
raise the exception if such user exists, otherwise we generate hash using the
:doc:`generate_password_hash </refs/server/task/m_generate_password_hash>`
method of the task and set the password value to None.

In the client module we defined the following on_field_get_text event handler.
It displays '**********' string insted of the password.

.. code-block:: js

  function on_field_get_text(field) {
      var item = field.owner;
      if (field.field_name === 'password') {
          if (item.id.value || field.value) {
              return '**********';
          }
      }
  }

Finally, we define the 
:doc:`on_login </refs/server/task/on_login>`
event handler in the task server module:

.. code-block:: py

  def on_login(task, form_data, info): 
      users = task.users.copy(handlers=False)
      users.set_where(login=form_data['login'])
      users.open()
      if users.rec_count == 1:
          if task.check_password_hash(users.password_hash.value, form_data['password']):
              return {
                  'user_id': users.id.value,
                  'user_name': users.name.value,
                  'role_id': users.role.value,
                  'role_name': users.role.display_text
              }

Now we must add an admin to **Users** that has rights to work with users.
After that we can set Safe mode in the project
:doc:`Parameters </admin/project/parameters>` 





