DocsServer features
Authorization
Define application-owned abilities and typed resource policies, then enforce them after authentication and before protected work.
Identity is not permission
| Question | Rahti mechanism |
|---|---|
| Who may load this page? | private_routes in authentication settings. |
| Who may call this RPC? | #[rpc(auth)]. |
| May this session perform the action? | A named authorization ability. |
| May it affect this resource? | A typed resource policy. |
Rahti does not prescribe roles or permission tables. Your application defines what stable names such as posts.update mean.
Define and install the gate
pub fn authorization() -> rahti::authorization::Gate {
let mut gate = rahti::authorization::Gate::new();
gate.define::<SessionUser, _>("reports.view", |user| {
user.can_view_reports
});
gate.define_resource::<SessionUser, PostPolicy, _>(
"posts.update",
|user, post| user.id == post.owner_id,
);
gate
}Install it once during initialize_application, after auth and before routes::router(). Empty, malformed, duplicate, or undefined ability names fail closed. Policies are synchronous and deterministic; database or network work belongs in the async handler.
Enforce inside the handler
#[rpc(auth)]
pub async fn update_post(id: i32, title: String) -> rahti::Result<Post> {
let post = load_post(id).await?;
rahti::authorization::authorize_resource(
"posts.update",
&PostPolicy { owner_id: post.owner_id },
)?;
update(post, title).await
}No session receives 401 from #[rpc(auth)]. A signed-in session rejected by the policy receives 403. Take caller identity from the signed session, never from a submitted user_id.
Test the boundary
Drive the real router and test unauthenticated, authenticated-but-denied, and allowed paths. For ownership rules, use two different users so a successful self-update does not conceal an insecure resource-id change.
