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