tsafe_core/
baseline_contracts.rs1use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct BaselineContract {
14 pub name: String,
15 pub description: String,
16 pub required_trust_profile: String,
17 pub access_level: AccessLevel,
18 pub secret_constraints: SecretConstraints,
19 pub target_constraints: Option<Vec<String>>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub enum AccessLevel {
24 ReadOnly,
25 ReadWrite,
26}
27
28impl std::fmt::Display for AccessLevel {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 AccessLevel::ReadOnly => write!(f, "read_only"),
32 AccessLevel::ReadWrite => write!(f, "read_write"),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct SecretConstraints {
39 pub allowed_secrets: Vec<String>,
41 pub required_secrets: Vec<String>,
43}
44
45pub fn read_only_contract() -> BaselineContract {
50 BaselineContract {
51 name: "read_only".to_string(),
52 description: "Read-only access: retrieve secrets, no vault modifications".to_string(),
53 required_trust_profile: "standard".to_string(),
54 access_level: AccessLevel::ReadOnly,
55 secret_constraints: SecretConstraints {
56 allowed_secrets: vec![],
57 required_secrets: vec![],
58 },
59 target_constraints: None,
60 }
61}
62
63pub fn ci_deploy_contract(targets: Vec<String>) -> BaselineContract {
68 BaselineContract {
69 name: "ci_deploy".to_string(),
70 description: "CI deployment: hardened env, target-restricted, read-only vault".to_string(),
71 required_trust_profile: "hardened".to_string(),
72 access_level: AccessLevel::ReadOnly,
73 secret_constraints: SecretConstraints {
74 allowed_secrets: vec![
75 "AWS_ACCOUNT_ID".to_string(),
76 "AWS_ACCESS_KEY_ID".to_string(),
77 "AWS_SECRET_ACCESS_KEY".to_string(),
78 "TERRAFORM_TOKEN".to_string(),
79 ],
80 required_secrets: vec![
81 "AWS_ACCESS_KEY_ID".to_string(),
82 "AWS_SECRET_ACCESS_KEY".to_string(),
83 ],
84 },
85 target_constraints: Some(targets),
86 }
87}
88
89pub fn ops_emergency_contract() -> BaselineContract {
94 BaselineContract {
95 name: "ops_emergency".to_string(),
96 description: "Operations emergency: hardened env, write access, strict targeting"
97 .to_string(),
98 required_trust_profile: "hardened".to_string(),
99 access_level: AccessLevel::ReadWrite,
100 secret_constraints: SecretConstraints {
101 allowed_secrets: vec![
102 "ADMIN_SSH_KEY".to_string(),
103 "SUDO_PASSWORD".to_string(),
104 "INCIDENT_TICKET_TOKEN".to_string(),
105 ],
106 required_secrets: vec!["ADMIN_SSH_KEY".to_string()],
107 },
108 target_constraints: Some(vec!["ssh".to_string(), "sudo".to_string()]),
109 }
110}
111
112pub fn baseline_contracts() -> HashMap<&'static str, BaselineContract> {
114 let mut contracts = HashMap::new();
115
116 contracts.insert("read_only", read_only_contract());
117 contracts.insert(
118 "ci_deploy",
119 ci_deploy_contract(vec!["terraform".to_string(), "ansible".to_string()]),
120 );
121 contracts.insert("ops_emergency", ops_emergency_contract());
122
123 contracts
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_read_only_contract_no_restrictions() {
132 let contract = read_only_contract();
133 assert_eq!(contract.access_level.to_string(), "read_only");
134 assert!(contract.secret_constraints.allowed_secrets.is_empty());
135 assert!(contract.target_constraints.is_none());
136 }
137
138 #[test]
139 fn test_ci_deploy_contract_has_required_secrets() {
140 let contract = ci_deploy_contract(vec!["terraform".to_string()]);
141 assert_eq!(contract.access_level.to_string(), "read_only");
142 assert!(!contract.secret_constraints.required_secrets.is_empty());
143 assert!(contract.target_constraints.is_some());
144 assert_eq!(contract.required_trust_profile, "hardened");
145 }
146
147 #[test]
148 fn test_ops_emergency_contract_allows_write() {
149 let contract = ops_emergency_contract();
150 assert_eq!(contract.access_level.to_string(), "read_write");
151 assert_eq!(contract.required_trust_profile, "hardened");
152 let targets = contract.target_constraints.unwrap();
153 assert!(targets.contains(&"ssh".to_string()));
154 }
155
156 #[test]
157 fn test_baseline_contracts_registry_is_complete() {
158 let registry = baseline_contracts();
159 assert!(registry.contains_key("read_only"));
160 assert!(registry.contains_key("ci_deploy"));
161 assert!(registry.contains_key("ops_emergency"));
162 }
163}