Skip to main content

sova_auth/
policy.rs

1//! Resource policies (Laravel-style abilities on a model).
2
3use crate::store::CurrentUser;
4use sova_core::{Error, Result};
5
6/// CRUD-ish abilities checked by [`Policy`].
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Ability {
9    View,
10    Create,
11    Update,
12    Delete,
13}
14
15/// Authorize actions against a resource type.
16pub trait Policy<R>: Send + Sync {
17    fn view(&self, user: &CurrentUser, r: &R) -> bool;
18    fn create(&self, _user: &CurrentUser) -> bool {
19        true
20    }
21    fn update(&self, user: &CurrentUser, r: &R) -> bool;
22    fn delete(&self, user: &CurrentUser, r: &R) -> bool;
23}
24
25/// Helpers on [`sova_core::Request`] via [`crate::AuthExt`].
26pub(crate) fn can_ability<P, R>(user: &CurrentUser, ability: Ability, resource: &R) -> bool
27where
28    P: Policy<R> + Default,
29{
30    let policy = P::default();
31    match ability {
32        Ability::View => policy.view(user, resource),
33        Ability::Create => policy.create(user),
34        Ability::Update => policy.update(user, resource),
35        Ability::Delete => policy.delete(user, resource),
36    }
37}
38
39pub(crate) fn authorize_ability<P, R>(
40    user: &CurrentUser,
41    ability: Ability,
42    resource: &R,
43) -> Result<()>
44where
45    P: Policy<R> + Default,
46{
47    if can_ability::<P, R>(user, ability, resource) {
48        Ok(())
49    } else {
50        Err(Error::Forbidden)
51    }
52}