Skip to main content

sofie_identity/role_grant/
factory.rs

1use sofie::http::{Permissions, is_permission};
2use sofie::prelude::*;
3
4use super::RoleGrant;
5use crate::role::RoleId;
6
7/// Idempotent: a grant `role` already holds is returned unchanged. Checks nothing of who
8/// grants, so it is for seeds and boot; a handler grants with `grant_as`.
9pub async fn grant(read: &Read, role: RoleId, permission: &str) -> Result<RoleGrant> {
10    refuse_unless_permission("permission", permission)?;
11    if let Some(held) = held(read, role, permission).await? {
12        return Ok(held);
13    }
14    Ok(RoleGrant {
15        meta: Meta::new()?,
16        role,
17        permission: permission.to_owned(),
18    })
19}
20
21/// Refuses a permission `granter` does not hold itself, so nobody widens a role past its own.
22pub async fn grant_as(
23    read: &Read,
24    granter: &Permissions,
25    role: RoleId,
26    permission: &str,
27) -> Result<RoleGrant> {
28    refuse_unless_held(granter, permission)?;
29    grant(read, role, permission).await
30}
31
32pub async fn revoke(read: &Read, role: RoleId, permission: &str) -> Result<RoleGrant> {
33    let mut held = held(read, role, permission).await?.ok_or(Error::NotFound)?;
34    held.delete();
35    Ok(held)
36}
37
38pub(crate) fn refuse_unless_permission(field: &str, text: &str) -> Result<()> {
39    if is_permission(text) {
40        return Ok(());
41    }
42    Err(Error::invalid(
43        field,
44        format!("`{text}` is not a permission such as `order.order.*.cancel`"),
45    ))
46}
47
48fn refuse_unless_held(granter: &Permissions, permission: &str) -> Result<()> {
49    refuse_unless_permission("permission", permission)?;
50    if granter.can_named(permission) {
51        return Ok(());
52    }
53    Err(Error::invalid(
54        "permission",
55        format!("`{permission}` is not covered by your own permissions, so you cannot grant it"),
56    ))
57}
58
59async fn held(read: &Read, role: RoleId, permission: &str) -> Result<Option<RoleGrant>> {
60    RoleGrant::query()
61        .where_(RoleGrant::ROLE.eq(role))
62        .where_(RoleGrant::PERMISSION.eq(permission.to_owned()))
63        .first(read)
64        .await
65}
66
67#[cfg(feature = "permitted-screens")]
68#[derive(Input)]
69pub struct Granted {
70    role: RoleId,
71    #[required]
72    #[unique]
73    permission: String,
74}
75
76/// Whoever posts the grant, read from the request, since the resource checks only the route.
77#[cfg(feature = "permitted-screens")]
78pub struct Granter(Permissions);
79
80#[cfg(feature = "permitted-screens")]
81impl<A: sofie::http::Area> sofie::http::OwnerOf<A> for Granter {
82    fn of(context: &sofie::http::RequestContext<A>) -> Result<Self> {
83        Ok(Self(context.permissions().clone()))
84    }
85}
86
87#[cfg(feature = "permitted-screens")]
88impl sofie::db::Create for RoleGrant {
89    type Input = Granted;
90    type Owner = Granter;
91
92    fn create(input: Granted, granter: Granter) -> Result<Self> {
93        refuse_unless_held(&granter.0, &input.permission)?;
94        let mut grant = RoleGrant {
95            meta: Meta::new()?,
96            role: input.role,
97            permission: String::new(),
98        };
99        sofie::db::Update::update(&mut grant, input)?;
100        Ok(grant)
101    }
102}
103
104#[cfg(feature = "permitted-screens")]
105impl sofie::db::Update for RoleGrant {
106    type Input = Granted;
107
108    fn update(&mut self, input: Granted) -> Result<()> {
109        refuse_unless_permission("permission", &input.permission)?;
110        self.role = input.role;
111        self.permission = input.permission;
112        Ok(())
113    }
114}