Authentication#
py-ispyb
relies on plugins to handle different methods of authenticating users to the system. There are some mechanisms that are implemented natively like LDAP, keycloak and dummy that can be used out-of-the-box. However, it is worth noting that anyone can write his own plugin.
There's a dedicated endpoint that allows to use the different plugins that are installed. This endpoint receives as parameters:
- plugin - name of the plugin to be used for authentication, as specified in configuration
- login (optional)
- password (optional)
- token (optional)
Example of the request:
curl -X 'POST' \
'http://localhost:8000/ispyb/api/v1/auth/login' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"plugin": "dummy",
"login": "test",
"password": "Admin",
"token": "Admin"
}'
If the authentication is successful the response will be a json with the following fields:
{
"login": "test",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJncm91cHMiOlsiQWRtaW4iXSwicGVybWlzc2lvbnMiOlsiQWRtaW4iXSwiaWF0IjoxNjUwOTgxNjA5LCJleHAiOjE2NTA5OTk2MDl9.3Iq2lGG5RR6Gebss5qEDdASrEMwCIne2jFhaVqp91m0",
"permissions": ["Admin"]
}
Authorization#
For any authentication plugin, permissions are configured in the database using the following tables (with example data):
- UserGroup: [Admin, user]
- Permission: [own_proposals, own_sessions, all_proposals, all_sessions]
- UserGroup_has_Permission: [{Admin, all_proposals}, {Admin, all_sessions}, {user, own_proposals}, {user, own_sessions}]
Configure a plugin#
One or more plugin can be enabled at the same time. A configuration file called auth.yml
at the root of the project contains their configuration parameters.
The next examples shows how to enable the dummy authentication plugin:
AUTH:
- dummy:
ENABLED: true
AUTH_MODULE: "pyispyb.app.extensions.auth.DummyAuthentication"
AUTH_CLASS: "DummyAuthentication"
List of plugins#
py-ISPyB is using the following authentication plugins, which code you can find in pyispyb/app/extension/auth
.
DummyAuthentication#
Provides easy authentication for tests
. Permissions listed in the password field are given.
Configuration
AUTH:
- dummy: # /!\/!\/!\ ONLY USE FOR TESTS /!\/!\/!\
ENABLED: false
AUTH_MODULE: "pyispyb.app.extensions.auth.DummyAuthentication"
AUTH_CLASS: "DummyAuthentication"
KeycloakDBGroupsAuthentication
#
Provides authentication using keycloak with DB-managed groups.
Configuration
AUTH:
ENABLED: true
AUTH_MODULE: "pyispyb.app.extensions.auth.KeycloakDBGroupsAuthentication"
AUTH_CLASS: "KeycloakAuthentication"
CONFIG:
KEYCLOAK_SERVER_URL: "your_server"
KEYCLOAK_CLIENT_ID: "your_client"
KEYCLOAK_REALM_NAME: "your_realm"
KEYCLOAK_CLIENT_SECRET_KEY: "your_secret"
LdapAuthentication
#
Provides authentication using LDAP users and groups.
Configuration
AUTH:
- ldap:
ENABLED: true
AUTH_MODULE: "pyispyb.app.extensions.auth.LdapAuthentication"
AUTH_CLASS: "LdapAuthentication"
CONFIG:
LDAP_URI: "ldap://your_ldap"
LDAP_BASE_INTERNAL: "ou=People,dc=test,dc=fr"
LDAP_BASE_GROUPS: "ou=Pxwebgroups,dc=test,dc=fr"
Implementing new plugins#
New plugins should inherit from AbstractAuthentication
and override either authenticate_by_login
or authenticate_by_token
dependning on whether they accept a login / password combination or an authorisation token. Both functions return Person
on success. This can be prepopulated with familyName
, givenName
, and emailAddress
, which can be used to auto-create a new Person
entry if the option is enabled (disabled by default)
For example:
from typing import Optional
from ispyb import models
from .AbstractAuthentication import AbstractAuthentication
class MyAuthentication(AbstractAuthentication):
"""My authentication class."""
def configure(self, config: dict[str, Any]):
self._config = config
def authenticate_by_login(self, login: str, password: str) -> Optional[models.Person]:
if ...
return models.Person(
login=login,
familyName=...,
givenName=...,
)
else:
logger.exception("Something went wrong")
Or for token based authentication:
from typing import Optional
from ispyb import models
from .AbstractAuthentication import AbstractAuthentication, AuthType
class MyAuthentication(AbstractAuthentication):
"""My authentication class."""
authentication_type = AuthType.token
def configure(self, config: dict[str, Any]):
self._config = config
def authenticate_by_token(self, token: str) -> Optional[models.Person]:
if ...
return models.Person(
login=login
)
else:
logger.exception("Something went wrong")
Plugins can export specific config variables to the UI as well by defining config_export
, these properties are made available to the /auth/config
endpoint:
from typing import Optional
from ispyb import models
from .AbstractAuthentication import AbstractAuthentication, AuthType
class MyAuthentication(AbstractAuthentication):
"""My authentication class."""
authentication_type = AuthType.token
config_export = ["MY_CONFIG_PROPERTY"]
...