scopes_rs/policy/
policy.rs

1use std::ops::{BitAnd, BitOr, Not};
2
3use crate::scope::{AsScopeRef, Scope};
4
5/// A policy to verify a set of scopes
6/// 
7/// Policies can be combined using the `&`, `|` and `!` operators :
8/// 
9/// ```
10/// # use scopes_rs::derive::Scope;
11/// # use scopes_rs::policy::IntoPolicy;
12/// # #[derive(Clone, PartialEq, Scope)]
13/// # enum MyScope {Foo, Bar, Baz}
14/// let policy_a = MyScope::Foo.into_policy() & MyScope::Bar.into_policy();
15/// 
16/// let policy_b = (MyScope::Baz.into_policy() | MyScope::Bar.into_policy()) & !policy_a;
17/// ```
18/// 
19/// You can also use a [`PolicyBuilder<S>`](crate::policy::PolicyBuilder) to build
20/// complex policies.
21/// 
22#[derive(Debug, PartialEq)]
23pub enum Policy<S: Scope> {
24
25    /// Requires a scope to be present
26    Scope(S),
27
28    /// Requires one of the policies to be verified
29    OneOf(Vec<Policy<S>>),
30
31    /// Requires all of the policies to be verified
32    AllOf(Vec<Policy<S>>),
33
34    /// Requires a policy not to be verified
35    Not(Box<Policy<S>>),
36
37    /// Policy that always accept everything
38    AllowAll,
39
40    /// Policy that accepts nothing
41    DenyAll,
42}
43
44impl<S> Default for Policy<S> where S: Scope {
45    fn default() -> Self {
46        Policy::DenyAll
47    }
48}
49
50impl<S> Clone for Policy<S>
51where S: Scope + Clone {
52    fn clone(&self) -> Self {
53        match self {
54            Self::Scope(arg0) => Self::Scope(arg0.clone()),
55            Self::OneOf(arg0) => Self::OneOf(arg0.clone()),
56            Self::AllOf(arg0) => Self::AllOf(arg0.clone()),
57            Self::Not(arg0) => Self::Not(arg0.clone()),
58            Self::AllowAll => Self::AllowAll,
59            Self::DenyAll => Self::DenyAll,
60        }
61    }
62}
63
64impl<S> Policy<S> where S: Scope {
65
66    /// Check if a set of scopes is authorized by a policy
67    pub fn verify<Iterator>(&self, scopes: Iterator) -> bool 
68    where 
69        Iterator: IntoIterator + Clone,
70        Iterator::Item: AsScopeRef<S>,
71    {
72        match self {
73            
74            #[cfg(not(feature = "hierarchy"))]
75            Policy::Scope(required) => scopes.into_iter().find(|s| s.as_scope_ref() == required).is_some(),
76
77            #[cfg(feature = "hierarchy")]
78            Policy::Scope(required) => scopes.into_iter().any(|s| s.as_scope_ref().includes(required)),
79
80            Policy::Not(policy) => !policy.verify(scopes),
81            Policy::OneOf(policies) => policies.iter().any(|p| p.verify(scopes.clone())),
82            Policy::AllOf(policies) => policies.iter().all(|p| p.verify(scopes.clone())),
83            Policy::AllowAll => true,
84            Policy::DenyAll => false,
85        }
86    }
87}
88
89impl<S, I> BitAnd<I> for Policy<S>
90where 
91    S: Scope,
92    I: IntoPolicy<S>,
93{
94    type Output = Policy<S>;
95
96    fn bitand(self, rhs: I) -> Self::Output {
97        let rhs: Policy<S> = rhs.into_policy();
98        match (self, rhs) {
99            // If the 2 policies are AllOf, merge them
100            (Policy::AllOf(mut left), Policy::AllOf(mut right)) => {
101
102                left.append(&mut right);
103                Policy::AllOf(left)
104            },
105
106            // If one of the policy is DenyAll, return DenyAll as it would always fail anyway
107            (Policy::DenyAll, _) | (_, Policy::DenyAll) => Policy::DenyAll,
108
109            // If one of them is already AllOf, add the other to it
110            (Policy::AllOf(mut policies), other)
111            | (other, Policy::AllOf(mut policies)) => {
112
113                policies.push(other);
114                Policy::AllOf(policies)
115            }
116
117            (left, right) => Policy::AllOf(vec![left, right])
118        }
119    }
120}
121
122impl<S, I> BitOr<I> for Policy<S>
123where 
124    S: Scope,
125    I: IntoPolicy<S>,
126{
127    type Output = Policy<S>;
128
129    fn bitor(self, rhs: I) -> Self::Output {
130        let rhs: Policy<S> = rhs.into_policy();
131        match (self, rhs) {
132            // If the 2 policies are OneOf, merge them
133            (Policy::OneOf(mut left), Policy::OneOf(mut right)) => {
134
135                left.append(&mut right);
136                Policy::OneOf(left)
137            },
138
139            // If one of the policy is AllowAll, return AllowAll as it would always evaluate to true
140            (Policy::AllowAll, _) | (_, Policy::AllowAll) => Policy::AllowAll,
141
142            // If one of them is already OneOf, add the other to it
143            (Policy::OneOf(mut policies), other)
144            | (other, Policy::OneOf(mut policies)) => {
145
146                policies.push(other);
147                Policy::OneOf(policies)
148            }
149
150            (left, right) => Policy::OneOf(vec![left, right])
151        }
152    }
153}
154
155impl<S> Not for Policy<S>
156where 
157    S: Scope,
158{
159    type Output = Policy<S>;
160
161    fn not(self) -> Self::Output {
162        match self {
163            Policy::AllowAll => Policy::DenyAll,
164            Policy::DenyAll => Policy::AllowAll,
165            Policy::Not(policy) => *policy,
166            p => Policy::Not(Box::new(p)),
167        }
168    }
169}
170
171impl<S> From<S> for Policy<S>
172where
173    S: Scope
174{
175    fn from(value: S) -> Self {
176        Policy::Scope(value)
177    }
178}
179
180impl<S> From<&S> for Policy<S>
181where
182    S: Scope + Clone
183{
184    fn from(value: &S) -> Self {
185        Policy::Scope(value.clone())
186    }
187}
188
189/// A trait implemented by a type that can be converted into a [`Policy<S>`].
190/// 
191/// This is implemented for anything that implements [`Scope`], and 
192/// allows passing them directly to a [`PolicyBuilder<S>`](crate::policy::PolicyBuilder)
193/// or on the right of `&`, `|` and `!` operators when combining policies.
194/// 
195/// ```
196/// # use scopes_rs::derive::Scope;
197/// # #[derive(Clone, PartialEq, Scope)]
198/// # enum MyScope {Foo, Bar, Baz}
199/// use scopes_rs::policy::{IntoPolicy, PolicyBuilder};
200/// 
201/// let policy = PolicyBuilder::not(MyScope::Bar).build();
202/// let policy = policy | MyScope::Baz;
203/// ```
204pub trait IntoPolicy<S> where S: Scope {
205
206    /// Converts this type to a [`Policy<S>`]
207    fn into_policy(self) -> Policy<S>;
208}
209
210impl<S, I> IntoPolicy<S> for I
211where 
212    S: Scope,
213    I: Into<Policy<S>>,
214{
215    fn into_policy(self) -> Policy<S> {
216        self.into()
217    }
218}