Skip to content

Analyst Provisioning Developer Guide

Ed Mozley edited this page Oct 11, 2026 · 1 revision

πŸ› οΈ Analyst provisioning β€” Developer Guide

How analysts are created and given access from outside the app (3.5.0). User-facing guide: REST API: Analysts. Built from a design contributed by @Kraleemil in PR #174. The PR was rewritten rather than merged; the review is summarised in the commit 75c08b47.

The pieces

File What it does
includes/services/analysts.php AnalystsService: every rule. create, update, access, setAccess, assertNotLastAdmin, managedRoleId. Throws ServiceError.
api/v1/resources/analysts.php The REST adapter. Five routes, plus the key rule (below).
api/v1/resources/reference.php GET /analysts, with ?email=, ?include_inactive=1 and is_active in every row.
includes/sso_claims.php SSO groups also call AnalystsService::setAccess() for the admin flag. See Single Sign-On.
api/system/role.php, roles.php, assets/js/system-roles.js System β†’ Roles shows a managed role read-only.
tests/analysts-provisioning.php 37 checks, rolled back.

Access has four parts

access() returns, and setAccess() takes, any of the following. Each list replaces that part; anything not sent stays as it is.

Part Stored in
is_admin analysts.is_admin
all_modules + modules[] analysts.can_access_all_modules + analyst_modules (keys checked against getModuleRegistry(); system is refused because it comes with admin)
all_companies + companies[] analysts.can_access_all_tenants + analyst_tenant_access (ids checked against tenants)
capabilities[] the analyst's managed role (below), keys resolved through capFromKey() and de-duplicated after resolving

access(..., effective: true) adds effective.modules / companies / capabilities from getAnalystAllowedModules(), getAccessibleTenantIds() and getAnalystCapabilities(). Those helpers cache per request, so call them only after any write.

Rules that hold whoever calls

  • A new analyst starts with nothing: no modules, no admin, an unusable password, and no companies on a multi-company install. can_access_all_tenants defaults to 1, which the PR missed.
  • The last active administrator is never demoted or deactivated (assertNotLastAdmin). This is the same rule api/tickets/save_analyst.php enforces for the screen.
  • An actor never deactivates or demotes themselves. The check compares against ActorContext->actorId; system actors (id 0) skip it.
  • Setting auth_provider_id switches Follow team off (auth_follow_team = 0), so a team change can't quietly replace it.
  • setAccess() is one transaction, and joins the caller's if one is open ($own = !$conn->inTransaction()). That's how the test runs inside a rolled-back transaction.

The key rule (adapter only)

πŸ”΄ Every write (POST /analysts, PATCH /analysts/{id}, PATCH /analysts/{id}/access) needs a key that acts as a System administrator (analystIsAdmin($apiKey['analyst_id'])) and is scoped to all companies (company_scope === null). Otherwise the response is a 403.

This mirrors the app, where only admins manage analysts (admin_api_guard.php). The PR only demanded an admin key for is_admin, which let a non-admin key with analyst_access.manage grant its own analyst any module or capability. Reads need only analysts.read / analyst_access.read.

The managed role

Capabilities are held on one role per analyst that the API (and SSO) owns: rbac_roles.managed_for_analyst_id, which has a UNIQUE key and FK fk_rbac_roles_managed_analyst ON DELETE CASCADE, so the role is deleted with its analyst.

  • It's created the first time a capability is set, named API: <full name>, and assigned to the analyst through rbac_analyst_roles.
  • Roles assigned by hand and team roles are never touched. Emptying the API capabilities empties only this role.
  • System β†’ Roles marks it API, shows no edit or delete buttons, and api/system/role.php refuses PUT and DELETE for it. Edited there, it would be overwritten by the next sync; assigned to others, it would hand them this analyst's grants.

Leavers

A deactivated analyst used to keep an open session. Since 3.5.0, getAnalystAllowedModules() returns [] and analystIsAdmin() returns false for an inactive analyst, so the next request is refused, and the System page gate asks the database. Requesters marked as left are refused on every portal route by requesterIsActive(). See Leavers could still sign in.

Tests

  • php tests/analysts-provisioning.php covers create, update, access, administrators and deletion, each refusal with a positive control.
  • The adapter's key rule was checked over HTTP with temporary keys (16 checks; see the commit). It isn't a test file, because it needs a running server.

Not built

There's no "set access by company" for a company-scoped key, and no bulk endpoint.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally