gcp-platforms-auto 0.8.3__tar.gz → 0.8.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcp_platforms_auto
3
- Version: 0.8.3
3
+ Version: 0.8.4
4
4
  Summary: A brief description of your package
5
5
  Author-email: ofir4858 <ofirshasha10@gmail.com>
6
6
  License: MIT
@@ -14,7 +14,9 @@ from .db import (
14
14
  Base
15
15
  )
16
16
  from .iam import (
17
- check_user_has_role_in_project,
17
+ check_user_has_role_in_project,
18
+ check_service_account_has_role_in_project,
19
+ check_group_has_role_in_project,
18
20
  get_projects_with_role
19
21
  )
20
22
 
@@ -0,0 +1,318 @@
1
+ """IAM access management utilities for GCP."""
2
+
3
+ import logging
4
+ import os
5
+ import google.cloud.logging
6
+ from google.cloud import asset_v1
7
+ from typing import Optional
8
+
9
+ # Initialize Google Cloud Logging
10
+ client = google.cloud.logging.Client()
11
+ client.setup_logging()
12
+
13
+ # Configure the logger
14
+ logger = logging.getLogger("uvicorn")
15
+ logger.setLevel(logging.INFO)
16
+
17
+
18
+ def check_user_has_role_in_project(
19
+ project_id: str,
20
+ user_email: str,
21
+ organization_id: str,
22
+ role: str = "roles/owner",
23
+ expand_groups: bool = True
24
+ ) -> bool:
25
+ """
26
+ Check if a user has a specific role in a GCP project.
27
+
28
+ Args:
29
+ project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
30
+ user_email: Email of the user to check (e.g., 'oshasha10@gcporg.com')
31
+ organization_id: GCP organization ID (e.g., '111111111111')
32
+ role: Role to check (e.g., 'roles/owner', 'roles/editor')
33
+ expand_groups: Whether to expand group memberships (default: True)
34
+
35
+ Returns:
36
+ bool: True if the user has the role in the project, False otherwise
37
+
38
+ Example:
39
+ >>> has_access = check_user_has_role_in_project(
40
+ ... project_id='sky-starfi-mam-res-gcpro-1',
41
+ ... user_email='oshasha10@gcporg.com',
42
+ ... organization_id='111111111111',
43
+ ... role='roles/owner'
44
+ ... )
45
+ """
46
+ client = asset_v1.AssetServiceClient()
47
+
48
+ # Construct the full resource name
49
+ scope = f"organizations/{organization_id}"
50
+ full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
51
+ identity = f"user:{user_email}"
52
+
53
+ # Build the request
54
+ request = asset_v1.AnalyzeIamPolicyRequest(
55
+ analysis_query=asset_v1.IamPolicyAnalysisQuery(
56
+ scope=scope,
57
+ resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
58
+ full_resource_name=full_resource_name
59
+ ),
60
+ identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
61
+ identity=identity
62
+ ),
63
+ access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
64
+ roles=[role]
65
+ ),
66
+ options=asset_v1.IamPolicyAnalysisQuery.Options(
67
+ expand_groups=expand_groups,
68
+ expand_roles=True,
69
+ )
70
+ )
71
+ )
72
+
73
+ try:
74
+ # Execute the analysis
75
+ logger.info(f"Checking if user {user_email} has role {role} in project {project_id}")
76
+ response = client.analyze_iam_policy(request=request)
77
+
78
+ # Check if any results were returned
79
+ if response.main_analysis and response.main_analysis.analysis_results:
80
+ logger.info(f"User {user_email} has role {role} in project {project_id}")
81
+ return True
82
+
83
+ logger.info(f"User {user_email} does not have role {role} in project {project_id}")
84
+ return False
85
+
86
+ except Exception as e:
87
+ logger.exception(f"Error analyzing IAM policy: {e}")
88
+ raise
89
+
90
+
91
+ def check_service_account_has_role_in_project(
92
+ project_id: str,
93
+ service_account_email: str,
94
+ organization_id: str,
95
+ role: str = "roles/owner",
96
+ expand_groups: bool = True
97
+ ) -> bool:
98
+ """
99
+ Check if a service account has a specific role in a GCP project.
100
+
101
+ Args:
102
+ project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
103
+ service_account_email: Email of the service account to check
104
+ (e.g., 'my-sa@project.iam.gserviceaccount.com')
105
+ organization_id: GCP organization ID (e.g., '111111111111')
106
+ role: Role to check (e.g., 'roles/owner', 'roles/editor')
107
+ expand_groups: Whether to expand group memberships (default: True)
108
+
109
+ Returns:
110
+ bool: True if the service account has the role in the project, False otherwise
111
+
112
+ Example:
113
+ >>> has_access = check_service_account_has_role_in_project(
114
+ ... project_id='sky-starfi-mam-res-gcpro-1',
115
+ ... service_account_email='my-sa@project.iam.gserviceaccount.com',
116
+ ... organization_id='111111111111',
117
+ ... role='roles/owner'
118
+ ... )
119
+ """
120
+ client = asset_v1.AssetServiceClient()
121
+
122
+ # Construct the full resource name
123
+ scope = f"organizations/{organization_id}"
124
+ full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
125
+ identity = f"serviceAccount:{service_account_email}"
126
+
127
+ # Build the request
128
+ request = asset_v1.AnalyzeIamPolicyRequest(
129
+ analysis_query=asset_v1.IamPolicyAnalysisQuery(
130
+ scope=scope,
131
+ resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
132
+ full_resource_name=full_resource_name
133
+ ),
134
+ identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
135
+ identity=identity
136
+ ),
137
+ access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
138
+ roles=[role]
139
+ ),
140
+ options=asset_v1.IamPolicyAnalysisQuery.Options(
141
+ expand_groups=expand_groups,
142
+ expand_roles=True,
143
+ )
144
+ )
145
+ )
146
+
147
+ try:
148
+ # Execute the analysis
149
+ logger.info(f"Checking if service account {service_account_email} has role {role} in project {project_id}")
150
+ response = client.analyze_iam_policy(request=request)
151
+
152
+ # Check if any results were returned
153
+ if response.main_analysis and response.main_analysis.analysis_results:
154
+ logger.info(f"Service account {service_account_email} has role {role} in project {project_id}")
155
+ return True
156
+
157
+ logger.info(f"Service account {service_account_email} does not have role {role} in project {project_id}")
158
+ return False
159
+
160
+ except Exception as e:
161
+ logger.exception(f"Error analyzing IAM policy: {e}")
162
+ raise
163
+
164
+
165
+ def check_group_has_role_in_project(
166
+ project_id: str,
167
+ group_email: str,
168
+ organization_id: str,
169
+ role: str = "roles/owner",
170
+ expand_groups: bool = True
171
+ ) -> bool:
172
+ """
173
+ Check if a group has a specific role in a GCP project.
174
+
175
+ Args:
176
+ project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
177
+ group_email: Email of the group to check (e.g., 'dev-team@gcporg.com')
178
+ organization_id: GCP organization ID (e.g., '111111111111')
179
+ role: Role to check (e.g., 'roles/owner', 'roles/editor')
180
+ expand_groups: Whether to expand group memberships (default: True)
181
+
182
+ Returns:
183
+ bool: True if the group has the role in the project, False otherwise
184
+
185
+ Example:
186
+ >>> has_access = check_group_has_role_in_project(
187
+ ... project_id='sky-starfi-mam-res-gcpro-1',
188
+ ... group_email='dev-team@gcporg.com',
189
+ ... organization_id='111111111111',
190
+ ... role='roles/owner'
191
+ ... )
192
+ """
193
+ client = asset_v1.AssetServiceClient()
194
+
195
+ # Construct the full resource name
196
+ scope = f"organizations/{organization_id}"
197
+ full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
198
+ identity = f"group:{group_email}"
199
+
200
+ # Build the request
201
+ request = asset_v1.AnalyzeIamPolicyRequest(
202
+ analysis_query=asset_v1.IamPolicyAnalysisQuery(
203
+ scope=scope,
204
+ resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
205
+ full_resource_name=full_resource_name
206
+ ),
207
+ identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
208
+ identity=identity
209
+ ),
210
+ access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
211
+ roles=[role]
212
+ ),
213
+ options=asset_v1.IamPolicyAnalysisQuery.Options(
214
+ expand_groups=expand_groups,
215
+ expand_roles=True,
216
+ )
217
+ )
218
+ )
219
+
220
+ try:
221
+ # Execute the analysis
222
+ logger.info(f"Checking if group {group_email} has role {role} in project {project_id}")
223
+ response = client.analyze_iam_policy(request=request)
224
+
225
+ # Check if any results were returned
226
+ if response.main_analysis and response.main_analysis.analysis_results:
227
+ logger.info(f"Group {group_email} has role {role} in project {project_id}")
228
+ return True
229
+
230
+ logger.info(f"Group {group_email} does not have role {role} in project {project_id}")
231
+ return False
232
+
233
+ except Exception as e:
234
+ logger.exception(f"Error analyzing IAM policy: {e}")
235
+ raise
236
+
237
+
238
+ def _get_all_service_projects(base_paths, prefix):
239
+ service_projects = []
240
+
241
+ for path in base_paths:
242
+ logger.info(f"[INFO] Searching for projects under: {path}")
243
+
244
+ for root, _, _ in os.walk(path):
245
+ if root == path:
246
+ continue
247
+
248
+ for _, _, projects in os.walk(root):
249
+ for project in projects:
250
+ if not project.endswith(".yaml"):
251
+ continue
252
+ if project.startswith(prefix):
253
+ service_projects.append(project.split('.')[0])
254
+ else:
255
+ service_projects.append(f"{prefix}-{project.split('.')[0]}")
256
+
257
+ return list(set(service_projects))
258
+
259
+
260
+ def get_projects_with_role(
261
+ user_email: str,
262
+ organization_id: str,
263
+ role: str = "roles/owner",
264
+ expand_groups: bool = True,
265
+ project_prefix: Optional[str] = None,
266
+ projects_base_paths: Optional[list] = []
267
+ ) -> list:
268
+ """
269
+ Get all projects where a user or service account has a specific role.
270
+ Searches for all service projects in the organization.
271
+
272
+ Args:
273
+ user_email: Email of the user or service account to check
274
+ (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
275
+ organization_id: GCP organization ID (e.g., '111111111111')
276
+ role: Role to check (e.g., 'roles/owner', 'roles/editor')
277
+ expand_groups: Whether to expand group memberships (default: True)
278
+
279
+ Returns:
280
+ list: List of project IDs where the user/service account has the specified role
281
+
282
+ Example:
283
+ >>> projects = get_projects_with_role(
284
+ ... user_email='oshasha10@gcporg.com',
285
+ ... organization_id='111111111111',
286
+ ... role='roles/owner'
287
+ ... )
288
+ """
289
+ logger.info(f"Fetching all projects in organization {organization_id} (including folders)")
290
+
291
+ try:
292
+ all_projects = _get_all_service_projects(
293
+ base_paths=projects_base_paths,
294
+ prefix=project_prefix
295
+ )
296
+
297
+ logger.info(f"Found {len(all_projects)} total service project(s) across organization {organization_id}")
298
+
299
+ # Filter projects where user has the specified role
300
+ matching_projects = []
301
+
302
+ for project_id in all_projects:
303
+ # Check if user has the role in this project
304
+ if check_user_has_role_in_project(
305
+ project_id=project_id,
306
+ user_email=user_email,
307
+ organization_id=organization_id,
308
+ role=role,
309
+ expand_groups=expand_groups
310
+ ):
311
+ matching_projects.append(project_id)
312
+
313
+ logger.info(f"Checked {len(all_projects)} projects, found {len(matching_projects)} where {user_email} has role {role}")
314
+ return sorted(matching_projects)
315
+
316
+ except Exception as e:
317
+ logger.exception(f"Error fetching projects: {e}")
318
+ raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcp_platforms_auto
3
- Version: 0.8.3
3
+ Version: 0.8.4
4
4
  Summary: A brief description of your package
