1use blindplane_access::{
4 AccessGrant, AccessIssuer, AuditContext, AuditEvent, AuditEventKind, CapabilityKind,
5 CapabilityRule, Decision, Effect, GrantSpec, Permissions, PolicySpec, PrincipalKeypair,
6 PrincipalKind, RevocationSpec, RevocationState, RoleKeypair, TenantPolicy, open_audit_event,
7 seal_audit_event,
8};
9use blindplane_core::{Author, fastest_payload_suite};
10
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 let issuer = AccessIssuer::generate("acme-admin")?;
13 let alice = PrincipalKeypair::generate("acme", "alice", PrincipalKind::User, 1)?;
14 let admin = PrincipalKeypair::generate("acme", "admin", PrincipalKind::Administrator, 1)?;
15
16 let alice_audit = RoleKeypair::generate("acme", "audit", "alice-audit", 1)?;
17 let tenant_audit = RoleKeypair::generate("acme", "audit", "tenant-audit", 1)?;
18
19 let alice_grant = AccessGrant::issue(
20 &issuer,
21 alice.principal(),
22 &alice_audit,
23 GrantSpec {
24 grant_id: "alice-audit-v1".into(),
25 scope: "audit".into(),
26 permissions: Permissions::READ | Permissions::WRITE,
27 authorization_epoch: 1,
28 not_before: 1,
29 not_after: u64::MAX,
30 },
31 )?;
32 let admin_grant = AccessGrant::issue(
33 &issuer,
34 admin.principal(),
35 &tenant_audit,
36 GrantSpec {
37 grant_id: "admin-audit-v1".into(),
38 scope: "audit".into(),
39 permissions: Permissions::READ | Permissions::OBSERVE,
40 authorization_epoch: 1,
41 not_before: 1,
42 not_after: u64::MAX,
43 },
44 )?;
45
46 let alice_state = RevocationState::issue(
47 &issuer,
48 "acme",
49 "alice",
50 "audit",
51 RevocationSpec {
52 revision: 1,
53 minimum_authorization_epoch: 1,
54 minimum_role_key_epoch: 1,
55 issued_at: 1,
56 },
57 )?;
58 let admin_state = RevocationState::issue(
59 &issuer,
60 "acme",
61 "admin",
62 "audit",
63 RevocationSpec {
64 revision: 1,
65 minimum_authorization_epoch: 1,
66 minimum_role_key_epoch: 1,
67 issued_at: 1,
68 },
69 )?;
70 let trusted = issuer.trusted();
71 let alice_role = alice_grant.open_role(
72 &alice,
73 &trusted,
74 100,
75 &alice_state.verify(&trusted, "acme", "alice", "audit")?,
76 )?;
77 let admin_role = admin_grant.open_role(
78 &admin,
79 &trusted,
80 100,
81 &admin_state.verify(&trusted, "acme", "admin", "audit")?,
82 )?;
83
84 let policy = TenantPolicy::issue(
85 &issuer,
86 alice.principal(),
87 PolicySpec {
88 revision: 1,
89 previous_hash: [0; 32],
90 authorization_epoch: 1,
91 issued_at: 1,
92 not_before: 1,
93 not_after: u64::MAX,
94 rules: vec![CapabilityRule::new(
95 CapabilityKind::Mcp,
96 "github",
97 Effect::Allow,
98 )?],
99 },
100 )?;
101 let verified = policy.verify(&trusted, "acme", "alice", 100, 1, 1)?;
102 assert_eq!(
103 verified.decision(CapabilityKind::Mcp, "github"),
104 Decision::Allow
105 );
106 assert_eq!(
107 verified.decision(CapabilityKind::Skill, "deploy"),
108 Decision::Deny
109 );
110
111 let author = Author::generate()?;
112 let event = AuditEvent {
113 tenant_id: "acme".into(),
114 subject_id: "alice".into(),
115 session_id: "session-42".into(),
116 sequence: 1,
117 timestamp: 100,
118 kind: AuditEventKind::Request,
119 media_type: "application/json".into(),
120 body: br#"{"tool":"github.search","query":"private"}"#.to_vec(),
121 };
122 let record = seal_audit_event(
123 &author,
124 AuditContext {
125 tenant_id: "acme".into(),
126 subject_id: "alice".into(),
127 stream_id: "session-42".into(),
128 epoch: 1,
129 sequence: 1,
130 },
131 &event,
132 &alice_role,
133 &admin_role,
134 fastest_payload_suite(),
135 )?;
136
137 assert_eq!(
138 open_audit_event(&record, &alice_role, author.public_key())?,
139 event
140 );
141 assert_eq!(
142 open_audit_event(&record, &admin_role, author.public_key())?,
143 event
144 );
145 println!("user and tenant administrator opened the encrypted audit event");
146 Ok(())
147}