User Management
QA-Board offers a user management system. It can be used to block access to anonymous users, or restrict executing code (via Tuning) to logged-in users.
Authentication Providers
LOCAL: an internal users list, created by qaboard admins.LDAPSAML: SSO via SAML
Users sign in from the Login button at the top right of the web app. With LOCAL and LDAP, it opens a form; with SAML, it redirects them to your identity provider.

To register a new
LOCALuser, either:
- add an entry to the database under
userstable.- use the signup endpoint (signup() in backend/backend/api/auth.py). It is enabled unless
QABOARD_DISABLE_SIGNUP=True:curl -d "user_name=<user_name>&password=<password>&email=<user_email>&full_name=<user_full_name>" -X POST '<qaboard_url>/api/v1/user/signup/'
This flow can be improved. So far all instances of QA-Board rely on external authentication providers, so it hasn't been a priority. We would welcome merge requests to add a UX flow to register new users.
Environment variables
To apply changes the backend server needs to be restarted.
With docker compose, set them in services.backend.environment in your site overlay (see deployments/example.yml). Only some of them (QABOARD_LOGIN_TYPE, QABOARD_LOGIN_REQUIRED, QABOARD_LOGIN_RESTRICTED, QABOARD_LOGIN_RESTRICTED_YAML) are forwarded from an .env file by docker-compose.yml.
The sign-in policy is set via environment variables such as QABOARD_LOGIN_*, QABOARD_LDAP_*, QABOARD_SAML_*, as described below:
| ENV Variable | Default | Usage |
|---|---|---|
QABOARD_LOGIN_TYPE | LOCAL | Set to LOCAL/LDAP/SAML |
QABOARD_LOGIN_REQUIRED | empty | Any non-empty value (e.g. true) blocks anonymous users. Leave it unset/empty to allow them: even false enables it |
QABOARD_DISABLE_SIGNUP | empty | Set to True (exactly) to disable the signup endpoint for LOCAL users |
QABOARD_LOGIN_RESTRICTED | empty | Any non-empty value (e.g. true) uses a configuration file to allow only specific users |
QABOARD_LOGIN_RESTRICTED_YAML | none | The path to the users configuration file |
QABOARD_LDAP_HOST | none | Server hostname (including port) |
QABOARD_LDAP_PORT | 389 | Server port, usually 389 (or 636 if SSL is used / not supported yet!). |
QABOARD_LDAP_USER_BASE | none | Search base for users. |
QABOARD_LDAP_BIND_DN | none | The Distinguished Name to bind as, this user will be used to lookup information about other users. |
QABOARD_LDAP_PASSWORD | none | The password to bind with for the lookup user. |
QABOARD_LDAP_USER_FILTER | none | User lookup filter, the placeholder {login} will be replaced by the user supplied login. (e.g. (&(objectClass=inetOrgPerson)(|(uid={login})(mail={login}))), or (&(objectClass=user)(|(sAMAccountName={login})))) |
QABOARD_LDAP_ATTRIBUTE_EMAIL | ||
QABOARD_LDAP_ATTRIBUTE_COMMON_NAME | cn | |
QABOARD_SAML_DIR | none | The path to the directory with the SAML configuration files, as in the python-saml docs |
QABOARD_SAML_ATTRIBUTE_EMAIL | none | |
QABOARD_SAML_ATTRIBUTE_COMMON_NAME | none | |
QABOARD_SAML_ATTRIBUTE_USER_NAME | none | |
QABOARD_SAML_ATTRIBUTE_ID | none |
Blocking anonymous users
To block anonymous users, who are not logged-in, from accessing any content on QA-Board, set the environment variable QABOARD_LOGIN_REQUIRED=true (any non-empty value) and restart the backend. The webapp reads it from the backend: no frontend rebuild is needed.
Restricting sign-in
To allow only specific users to sign-in, set the environment variables:
QABOARD_LOGIN_RESTRICTED=trueQABOARD_LOGIN_RESTRICTED_YAML=path/to/users_restrict.yml
The YAML file specifies multiple cases where users will be accepted. It can be based on the user name, email, or any parameter from the authentication provider (or users table in the database).
Example:
login:
# only users with one of those emails will be able to login
email:
- mr.nobody@example.com
- user3@example.com
# those users will also be allowed to login
user_name:
- john.doe
- jane.doe
# here we can use SAML/LDAP attributes to restrict based on the rank, job position...
data:
http://sso.company.com/2023/11/CompId:
- C123
- C777
http://sso.company.com/2023/11/GrdName:
- Staff
- Team Leader
Managing Projects
If you need to restrict a project so that only specific users have access, you can configure this in the configuration file.
Example:
projects:
# restricting a namespace
my_namespace:
# only users with one of those emails will be able to access projects that are under the namespace "my_namespace", but not to "my_namespace/proj1"
email:
- mr.nobody@example.com
- user3@example.com
# those users will also be allowed to access projects that are under the namespace "my_namespace", but not to "my_namespace/proj1"
user_name:
- john.doe
- jane.doe
# here we can use SAML/LDAP attributes to restrict based on the rank, job position...
data:
http://sso.company.com/2023/11/CompId:
- C123
- C777
http://sso.company.com/2023/11/GrdName:
- Staff
- Team Leader
# restricting a project
my_namespace/proj1:
# only users with one of those emails will be able to access proj1
email:
- mr.nobody@example.com
# those users will also be allowed to access proj1
user_name:
- john.doe
If both a 'namespace' and a 'project' are set, the project will take priority. For example for the following "namespace/project", you can restrict the specific project to a certain group and restrict the namespace to a larger group.
A full configuration file that has both 'login' and 'project' restrictions will look like this:
login:
# only users with one of those emails will be able to login
email:
- mr.nobody@example.com
- user3@example.com
# those users will also be allowed to login
user_name:
- john.doe
- jane.doe
# here we can use SAML/LDAP attributes to restrict based on the rank, job position...
data:
http://sso.company.com/2023/11/CompId:
- C123
- C777
http://sso.company.com/2023/11/GrdName:
- Staff
- Team Leader
projects:
# restricting a namespace
my_namespace:
# only users with one of those emails will be able to access projects that are under the namespace "my_namespace", but not to "my_namespace/proj1"
email:
- mr.nobody@example.com
- user3@example.com
# those users will also be allowed to access projects that are under the namespace "my_namespace", but not to "my_namespace/proj1"
user_name:
- john.doe
- jane.doe
# here we can use SAML/LDAP attributes to restrict based on the rank, job position...
data:
http://sso.company.com/2023/11/CompId:
- C123
- C777
http://sso.company.com/2023/11/GrdName:
- Staff
- Team Leader
# restricting a project
my_namespace/proj1:
# only users with one of those emails will be able to access proj1
email:
- mr.nobody@example.com
# those users will also be allowed to access proj1
user_name:
- john.doe
Restricting who can read files
nginx serves the output and artifacts files under /s/, and by default anyone who can reach the server can read them. You can restrict them in two ways, and combine them:
- Restricted projects: with
QABOARD_LOGIN_RESTRICTED, only the users allowed to see a project read its files. - Unix permissions: in some folders, like
/home, users only read what their own Unix account can read.
In both cases, nginx must ask the backend before sending files: add include snippets/qaboard-files-auth.conf; to all the /s/ locations of your nginx configuration (with the helm chart, set proxy.restrictFiles: true). Files can be reached from several locations through symlinks: the backend checks where links lead, but only for the locations that ask it.
In the file browser, users who are not signed in see Sign in to see these files, others You don't have access. Scripts can authenticate with an API token: curl -H "Authorization: Bearer <token>" https://qaboard/s/...
Restricted projects
The projects restricted in users_restrict.yml also restrict their files: the outputs and artifacts folders from the projects' storage in qaboard.yaml. If needed, you can also restrict other folders under paths, with the same rules as projects:
paths:
/mnt/qaboard/confidential:
user_name:
- john.doe
# The most specific folder wins. With no rules, anyone can read it
/mnt/qaboard/confidential/public: {}
Unix permissions
Set QABOARD_FILES_UNIX_PERMISSIONS to the folders where it applies, separated by : (e.g. /home:/mnt/users). There, users read a file only if their Unix account could: it must be allowed to read the file, and to traverse the folders above it. Users who are not signed in only read what everyone can read.
- The backend finds users' accounts by their QA-Board user name: see Users' accounts. Users without an account get the permissions of "others".
- ACLs are ignored.
Users' accounts
The file browser shows who owns each file, and Unix permissions need users' groups. The backend reads the accounts in its own /etc/passwd and /etc/group. If you have complete files, mount them.
With a directory service (LDAP, SSSD, NIS...), you usually don't: the backend can instead run a command that prints the accounts, in the /etc/passwd format, and another one for the groups (/etc/group format). For instance, through ssh to a host that sees your users:
QABOARD_ACCOUNTS_COMMAND=ssh qaboard@some-host getent passwd
QABOARD_GROUPS_COMMAND=ssh qaboard@some-host getent group
- The backend runs them in the background when their last output is older than
QABOARD_ACCOUNTS_REFRESHseconds (default: a day), and keeps it in$QABOARD_DATA_SHARED_DIR/accounts: one run serves all the backend's workers and replicas. To refresh now, delete that folder'spasswd. - If a command fails, or prints less than half of the accounts it printed last time, the backend keeps the previous accounts, and tries again 10 minutes later. Errors show in its logs (
[accounts]). - Only lines in the right format count: ssh banners (sent to stderr) and messages printed by shell startup files are ignored.
- Accounts the commands don't know are looked up in
/etc/passwdand/etc/group. - With SSSD,
getent passwdalone only lists all users if enumeration is enabled (enumerate = true), which is often not the case. Check the number of lines, or use a command that queries the directory itself, e.g.ldapsearch, formatted asname:x:uid:gid:gecos:home:shell. - The command runs with the backend's environment, e.g. the ssh key used for the LSF bridge, and its own user's permissions.
- nginx asks the backend before sending each file. It answers quickly, without database queries for public files, but if the backend is down, nginx doesn't serve files.
- Sub-projects share the storage of their git repository: restricting a sub-project restricts the files of the whole repository.
- With a folder per user in the storage (e.g.
outputs: /mnt/qaboard/outputs/{user}), the project's folder is restricted in every user's folder. - Results of restricted projects saved outside of their storage (e.g.
--shareto another folder) are not restricted, unless you list their folders underpaths. The backend readsusers_restrict.ymlwhen it starts, and the projects' folders every 5 minutes. - The image server (IIIF) and the API endpoints that read images don't check permissions. Their paths are hard to guess (they contain commit hashes), but whoever knows one can see its images.
- WebDAV (services/nginx/webdav.conf) doesn't check them either: it's not enabled by default.