Skip to main content

sova_scope/
types.rs

1//! Core scope authorization types.
2
3use serde::Serialize;
4use sova_core::Error;
5
6pub const ROLE_OWNER: &str = "owner";
7pub const ROLE_ADMIN: &str = "admin";
8pub const ROLE_EDITOR: &str = "editor";
9pub const ROLE_VIEWER: &str = "viewer";
10
11pub const INVITE_PENDING: &str = "pending";
12pub const INVITE_ACCEPTED: &str = "accepted";
13pub const INVITE_REVOKED: &str = "revoked";
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16pub struct ScopeRef {
17    pub kind: String,
18    pub id: i64,
19}
20
21impl ScopeRef {
22    pub fn new(kind: impl Into<String>, id: i64) -> Self {
23        Self {
24            kind: kind.into(),
25            id,
26        }
27    }
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31pub struct ResourceRef {
32    pub kind: &'static str,
33    pub id: i64,
34}
35
36impl ResourceRef {
37    pub fn new(kind: &'static str, id: i64) -> Self {
38        Self { kind, id }
39    }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
43#[serde(rename_all = "snake_case")]
44pub enum Ability {
45    View,
46    Create,
47    Update,
48    Delete,
49    Manage,
50}
51
52#[derive(Clone, Debug, Serialize)]
53pub struct Membership {
54    pub id: i64,
55    pub scope_kind: String,
56    pub scope_id: i64,
57    pub user_id: i64,
58    pub role: String,
59}
60
61#[derive(Clone, Copy, Debug, Default, Serialize)]
62pub struct Capabilities {
63    pub view: bool,
64    pub create: bool,
65    pub update: bool,
66    pub delete: bool,
67    pub manage: bool,
68}
69
70impl Capabilities {
71    pub fn full() -> Self {
72        Self {
73            view: true,
74            create: true,
75            update: true,
76            delete: true,
77            manage: true,
78        }
79    }
80
81    pub fn read_only() -> Self {
82        Self {
83            view: true,
84            ..Default::default()
85        }
86    }
87
88    pub fn has(&self, ability: Ability) -> bool {
89        match ability {
90            Ability::View => self.view,
91            Ability::Create => self.create,
92            Ability::Update => self.update,
93            Ability::Delete => self.delete,
94            Ability::Manage => self.manage,
95        }
96    }
97}
98
99pub fn normalize_role(role: &str) -> Option<&'static str> {
100    match role.trim().to_lowercase().as_str() {
101        "owner" => Some(ROLE_OWNER),
102        "admin" => Some(ROLE_ADMIN),
103        "editor" => Some(ROLE_EDITOR),
104        "viewer" | "read" => Some(ROLE_VIEWER),
105        _ => None,
106    }
107}
108
109pub fn is_privileged_role(role: &str) -> bool {
110    role == ROLE_OWNER || role == ROLE_ADMIN
111}
112
113pub fn ability_error(ability: Ability) -> Error {
114    match ability {
115        Ability::View => Error::NotFound,
116        _ => Error::custom(403, "forbidden"),
117    }
118}