Skip to main content

kcode_k1_access/
lib.rs

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}