Skip to main content

openauth_plugins/organization/
permissions.rs

1use serde::{Deserialize, Serialize};
2
3use super::options::OrganizationOptions;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum OrganizationRole {
7    Owner,
8    Admin,
9    Member,
10}
11
12impl OrganizationRole {
13    pub fn as_str(self) -> &'static str {
14        match self {
15            Self::Owner => "owner",
16            Self::Admin => "admin",
17            Self::Member => "member",
18        }
19    }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23pub enum OrganizationPermission {
24    OrganizationUpdate,
25    OrganizationDelete,
26    MemberCreate,
27    MemberUpdate,
28    MemberDelete,
29    InvitationCreate,
30    InvitationCancel,
31    TeamCreate,
32    TeamUpdate,
33    TeamDelete,
34    AcCreate,
35    AcRead,
36    AcUpdate,
37    AcDelete,
38}
39
40impl OrganizationPermission {
41    pub(crate) fn resource_action(self) -> (&'static str, &'static str) {
42        match self {
43            Self::OrganizationUpdate => ("organization", "update"),
44            Self::OrganizationDelete => ("organization", "delete"),
45            Self::MemberCreate => ("member", "create"),
46            Self::MemberUpdate => ("member", "update"),
47            Self::MemberDelete => ("member", "delete"),
48            Self::InvitationCreate => ("invitation", "create"),
49            Self::InvitationCancel => ("invitation", "cancel"),
50            Self::TeamCreate => ("team", "create"),
51            Self::TeamUpdate => ("team", "update"),
52            Self::TeamDelete => ("team", "delete"),
53            Self::AcCreate => ("ac", "create"),
54            Self::AcRead => ("ac", "read"),
55            Self::AcUpdate => ("ac", "update"),
56            Self::AcDelete => ("ac", "delete"),
57        }
58    }
59}
60
61pub fn has_permission(
62    role: &str,
63    options: &OrganizationOptions,
64    permission: OrganizationPermission,
65) -> bool {
66    role.split(',').map(str::trim).any(|role| {
67        if role == options.creator_role {
68            return true;
69        }
70        if custom_role_has_permission(role, options, permission) {
71            return true;
72        }
73        match role {
74            "owner" => true,
75            "admin" => !matches!(permission, OrganizationPermission::OrganizationDelete),
76            "member" => matches!(permission, OrganizationPermission::AcRead),
77            _ => false,
78        }
79    })
80}
81
82pub(crate) fn parse_roles(role: impl AsRef<str>) -> String {
83    role.as_ref()
84        .split(',')
85        .map(str::trim)
86        .filter(|role| !role.is_empty())
87        .collect::<Vec<_>>()
88        .join(",")
89}
90
91pub(crate) fn is_known_static_role(role: &str, options: &OrganizationOptions) -> bool {
92    role == options.creator_role
93        || options.custom_roles.contains_key(role)
94        || matches!(role, "owner" | "admin" | "member")
95}
96
97pub(crate) fn permission_value_has_permission(
98    permission: &serde_json::Value,
99    required: OrganizationPermission,
100) -> bool {
101    let (resource, action) = required.resource_action();
102    permission
103        .get(resource)
104        .and_then(serde_json::Value::as_array)
105        .map(|actions| actions.iter().any(|value| value.as_str() == Some(action)))
106        .unwrap_or(false)
107}
108
109fn custom_role_has_permission(
110    role: &str,
111    options: &OrganizationOptions,
112    permission: OrganizationPermission,
113) -> bool {
114    options
115        .custom_roles
116        .get(role)
117        .map(|value| permission_value_has_permission(value, permission))
118        .unwrap_or(false)
119}