openauth_plugins/organization/
permissions.rs1use 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 ApiKeyCreate,
39 ApiKeyRead,
40 ApiKeyUpdate,
41 ApiKeyDelete,
42}
43
44impl OrganizationPermission {
45 pub(crate) fn resource_action(self) -> (&'static str, &'static str) {
46 match self {
47 Self::OrganizationUpdate => ("organization", "update"),
48 Self::OrganizationDelete => ("organization", "delete"),
49 Self::MemberCreate => ("member", "create"),
50 Self::MemberUpdate => ("member", "update"),
51 Self::MemberDelete => ("member", "delete"),
52 Self::InvitationCreate => ("invitation", "create"),
53 Self::InvitationCancel => ("invitation", "cancel"),
54 Self::TeamCreate => ("team", "create"),
55 Self::TeamUpdate => ("team", "update"),
56 Self::TeamDelete => ("team", "delete"),
57 Self::AcCreate => ("ac", "create"),
58 Self::AcRead => ("ac", "read"),
59 Self::AcUpdate => ("ac", "update"),
60 Self::AcDelete => ("ac", "delete"),
61 Self::ApiKeyCreate => ("apiKey", "create"),
62 Self::ApiKeyRead => ("apiKey", "read"),
63 Self::ApiKeyUpdate => ("apiKey", "update"),
64 Self::ApiKeyDelete => ("apiKey", "delete"),
65 }
66 }
67}
68
69pub fn has_permission(
70 role: &str,
71 options: &OrganizationOptions,
72 permission: OrganizationPermission,
73) -> bool {
74 role.split(',').map(str::trim).any(|role| {
75 if role == options.creator_role {
76 return true;
77 }
78 if custom_role_has_permission(role, options, permission) {
79 return true;
80 }
81 match role {
82 "owner" => true,
83 "admin" => !matches!(permission, OrganizationPermission::OrganizationDelete),
84 "member" => matches!(permission, OrganizationPermission::AcRead),
85 _ => false,
86 }
87 })
88}
89
90pub(crate) fn parse_roles(role: impl AsRef<str>) -> String {
91 role.as_ref()
92 .split(',')
93 .map(str::trim)
94 .filter(|role| !role.is_empty())
95 .collect::<Vec<_>>()
96 .join(",")
97}
98
99pub(crate) fn is_known_static_role(role: &str, options: &OrganizationOptions) -> bool {
100 role == options.creator_role
101 || options.custom_roles.contains_key(role)
102 || matches!(role, "owner" | "admin" | "member")
103}
104
105pub(crate) fn permission_value_has_permission(
106 permission: &serde_json::Value,
107 required: OrganizationPermission,
108) -> bool {
109 let (resource, action) = required.resource_action();
110 permission
111 .get(resource)
112 .or_else(|| {
113 (resource == "apiKey")
114 .then(|| permission.get("api_key"))
115 .flatten()
116 })
117 .and_then(serde_json::Value::as_array)
118 .map(|actions| actions.iter().any(|value| value.as_str() == Some(action)))
119 .unwrap_or(false)
120}
121
122fn custom_role_has_permission(
123 role: &str,
124 options: &OrganizationOptions,
125 permission: OrganizationPermission,
126) -> bool {
127 options
128 .custom_roles
129 .get(role)
130 .map(|value| permission_value_has_permission(value, permission))
131 .unwrap_or(false)
132}