edc-auth


Nameedc-auth JSON
Version 0.3.90 PyPI version JSON
download
home_pagehttps://github.com/clinicedc/edc-auth
SummaryAuthentication for clinicedc/edc projects
upload_time2024-11-20 22:23:05
maintainerNone
docs_urlNone
authorErik van Widenfelt
requires_python>=3.12
licenseGPL license, see LICENSE
keywords django authentication permissions roles clinicedc clinical trials
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            |pypi| |actions| |codecov| |downloads|

edc-auth
--------

Authentication and permissions for the Edc

Default Groups
++++++++++++++


The default groups are required for the normal operation of an EDC deployment. The default groups are:

* ``ACCOUNT_MANAGER``: members may add/change and delete user accounts
* ``ADMINISTRATION``: members may view the Administration page
* ``AUDITOR``: members may view all forms but have no add/change permissions.
* ``CLINIC``: members may add/edit/delete all CRFs, Requisitions, Actions and other required clinic trial data entry forms. They may also view the Requisition page of the Lab section;
* ``EVERYONE``: members may access the EDC;
* ``LAB``: members may perform all functions in the Lab section (Edit requisitions, receive, process, pack, manage manifests, etc);
* ``PHARMACY``:
* ``PII``: members may view all personally identifiable data and edit forms that manage such data (Screening, Consents, Patient registration);
* ``PII_VIEW``: members may view personally identifiable data but have no add/edit permissions for any of the forms that store such data.

Permissions
+++++++++++

Permissions use Django's permission framework,  therefore, all permissions are linked to some model.

Permissions don't always naturally link to a model. In such cases, a dummy model is created.
For example, with Navigation bars from `edc_navbar`. Permissions to follow an item on a
navigation bar are associated with model `edc_navbar.Navbar`. A similar approach is used for
`listboard` permissions using `edc_dashboard.Dashboard`.

Extending permissions with `site_auths` global
++++++++++++++++++++++++++++++++++++++++++++++

A module can add new or update existing groups and roles and even add custom codenames.

The ``site_auths`` global ``autodiscovers`` configurations from ``auths.py`` in the root of your module.
The ``site_auths`` global gathers but does not validate or change any data in django's
``group``/``permission`` models or the Edc's ``role`` model.

The ``site_auths`` global gathers data:
* to ADD new groups,
* to update codenames for an existing group,
* to add a new role
* to update the group list for an existing role
* to add to the list of PII models
* to specifiy custom functions to run before and after groups and roles have been updated

For example,

.. code-block:: python

    # auths.py
    from edc_auth.constants import CLINICIAN_ROLE, STATISTICIAN_ROLE
    from edc_auth.site_auths import site_auths

    from edc_protocol_violation.auth_objects import (
        PROTOCOL_VIOLATION,
        PROTOCOL_VIOLATION_VIEW,
        protocol_violation_codenames,
        protocol_violation_view_codenames,
    )

    # add a new group specific to models in this module
    site_auths.add_group(*protocol_violation_codenames, name=PROTOCOL_VIOLATION)
    # add a new group specific to models in this module
    site_auths.add_group(*protocol_violation_view_codenames, name=PROTOCOL_VIOLATION_VIEW)
    # update the existing role CLINICIAN_ROLE to add the group PROTOCOL_VIOLATION
    site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_ROLE)
    # update the existing role STATISTICIAN_ROLE to add the group PROTOCOL_VIOLATION_VIEW
    site_auths.update_role(PROTOCOL_VIOLATION_VIEW, name=STATISTICIAN_ROLE)


As a convention, we define group names, lists of codenames and custom functions ``auth_objects.py``.

In the above example, the ``auth_objects.py`` looks like this:

.. code-block:: python

    # auth_objects.py

    # declare group names
    PROTOCOL_VIOLATION = "PROTOCOL_VIOLATION"
    PROTOCOL_VIOLATION_VIEW = "PROTOCOL_VIOLATION_VIEW"
    # add/change/delete/view codenames
    protocol_violation_codenames = (
        "edc_protocol_violation.add_protocoldeviationviolation",
        "edc_protocol_violation.change_protocoldeviationviolation",
        "edc_protocol_violation.delete_protocoldeviationviolation",
        "edc_protocol_violation.view_protocoldeviationviolation",
    )
    # view only codename
    protocol_violation_view_codenames = (
        "edc_protocol_violation.view_protocoldeviationviolation",
    )


