Policies
Policies encapsulate resource-specific authorization rules. They are ordinary classes whose methods receive the current user and the resource in question, and return (or resolve) a boolean.
Defining A Policy
export default class PostPolicy {
async view(user, post) {
return post.status === 'published' || post.userId === user.id;
}
async update(user, post) {
return user.id === post.userId;
}
async delete(user, post) {
return user.hasRole('admin');
}
}
Each method name maps to an ability. When authorization is asked for update against a Post, the registered policy's update method is called.
Registering Policies
import { registerPolicy } from '@admicaa/netpress-permissions';
import Post from '../models/Post.js';
import PostPolicy from './policies/PostPolicy.js';
registerPolicy(Post, PostPolicy);
You can also register a bulk map:
import { registerPolicies } from '@admicaa/netpress-permissions';
registerPolicies({
Post: PostPolicy,
Comment: CommentPolicy,
});
The key can be a class, a class name, or any string the framework uses to identify the resource (for example, a morph alias).
Where To Register
The tidiest place is a dedicated service provider:
import { ServiceProvider } from '@admicaa/netpress';
import { registerPolicy } from '@admicaa/netpress-permissions';
import Post from '../models/Post.js';
import PostPolicy from './policies/PostPolicy.js';
export default class AuthServiceProvider extends ServiceProvider {
async boot() {
registerPolicy(Post, PostPolicy);
}
}
Providers run at application boot so the policies are available before any request reaches a controller.
Consuming Policies
Anywhere can() is called with a resource, policies are consulted first:
import { can } from '@admicaa/netpress-permissions';
await user.can('update', post); // via HasPermissions trait
await can(user, 'update', post); // core helper
await AuthGuard.can('update', 'id'); // route middleware
If the method is missing from the policy, authorization falls through to the user's role/permission graph, letting you mix policies and permissions in the same app without ceremony.
Returning Reasons
Policies can throw or return enriched objects, but authorization only inspects their truthy/falsy status. If you need to surface reasons for failure, throw a custom exception from inside the policy and catch it in your controller.
Inspecting Registered Policies
import { listPolicies, resolvePolicy } from '@admicaa/netpress-permissions';
listPolicies(); // [[resource, PolicyClass], ...]
resolvePolicy(Post); // PolicyClass or null
Both methods are useful in dev-only dashboards and tests.
Resetting Policies
resetPolicies() clears the registered policy map. Call it in test teardown when you need to isolate policy state between suites.