Skip to content

Commit

Permalink
Security refactor, REQUIRE_AUTH mode, doc, tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Etienne Jodry authored and Etienne Jodry committed Oct 31, 2024
1 parent d8562b8 commit ed48b12
Show file tree
Hide file tree
Showing 34 changed files with 2,630 additions and 528 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
python-version: ["3.11"] # , "3.12"
steps:
- uses: actions/checkout@v4
with:
Expand Down
21 changes: 20 additions & 1 deletion docs/developer_manual/advanced_use.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ a ``ResourceController``.
.. code-block:: python
:caption: s3controller.py
from biodm.routing import Route, PublicRoute
class S3Controller(ResourceController):
"""Controller for entities involving file management leveraging an S3Service."""
def _infer_svc(self) -> Type[S3Service]:
Expand All @@ -200,7 +202,7 @@ a ``ResourceController``.
prefix = f'{self.prefix}/{self.qp_id}/'
file_routes = [
Route(f'{prefix}download', self.download, methods=[HttpMethod.GET]),
Route(f'{prefix}post_success', self.post_success, methods=[HttpMethod.GET]),
PublicRoute(f'{prefix}post_success', self.post_success, methods=[HttpMethod.GET]),
...
]
# Set an extra attribute for later.
Expand Down Expand Up @@ -420,3 +422,20 @@ A lot of that code has to do with retrieving async SQLAlchemy objects attributes
# Generate a new form.
await self.gen_upload_form(file, session=session)
return file
.. _dev-routing:

Routing and Auth
----------------

As shown in the ``S3Controller`` example above, ``BioDM`` provides two
``Routes`` class: ``PublicRoute`` and ``Route``.

In case you are defining your own routes you should use those ones instead of
starlette's ``Route``.

Ultimately, this allows to use the config parameter ``REQUIRE_AUTH`` which when set to ``True``
will require authentication on all endpoints routed
with simple ``Routes`` while leaving endpoints marked with ``PublicRoute`` public.
This distinction can be important as in the example above, s3 bucket is **not** authenticated
when sending us a successful notice of file upload.
2 changes: 1 addition & 1 deletion docs/developer_manual/demo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ we will go over the following minimal example.
# Tables
class Dataset(bd.components.Versioned, bd.components.Base):
id = Column(Integer, primary_key=True, autoincrement=not 'sqlite' in config.DATABASE_URL)
id = Column(Integer, primary_key=True, autoincrement=not 'sqlite' in str(config.DATABASE_URL))
name : sao.Mapped[str] = sa.Column(sa.String(50), nullable=False)
description : sao.Mapped[str] = sa.Column(sa.String(500), nullable=False)
username_owner: sao.Mapped[int] = sa.Column(sa.ForeignKey("USER.username"), nullable=False)
Expand Down
11 changes: 11 additions & 0 deletions docs/developer_manual/permissions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ be provided in a ``.env`` file at the same level as your ``demo.py`` script.
KC_CLIENT_ID=
KC_CLIENT_SECRET=
Server level: REQUIRE_AUTH
--------------------------

Setting ``REQUIRE_AUTH=True`` config argument, will make all routes, except the ones explicitely
marked public (such as ``/login`` and ``/[resources/|]schemas)`` require authentication.


See more at :ref:`dev-routing`


Coarse: Static rule on a Controller endpoint
---------------------------------------------

Expand Down
26 changes: 24 additions & 2 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ Keycloak also needs a databse:
docker exec -u postgres biodm-pg createdb keycloak
Then you may start keycloak itself:
.. code-block:: bash
Expand All @@ -164,9 +163,32 @@ Once you've created the realm, create the client. Then
* **dev**: `http://*` and `https://*`
* **prod**: provide the url of the login callback `{SERVER_HOST}/syn_ack`.
Additionally, ``BioDM`` expects token to feature groups. For this, a client scope is necessary. Go to
* `Client Scopes` -> `Create Client Scope`
* Name: `groups`
* Protocol: `OpenID Connect`
* Type: `Default`
* Include in token scope: `On`
* Save
* `Client Scopes` -> `Groups` -> `Mappers` -> `Configure a new mapper` -> `Group membership`
* Name: `groups`
* token claim name: `groups`
* At least `Full group path` and `Add to access token`: `On`
* Save
* `Clients` -> `MyClient` -> `Client Scopes` -> `Add Client Scope` -> `Groups` -> `Add - Default`
Moreover, admin privileges are granted to users belonging to `admin` group.
It is recommended to create that group and at least one user in it,
if you want to create keycloak entities from the API for instance.
.. note::
Depending on your keycloak version or running instance `SERVER_HOST` may have to be appended with `/auth`.
Depending on your keycloak version or running instance `KC_HOST` may have to be appended with `/auth`.
Then you should provide the server with the `SECRET` field located in the
`Credentials` tab, that appears **after** you changed access type and the realm public key
Expand Down
21 changes: 12 additions & 9 deletions docs/user_manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,11 @@ This triggers creation of a new row with a version increment.

.. note::

``PUT /release`` is the way of updating versioned resources.
The endpoint ``PUT /`` (a.k.a ``update``) will not be available for such resources, and
any attempt at updating by reference through ``POST /`` will raise an error.
``POST /release`` is the way of updating versioned resources.
The endpoint ``PUT /`` (a.k.a ``update``) is available, however it is meant to be used
in order to update nested objects and collections of that resource. Thus,
any attempt at updating a versioned resource through either ``PUT /`` or ``POST /``
shall raise an error.


**E.g.**
Expand Down Expand Up @@ -178,15 +180,16 @@ and followed by:
* Use ``nested.field=val`` to select on a nested attribute field
* Use ``*`` in a string attribute for wildcards

* ``field.op(value)``

* Currently only ``[lt, le, gt, ge]`` operators are supported for numerical values.
* numeric operators ``field.op([value])``

**e.g.**
* ``[lt, le, gt, ge]`` are supported with a value.

* ``[min, max]`` are supported without a value

.. note::

When querying with ``curl``, don't forget to escape ``&`` symbol or enclose the whole url in quotes, else your scripting language may intepret it as several commands.
When querying with ``curl``, don't forget to escape ``&`` symbol or enclose the whole url
in quotes, else your scripting language may intepret it as several commands.


Query a nested collection
Expand Down Expand Up @@ -366,6 +369,6 @@ otherwise.

- Passing a top level group will allow all descending children group for that verb/resource tuple.

- Permissions are taken into account if and only if keyclaok functionalities are enabled.
- Permissions are taken into account if and only if keycloak functionalities are enabled.

- Without keycloak, no token exchange -> No way of getting back protected data.
Loading

0 comments on commit ed48b12

Please sign in to comment.