Search the site
BACKEND
Write who may do what once, in a policy class.
Pages, backend functions and the admin all ask the same policy.
ON THIS PAGE
Write a policy class
Guard pages and functions
Check a permission in code
Register a rule without a class
Status
// lib/policies/order_policy.dart
import 'package:dartvel_core/dartvel.dart';
class const Order({required final String id, required final String ownerId});
@DVPolicy(Order)
class OrderPolicy {
// A route has no order to pass, so the resource is nullable.
bool view(DVSessionPrincipal? user, Order? order) => user != null;
bool update(DVSessionPrincipal? user, Order? order) =>
user != null && (order == null || order.ownerId == user.userId);
}Copy code to clipboard
@DVPolicy(Order) registers each method named after an action: viewAny, view, create, update, delete, restore, forceDelete, export or impersonate.
Each method takes the user and the resource. Make the resource nullable to answer for a route, which has none.
The class can live anywhere under lib and needs a constructor with no arguments.
Keep server policies free of Flutter
The backend loads a policy only when its file does not import Flutter. The generated barrel exports Flutter, so import dartvel_core in a policy the server enforces.
// lib/backend/functions/orders/[id].put.dart is served at PUT /api/orders/:id.
import 'package:dartvel_core/dartvel.dart';
@DVBackendFunction(
policy: 'Order.update',
mfa: DVMfa.recent(Duration(minutes: 15)),
)
Future<Map<String, Object?>> _updateOrder(
DVContext context, // injected, and never sent by the client
String id,
String status,
) async {
context.afterCommit(() => notifyCustomer(id));
return <String, Object?>{
'id': id,
'status': status,
'by': context.session?.userId,
};
}Copy code to clipboard
Functional
Class
@DVPage(title: 'Articles admin', policy: DVPolicies.viewAdmin)
Widget _articlesAdminPage(BuildContext context) => Article.Admin();Copy code to clipboard
policy: 'Order.update' asks OrderPolicy.update.
DVPolicies has shared names: viewAdmin, refund, manageBilling, exportData and impersonate.
A route whose policy no class answers stops the build.
final bool canEdit = await DV.Auth.authorization.canAction(
DV.Auth.currentUser,
'Order.update',
resource: order,
);Copy code to clipboard
An action nothing registered is denied. authorize(...) throws instead of returning false.
DV.Auth.authorization.register<DVAuthUser, Article>(
'Article.publish',
(DVAuthUser user, Article article) => user.email?.endsWith('@example.com') ?? false,
);Copy code to clipboard
A rule you register yourself wins over a declared policy for the same action.
Built
Spec section: Authorization
Implementation notes
A standalone Model.Form checks no policy.
The admin does not check view or viewAny yet.
FSL-1.1-MIT licensed. Built with Dartvel.
Dartvel is made by
To the bottom