--- name: neo4j-security-skill description: Programmatic security management in Neo4j — RBAC/ABAC, user lifecycle (CREATE/ALTER/DROP USER), role lifecycle (CREATE/GRANT ROLE/DROP ROLE), privilege grants and denies (GRANT/DENY/REVOKE on graph, database, DBMS), property-level access control, sub-graph access control, SHOW PRIVILEGES inspection, and auth provider config reference (LDAP, OIDC/SSO). Use when an agent needs to manage users, roles, or privileges programmatically via Cypher on the system database. Does NOT handle Cypher query writing — use neo4j-cypher-skill. Does NOT handle cluster ops or backups — use neo4j-cli-tools-skill. Property-level security and ABAC require Enterprise Edition. allowed-tools: Bash WebFetch version: 1.0.1 --- ## When to Use - Creating, altering, suspending, or dropping users - Creating roles, granting/revoking role membership - Granting/denying/revoking graph, database, or DBMS privileges - Inspecting current privileges (`SHOW PRIVILEGES`) - Implementing property-level access control (read/write per property) - Setting up ABAC rules against OIDC claims - Referencing LDAP/SSO auth provider configuration ## When NOT to Use - **Writing Cypher queries against application data** → `neo4j-cypher-skill` - **Cluster ops, backups, server config** → `neo4j-cli-tools-skill` - **Driver connection setup** → `neo4j-driver-*-skill` --- ## MCP Write Gate — MANDATORY Before executing ANY of the following, show the planned command and wait for explicit confirmation: - `CREATE USER` / `ALTER USER` / `DROP USER` - `CREATE ROLE` / `DROP ROLE` - `GRANT` / `DENY` / `REVOKE` (any privilege) - `CREATE AUTH RULE` / `DROP AUTH RULE` Never auto-execute privilege changes. Show exact Cypher, annotate impact, get "yes". --- ## Execution Context All security Cypher runs against the **system** database: ```cypher // Neo4j auto-routes CREATE/ALTER/SHOW USER|ROLE|PRIVILEGE to system // If using cypher-shell: cypher-shell -d system // If using driver: use database="system" ``` --- ## 1. User Management ### Create user ```cypher CREATE USER alice SET PASSWORD 'secret' CHANGE NOT REQUIRED; // CHANGE REQUIRED (default): forces password change on first login // CHANGE NOT REQUIRED: password valid immediately // SET STATUS ACTIVE (default) | SUSPENDED ``` ### Parameterised password (preferred in scripts) ```cypher CREATE USER $username SET PASSWORD $password CHANGE NOT REQUIRED; ``` ### Alter user ```cypher ALTER USER alice SET PASSWORD $newPw CHANGE NOT REQUIRED; ALTER USER alice SET STATUS SUSPENDED; // lock account ALTER USER alice SET STATUS ACTIVE; // unlock ALTER USER alice SET HOME DATABASE mydb; // default db on connect ALTER USER alice IF EXISTS SET PASSWORD $pw; // safe if missing ``` ### Show users ```cypher SHOW USERS YIELD username, roles, passwordChangeRequired, suspended, homeDatabase WHERE suspended = false RETURN username, roles ORDER BY username; ``` ### Drop user ```cypher DROP USER alice IF EXISTS; ``` --- ## 2. Role Management ### Create / drop role ```cypher CREATE ROLE analyst; CREATE ROLE analyst IF NOT EXISTS; DROP ROLE analyst IF EXISTS; ``` ### Assign / remove roles ```cypher GRANT ROLE analyst TO alice; GRANT ROLE analyst, writer TO alice, bob; // bulk REVOKE ROLE analyst FROM alice; ``` ### Inspect roles ```cypher SHOW ROLES YIELD role, member ORDER BY role; SHOW ROLE analyst PRIVILEGES AS COMMANDS; // returns runnable GRANT commands SHOW POPULATED ROLES YIELD role; // only roles with members ``` --- ## 3. Privilege Decision Table | Goal | Command | |---|---| | Allow db connection | `GRANT ACCESS ON DATABASE mydb TO analyst` | | Read all graph data | `GRANT MATCH {*} ON GRAPH mydb ELEMENTS * TO analyst` | | Read specific label | `GRANT MATCH {*} ON GRAPH mydb NODES Person TO analyst` | | Read specific rel type | `GRANT MATCH {*} ON GRAPH mydb RELATIONSHIPS KNOWS TO analyst` | | Read one property | `GRANT READ {email} ON GRAPH mydb NODES Person TO analyst` | | Traverse but hide properties | `GRANT TRAVERSE ON GRAPH mydb NODES Person TO analyst` | | Write (create/set) | `GRANT WRITE ON GRAPH mydb TO writer` | | Create nodes only | `GRANT CREATE ON GRAPH mydb NODES Person TO writer` | | Delete nodes only | `GRANT DELETE ON GRAPH mydb NODES Person TO writer` | | Execute procedure | `GRANT EXECUTE PROCEDURE apoc.* TO analyst` | | Execute function | `GRANT EXECUTE USER DEFINED FUNCTION apoc.* TO analyst` | | All on one db | `GRANT ALL ON DATABASE mydb TO dba` | | Full DBMS admin | `GRANT ALL ON DBMS TO dba` | | Manage users | `GRANT USER MANAGEMENT ON DBMS TO secadmin` | | Manage roles | `GRANT ROLE MANAGEMENT ON DBMS TO secadmin` | | Schema changes | `GRANT CREATE ELEMENT TYPES ON DATABASE mydb TO schemaadmin` | ### DENY overrides GRANT ```cypher // Analyst can read Person but NOT the ssn property GRANT MATCH {*} ON GRAPH mydb NODES Person TO analyst; DENY READ {ssn} ON GRAPH mydb NODES Person TO analyst; ``` ### REVOKE removes a specific grant or deny ```cypher REVOKE GRANT READ {email} ON GRAPH mydb NODES Person FROM analyst; REVOKE DENY READ {ssn} ON GRAPH mydb NODES Person FROM analyst; REVOKE MATCH {*} ON GRAPH mydb NODES Person FROM analyst; // removes both grant+deny ``` --- ## 4. Common Role Patterns ### Read-only analyst ```cypher CREATE ROLE analyst IF NOT EXISTS; GRANT ACCESS ON DATABASE mydb TO analyst; GRANT MATCH {*} ON GRAPH mydb ELEMENTS * TO analyst; GRANT EXECUTE PROCEDURE apoc.* TO analyst; ``` ### Write role (no admin) ```cypher CREATE ROLE writer IF NOT EXISTS; GRANT ACCESS ON DATABASE mydb TO writer; GRANT MATCH {*} ON GRAPH mydb ELEMENTS * TO writer; GRANT WRITE ON GRAPH mydb TO writer; ``` ### Read-only on specific labels only ```cypher CREATE ROLE limited_reader IF NOT EXISTS; GRANT ACCESS ON DATABASE mydb TO limited_reader; GRANT TRAVERSE ON GRAPH mydb ELEMENTS * TO limited_reader; // can traverse GRANT MATCH {*} ON GRAPH mydb NODES Person TO limited_reader; // Person props visible GRANT MATCH {*} ON GRAPH mydb NODES Company TO limited_reader; // Company props visible // Other labels: traversable but properties invisible ``` ### DBA role (full admin) ```cypher CREATE ROLE dba IF NOT EXISTS; GRANT ALL ON DBMS TO dba; GRANT ALL ON DATABASE * TO dba; ``` --- ## 5. Property-Level Access Control (Enterprise) Restrict read access to individual properties: ```cypher // Grant read on all Person props, then deny sensitive ones GRANT MATCH {*} ON GRAPH mydb NODES Person TO analyst; DENY READ {ssn, dateOfBirth} ON GRAPH mydb NODES Person TO analyst; ``` Property-based pattern matching (sub-graph access): ```cypher // Only see Person nodes where classification = 'public' GRANT MATCH {*} ON GRAPH mydb FOR (n:Person) WHERE n.classification = 'public' TO analyst; // Block access to classified nodes DENY MATCH {*} ON GRAPH mydb FOR (n) WHERE n.classification <> 'UNCLASSIFIED' TO regularUsers; ``` **Constraints:** - `FOR` pattern applies to read privileges only — not write - Each property-based privilege restricted by a single property - Performance overhead scales with number of rules; `TRAVERSE` rules cost more than `READ` - Ensure the property used for rules cannot be modified by the restricted role --- ## 6. ABAC — Attribute-Based Access Control (Enterprise + OIDC) ABAC grants roles dynamically from JWT/OIDC claims rather than explicit `GRANT ROLE ... TO user`. ### Prerequisites ``` # neo4j.conf dbms.security.abac.authorization_providers= ``` ### Create auth rule ```cypher CREATE AUTH RULE salesRule SET CONDITION abac.oidc.user_attribute('department') = 'sales'; GRANT ROLE analyst TO AUTH RULE salesRule; ``` ### Compound conditions ```cypher CREATE OR REPLACE AUTH RULE seniorRule SET CONDITION abac.oidc.user_attribute('department') = 'engineering' AND abac.oidc.user_attribute('level') >= 5; GRANT ROLE senior_engineer TO AUTH RULE seniorRule; ``` ### Manage auth rules ```cypher SHOW AUTH RULES YIELD ruleName, condition, roles; ALTER AUTH RULE salesRule SET ENABLED false; // disable without dropping RENAME AUTH RULE salesRule TO salesDeptRule; DROP AUTH RULE salesDeptRule; REVOKE ROLE analyst FROM AUTH RULE salesRule; ``` Native users [2026.06+]: tag native DB users and match tags in rules via `abac.native.user_tags()` — no OIDC required: ```cypher CREATE AUTH RULE nativeSalesRule SET CONDITION 'sales' IN abac.native.user_tags(); GRANT ROLE analyst TO AUTH RULE nativeSalesRule; ``` **Notes:** - Missing claims evaluate to NULL → rule condition false → role not granted - Rules apply immediately to existing sessions when claims are already loaded - OIDC claims via `abac.oidc.user_attribute()`; native user tags via `abac.native.user_tags()` [2026.06+]; LDAP not supported - User-defined functions rejected in PBAC property-rule predicates [2026.06+] --- ## 7. SHOW PRIVILEGES Patterns ```cypher // All privileges in the system SHOW PRIVILEGES YIELD *; // Privileges for a specific user (as runnable commands) SHOW USER alice PRIVILEGES AS COMMANDS; // Privileges for a specific role SHOW ROLE analyst PRIVILEGES YIELD privilege, action, resource, graph, segment; // Find who has access to a database SHOW PRIVILEGES YIELD * WHERE graph = 'mydb' RETURN role, action, resource, segment ORDER BY role; // Find all DENY rules SHOW PRIVILEGES YIELD * WHERE access = 'DENIED' RETURN role, action, resource, segment; ``` --- ## 8. Built-in Roles (do not drop) | Role | Scope | |---|---| | `admin` | Full DBMS + all databases | | `architect` | Schema changes + write on all databases | | `publisher` | Write on all databases | | `editor` | Write excluding schema changes | | `reader` | Read-only on all databases | | `public` | All users implicitly; default home database access | Assign built-in roles: `GRANT ROLE reader TO alice;` --- ## 9. Auth Provider Config Reference (operational — not Cypher) ### Native (default) ``` dbms.security.auth_enabled=true dbms.security.auth_max_failed_attempts=3 # lockout threshold ``` ### LDAP ``` dbms.security.auth_provider=ldap dbms.security.ldap.host=ldap://ldap.example.com dbms.security.ldap.authentication.mechanism=simple dbms.security.ldap.authentication.user_dn_template=uid={0},ou=users,dc=example,dc=com dbms.security.ldap.authorization.group_membership_attributes=memberOf dbms.security.ldap.authorization.group_to_role_mapping=\ "cn=analysts,ou=groups,dc=example,dc=com" = analyst;\ "cn=admins,ou=groups,dc=example,dc=com" = admin ``` ### OIDC / SSO (Okta, Auth0, Entra ID) ``` dbms.security.oidc..display_name=Okta dbms.security.oidc..auth_flow=pkce dbms.security.oidc..well_known_discovery_uri=https://example.okta.com/.well-known/openid-configuration dbms.security.oidc..audience=neo4j dbms.security.oidc..claims.username=email dbms.security.oidc..claims.groups=groups dbms.security.oidc..authorization.group_to_role_mapping=\ "neo4j-analysts" = analyst;\ "neo4j-admins" = admin ``` Config changes require **server restart**. Roles referenced in mappings must exist in Neo4j (native or created via Cypher). --- ## Checklist — New Role Setup - [ ] Determine required operations: read / write / admin - [ ] Identify target database(s) and graph scope (all labels vs specific) - [ ] Identify any properties that must be hidden (→ DENY READ) - [ ] Create role: `CREATE ROLE ... IF NOT EXISTS` - [ ] Grant ACCESS on database - [ ] Grant MATCH / TRAVERSE / WRITE as needed - [ ] Apply DENY for restricted properties - [ ] Run `SHOW ROLE ... PRIVILEGES AS COMMANDS` to verify - [ ] Assign to users: `GRANT ROLE ... TO ...` - [ ] Test with `SHOW USER ... PRIVILEGES AS COMMANDS` Full privilege syntax → [references/privilege-reference.md](references/privilege-reference.md)