Skip to content

Commit

Permalink
adr: Postulate adr006 (combined tombstoning of uid and login)
Browse files Browse the repository at this point in the history
Refs #597
  • Loading branch information
lukasjuhrich committed Feb 4, 2023
1 parent 2debeee commit 5623e16
Showing 1 changed file with 148 additions and 0 deletions.
148 changes: 148 additions & 0 deletions doc/arch/adr-006.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
ADR006
======

:Number: 006
:Title: Combined tombstoning of uid and login
:Author: Lukas Juhrich, Jakob Müller
:Created: 2023-01-15
:Status: Postulated

.. contents:: Table of Contents

Context
-------
In the light of data economy, we should not store the login of a :class:`User` –
which implicitly is a (partial) property of the :class:`UnixAccount` –
more than necessary.

The only reason to keep the login is to avoid assigning the same mail address
to multiple people across time; otherwise,
mails might be send to the wrong addressee.

Thus it has been decided (external from the development team) to store
the login in form of a hash whenever it stops being required for providing
services to a user.

Requirements
~~~~~~~~~~~~
The implementation has to ensure

1. absence of the ``login`` whenever it is not needed *(legal requirement)*
2. that no login is doubly assigned *(legal / business requirement)*

Consequently, we need to track the ``login`` hash in some kind of “tombstone”,
such that

* it is referenced by a ``User`` or a ``UnixAccount``
* it exists in all scenarios and is never deleted

Since the ``login`` and the ``uid`` have the same lifecycle, and both
require tombstoning –
albeit the ``uid`` needs to be kept only for technical reasons,
not for legal ones –
it makes sense to let such a ``tombstone`` maintain both pieces of data.

Current model
~~~~~~~~~~~~~

In the current model, the relevant entities are related as follows:

1. ``User``: has a ``login`` (nullable) and a ``unix_account_id`` (nullable).
2. ``UnixAccount``: Has the columns ``uid``, ``gid``, ``home``, and ``login_shell``,
but no ``login``.
3. An ldap account (Derived entity):
If a ``User`` has a ``UnixAccount`` and the correct permissions,
this causes an LDAP account to be exported by the ldap syncer.

Since ``user.unix_account`` is a foreign key constraint,
we have the following possible states (``U``: User, ``UA``: UnixAccount):

========== === ========= =================== ====
# ∃U? ∃U.login? ∃U.unix_account_id? ∃UA?
========== === ========= =================== ====
1 ✓ ✗ ✗ ✗
2 ✓ ✗ ✓ ✓
3 ✓ ✓ ✗ ✗
4 ✓ ✓ ✓ ✓
---------- --- --------- ------------------- ----
5 ✗ ✗ ✗ ✓
6 ✗ ✗ ✗ ✗
========== === ========= =================== ====

With regards to tombstoning, the states imply the following requirements:

State 1. No login, no unix account
No requirements.

State 2. No login, but unix account
This is currently allowed, but issues a warning in the ldap syncer.
Indeed, without the login the unix account cannot be exported.
With tombstones however, we require

* :math:`(T_u)` there shall exist a tombstone with the same uid as the account

State 3. Login, but no unix account
* :math:`(T_l)` there shall exist a tombstone with the implied login hash

State 4. Login and unix account
* :math:`(T_u)` there shall exist a tombstone with the same uid as the account
* :math:`(T_l)` there shall exist a tombstone with the implied login hash
* :math:`(E_{ul})` both tombstones in question should be equal

State 5. Unix account without user
* :math:`(T_u)` there shall exist a tombstone with the same uid as the account

State 6. Tautological state
Nothing exists


Decision
--------

There shall be

1. A new entity ``unix_tombstone(uid int, login_hash text)`` satisfying

* ``uid`` is unique
* ``login_hash`` is unique
* Either column may be ``null``, but not both
* ``uid`` and ``login_hash`` form the primary key

2. A generated column ``login_hash`` on the ``user`` relation
(see :class:`sqlalchemy.schema.Computed`)
3. :math:`(T_l)`: A foreign key constraint ``User.login_hash → UnixTombstone.login_hash``
4. :math:`(T_l)`: A foreign key constraint ``UnixAccount.uid → UnixTombstone.uid``
5. :math:`(E_{ul})`: A constraint checking consistency for users with login and unix account:
In this case, the tombstone induced by the ``account.uid`` should agree
with the tombstone induced by the ``user.login_hash``

Consequences
------------

* That the ``login_hash`` is optional allows for
``unix_accounts`` which don't have a ``unix_login`` associated to them
to have valid tombstones as well.
However, this implies that were one to couple these accounts to users again,
the tombstone has to be modified to reflect the user's ``login`` (if it exists).

* using a combined entity instead of an entity for the ``login`` and ``uid``,
respectively, has the advantage that one can identify ``login`` tombstones
which never had a respective ``unix_account``.
Database administrators can then decide on whether to keep these entries or not,
since technically these logins have not been used anywhere.
This might not serve any particular purpose but the

* In the most frequent use case of creating a user with login and unix account,
A tombstone has to be created as well. This is slightly more effort
than the current implementation.
To avoid this, triggers may be created that take care of this automatically.

.. note:: This ADR does not take a stance on whether or not to add triggers
as it is mainly concerned with ensuring the critical legal and business
requirements.

* Tests have to be written to ensure that with any state change of the ``user``
or ``unix_account`` relations,
the information contained in the ``tombstone`` tables is monotonous,
i.e that neither does a tombstone get deleted via a cascade
nor is a field set to ``null`` when it has been non-null before.

0 comments on commit 5623e16

Please sign in to comment.