1#![allow(dead_code)]
2
3use std::collections::HashMap;
9
10use bitwarden_organizations::{OrganizationUserStatusType, OrganizationUserType};
11use uuid::Uuid;
12
13use crate::{
14 models::{OrganizationUserPolicyContext, PolicyView},
15 policy_type::PolicyType,
16};
17
18pub trait Policy: Send + Sync + 'static {
22 fn policy_type(&self) -> PolicyType;
24
25 fn exempt_roles(&self) -> &[OrganizationUserType] {
30 &[OrganizationUserType::Owner, OrganizationUserType::Admin]
31 }
32
33 fn exempt_providers(&self) -> bool {
37 true
38 }
39
40 fn applicable_statuses(&self) -> &[OrganizationUserStatusType] {
45 &[
46 OrganizationUserStatusType::Accepted,
47 OrganizationUserStatusType::Confirmed,
48 ]
49 }
50}
51
52pub trait PolicyFilter: Policy {
56 fn filter<'a>(
63 &self,
64 policies: &'a [PolicyView],
65 organization_user_policy_contexts: &[OrganizationUserPolicyContext],
66 ) -> Vec<&'a PolicyView> {
67 let org_map: HashMap<&Uuid, &OrganizationUserPolicyContext> =
68 organization_user_policy_contexts
69 .iter()
70 .map(|o| (&o.id, o))
71 .collect();
72
73 policies
74 .iter()
75 .filter(|p| p.r#type == self.policy_type())
76 .filter(|p| p.enabled)
77 .filter(|p| {
78 match org_map.get(&p.organization_id) {
79 Some(org) => {
80 org.enabled
81 && org.use_policies
82 && self.applicable_statuses().contains(&org.status)
83 && !self.exempt_roles().contains(&org.role)
84 && !(org.is_provider_user && self.exempt_providers())
85 }
86 None => true, }
88 })
89 .collect()
90 }
91}
92
93impl<T: Policy> PolicyFilter for T {}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 fn policy_view(organization_id: Uuid, policy_type: PolicyType, enabled: bool) -> PolicyView {
100 PolicyView {
101 id: Uuid::new_v4(),
102 organization_id,
103 r#type: policy_type,
104 data: None,
105 enabled,
106 revision_date: Default::default(),
107 }
108 }
109
110 fn organization(
111 id: Uuid,
112 user_type: OrganizationUserType,
113 status: OrganizationUserStatusType,
114 provider: bool,
115 ) -> OrganizationUserPolicyContext {
116 OrganizationUserPolicyContext {
117 id,
118 role: user_type,
119 status,
120 enabled: true,
121 use_policies: true,
122 is_provider_user: provider,
123 }
124 }
125
126 struct TestPolicy;
127 impl Policy for TestPolicy {
128 fn policy_type(&self) -> PolicyType {
129 PolicyType::MasterPassword
130 }
131
132 fn exempt_roles(&self) -> &[OrganizationUserType] {
135 &[OrganizationUserType::Owner, OrganizationUserType::Admin]
136 }
137
138 fn exempt_providers(&self) -> bool {
139 true
140 }
141
142 fn applicable_statuses(&self) -> &[OrganizationUserStatusType] {
143 &[
144 OrganizationUserStatusType::Accepted,
145 OrganizationUserStatusType::Confirmed,
146 ]
147 }
148 }
149
150 #[test]
151 fn matching_policy_is_returned() {
152 let org_id = Uuid::new_v4();
153 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
154 let orgs = [organization(
155 org_id,
156 OrganizationUserType::User,
157 OrganizationUserStatusType::Confirmed,
158 false,
159 )];
160
161 let result = TestPolicy.filter(&policies, &orgs);
162 assert_eq!(result.len(), 1);
163 }
164
165 #[test]
166 fn disabled_organization_is_filtered_out() {
167 let org_id = Uuid::new_v4();
168 let orgs = [OrganizationUserPolicyContext {
169 enabled: false,
170 id: org_id,
171 role: OrganizationUserType::User,
172 status: OrganizationUserStatusType::Confirmed,
173 use_policies: true,
174 is_provider_user: false,
175 }];
176 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
177
178 let result = TestPolicy.filter(&policies, &orgs);
179 assert!(result.is_empty());
180 }
181
182 #[test]
183 fn disabled_policy_is_filtered_out() {
184 let org_id = Uuid::new_v4();
185 let policies = [policy_view(org_id, PolicyType::MasterPassword, false)];
186 let orgs = [organization(
187 org_id,
188 OrganizationUserType::User,
189 OrganizationUserStatusType::Confirmed,
190 false,
191 )];
192
193 let result = TestPolicy.filter(&policies, &orgs);
194 assert!(result.is_empty());
195 }
196
197 #[test]
198 fn wrong_policy_type_is_filtered_out() {
199 let org_id = Uuid::new_v4();
200 let policies = [policy_view(org_id, PolicyType::PasswordGenerator, true)];
201 let orgs = [organization(
202 org_id,
203 OrganizationUserType::User,
204 OrganizationUserStatusType::Confirmed,
205 false,
206 )];
207
208 let result = TestPolicy.filter(&policies, &orgs);
209 assert!(result.is_empty());
210 }
211
212 #[test]
213 fn use_policies_false_is_filtered_out() {
214 let org_id = Uuid::new_v4();
215 let orgs = [OrganizationUserPolicyContext {
216 id: org_id,
217 role: OrganizationUserType::User,
218 status: OrganizationUserStatusType::Confirmed,
219 enabled: true,
220 use_policies: false,
221 is_provider_user: false,
222 }];
223 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
224
225 let result = TestPolicy.filter(&policies, &orgs);
226 assert!(result.is_empty());
227 }
228
229 #[test]
230 fn exempt_role_is_filtered_out() {
231 let org_id = Uuid::new_v4();
232 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
233 let orgs = [organization(
234 org_id,
235 OrganizationUserType::Owner,
236 OrganizationUserStatusType::Confirmed,
237 false,
238 )];
239
240 let result = TestPolicy.filter(&policies, &orgs);
241 assert!(result.is_empty());
242 }
243
244 #[test]
245 fn non_applicable_status_is_filtered_out() {
246 let org_id = Uuid::new_v4();
247 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
248 let orgs = [organization(
249 org_id,
250 OrganizationUserType::User,
251 OrganizationUserStatusType::Revoked,
252 false,
253 )];
254
255 let result = TestPolicy.filter(&policies, &orgs);
256 assert!(result.is_empty());
257 }
258
259 #[test]
260 fn provider_is_filtered_out() {
261 let org_id = Uuid::new_v4();
262 let policies = [policy_view(org_id, PolicyType::MasterPassword, true)];
263 let orgs = [organization(
264 org_id,
265 OrganizationUserType::User,
266 OrganizationUserStatusType::Confirmed,
267 true,
268 )];
269
270 let result = TestPolicy.filter(&policies, &orgs);
271 assert!(result.is_empty());
272 }
273
274 #[test]
275 fn missing_org_enforces_by_default() {
276 let policies = [policy_view(
277 Uuid::new_v4(),
278 PolicyType::MasterPassword,
279 true,
280 )];
281
282 let result = TestPolicy.filter(&policies, &[]);
283 assert_eq!(result.len(), 1);
284 }
285}