Role-Based Access Control (RBAC)
PuppyGraph supports role-based access control (RBAC) to restrict what each user can do. This page explains the built-in roles, how to manage users through the UI, and how to configure SSO group mappings.
RBAC governs which operations a user may perform. To restrict which rows a user sees within an operation they are allowed to run, see Row-Level Security.
Roles
PuppyGraph has five built-in roles:
| Role | Intended for | What they can do |
|---|---|---|
Admin |
PuppyGraph administrators | Everything: full access to all features including user management, graph schema, catalogs, queries, cluster, and system operations |
UserAdmin |
Team leads, security officers | Manage users and roles; read-only access to graphs, catalogs, and queries. Cannot modify graphs, catalogs, or cluster infrastructure |
GraphAdmin |
Data engineers | Build and maintain graph schemas; run queries; read catalog metadata. Cannot create or delete catalogs, and cannot manage users |
Analyst |
Data analysts, application developers | Run queries; read graph schemas and catalog metadata |
Viewer |
Stakeholders, reviewers | Read graph schemas and catalog metadata. Cannot run queries |
Permission Matrix
| Permission | Admin | UserAdmin | GraphAdmin | Analyst | Viewer |
|---|---|---|---|---|---|
| View graph schema | ✓ | ✓ | ✓ | ✓ | ✓ |
| Modify graph schema | ✓ | ✓ | |||
| View catalogs | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create/delete catalogs | ✓ | ||||
| View export locations (credentials masked) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Add/edit/delete export locations | ✓ | ||||
| Run queries | ✓ | ✓ | ✓ | ✓ | |
| Export query results to registered locations | ✓ | ✓ | ✓ | ✓ | |
| View data and cache | ✓ | ✓ | ✓ | ✓ | |
| Manage data and cache | ✓ | ✓ | |||
| View cluster status | ✓ | ✓ | ✓ | ||
| Manage cluster nodes | ✓ | ||||
| System administration | ✓ | ||||
| View users and audit log | ✓ | ✓ | |||
| Manage users and roles | ✓ | ✓ | |||
| Assign Admin/UserAdmin roles | ✓ |
Note
UserAdmin and GraphAdmin are peers; neither is a superset of the other. Only Admin can assign the Admin or UserAdmin roles to other users.
Schema uploads with embedded catalogs
If a schema file includes a catalog section, uploading it requires CATALOG:write permission (Admin only). Users with the GraphAdmin role must remove the catalog section from their schema file and have an admin register the catalogs separately via Catalogs in the left sidebar.
Export locations
Listing export locations requires CATALOG:read, which all built-in roles have; the stored credentials (identifier, credential, and clientSecret) are masked.
Admin: add, edit, or delete locations through Settings → Exports, the REST API, Gremlin, or Cypher (CATALOG:write), and export to them.UserAdmin,GraphAdmin, andAnalyst: export to every registered location (QUERY:execute), but cannot manage locations. Ask anAdminto add a destination or change its storage settings or credentials.Viewer: view locations only; cannot manage them or run export queries.
Queries reference a location and directory; storage settings and credentials stay in the location. Registered catalog exports are also supported. See Exporting Query Results.
Managing Users
RBAC settings are in Settings (gear icon in the left sidebar). The management tabs (Users, Service Accounts, SSO Configuration, and Audit Log) are only visible to users with the Admin or UserAdmin role.
Creating a Local User
Local users authenticate with a username and password managed by PuppyGraph.
- Go to Settings → Users
- Click Create User
- Enter a username and select a role (defaults to Analyst)
- Click Create. A generated password appears; copy it now, because it is shown only once and cannot be retrieved later.
- Share the username and password with the new user.
Note
Only Admin can assign the Admin or UserAdmin roles when creating a user.
Changing a User's Role
- Go to Settings → Users
- Find the user in the table
- Use the Role dropdown in their row to select a new role
The role change takes effect the next time the user logs in.
Resetting a User's Password
For local users who have forgotten their password:
- Go to Settings → Users
- Find the user and click the Password button
- Confirm the reset. A new generated password is shown once. Share it with the user.
Deleting a User
- Go to Settings → Users
- Find the user and click the Delete button
- Confirm the deletion
Note
The bootstrap admin (puppygraph by default) cannot be deleted or have its role changed.
Changing Your Own Password
Local users can change their own password from the Preferences tab:
- Go to Settings → Preferences
- Scroll to the Password section
- Click Change Password
- Enter your current password and new password (minimum 8 characters)
SSO Users
When SSO is enabled, users who authenticate through your identity provider (IdP) are created automatically on first login. For how to wire PuppyGraph up to your IdP, see OAuth / OIDC Single Sign-On. The role for each SSO user is determined in this order:
- Explicit assignment. If an admin has assigned a role via the Users tab, it takes precedence and is not re-evaluated on future logins.
- Group mapping. If the user's IdP groups match a configured mapping (see below), the matching role is assigned.
- Default role. Falls back to the
RBAC_DEFAULT_ROLEenvironment variable (default:Analyst).
Configuring SSO Group Mappings
Group mappings automatically assign roles to SSO users based on their IdP group membership, eliminating the need to manually assign roles to each user.
To add a group mapping:
- Go to Settings → SSO Configuration.
- Click Add Group Mapping.
- Fill in:
- SSO Group Name. Must match exactly how the group appears in your IdP's token claim.
- Role. The PuppyGraph role to assign to members of this group.
- Priority. Lower number = higher precedence, used when a user belongs to multiple mapped groups.
- Click Add.
For example: if a user belongs to both data-engineering (mapped to GraphAdmin, priority 10) and all-employees (mapped to Viewer, priority 100), they get GraphAdmin because 10 < 100.
To edit a mapping, click the edit icon on any row. You can change the Role and Priority, but not the Group Name. To delete a mapping, click the delete icon on the row.
Tip
Set SSO_GROUPS_CLAIM to the name of the claim your IdP uses for group memberships (default: groups).
Service Accounts
Service accounts provide programmatic access for scripts, pipelines, and applications. They authenticate using a name and secret instead of a password.
Creating a Service Account
- Go to Settings → Service Accounts.
- Click Create Service Account.
- Fill in:
- Name. Alphanumeric, hyphens, and underscores only; no colons.
- Role. The role this service account will have.
- Description. Optional, for identification.
- Allow user impersonation. Enables acting as a specific user (see Impersonation).
- Click Create. A secret is shown; copy it now, because it is shown only once.
Using a service account:
# REST API
curl -u 'etl-pipeline:<secret>' http://localhost:8081/schemajson
# Gremlin (WebSocket): use full sa: prefix
username: sa:etl-pipeline
password: <secret>
# Bolt (Neo4j driver): use full sa: prefix
auth=('sa:etl-pipeline', '<secret>')
Managing Service Accounts
From Settings → Service Accounts:
- Change role: use the Role dropdown in the service account's row.
- Toggle impersonation: use the switch in the Impersonation column.
- Rotate secret: click the refresh icon, then confirm to generate a new secret (shown once; old secret is immediately invalidated).
- Delete: click the delete icon.
The Last Used column shows when the service account last authenticated.
Impersonation
A service account with impersonation enabled can act as a specific user, inheriting that user's role and permissions. This is useful for multi-tenant applications where a single service account serves many users.
Constraints:
- The service account must have
Allow user impersonationenabled. - The target user must exist, unless it is an
sso:identity andRBAC_IMPERSONATION_MISSING_USERisjitorpassthrough. SSO users get a record on their first browser login; for users who have never signed in, see Impersonating SSO users who have never signed in. - The target user's role must be equal to or lower than the service account's role (prevents privilege escalation).
- Impersonation is recorded in the audit log with both identities.
Impersonation needs authentication turned on for the protocol it arrives through. With Bolt authentication off, the Bolt server refuses any session that sets impersonated_user, with Neo.ClientError.Security.Forbidden: impersonation requires Bolt authentication to be enabled. Gremlin validates impersonation only when its authentication is turned on. See Prerequisites for headless access.
Using impersonation:
# Bolt (Neo4j Python driver)
with driver.session(impersonated_user='sso:alice@example.com') as session:
result = session.run('MATCH (n) RETURN n LIMIT 10')
# Gremlin (WebSocket): fixed per connection. The header is honored only with a
# bearer token, so log in as the service account first (POST /login, field Token).
token = requests.post('http://puppygraph-host:8081/login',
data={'username': 'sa:dashboard-app', 'password': '<secret>'}).json()['Token']
c = client.Client(
'ws://puppygraph-host:8182/gremlin', 'g',
headers={'Authorization': f'Bearer {token}',
'X-PuppyGraph-Assume-User': 'sso:alice@example.com'}
)
# REST API
curl -u 'dashboard-app:<secret>' \
-H 'X-PuppyGraph-Assume-User: sso:alice@example.com' \
http://localhost:8081/submitCypher -d '{"query": "MATCH (n) RETURN n"}'
Prerequisites for headless access
An application that queries on behalf of users, without those users signing in to the Web UI, needs these settings in addition to RBAC. Set them to the same value on every node (control plane, leaders, and compute):
| Environment Variable | Value | Why it is needed |
|---|---|---|
RBAC_ENABLED |
true (default) |
Impersonation is an RBAC feature. |
BOLTSERVER_AUTHENTICATION_ENABLED |
true |
With Bolt authentication off, the Bolt server does not authenticate the connection and refuses any session that sets impersonated_user (impersonation requires Bolt authentication to be enabled). Never turn Bolt authentication off while Gremlin authentication is on: queries over an unauthenticated Bolt connection run without any user's permissions applied. |
GREMLINSERVER_AUTHENTICATION_ENABLED |
true |
Without it, the Gremlin WebSocket authenticates no one, so queries carry no user identity and an impersonation-enabled catalog refuses them. Gremlin reads X-PuppyGraph-Assume-User only from a connection that presents a bearer token. The control plane reads both authentication settings too, to decide whether internal calls that carry no user are allowed, so set them there as well. |
AUTHENTICATION_JWT_SECRETKEY |
A random value of at least 32 bytes | Signs the tokens that carry the service account and impersonated identity from the control plane to the query path. When unset, the key is derived from CLUSTER_SECURITYKEY, whose default (puppygraph) is public, so anyone could mint a token for any user. Every node must use the same value. Change CLUSTER_SECURITYKEY from its default as well: it also authenticates traffic between nodes, and setting AUTHENTICATION_JWT_SECRETKEY does not change it. |
The REST API on port 8081 always authenticates and needs no extra switch.
Impersonating SSO users who have never signed in
An SSO user record is normally created the first time the user signs in through the browser. A headless application may need to act for a user who never has. There are two ways to allow it.
Pre-provision the user. An Admin or UserAdmin creates the record ahead of time, with an explicit role and no password:
curl -u 'puppygraph:<password>' -X POST http://localhost:8081/api/users \
-H 'Content-Type: application/json' \
-d '{"username": "alice@example.com", "role": "Analyst", "type": "sso"}'
This creates sso:alice@example.com. When the user later signs in through the IdP, the explicit role is kept and group mappings are not applied.
Or accept missing users at impersonation time with RBAC_IMPERSONATION_MISSING_USER. Set it on the control plane, which resolves every impersonation request, including those arriving over Bolt and Gremlin.
| Value | Behavior when the target sso: user has no record |
|---|---|
reject (default) |
The request is refused with target user not found. |
jit |
A user record is created with the role in RBAC_DEFAULT_ROLE, and the request proceeds as that user. The creation is recorded in the audit log as user_jit_provisioned, with the service account as the actor. The record then appears under Settings → Users. Because its role came from the default, group mappings are applied if the user later signs in through the browser. |
passthrough |
The request proceeds as the target identity with the role in RBAC_DEFAULT_ROLE, and no record is created. Nothing is stored, so the identity never appears under Settings → Users, and the same resolution happens on every request. The impersonation itself is still recorded in the audit log. |
Rules that apply in every mode:
- The value is case-insensitive. An unrecognized value is logged as a warning and treated as
reject. - Only identities that start with
sso:are covered. A missing target with any other prefix, such aslocal:, is always refused. - An existing record always takes precedence. A pre-provisioned or previously signed-in user keeps their stored role in
jitandpassthroughmode. - The service account's role must be equal to or higher than
RBAC_DEFAULT_ROLE, because that is the role a missing user receives. Otherwise the request is refused withtarget user not foundrather than with a role error. Over Bolt the reason is not returned, and a refused target readsimpersonation denied for requested user. - Group mappings are not evaluated for headless requests, because no IdP token is involved. To give a headless user a role other than the default, pre-provision them or assign the role in Settings → Users.
RBAC_DEFAULT_ROLEmust allow queries (Analystor higher) for missing users to run queries.
passthrough is meant for deployments where row access is enforced by the data source or by PuppyGraph row-level security, not by PuppyGraph user records: Snowflake query impersonation or row-level security. Any identity the service account names is accepted, so pair it with SECURITY_REQUIRE_ENFORCEMENT=true. That way a graph with a data source outside those enforcement layers is refused instead of exposing its rows to every identity at the default role.
Mandatory Enforcement Gate
The gate is available in PuppyGraph 1.2.0 and later. The coverage rules and error messages on this page apply to 1.11.0 and later. Earlier versions count only Snowflake query impersonation as coverage.
SECURITY_REQUIRE_ENFORCEMENT=true guarantees that no graph query reads a data source that is not covered by an enforcement layer. Use it when access control is delegated to Snowflake query impersonation or row-level security, especially with RBAC_IMPERSONATION_MISSING_USER=passthrough.
| Value | Behavior |
|---|---|
false (default) |
No check. Data sources without an enforcement layer are readable by every user whose role can run queries. |
true |
Every data source in the graph schema must be covered, or the schema is rejected and queries are refused. |
Set it to exactly true or false, with the same value on every node. The control plane checks schema uploads, and the leaders check schemas and queries, so a node that is missing the setting does not enforce it. The two parse the value differently: a value such as 1 turns the gate on for the control plane but off for the leaders.
An external data source counts as covered when either of these holds. A local data source never counts as covered:
- Its catalog has identity propagation configured, in any mode the catalog type supports (for example Snowflake query impersonation in mode
proxyUserorsessionClaims). - Row-level security is enabled (
rowLevelSecurity.enabled: true) and atableSecurityFilterwith at least one filter names that exact catalog, schema, and table. Enabling row-level security without a filter for the table does not cover it.
The gate checks PuppyGraph's configuration, not the data source's. When a catalog has identity propagation, every table in it counts as covered, whether or not the source has a policy on that table. A Snowflake table without a row access policy is readable in full by every impersonated identity, so add a policy with each new table.
With the gate on, PuppyGraph refuses the following:
- A schema upload that leaves any data source uncovered. The error begins
Mandatory enforcement gate (SECURITY_REQUIRE_ENFORCEMENT) is enabled but the following data sources have neither identity propagation on their catalog nor a row-level security filter:and lists every uncovered source, for examplenode "Person": snowflake_prod.PUBLIC.PERSON. - Any query against a graph that was loaded before the gate was turned on and has an uncovered source. It fails with the same list of uncovered nodes and edges. The whole graph is refused, not only the scans of the uncovered source, and this applies to every user,
Adminincluded. - Local data sources, always, because a local copy is read without the source's own access control.
It also checks the following, which are easy to miss:
- Each input of a union data source is checked separately.
- Disabled data sources (
enabled: false) are checked too, since they can be switched on later without another upload.
It does not affect:
- Catalog management and metadata or schema discovery (listing tables and columns). These use the catalog's configured service account rather than a user's identity.
- Anything outside the graph schema, such as user and service account management.
Audit Log
The audit log records all RBAC-related actions.
- Go to Settings → Audit Log
- The table shows Timestamp, Actor (who performed the action), Action, and Target user
- Click the expand icon on any row to see the full detail (e.g., role before and after a change)
- Click Load More to fetch older entries
Recorded actions include: user creation, role changes, password resets, service account operations, group mapping changes, and access denials.
Configuration
| Environment Variable | Default | Description |
|---|---|---|
RBAC_ENABLED |
true |
Set to false to disable RBAC (all authenticated users treated as Admin) |
RBAC_DEFAULT_ROLE |
Analyst |
Default role for new SSO users who don't match any group mapping, and for headless users accepted by jit or passthrough |
RBAC_IMPERSONATION_MISSING_USER |
reject |
What impersonation does when the target sso: user has no record: reject, jit, or passthrough. See Impersonating SSO users who have never signed in |
SECURITY_REQUIRE_ENFORCEMENT |
false |
When true, refuse schemas and queries with a local data source, or an external one covered by neither identity propagation nor a row-level security filter. See Mandatory Enforcement Gate |
PUPPYGRAPH_USERNAME |
puppygraph |
Bootstrap admin username. Always Admin; cannot be changed via API |
PUPPYGRAPH_PASSWORD |
puppygraph123 |
Bootstrap admin password. For production deployments, set this to a strong secret and rotate it periodically; do not rely on the default value. |
SSO_GROUPS_CLAIM |
groups |
Name of the IdP token claim that contains the user's group memberships |