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        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}