docs / Platform

Users, groups and roles

Local and LDAP sign-in, the four roles and where they come from, the read-only viewer, field-level permissions, sessions, and what is not supported yet.

Sign-in

Two providers work today: local accounts with a password, and LDAP / Active Directory. Both can be on at once; the login form offers whichever are enabled. Providers are declared in the chart's users/*.yaml, mounted at /etc/itops/users and read at every start. The Auth Providers page in the UI shows them and is read-only: adding or changing a provider is a values change and a redeploy.

Local accounts

Enabled by default (users/local.yaml, local.enabled: true). The first start seeds admin / Password123! as a superadmin, only if the local provider is enabled and only if no admin exists. Change that password at first login.

Password policy for local accounts, from users/local.yaml: at least 12 characters, upper and lower case, a digit, symbol optional, the last five passwords remembered. local.allowRegistration is false; set it to true for self-service sign-up.

Password reset is a one-time link, never a password in an email or on screen. resetPassword issues the link; setPasswordWithToken consumes it.

LDAP and Active Directory

# values.yaml
env:
  ITOPS_FEATURES_LDAP_AUTH: "true"
auth:
  ldap:
    bindPasswordSecret: { name: itops-ldap, key: bindPassword }   # an existing Secret
# users/ldap.yaml
ldap:
  enabled: true
  name: corp-ad
  displayName: "Corporate Active Directory"
  isDefault: true
  jitProvisioning: true
  host: ldap.corp.example.com
  port: 636
  useTLS: true                 # ldaps; or startTLS: true on 389
  bindDn: cn=itops,ou=service,dc=corp,dc=example,dc=com
  bindPasswordSecret: { name: itops-ldap, key: bindPassword }
  baseDn: dc=corp,dc=example,dc=com
  userFilter: "(sAMAccountName=%s)"
  groupFilter: "(member=%s)"
  userAttrs: { username: sAMAccountName, email: mail, displayName: cn, firstName: givenName, lastName: sn }
  groupMappings: []

Users are created on their first successful bind (jitProvisioning). The bind password comes from your own Secret, mounted at /etc/itops/auth/ldap-secret. The bindPassword key in the YAML exists for development and is not what a production install should use.

Group auto-join. An LDAP group whose cn equals an ITOps group name joins the user to that group at login. The built-in admin groups are prefixed itops- on purpose, so a company-wide cn=admins never promotes anyone by accident. An explicit entry in auth.ldap.groupMappings[] wins over name matching. syncLDAPGroups re-reads memberships on demand.

The chart's network policy allows LDAP egress to a same-namespace pod labelled app=ldap on 389 and 636 (networkPolicy.allowLdapEgress). A directory outside the namespace needs its own egress rule.

Not supported yet

Entra ID, Google Workspace, Okta, Auth0, Keycloak, OneLogin, JumpCloud, AWS IAM Identity Center, ADFS, GitHub, GitLab, generic SAML and generic OIDC appear in the provider catalogue and can be configured, but signing in through them returns "not yet implemented". MFA is a feature flag that, if enabled, refuses every password login rather than pretend. The placeholders users/m365.yaml and users/aws.yaml in the chart are not wired to anything.

Roles

A person's role is derived from group membership at every request. It is a ceiling, not a floor, and every check fails closed.

Role Granted by membership in May
admin itops-admins, itops-trust-admins (also the legacy admins, infra-admins) everything: users, groups, licence, deletes, permission rules
operator infra-admins, security-team, compliance, pki-operators day-to-day work: services, tickets, SLA definitions, exclusion windows, tickets on behalf of others
requester any signed-in user in none of the above raise and follow their own tickets, read what their field permissions allow
viewer itops-viewers read everything, change nothing

The viewer check runs last, so it never demotes an admin who also happens to be a viewer. Every mutation that changes the world requires at least operator; deleting a user, a service or a node requires admin; the REST attachment upload requires a user session, not the operator key. The public demo's demo account is a viewer, which is how it can be handed to strangers.

Roles apply to the API, not only the UI. A viewer's deleteService fails with FORBIDDEN whether it comes from a button or from curl.

Groups

Groups are rows in the database, hierarchical by name, managed on the Users page (admin) or upserted from the chart's groups/*.yaml at core start. Four ticketing groups ship by default (incident-responders, change-implementers, problem-investigators, provisioners); the role groups above are created as needed. Group names may take the path form, prod/eu-central-1/db-admins, which the ticketing lookup walks from the most specific level up.

Users are not in YAML by design: passwords, sessions and login history are runtime data. Create people in the UI (createUser takes groupIds), or let them arrive through LDAP.

Field-level permissions

Beyond the four roles, individual fields can be HIDDEN, READ, WRITE or ADMIN per group. The plugins register the fields that matter: on a ticket title, description, priority, status, assignee; on an SLA definition definition_name, target_value, actual_value, warning_threshold, critical_threshold, change_reason. Rules carry a priority and optional conditions: only the user's own records, only certain statuses or priorities, only within a time window (hours, days, timezone). The same mechanism covers screens and UI elements (screenPermissions, myUIPermissions) and environment-level access (checkEnvironmentAccess).

Managed on the Permissions page or with addPermissionRule, updatePermissionRule, deletePermissionRule, updateFieldDefinition. The UI reads myFieldPermissions and myUIPermissions at login and hides or locks accordingly; the API enforces the same rules on every read and write.

Sessions

Sign-in returns a 15-minute access token and a 7-day refresh token (HS256, ITOPS_JWT_SECRET). mySessions lists a user's sessions with IP address, user agent, creation, expiry and last activity; revokeSession and revokeAllSessions end them; extendSession pushes the idle timeout, which defaults to 8 hours.

Rate limiting and headers

Per-IP token-bucket rate limiting (10 requests per second, burst 20) is forced on in production. The client address is the TCP peer unless ITOPS_SECURITY_TRUST_PROXY is set, in which case X-Real-IP is used. Read the ingress warning before trusting the proxy. Production also forces the security headers and HSTS.

Audit

Every ticket keeps its status, assignment and field history. Every failed API-key check lands in the SLA event log as auth_failed. User and group changes are logged by the core. There is no separate immutable audit store in this release.

Documentation for ITOps 4.2 · charts itops 2.0.0, sla-portal 1.4.0 · rendered 2026-09-11