AuthUpdater
+++++++++++
The ``AuthUpdater`` class runs in a post_migrate signal declared in ``apps.py``.
The ``AuthUpdater`` reads and validates the data gathered by ``site_auths``. Once all
validation checks pass, the ``AuthUpdater`` updates Django's ``group`` and ``permission``
models as well as the Edc's ``Role`` model.

Validation checks include confirming models refered to in codenames exist. This means that
the app where models are declared must be in your ``INSTALLED_APPS``.

During tests having all codenames load may not be ideal. See below on some strategies for testing.


Testing SiteAuths, AuthUpdater
++++++++++++++++++++++++++++++

An app sets up its own groups and roles using the ``site_auths`` global in ``auths.py``. To test just your apps
configuration, you can prevent ``site_auths`` from autodiscovering other modules by setting::

    EDC_AUTH_SKIP_SITE_AUTHS=True

You can prevent the ``AuthUpdater`` from updating groups and permissions by setting::

    EDC_AUTH_SKIP_AUTH_UPDATER=True

You can then override these attributes in your tests

.. code-block:: python

    @override_settings(
        EDC_AUTH_SKIP_SITE_AUTHS=True,
        EDC_AUTH_SKIP_AUTH_UPDATER=False
    )
    class TestMyTests(TestCase):
        ...


Above the ``site_auths`` global ``autodiscover`` is still disabled but the ``AuthUpdater`` is not.
In your test setup you can update ``site_auths`` manually so that your tests focus on the
add/update or groups/roles/codenames/tuples relevant to your app.

You can emulate ``autodiscover`` behaviour by explicitly importing ``auths`` modules needed for your tests.

For example:

.. code-block:: python

    from importlib import import_module

    from django.test import TestCase, override_settings
    from edc_auth.auth_updater import AuthUpdater


    class TestAuths(TestCase):
        @override_settings(
            EDC_AUTH_SKIP_SITE_AUTHS=True,
            EDC_AUTH_SKIP_AUTH_UPDATER=True,
        )
        def test_load(self):
            import_module(f"edc_dashboard.auths")
            import_module(f"edc_navbar.auths")
            AuthUpdater(verbose=True)


You can ``clear`` the ``site_auths`` registry and add back specific items need for your tests.

For example:

.. code-block:: python

    # taken from edc-dashboard
    @override_settings(EDC_AUTH_SKIP_SITE_AUTHS=True, EDC_AUTH_SKIP_AUTH_UPDATER=False)
    class TestMyTests(TestCase):
        def setUpTestData(cls):
            site_auths.clear()
            site_auths.add_group("edc_dashboard.view_my_listboard", name=CLINIC)
            site_auths.add_custom_permissions_tuples(
                model="edc_dashboard.edcpermissions",
                codename_tuples=(("edc_dashboard.view_my_listboard", "View my listboard"),),
            )
            AuthUpdater(verbose=False, warn_only=True)
            return super().setUpTestData()

        def test_me(self):
            ...



Importing users
+++++++++++++++

You create user accounts by importing a specially formatted CSV file. Once an account is created a "Welcome" email may be sent.

Import users from a CSV file with columns:

.. code-block:: bash

    username
    is_staff
    is_active
    first_name
    last_name
    job_title
    email
    mobile
    alternate_email
    site_names: a comma-separated list of sites
    role_names: a comma-separated list of roles


Then import the users from your application commandline

.. code-block:: bash

    python manage.py import_users --csvfile=/Users/erikvw/meta_users.csv --notify-to-test-email=ew2789@gmail --resource-name=meta.clinicedc.org --resend-as-new

Legacy notes
++++++++++++

**Important:** If you are upgrading from edc_base.auth:

The ``userprofile`` table is now in ``edc_auth``. ``edc_auth`` has one migration for this table.
Copy the same table from ``edc_base`` and fake the ``edc_auth`` migration.

.. code-block:: sql

    CREATE TABLE edc_auth_userprofile LIKE edc_base_userprofile;

    INSERT edc_auth_userprofile SELECT * FROM edc_base_userprofile;


.. code-block:: bash

    python manage.py migrate edc_auth --fake

You can now run the ``edc_base`` migration safely.

.. |pypi| image:: https://img.shields.io/pypi/v/edc-auth.svg
  :target: https://pypi.python.org/pypi/edc-auth