5
5
  Author-email: ofir4858 <ofirshasha10@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gcp_platforms_auto"
7
- version = "0.8.3"
7
+ version = "0.8.4"
8
8
  description = "A brief description of your package"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,190 +0,0 @@
1
- """IAM access management utilities for GCP."""
2
-
3
- import logging
4
- import os
5
- import google.cloud.logging
6
- from google.cloud import asset_v1
7
- from typing import Optional
8
-
9
- # Initialize Google Cloud Logging
10
- client = google.cloud.logging.Client()
11
- client.setup_logging()
12
-
13
- # Configure the logger
14
- logger = logging.getLogger("uvicorn")
15
- logger.setLevel(logging.INFO)
16
-
17
-
18
- def _get_identity_string(email: str) -> str:
19
- """
20
- Helper function to construct the proper identity string.
21
- Automatically detects if the email is a service account or user.
22
-
23
- Args:
24
- email: Email address (user or service account)
25
-
26
- Returns:
27
- str: Properly formatted identity string
28
- """
29
- if ".gserviceaccount.com" in email.lower():
30
- return f"serviceAccount:{email}"
31
- else:
32
- return f"user:{email}"
33
-
34
-
35
- def check_user_has_role_in_project(
36
- project_id: str,
37
- user_email: str,
38
- organization_id: str,
39
- role: str = "roles/owner",
40
- expand_groups: bool = True
41
- ) -> bool:
42
- """
43
- Check if a user or service account has a specific role in a GCP project.
44
-
45
- Args:
46
- user_email: Email of the user or service account to check
47
- (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
48
- role: Role to check (e.g., 'roles/owner', 'roles/editor')
49
- project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
50
- organization_id: GCP organization ID (e.g., '111111111111')
51
- expand_groups: Whether to expand group memberships (default: True)
52
-
53
- Returns:
54
- bool: True if the user/service account has the role in the project, False otherwise
55
-
56
- Example:
57
- >>> has_access = check_user_has_role_in_project(
58
- ... user_email='oshasha10@gcporg.com',
59
- ... role='roles/owner',
60
- ... project_id='sky-starfi-mam-res-gcpro-1',
61
- ... )
62
- """
63
- client = asset_v1.AssetServiceClient()
64
-
65
- # Construct the full resource name
66
- scope = f"organizations/{organization_id}"
67
- full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
68
- identity = _get_identity_string(user_email)
69
-
70
- # Build the request
71
- request = asset_v1.AnalyzeIamPolicyRequest(
72
- analysis_query=asset_v1.IamPolicyAnalysisQuery(
73
- scope=scope,
74
- resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
75
- full_resource_name=full_resource_name
76
- ),
77
- identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
78
- identity=identity
79
- ),
80
- access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
81
- roles=[role]
82
- ),
83
- options=asset_v1.IamPolicyAnalysisQuery.Options(
84
- expand_groups=expand_groups,
85
- expand_roles=True,
86
- # expand_resources=True
87
- )
88
- )
89
- )
90
-
91
- try:
92
- # Execute the analysis
93
- logger.info(f"Checking if {user_email} has role {role} in project {project_id}")
94
- response = client.analyze_iam_policy(request=request)
95
-
96
- # Check if any results were returned
97
- # If the user has the role, there will be analysis results
98
- if response.main_analysis and response.main_analysis.analysis_results:
99
- logger.info(f"{user_email} has role {role} in project {project_id}")
100
- return True
101
-
102
- logger.info(f"{user_email} does not have role {role} in project {project_id}")
103
- return False
104
-
105
- except Exception as e:
106
- logger.exception(f"Error analyzing IAM policy: {e}")
107
- raise
108
-
109
-
110
- def _get_all_service_projects(base_paths, prefix):
111
- service_projects = []
112
-
113
- for path in base_paths:
114
- logger.info(f"[INFO] Searching for projects under: {path}")
115
-
116
- for root, _, _ in os.walk(path):
117
- if root == path:
118
- continue
119
-
120
- for _, _, projects in os.walk(root):
121
- for project in projects:
122
- if not project.endswith(".yaml"):
123
- continue
124
- if project.startswith(prefix):
125
- service_projects.append(project.split('.')[0])
126
- else:
127
- service_projects.append(f"{prefix}-{project.split('.')[0]}")
128
-
129
- return list(set(service_projects))
130
-
131
-
132
- def get_projects_with_role(
133
- user_email: str,
134
- organization_id: str,
135
- role: str = "roles/owner",
136
- expand_groups: bool = True,
137
- project_prefix: Optional[str] = None,
138
- projects_base_paths: Optional[list] = []
139
- ) -> list:
140
- """
141
- Get all projects where a user or service account has a specific role.
142
- Searches for all service projects in the organization.
143
-
144
- Args:
145
- user_email: Email of the user or service account to check
146
- (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
147
- organization_id: GCP organization ID (e.g., '111111111111')
148
- role: Role to check (e.g., 'roles/owner', 'roles/editor')
149
- expand_groups: Whether to expand group memberships (default: True)
150
-
151
- Returns:
152
- list: List of project IDs where the user/service account has the specified role
153
-
154
- Example:
155
- >>> projects = get_projects_with_role(
156
- ... user_email='oshasha10@gcporg.com',
157
- ... organization_id='111111111111',
158
- ... role='roles/owner'
159
- ... )
160
- """
161
- logger.info(f"Fetching all projects in organization {organization_id} (including folders)")
162
-
163
- try:
164
- all_projects = _get_all_service_projects(
165
- base_paths=projects_base_paths,
166
- prefix=project_prefix
167
- )
168
-
169
- logger.info(f"Found {len(all_projects)} total service project(s) across organization {organization_id}")
170
-
171
- # Filter projects where user has the specified role
172
- matching_projects = []
173
-
174
- for project_id in all_projects:
175
- # Check if user has the role in this project
176
- if check_user_has_role_in_project(
177
- project_id=project_id,
178
- user_email=user_email,
179
- organization_id=organization_id,
180
- role=role,
181
- expand_groups=expand_groups
182
- ):
183
- matching_projects.append(project_id)
184
-
185
- logger.info(f"Checked {len(all_projects)} projects, found {len(matching_projects)} where {user_email} has role {role}")
186
- return sorted(matching_projects)
187
-
188
- except Exception as e:
189
- logger.exception(f"Error fetching projects: {e}")
190
- raise