Guard
Introduction
A guard in access-gate serves two primary responsibilities:
- Providing a Decision:
- Guards can either allow, deny, or remain undecided about an action.
- Providing Dependencies:
- Guards can inject dependencies that are accessible during access evaluation.
Guards are a powerful mechanism for implementing dynamic, context-aware access control logic.
Core Concepts
Order Matters:
- Guards are executed in the order they are added.
- As soon as a guard provides a definitive decision (
true
orfalse
), execution stops, and the decision becomes final.
Sync vs. Async Guards:
- Synchronous guards are applied first.
- If no decision is reached, asynchronous guards are executed.
Global vs. Lazy Guards:
Global Guards:
- Added at the Gate level.
- Applied during the
build()
orbuildAsync()
method execution.
Lazy Guards:
- Evaluated during every
can()
orcould()
method call. - Includes both
lazyGuards
andasyncLazyGuards
.
Guard Execution Flow:
During build()
:
- Only global guards (sync and async) are evaluated.
During can()
or could()
:
- Lazy guards execute in the following order:
- Global
Gate
lazy guards. Policy
-specific lazy guards.- Global and policy-specific async lazy guards (only in
could()
).
- Global
Policy-Specific Guards:
- Guards can be attached to a specific
Policy
. - These are executed during
access()
orasyncAccess()
calls on the Representative. - Policy lazy guards are merged with the global Gate lazy guards for evaluation in
can()
andcould()
.
Using Guards
Global Guards
Add guards at the Gate level:
const gate = new Gate();
gate.guard((representative) => {
if (representative.isAdmin) return true; // Grant access
return undefined; // No decision
});
gate.asyncGuard(async (representative) => {
if (!representative.isActive) return false; // Deny access
return undefined; // No decision
});
Lazy Guards
Lazy guards are evaluated during can()
and could()
calls:
gate.lazyGuard((representative, entity) => {
if (entity.status === "draft") return false; // Deny access to drafts
return undefined; // No decision
});
gate.asyncLazyGuard(async (representative, entity) => {
const isVerified = await checkVerificationStatus(representative.id);
return isVerified ? undefined : false; // Deny if not verified
});
Policy-Specific Guards
Attach guards to specific policies:
const userPolicy = new Policy("user");
userPolicy.guard((representative) => {
if (representative.role === "editor") return true;
return undefined;
});
userPolicy.lazyGuard((representative, entity) => {
return representative.id === entity.ownerId ? true : undefined;
});
userPolicy.asyncLazyGuard(async (representative, entity) => {
const isTrusted = await isTrustedUser(representative.id);
return isTrusted ? undefined : false;
});
Guard Evaluation Example
const representative = { id: 1, role: "admin", isActive: true };
const post = { id: 101, status: "published", ownerId: 1 };
const gate = new Gate();
const postPolicy = new Policy("post");
postPolicy.lazyGuard((rep, entity) => entity.ownerId === rep.id);
gate.addPolicy(postPolicy);
gate.lazyGuard((rep) => (rep.role === "guest" ? false : undefined));
// Build the representative
const rep = gate.build(representative);
// Access evaluation
const access = rep.access("post", "view");
// Sync evaluation
console.log(access.can(post)); // true
// Async evaluation
console.log(await access.could(post)); // true
API
Guard
Guard<T = unknown, D = unknown> = (
representative: T,
provide: (name: string, dependency: D) => void
) => boolean | undefined;
LazyGuard
LazyGuard<T = unknown, E = unknown, D = unknown> = (
representative: T,
entity: E,
provide: (name: string, dependency: D) => void
) => boolean | undefined;
AsyncGuard
AsyncGuard<T = unknown, D = unknown> = (
representative: T,
provide: (name: string, dependency: D) => void
) => Promise<boolean | undefined>;
AsyncLazyGuard
AsyncLazyGuard<T = unknown, E = unknown, D = unknown> = (
representative: T,
entity: E,
provide: (name: string, dependency: D) => void
) => Promise<boolean | undefined>;
Next Steps
For more details about specific components, refer to:
- Policy: Learn about policy-specific guard behaviors.
- Representative: Understand how guards are evaluated during access.
- Decision: Explore how decisions are constructed based on guard outputs.
On This Page
- Introduction
- Core Concepts
- Order Matters:
- Sync vs. Async Guards:
- Global vs. Lazy Guards:
- Global Guards:
- Lazy Guards:
- Guard Execution Flow:
- During build():
- During can() or could():
- Policy-Specific Guards:
- Using Guards
- Global Guards
- Lazy Guards
- Policy-Specific Guards
- Guard Evaluation Example
- API
- Guard
- LazyGuard
- AsyncGuard
- AsyncLazyGuard
- Next Steps