Repository navigation
Public PHP API
Imports used below:
use Illuminate\Database\Eloquent\Model;
use Moztopia\Access\Traits\HasAccess;
use Moztopia\Access\Models\AccessPermission;
use Moztopia\Access\Models\AccessRole;
use Moztopia\Access\Models\AccessTeam;
use Moztopia\Access\Services\AccessService;The user mutation API is assignRole(), assignPermission(), assignTeam(), revokeRole(), revokePermission() and revokeTeam(). There are no alternate mutation spellings. See the recorded naming decision.
assignTeam() adds membership only through the same service rules as addMember(). revokeTeam() delegates to removeMember(): it removes membership and its scoped assignments, preserves the team and unrelated grants, and is a safe no-op for absent membership. An unknown team still fails clearly. It never assigns roles, grants permissions or creates an unknown team. A team parameter accepts an existing AccessTeam object or its exact stable name, never an arbitrary display label or an overloaded UUID string.
Add HasAccess to the configured host Eloquent authenticatable. The host owns authentication and UUID creation. Existing Laravel can, cannot and policy behavior is preserved. Resolve collisions with pre-existing methods such as teams() before using the trait.
$user->assignRole(string $name, AccessTeam|string|null $team = null): void;
$user->revokeRole(string $name, AccessTeam|string|null $team = null): void;
$user->hasRole(string $name, AccessTeam|string|null $team = null): bool;
$user->assignPermission(string $name, AccessTeam|string|null $team = null): void;
$user->revokePermission(string $name, AccessTeam|string|null $team = null): void;
$user->hasPermission(string $name, AccessTeam|string|null $team = null): bool;
$user->assignTeam(AccessTeam|string $team): void;
$user->revokeTeam(AccessTeam|string $team): void;
$user->hasTeam(AccessTeam|string $team): bool;
$user->teams(): BelongsToMany;These are signature descriptions; omit the parameter types and return annotations when making actual calls. teams() is a native Eloquent relationship: $user->teams()->paginate(25) performs a fresh query. Loaded $user->teams collections follow normal Eloquent caching and are not used by Access checks. Do not mutate membership through relationship attachment APIs; use the service to preserve locking and validation.
All mutation methods are trusted server-side operations. The receiver is the target user, not an actor. Grant methods require a known definition, persisted UUID user, and active membership for a team grant. Revocation affects exactly its supplied scope; revoking an absent known assignment is safe, even after membership removal. A supplied team must still exist. Unknown definitions during revocation are errors, not no-ops.
Checks without a team consider only global assignments. Team checks require membership and combine global plus exact-team assignments. hasRole checks role assignment only; hasPermission unions direct and inherited permissions. Unknown names return false. Invalid unsaved user/team handles fail closed; deleted persisted rows cannot contribute access because their grants/memberships cascade away. Database failures propagate rather than being silently treated as successful checks.
Resolve a service with app(AccessService::class). It keeps no mutable user or team state and may be reused safely. All Model $user parameters require an instance of the configured Eloquent authenticatable on the default connection.
$access->definePermission(string $name): AccessPermission;
$access->defineRole(string $name, array $permissions = []): AccessRole;
$access->setRolePermissions(string $name, array $permissions): void;
$access->createTeam(string $name, ?string $displayName = null): AccessTeam;
$access->updateTeam(AccessTeam|string $team, string $displayName): AccessTeam;
$access->addMember(AccessTeam|string $team, Model $user): void;
$access->removeMember(AccessTeam|string $team, Model $user): void;
$access->roles(Model $user, AccessTeam|string|null $team = null): Collection;
$access->effectivePermissions(Model $user, AccessTeam|string|null $team = null): Collection;
$access->deleteRole(string $name): void;
$access->deletePermission(string $name): void;
$access->deleteTeam(AccessTeam|string $team): void;definePermission returns the existing definition or creates it. defineRole returns/creates the role and atomically replaces its entire permission set; omitting the array clears its permissions. setRolePermissions requires an existing role and also replaces the whole set. Permission arrays are lists of existing permission names; duplicates are collapsed. Any unknown permission rolls back the entire change, including a newly created role.
Definition names contain 1–128 lowercase ASCII letters, digits, dots, underscores or hyphens, starting with a letter. Examples: editor, documents.edit. Names are exact and case-sensitive at the public API. Team lookup names use the same unique lowercase ASCII format. Team display names contain 1–128 characters and cannot be blank; labels need not be unique. createTeam defaults the display label to its lookup name if omitted; duplicate lookup names fail the database uniqueness constraint. updateTeam changes only the display label, preserving the lookup name and UUID. Definition lookup names cannot be renamed through the supported API in this version; create a replacement and migrate assignments deliberately.
Adding an existing member is idempotent; removing an absent member is safe. Removing membership permanently deletes that user's exact-team role and direct-permission grants. Team deletion removes all memberships and scoped assignments. Role deletion removes assigned roles and role-permission links, leaving direct permissions. Permission deletion removes direct grants and all role-permission links. Global grants are unaffected by team deletion. Hard-deleting a host user cascades their relationships; soft-deleting a host user does not remove membership/assignments and must be managed by the host's authentication and lifecycle rules.
Inspection methods return collections ordered by name: roles contain AccessRole models; effective permissions contain distinct AccessPermission models. They apply precisely the same scope semantics as boolean checks. Invalid scope handles return empty collections. They do not return provenance, introduce a second authorization framework or imply actor authorization.
The service exposes targeted user assignment/check operations:
$access->assignRole(Model $user, string $name, AccessTeam|string|null $team = null): void;
$access->revokeRole(Model $user, string $name, AccessTeam|string|null $team = null): void;
$access->hasRole(Model $user, string $name, AccessTeam|string|null $team = null): bool;
$access->assignPermission(Model $user, string $name, AccessTeam|string|null $team = null): void;
$access->revokePermission(Model $user, string $name, AccessTeam|string|null $team = null): void;
$access->hasPermission(Model $user, string $name, AccessTeam|string|null $team = null): bool;
$access->hasTeam(Model $user, AccessTeam|string $team): bool;Unknown known-format role/permission names in mutations throw DomainException with the kind and name. Missing active membership on a grant throws DomainException. Invalid names, user identity or connection topology throw InvalidArgumentException. Unknown named teams throw DomainException; malformed names throw InvalidArgumentException. Checks on unknown/malformed team names fail closed. Missing/deleted object-referenced team or user rows during locking throw Laravel's ModelNotFoundException. Validation happens transactionally before writes; SQL integrity/locking failures propagate as Laravel database exceptions when retries cannot resolve them.
AccessPermission, AccessRole and AccessTeam are final Eloquent models with UUIDv7 IDs and timestamps. Role/permission models allow mass assignment of name; teams also allow display_name. Their inherited query API can inspect definitions, for example AccessRole::query()->orderBy('name')->get(). Prefer AccessService for all writes. Service-provider methods and private service helpers are framework/internal implementation details, not additional management APIs.
The only configuration key is moztopia-access.user_model, defaulting to App\Models\User. Publish moztopia-access-config and set it before publishing/running migrations if the host differs. The provider-discovery class is Moztopia\Access\AccessServiceProvider. Tags: moztopia-access-config and moztopia-access-migrations. No automatic migration loading occurs. There are no cache, current-team, guard, bypass or automatic-route options.
Copyright © 2026 Moztopia · Source · Licensing status · Security