Skip to main content

Project access and roles

Decide who can see each project, and who can send results or act on its runs, from the web app. Projects' owners manage their members in Project → Access; you set the defaults and the groups. Users see the same page in the user guide.

Nothing changes when you upgrade​

The defaults keep how QA-Board worked before roles:

  • Projects are public: anyone who can open QA-Board sees them (projects.default_visibility).
  • Everyone signed in is a developer of public and internal projects: they can send results, redo, stop or delete runs (projects.signed_in_role).
  • Projects in QABOARD_LOGIN_RESTRICTED_YAML stay restricted: they're private, and the users its rules match are their developers.

Give projects owners, then tighten​

  1. Make owners. Open a project's Access tab, add its owners (you're an owner of every project as an admin). Owners of group own every project under it, e.g. group/repo and group/repo/subproject.
  2. Owners add their teams: users or groups, as viewers, developers or owners, optionally until a date.
  3. Set projects.signed_in_role to viewer in Admin → Settings: signed-in users still see public and internal projects, and only members act on them. The admin overview says how many projects have owners.
  4. Make sensitive projects private: only their members see them, their runs, and their files.
RoleCan
Viewersee the project, its runs, metrics and files
Developeralso send results (qa run, qa batch), redo, stop, rename or delete runs, start tuning, export, save milestones
Owneralso choose the visibility, add and remove members, and decide access requests

Admins are owners of every project.

How QA-Board decides

A user's role on a/b/c is the highest of:

  • their grants, and their groups' grants, on a/b/c, a/b, a, or all projects (Admin → Groups → Roles on all projects). Expired grants don't count;
  • what the visibility gives: viewer for anyone on public projects, projects.signed_in_role for signed-in users on public and internal ones, nothing on private ones;
  • the YAML file's rules, for its projects (developer);
  • owner, for admins.

Visibility is inherited: a project's effective visibility is the most restrictive of its own and its prefixes'. A project can be more private than its group, never more public. Without any, projects.default_visibility applies.

Tokens never have more than their user, their scopes (read, ingest, act) and their projects allow.

Groups​

Create groups in Admin → Groups, then grant them roles on projects. Add members by hand, or with rules that add users from their groups in your directory or identity provider:

ClaimSigns in withExample values
groupssingle sign-on (OpenID Connect), if your IdP sends itcamera-team
realm_access.rolesKeycloak's realm roles (nested claims are written with dots)camera
memberOfLDAP (or the attributes in QABOARD_LDAP_GROUP_ATTRIBUTES), SAML attributesCN=camera-algo,OU=Groups,DC=example,DC=com, or just camera-algo
email_domainLDAP, SAML, single sign-on if your IdP says the email is verified (email_verified); not local accountsexample.com

Values match case-insensitively; an LDAP group matches by its full DN or its name. Members you add by hand stay. To write rules, look at Admin → Audit: auth.login events list the groups each sign-in put users in, and for single sign-on the groups the identity provider sent.

When rules apply: when users sign in, when you add or delete a rule (for everyone, in a minute), and every day, with the Users' groups schedule the jobs worker creates (Admin → Storage → Schedules, QABOARD_GROUPS_SYNC_AUTO=0 to manage it yourself). Update now in Admin → Groups runs it at once. A rule for one sign-in method reads what it last said, a rule for any reads them all: users who sign in with LDAP one day and single sign-on the next keep the groups both give.

Groups from LDAP

Every day, QA-Board looks up each LDAP account with the service account (QABOARD_LDAP_BIND_DN), one query per user on one connection, and reads memberOf (or QABOARD_LDAP_GROUP_ATTRIBUTES). Users who leave a group in the directory leave the QA-Board group the next day, without signing in again. Accounts the directory doesn't know anymore lose their directory groups. When it doesn't find over 20% of the accounts (a wrong base, an outage), nothing changes and the job reports an error.

Single sign-on accounts matched to LDAP accounts (QABOARD_OIDC_MATCH_USERS=username) are LDAP accounts: their directory groups are kept up to date too.

memberOf only lists the groups users are directly in. With Active Directory, set QABOARD_LDAP_NESTED_GROUPS=true to add the groups of their groups: it costs one more query per user.

Groups from single sign-on

Identity providers send the groups when users sign in. To keep them up to date between sign-ins, enable Refresh groups daily on the provider (QABOARD_OIDC_REFRESH_GROUPS=true for the environment's): QA-Board asks for offline_access, keeps each user's refresh token encrypted (it needs QABOARD_ENCRYPTION_KEY), and reads their claims again every day. When the provider refuses a refresh token, e.g. the user was disabled, the groups it gave end, and the user's QA-Board sessions from that provider too: signing in again gives the groups back.

  • Keycloak: add a Group Membership mapper to the client (groups), or use realm roles (realm_access.roles). Offline tokens need the offline_access role, which users have by default.
  • Microsoft Entra ID: in the app registration's Token configuration, add a groups claim, emitted as sAMAccountName for groups synced from Active Directory (else they're ids). Users in over 200 groups get none (Entra's "overage"): choose Groups assigned to the application. QA-Board warns about it in the audit log (groups_overage).
  • Okta: add a groups claim to the ID token, with a filter.

Access requests​

Users without the role they need get a clear refusal: in the web app with the project's owners and a Request access button, in qa with whom to ask. Owners get an email (when QA-Board sends emails), and approve or deny in the project's Access tab; the requester gets an email.

What else follows roles​

  • Results sent by qa: a signed-in sender (or a token) needs the developer role. With ingest.auth set to token, others are refused; with off or warn, their results are saved as unverified, like anonymous ones. Anonymous results are still accepted with off and warn, also on private projects: set ingest.auth to token to stop them (results sent by qa).
  • Files under /s/: the outputs and artifacts folders of a project follow its roles, when nginx checks who can read files (restricting who can read files). Sub-projects share their repository's folders: reading them needs access to every non-public project stored there.
  • Milestones: saving or editing shared ones needs the developer role; favorites only need to see the project.

The YAML file​

QABOARD_LOGIN_RESTRICTED_YAML keeps working. Its projects: become private projects whose matching users are developers, on top of their grants. Its login: rules still decide who can sign in, and paths: which folders users can read. Move its projects to grants when owners are in place, then remove them from the file.

Settings and API
SettingEnvironment variableDefaultWhat it does
projects.default_visibilityQABOARD_DEFAULT_VISIBILITYpublicThe visibility of projects without one: public, internal, private
projects.signed_in_roleQABOARD_SIGNED_IN_ROLEdeveloperThe role of signed-in users on public and internal projects: viewer or developer

The web app uses /api/v1/project/access (visibility, grants, requests), /api/v1/directory (the member picker) and /api/v1/admin/groups, /api/v1/admin/grants. Scripts can call them with a token of an owner (the act scope), or an admin (admin for the admin API).