Skip to main content

alien_permissions/
lib.rs

1pub mod error;
2pub mod generators;
3pub mod initial_setup;
4pub mod registry;
5pub mod variables;
6
7pub use error::*;
8pub use registry::{get_permission_set, has_permission_set, list_permission_set_ids};
9pub use variables::VariableInterpolator;
10
11// Core types are re-exported by the generators that need them
12
13/// Context for generating permissions with type-safe variables
14#[derive(Debug, Clone)]
15pub struct PermissionContext {
16    // AWS variables
17    pub aws_account_id: Option<String>,
18    pub aws_region: Option<String>,
19
20    // GCP variables
21    pub project_name: Option<String>,
22    pub project_number: Option<String>,
23    pub region: Option<String>,
24
25    // Azure variables
26    pub subscription_id: Option<String>,
27    pub resource_group: Option<String>,
28    pub storage_account_name: Option<String>,
29
30    // GCP cross-project management variables
31    pub managing_project_id: Option<String>,
32
33    // Azure cross-subscription management variables
34    pub managing_subscription_id: Option<String>,
35    pub managing_resource_group: Option<String>,
36
37    // Common variables
38    pub stack_prefix: Option<String>,
39    pub resource_name: Option<String>,
40    pub service_account_name: Option<String>,
41    pub principal_id: Option<String>,
42    pub external_id: Option<String>,
43    pub managing_role_arn: Option<String>,
44    pub managing_account_id: Option<String>,
45}
46
47impl PermissionContext {
48    /// Create a new permission context
49    pub fn new() -> Self {
50        Self {
51            aws_account_id: None,
52            aws_region: None,
53            project_name: None,
54            project_number: None,
55            region: None,
56            subscription_id: None,
57            resource_group: None,
58            storage_account_name: None,
59            managing_project_id: None,
60            managing_subscription_id: None,
61            managing_resource_group: None,
62            stack_prefix: None,
63            resource_name: None,
64            service_account_name: None,
65            principal_id: None,
66            external_id: None,
67            managing_role_arn: None,
68            managing_account_id: None,
69        }
70    }
71
72    /// Builder pattern for AWS account ID
73    pub fn with_aws_account_id(mut self, aws_account_id: impl Into<String>) -> Self {
74        self.aws_account_id = Some(aws_account_id.into());
75        self
76    }
77
78    /// Builder pattern for AWS region
79    pub fn with_aws_region(mut self, aws_region: impl Into<String>) -> Self {
80        self.aws_region = Some(aws_region.into());
81        self
82    }
83
84    /// Builder pattern for GCP project name
85    pub fn with_project_name(mut self, project_name: impl Into<String>) -> Self {
86        self.project_name = Some(project_name.into());
87        self
88    }
89
90    /// Builder pattern for GCP project number (numeric, used in IAM condition expressions)
91    pub fn with_project_number(mut self, project_number: impl Into<String>) -> Self {
92        self.project_number = Some(project_number.into());
93        self
94    }
95
96    /// Builder pattern for GCP region (used in artifact-registry, function, network permission sets)
97    pub fn with_region(mut self, region: impl Into<String>) -> Self {
98        self.region = Some(region.into());
99        self
100    }
101
102    /// Builder pattern for Azure subscription ID
103    pub fn with_subscription_id(mut self, subscription_id: impl Into<String>) -> Self {
104        self.subscription_id = Some(subscription_id.into());
105        self
106    }
107
108    /// Builder pattern for Azure resource group
109    pub fn with_resource_group(mut self, resource_group: impl Into<String>) -> Self {
110        self.resource_group = Some(resource_group.into());
111        self
112    }
113
114    /// Builder pattern for Azure storage account name
115    pub fn with_storage_account_name(mut self, storage_account_name: impl Into<String>) -> Self {
116        self.storage_account_name = Some(storage_account_name.into());
117        self
118    }
119
120    /// Builder pattern for GCP managing project ID (cross-project management)
121    pub fn with_managing_project_id(mut self, id: impl Into<String>) -> Self {
122        self.managing_project_id = Some(id.into());
123        self
124    }
125
126    /// Builder pattern for Azure managing subscription ID (cross-subscription management)
127    pub fn with_managing_subscription_id(mut self, id: impl Into<String>) -> Self {
128        self.managing_subscription_id = Some(id.into());
129        self
130    }
131
132    /// Builder pattern for Azure managing resource group (cross-subscription management)
133    pub fn with_managing_resource_group(mut self, rg: impl Into<String>) -> Self {
134        self.managing_resource_group = Some(rg.into());
135        self
136    }
137
138    /// Builder pattern for stack prefix
139    pub fn with_stack_prefix(mut self, stack_prefix: impl Into<String>) -> Self {
140        self.stack_prefix = Some(stack_prefix.into());
141        self
142    }
143
144    /// Builder pattern for resource name
145    pub fn with_resource_name(mut self, resource_name: impl Into<String>) -> Self {
146        self.resource_name = Some(resource_name.into());
147        self
148    }
149
150    /// Builder pattern for service account name
151    pub fn with_service_account_name(mut self, service_account_name: impl Into<String>) -> Self {
152        self.service_account_name = Some(service_account_name.into());
153        self
154    }
155
156    /// Builder pattern for principal ID
157    pub fn with_principal_id(mut self, principal_id: impl Into<String>) -> Self {
158        self.principal_id = Some(principal_id.into());
159        self
160    }
161
162    /// Builder pattern for external ID
163    pub fn with_external_id(mut self, external_id: impl Into<String>) -> Self {
164        self.external_id = Some(external_id.into());
165        self
166    }
167
168    /// Builder pattern for managing role ARN
169    pub fn with_managing_role_arn(mut self, managing_role_arn: impl Into<String>) -> Self {
170        self.managing_role_arn = Some(managing_role_arn.into());
171        self
172    }
173
174    /// Builder pattern for managing account ID
175    pub fn with_managing_account_id(mut self, managing_account_id: impl Into<String>) -> Self {
176        self.managing_account_id = Some(managing_account_id.into());
177        self
178    }
179
180    /// Extract AWS account ID from an IAM role ARN
181    /// Format: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME
182    pub fn extract_account_id_from_role_arn(role_arn: &str) -> Option<String> {
183        let parts: Vec<&str> = role_arn.split(':').collect();
184        if parts.len() >= 5 && parts[0] == "arn" && parts[2] == "iam" {
185            Some(parts[4].to_string())
186        } else {
187            None
188        }
189    }
190
191    /// Get a variable by name (for backward compatibility with interpolation)
192    pub fn get_variable(&self, key: &str) -> Option<&str> {
193        match key {
194            "awsAccountId" => self.aws_account_id.as_deref(),
195            "awsRegion" => self.aws_region.as_deref(),
196            "projectName" => self.project_name.as_deref(),
197            "projectNumber" => self.project_number.as_deref(),
198            "region" => self.region.as_deref(),
199            "subscriptionId" => self.subscription_id.as_deref(),
200            "resourceGroup" => self.resource_group.as_deref(),
201            "storageAccountName" => self.storage_account_name.as_deref(),
202            "stackPrefix" => self.stack_prefix.as_deref(),
203            "resourceName" => self.resource_name.as_deref(),
204            "serviceAccountName" => self.service_account_name.as_deref(),
205            "principalId" => self.principal_id.as_deref(),
206            "externalId" => self.external_id.as_deref(),
207            "managingRoleArn" => self.managing_role_arn.as_deref(),
208            "managingAccountId" => self.managing_account_id.as_deref(),
209            "managingProjectId" => self.managing_project_id.as_deref(),
210            "managingSubscriptionId" => self.managing_subscription_id.as_deref(),
211            "managingResourceGroup" => self.managing_resource_group.as_deref(),
212            _ => None,
213        }
214    }
215}
216
217impl Default for PermissionContext {
218    fn default() -> Self {
219        Self::new()
220    }
221}
222
223/// Binding target type for permissions
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
225pub enum BindingTarget {
226    /// Stack-level binding
227    Stack,
228    /// Resource-level binding  
229    Resource,
230}
231
232impl std::fmt::Display for BindingTarget {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            BindingTarget::Stack => write!(f, "stack"),
236            BindingTarget::Resource => write!(f, "resource"),
237        }
238    }
239}