.. |actions| image:: https://github.com/clinicedc/edc-auth/actions/workflows/build.yml/badge.svg
  :target: https://github.com/clinicedc/edc-auth/actions/workflows/build.yml

.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-auth/branch/develop/graph/badge.svg
  :target: https://codecov.io/gh/clinicedc/edc-auth

.. |downloads| image:: https://pepy.tech/badge/edc-auth
   :target: https://pepy.tech/project/edc-auth

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/clinicedc/edc-auth",
    "name": "edc-auth",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "django authentication permissions roles clinicedc clinical trials",
    "author": "Erik van Widenfelt",
    "author_email": "ew2789@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/23/a7/ff018c0987fb8ebe0ddd6194c658a7d6b2df24bd6c0b4e53416a4f7470a1/edc_auth-0.3.90.tar.gz",
    "platform": null,
    "description": "|pypi| |actions| |codecov| |downloads|\n\nedc-auth\n--------\n\nAuthentication and permissions for the Edc\n\nDefault Groups\n++++++++++++++\n\n\nThe default groups are required for the normal operation of an EDC deployment. The default groups are:\n\n* ``ACCOUNT_MANAGER``: members may add/change and delete user accounts\n* ``ADMINISTRATION``: members may view the Administration page\n* ``AUDITOR``: members may view all forms but have no add/change permissions.\n* ``CLINIC``: members may add/edit/delete all CRFs, Requisitions, Actions and other required clinic trial data entry forms. They may also view the Requisition page of the Lab section;\n* ``EVERYONE``: members may access the EDC;\n* ``LAB``: members may perform all functions in the Lab section (Edit requisitions, receive, process, pack, manage manifests, etc);\n* ``PHARMACY``:\n* ``PII``: members may view all personally identifiable data and edit forms that manage such data (Screening, Consents, Patient registration);\n* ``PII_VIEW``: members may view personally identifiable data but have no add/edit permissions for any of the forms that store such data.\n\nPermissions\n+++++++++++\n\nPermissions use Django's permission framework,  therefore, all permissions are linked to some model.\n\nPermissions don't always naturally link to a model. In such cases, a dummy model is created.\nFor example, with Navigation bars from `edc_navbar`. Permissions to follow an item on a\nnavigation bar are associated with model `edc_navbar.Navbar`. A similar approach is used for\n`listboard` permissions using `edc_dashboard.Dashboard`.\n\nExtending permissions with `site_auths` global\n++++++++++++++++++++++++++++++++++++++++++++++\n\nA module can add new or update existing groups and roles and even add custom codenames.\n\nThe ``site_auths`` global ``autodiscovers`` configurations from ``auths.py`` in the root of your module.\nThe ``site_auths`` global gathers but does not validate or change any data in django's\n``group``/``permission`` models or the Edc's ``role`` model.\n\nThe ``site_auths`` global gathers data:\n* to ADD new groups,\n* to update codenames for an existing group,\n* to add a new role\n* to update the group list for an existing role\n* to add to the list of PII models\n* to specifiy custom functions to run before and after groups and roles have been updated\n\nFor example,\n\n.. code-block:: python\n\n    # auths.py\n    from edc_auth.constants import CLINICIAN_ROLE, STATISTICIAN_ROLE\n    from edc_auth.site_auths import site_auths\n\n    from edc_protocol_violation.auth_objects import (\n        PROTOCOL_VIOLATION,\n        PROTOCOL_VIOLATION_VIEW,\n        protocol_violation_codenames,\n        protocol_violation_view_codenames,\n    )\n\n    # add a new group specific to models in this module\n    site_auths.add_group(*protocol_violation_codenames, name=PROTOCOL_VIOLATION)\n    # add a new group specific to models in this module\n    site_auths.add_group(*protocol_violation_view_codenames, name=PROTOCOL_VIOLATION_VIEW)\n    # update the existing role CLINICIAN_ROLE to add the group PROTOCOL_VIOLATION\n    site_auths.update_role(PROTOCOL_VIOLATION, name=CLINICIAN_ROLE)\n    # update the existing role STATISTICIAN_ROLE to add the group PROTOCOL_VIOLATION_VIEW\n    site_auths.update_role(PROTOCOL_VIOLATION_VIEW, name=STATISTICIAN_ROLE)\n\n\nAs a convention, we define group names, lists of codenames and custom functions ``auth_objects.py``.\n\nIn the above example, the ``auth_objects.py`` looks like this:\n\n.. code-block:: python\n\n    # auth_objects.py\n\n    # declare group names\n    PROTOCOL_VIOLATION = \"PROTOCOL_VIOLATION\"\n    PROTOCOL_VIOLATION_VIEW = \"PROTOCOL_VIOLATION_VIEW\"\n    # add/change/delete/view codenames\n    protocol_violation_codenames = (\n        \"edc_protocol_violation.add_protocoldeviationviolation\",\n        \"edc_protocol_violation.change_protocoldeviationviolation\",\n        \"edc_protocol_violation.delete_protocoldeviationviolation\",\n        \"edc_protocol_violation.view_protocoldeviationviolation\",\n    )\n    # view only codename\n    protocol_violation_view_codenames = (\n        \"edc_protocol_violation.view_protocoldeviationviolation\",\n    )\n\n\nAuthUpdater\n+++++++++++\nThe ``AuthUpdater`` class runs in a post_migrate signal declared in ``apps.py``.\nThe ``AuthUpdater`` reads and validates the data gathered by ``site_auths``. Once all\nvalidation checks pass, the ``AuthUpdater`` updates Django's ``group`` and ``permission``\nmodels as well as the Edc's ``Role`` model.\n\nValidation checks include confirming models refered to in codenames exist. This means that\nthe app where models are declared must be in your ``INSTALLED_APPS``.\n\nDuring tests having all codenames load may not be ideal. See below on some strategies for testing.\n\n\nTesting SiteAuths, AuthUpdater\n++++++++++++++++++++++++++++++\n\nAn app sets up its own groups and roles using the ``site_auths`` global in ``auths.py``. To test just your apps\nconfiguration, you can prevent ``site_auths`` from autodiscovering other modules by setting::\n\n    EDC_AUTH_SKIP_SITE_AUTHS=True\n\nYou can prevent the ``AuthUpdater`` from updating groups and permissions by setting::\n\n    EDC_AUTH_SKIP_AUTH_UPDATER=True\n\nYou can then override these attributes in your tests\n\n.. code-block:: python\n\n    @override_settings(\n        EDC_AUTH_SKIP_SITE_AUTHS=True,\n        EDC_AUTH_SKIP_AUTH_UPDATER=False\n    )\n    class TestMyTests(TestCase):\n        ...\n\n\nAbove the ``site_auths`` global ``autodiscover`` is still disabled but the ``AuthUpdater`` is not.\nIn your test setup you can update ``site_auths`` manually so that your tests focus on the\nadd/update or groups/roles/codenames/tuples relevant to your app.\n\nYou can emulate ``autodiscover`` behaviour by explicitly importing ``auths`` modules needed for your tests.\n\nFor example:\n\n.. code-block:: python\n\n    from importlib import import_module\n\n    from django.test import TestCase, override_settings\n    from edc_auth.auth_updater import AuthUpdater\n\n\n    class TestAuths(TestCase):\n        @override_settings(\n            EDC_AUTH_SKIP_SITE_AUTHS=True,\n            EDC_AUTH_SKIP_AUTH_UPDATER=True,\n        )\n        def test_load(self):\n            import_module(f\"edc_dashboard.auths\")\n            import_module(f\"edc_navbar.auths\")\n            AuthUpdater(verbose=True)\n\n\nYou can ``clear`` the ``site_auths`` registry and add back specific items need for your tests.\n\nFor example:\n\n.. code-block:: python\n\n    # taken from edc-dashboard\n    @override_settings(EDC_AUTH_SKIP_SITE_AUTHS=True, EDC_AUTH_SKIP_AUTH_UPDATER=False)\n    class TestMyTests(TestCase):\n        def setUpTestData(cls):\n            site_auths.clear()\n            site_auths.add_group(\"edc_dashboard.view_my_listboard\", name=CLINIC)\n            site_auths.add_custom_permissions_tuples(\n                model=\"edc_dashboard.edcpermissions\",\n                codename_tuples=((\"edc_dashboard.view_my_listboard\", \"View my listboard\"),),\n            )\n            AuthUpdater(verbose=False, warn_only=True)\n            return super().setUpTestData()\n\n        def test_me(self):\n            ...\n\n\n\nImporting users\n+++++++++++++++\n\nYou create user accounts by importing a specially formatted CSV file. Once an account is created a \"Welcome\" email may be sent.\n\nImport users from a CSV file with columns:\n\n.. code-block:: bash\n\n    username\n    is_staff\n    is_active\n    first_name\n    last_name\n    job_title\n    email\n    mobile\n    alternate_email\n    site_names: a comma-separated list of sites\n    role_names: a comma-separated list of roles\n\n\nThen import the users from your application commandline\n\n.. code-block:: bash\n\n    python manage.py import_users --csvfile=/Users/erikvw/meta_users.csv --notify-to-test-email=ew2789@gmail --resource-name=meta.clinicedc.org --resend-as-new\n\nLegacy notes\n++++++++++++\n\n**Important:** If you are upgrading from edc_base.auth:\n\nThe ``userprofile`` table is now in ``edc_auth``. ``edc_auth`` has one migration for this table.\nCopy the same table from ``edc_base`` and fake the ``edc_auth`` migration.\n\n.. code-block:: sql\n\n    CREATE TABLE edc_auth_userprofile LIKE edc_base_userprofile;\n\n    INSERT edc_auth_userprofile SELECT * FROM edc_base_userprofile;\n\n\n.. code-block:: bash\n\n    python manage.py migrate edc_auth --fake\n\nYou can now run the ``edc_base`` migration safely.\n\n.. |pypi| image:: https://img.shields.io/pypi/v/edc-auth.svg\n  :target: https://pypi.python.org/pypi/edc-auth\n\n.. |actions| image:: https://github.com/clinicedc/edc-auth/actions/workflows/build.yml/badge.svg\n  :target: https://github.com/clinicedc/edc-auth/actions/workflows/build.yml\n\n.. |codecov| image:: https://codecov.io/gh/clinicedc/edc-auth/branch/develop/graph/badge.svg\n  :target: https://codecov.io/gh/clinicedc/edc-auth\n\n.. |downloads| image:: https://pepy.tech/badge/edc-auth\n   :target: https://pepy.tech/project/edc-auth\n",
    "bugtrack_url": null,
    "license": "GPL license, see LICENSE",
    "summary": "Authentication for clinicedc/edc projects",
    "version": "0.3.90",
    "project_urls": {
        "Homepage": "https://github.com/clinicedc/edc-auth"
    },
    "split_keywords": [
        "django",
        "authentication",
        "permissions",
        "roles",
        "clinicedc",
        "clinical",
        "trials"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "60a44498a33907e51085126ad045c34d8460d516ccbdc0e61e04f0ba345fa1ea",
                "md5": "a3ce79338ebc31628f9defa1eccfdab7",
                "sha256": "614ca22279df968f0bb5e7076035b5c56971a6369081a68649c29a588f3020d1"
            },
            "downloads": -1,
            "filename": "edc_auth-0.3.90-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a3ce79338ebc31628f9defa1eccfdab7",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 102990,
            "upload_time": "2024-11-20T22:23:03",
            "upload_time_iso_8601": "2024-11-20T22:23:03.981616Z",
            "url": "https://files.pythonhosted.org/packages/60/a4/4498a33907e51085126ad045c34d8460d516ccbdc0e61e04f0ba345fa1ea/edc_auth-0.3.90-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "23a7ff018c0987fb8ebe0ddd6194c658a7d6b2df24bd6c0b4e53416a4f7470a1",
                "md5": "7606cc53cdaec174072e390a7eddfb3f",
                "sha256": "dd6c875d8041ee284e5d7a17b9c2b5c3fb8e28ff4faa7e247cff333e88c76fc7"
            },
            "downloads": -1,
            "filename": "edc_auth-0.3.90.tar.gz",
            "has_sig": false,
            "md5_digest": "7606cc53cdaec174072e390a7eddfb3f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 73336,
            "upload_time": "2024-11-20T22:23:05",
            "upload_time_iso_8601": "2024-11-20T22:23:05.305521Z",
            "url": "https://files.pythonhosted.org/packages/23/a7/ff018c0987fb8ebe0ddd6194c658a7d6b2df24bd6c0b4e53416a4f7470a1/edc_auth-0.3.90.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-20 22:23:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "clinicedc",
    "github_project": "edc-auth",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "edc-auth"
}
        
Elapsed time: 0.97717s