1use std::{path::Path, sync::Arc};
2
3use kcode_k1_access_driver::K1AccessDriver;
4use kcode_k1_groups::K1Groups;
5use kcode_k1_peering::K1Peering;
6use kcode_k1_txn_ordering::K1TxnOrdering;
7
8pub use kcode_k1_access_types::{
9 AccessCheck, AccessId, AccessRevision, Authorizations, GroupId, ModelId, OwnerSubject,
10 RequestPrincipal, Target, TxId, UserId, ViewerSubject,
11};
12pub use kcode_k1_transaction::SubsystemId;
13
14const ACCESS_DENIED: &str = "access denied";
15
16pub struct K1Access {
17 driver: K1AccessDriver,
18 groups: Arc<K1Groups>,
19}
20
21impl K1Access {
22 pub fn open(
23 root: &Path,
24 ordering: Arc<K1TxnOrdering>,
25 peering: Arc<K1Peering>,
26 groups: Arc<K1Groups>,
27 ) -> Result<Self, String> {
28 Ok(Self {
29 driver: K1AccessDriver::open(root, ordering, peering)?,
30 groups,
31 })
32 }
33
34 pub fn create(
35 &self,
36 target: Target,
37 authorizations: Authorizations,
38 ) -> Result<AccessRevision, String> {
39 self.driver.create(target, authorizations)
40 }
41
42 pub fn set_authorizations(
43 &self,
44 principal: RequestPrincipal,
45 access_id: AccessId,
46 authorizations: Authorizations,
47 ) -> Result<AccessRevision, String> {
48 let memberships = self
49 .groups
50 .memberships(principal.user(), principal.model())?;
51 let witness =
52 self.driver
53 .owner_witness(access_id, principal.user(), memberships.user_groups())?;
54 let witness = witness.ok_or_else(|| "principal is not an access owner".to_owned())?;
55 self.driver.replace(
56 access_id,
57 principal.user(),
58 memberships.revision(),
59 witness,
60 authorizations,
61 )
62 }
63
64 pub fn check(
65 &self,
66 principal: RequestPrincipal,
67 access_id: AccessId,
68 expected_subsystem: SubsystemId,
69 ) -> Result<AccessCheck, String> {
70 self.ensure_discovery(access_id, expected_subsystem)?;
71 let memberships = self
72 .groups
73 .memberships(principal.user(), principal.model())?;
74 self.driver.check(
75 principal,
76 access_id,
77 expected_subsystem,
78 memberships.user_groups(),
79 memberships.model_groups(),
80 memberships.revision(),
81 )
82 }
83
84 pub fn check_user(
85 &self,
86 user: UserId,
87 access_id: AccessId,
88 expected_subsystem: SubsystemId,
89 ) -> Result<AccessCheck, String> {
90 self.ensure_discovery(access_id, expected_subsystem)?;
91 let groups = self.groups.groups_for_user(user)?;
92 self.driver
93 .check_user(user, access_id, expected_subsystem, &groups, None)
94 }
95
96 pub fn list_user(
97 &self,
98 principal: RequestPrincipal,
99 expected_subsystem: SubsystemId,
100 ) -> Result<Vec<AccessId>, String> {
101 self.list_for_user(principal.user(), expected_subsystem)
102 }
103
104 pub fn list_for_user(
105 &self,
106 user: UserId,
107 expected_subsystem: SubsystemId,
108 ) -> Result<Vec<AccessId>, String> {
109 self.driver.discovered_for_user(user, expected_subsystem)
110 }
111
112 pub fn list_group(
113 &self,
114 principal: RequestPrincipal,
115 group: GroupId,
116 expected_subsystem: SubsystemId,
117 ) -> Result<Vec<AccessId>, String> {
118 self.list_group_for_user(principal.user(), group, expected_subsystem)
119 }
120
121 pub fn list_group_for_user(
122 &self,
123 user: UserId,
124 group: GroupId,
125 expected_subsystem: SubsystemId,
126 ) -> Result<Vec<AccessId>, String> {
127 let groups = self.groups.groups_for_user(user)?;
128 require_group(&groups, group)?;
129 self.driver.discovered_for_group(group, expected_subsystem)
130 }
131
132 fn ensure_discovery(
133 &self,
134 access_id: AccessId,
135 expected_subsystem: SubsystemId,
136 ) -> Result<(), String> {
137 if self
138 .driver
139 .discovery_missing(access_id, expected_subsystem)?
140 {
141 self.driver.ensure_discovery(access_id)?;
142 }
143 Ok(())
144 }
145}
146
147fn require_group(groups: &[GroupId], group: GroupId) -> Result<(), String> {
148 if groups.contains(&group) {
149 Ok(())
150 } else {
151 Err(ACCESS_DENIED.to_owned())
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 fn group(byte: u8) -> GroupId {
160 GroupId::new(TxId::from_bytes([byte; 12]))
161 }
162
163 #[test]
164 fn requested_group_is_required() {
165 let requested = group(1);
166 assert_eq!(require_group(&[requested], requested), Ok(()));
167 assert_eq!(
168 require_group(&[group(2), group(3)], requested),
169 Err(ACCESS_DENIED.to_owned())
170 );
171 }
172}