Skip to main content

tsafe_core/
baseline_contracts.rs

1/// Team Policy Pack: Baseline Authority Contract Definitions
2///
3/// This module provides canonical baseline team contracts that enforce
4/// least-privilege access patterns:
5/// - ReadOnly: No vault modification, full env inheritance
6/// - CIDeploy: Hardened deployment target lock-down, no vault modification
7/// - OpsEmergency: Time-limited emergency access with audit compliance
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// Baseline authority contract template
12#[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    /// Secrets allowed to be injected (empty = all)
40    pub allowed_secrets: Vec<String>,
41    /// Secrets that must exist or the exec fails
42    pub required_secrets: Vec<String>,
43}
44
45/// Read-only team member access contract.
46///
47/// Grants access to retrieve secrets without modification capability.
48/// Suitable for developers, analysts, and read-only operators.
49pub 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
63/// CI deployment contract with hardened trust.
64///
65/// For automated CI/CD pipelines. Enforces minimal env inheritance,
66/// target validation, and read-only vault access.
67pub 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
89/// Operations emergency contract for time-limited incident response.
90///
91/// Allows read-write vault access for emergency remediation but enforces
92/// hardened env, strict target validation, and comprehensive audit logging.
93pub 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
112/// Registry of all baseline contracts.
113pub 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}