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 if self
71 .driver
72 .discovery_missing(access_id, expected_subsystem)?
73 {
74 self.driver.ensure_discovery(access_id)?;
75 }
76 let memberships = self
77 .groups
78 .memberships(principal.user(), principal.model())?;
79 self.driver.check(
80 principal,
81 access_id,
82 expected_subsystem,
83 memberships.user_groups(),
84 memberships.model_groups(),
85 memberships.revision(),
86 )
87 }
88
89 pub fn list_user(
90 &self,
91 principal: RequestPrincipal,
92 expected_subsystem: SubsystemId,
93 ) -> Result<Vec<AccessId>, String> {
94 self.driver
95 .discovered_for_user(principal.user(), expected_subsystem)
96 }
97
98 pub fn list_group(
99 &self,
100 principal: RequestPrincipal,
101 group: GroupId,
102 expected_subsystem: SubsystemId,
103 ) -> Result<Vec<AccessId>, String> {
104 let groups = self.groups.groups_for_user(principal.user())?;
105 require_group(&groups, group)?;
106 self.driver.discovered_for_group(group, expected_subsystem)
107 }
108}
109
110fn require_group(groups: &[GroupId], group: GroupId) -> Result<(), String> {
111 if groups.contains(&group) {
112 Ok(())
113 } else {
114 Err(ACCESS_DENIED.to_owned())
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn group(byte: u8) -> GroupId {
123 GroupId::new(TxId::from_bytes([byte; 12]))
124 }
125
126 #[test]
127 fn requested_group_is_required() {
128 let requested = group(1);
129 assert_eq!(require_group(&[requested], requested), Ok(()));
130 assert_eq!(
131 require_group(&[group(2), group(3)], requested),
132 Err(ACCESS_DENIED.to_owned())
133 );
134 }
135}