Skip to content

Directory group → role mapping

Directory group → role mapping

The bridge maps a SCIM/directory group onto a Spatie role: everyone in the group holds the role, and the grant is kept in sync as membership changes.

use Cbox\Id\AccessControl\Contracts\GroupRoleMappings;

$mappings = app(GroupRoleMappings::class);   // SpatieGroupRoleMappings

// Everyone in the "Engineering" directory group holds the "engineer" role in org_1.
$mappings->map($organizationId, $groupId, $roleId);

// Later — remove the mapping; derived grants are revoked.
$mappings->unmap($organizationId, $groupId, $roleId);

map() takes effect immediately for everyone already in the group. After that, reconciliation runs automatically: the package listens for the platform's directory.group.membership_changed domain event (emitted by SCIM create/replace/patch/ delete) and reconciles the affected group. You can also drive it by hand with reconcileUser() or reconcileGroup().

What reconciliation does

For a subject, it compares two sets:

  • the roles they should hold — from the mappings of every directory group they are an active member of, within the organization;
  • the roles they currently hold through the bridge.

It grants the difference and revokes the reverse. Membership is read from the platform's directory tables, and an inactive directory user is treated as a non-member.

Manual grants are never reconciled away

This is the property that makes the bridge safe to run against a live Spatie install.

Spatie stores a role assignment with no notion of why it exists, so the bridge keeps its own ledger (id_spatie_pushed_assignments) of the grants it made. Reconciliation only ever revokes what is in that ledger:

Situation Result
Role granted by the bridge, subject leaves the group Revoked
Role granted by an admin, subject leaves the group Kept
Role granted by an admin, later also pushed, then push revoked Kept (the ledger's pre_existing flag)
Role granted by an admin, admin revokes it Revoked

Because of the ledger, assignmentsForSubject() and assignmentsInOrganization() also report an accurate GrantSourcepushed for a directory-derived grant, manual otherwise — which is what access-governance campaigns display.

Storage

Two tables ship with this package, created by its own migration (the platform's equivalents are gated off under the external driver):

  • id_spatie_group_role_mappings — organization, group, role, priority.
  • id_spatie_pushed_assignments — the ledger described above.

priority is stored and orders forOrganization(); a role is granted whenever any mapped group matches.