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