gcp-platforms-auto 0.8.2__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.2
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
@@ -13,7 +13,6 @@ Requires-Dist: requests
13
13
  Requires-Dist: pyjwt
14
14
  Requires-Dist: google-cloud-logging
15
15
  Requires-Dist: google-cloud-asset
16
- Requires-Dist: google-cloud-resource-manager
17
16
  Requires-Dist: gitpython
18
17
  Requires-Dist: sqlalchemy
19
18
  Requires-Dist: pg8000
@@ -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.2
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
@@ -13,7 +13,6 @@ Requires-Dist: requests
13
13
  Requires-Dist: pyjwt
14
14
  Requires-Dist: google-cloud-logging
15
15
  Requires-Dist: google-cloud-asset
16
- Requires-Dist: google-cloud-resource-manager
17
16
  Requires-Dist: gitpython
18
17
  Requires-Dist: sqlalchemy
19
18
  Requires-Dist: pg8000
@@ -2,7 +2,6 @@ requests
2
2
  pyjwt
3
3
  google-cloud-logging
4
4
  google-cloud-asset
5
- google-cloud-resource-manager
6
5
  gitpython
7
6
  sqlalchemy
8
7
  pg8000
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gcp_platforms_auto"
7
- version = "0.8.2"
7
+ version = "0.8.4"
8
8
  description = "A brief description of your package"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -16,7 +16,6 @@ dependencies = [
16
16
  "pyjwt",
17
17
  "google-cloud-logging",
18
18
  "google-cloud-asset",
19
- "google-cloud-resource-manager",
20
19
  "gitpython",
21
20
  "sqlalchemy",
22
21
  "pg8000",
@@ -1,212 +0,0 @@
1
- """IAM access management utilities for GCP."""
2
-
3
- import logging
4
- import google.cloud.logging
5
- from google.cloud import asset_v1, resourcemanager_v3
6
- from typing import Optional
7
-
8
- # Initialize Google Cloud Logging
9
- client = google.cloud.logging.Client()
10
- client.setup_logging()
11
-
12
- # Configure the logger
13
- logger = logging.getLogger("uvicorn")
14
- logger.setLevel(logging.INFO)
15
-
16
-
17
- def _get_identity_string(email: str) -> str:
18
- """
19
- Helper function to construct the proper identity string.
20
- Automatically detects if the email is a service account or user.
21
-
22
- Args:
23
- email: Email address (user or service account)
24
-
25
- Returns:
26
- str: Properly formatted identity string
27
- """
28
- if ".gserviceaccount.com" in email.lower():
29
- return f"serviceAccount:{email}"
30
- else:
31
- return f"user:{email}"
32
-
33
-
34
- def check_user_has_role_in_project(
35
- project_id: str,
36
- user_email: str,
37
- organization_id: str,
38
- role: str = "roles/owner",
39
- expand_groups: bool = True
40
- ) -> bool:
41
- """
42
- Check if a user or service account has a specific role in a GCP project.
43
-
44
- Args:
45
- user_email: Email of the user or service account to check
46
- (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
47
- role: Role to check (e.g., 'roles/owner', 'roles/editor')
48
- project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
49
- organization_id: GCP organization ID (e.g., '111111111111')
50
- expand_groups: Whether to expand group memberships (default: True)
51
-
52
- Returns:
53
- bool: True if the user/service account has the role in the project, False otherwise
54
-
55
- Example:
56
- >>> has_access = check_user_has_role_in_project(
57
- ... user_email='oshasha10@gcporg.com',
58
- ... role='roles/owner',
59
- ... project_id='sky-starfi-mam-res-gcpro-1',
60
- ... )
61
- """
62
- client = asset_v1.AssetServiceClient()
63
-
64
- # Construct the full resource name
65
- scope = f"organizations/{organization_id}"
66
- full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
67
- identity = _get_identity_string(user_email)
68
-
69
- # Build the request
70
- request = asset_v1.AnalyzeIamPolicyRequest(
71
- analysis_query=asset_v1.IamPolicyAnalysisQuery(
72
- scope=scope,
73
- resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
74
- full_resource_name=full_resource_name
75
- ),
76
- identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
77
- identity=identity
78
- ),
79
- access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
80
- roles=[role]
81
- ),
82
- options=asset_v1.IamPolicyAnalysisQuery.Options(
83
- expand_groups=expand_groups,
84
- expand_roles=True,
85
- # expand_resources=True
86
- )
87
- )
88
- )
89
-
90
- try:
91
- # Execute the analysis
92
- logger.info(f"Checking if {user_email} has role {role} in project {project_id}")
93
- response = client.analyze_iam_policy(request=request)
94
-
95
- # Check if any results were returned
96
- # If the user has the role, there will be analysis results
97
- if response.main_analysis and response.main_analysis.analysis_results:
98
- logger.info(f"{user_email} has role {role} in project {project_id}")
99
- return True
100
-
101
- logger.info(f"{user_email} does not have role {role} in project {project_id}")
102
- return False
103
-
104
- except Exception as e:
105
- logger.exception(f"Error analyzing IAM policy: {e}")
106
- raise
107
-
108
-
109
- def _get_all_folders_recursive(parent: str, folders_client) -> list:
110
- """
111
- Recursively get all folders under a parent (organization or folder).
112
-
113
- Args:
114
- parent: Parent resource (e.g., 'organizations/123' or 'folders/456')
115
- folders_client: FoldersClient instance
116
-
117
- Returns:
118
- list: List of all folder resource names
119
- """
120
- all_folders = []
121
-
122
- try:
123
- request = resourcemanager_v3.ListFoldersRequest(parent=parent)
124
- folders = folders_client.list_folders(request=request)
125
-
126
- for folder in folders:
127
- folder_name = folder.name # Format: folders/123
128
- all_folders.append(folder_name)
129
- logger.info(f"Found folder: {folder_name}")
130
-
131
- # Recursively get subfolders
132
- subfolders = _get_all_folders_recursive(folder_name, folders_client)
133
- all_folders.extend(subfolders)
134
-
135
- except Exception as e:
136
- logger.warning(f"Error listing folders under {parent}: {e}")
137
-
138
- return all_folders
139
-
140
-
141
- def get_projects_with_role(
142
- user_email: str,
143
- organization_id: str,
144
- role: str = "roles/owner",
145
- expand_groups: bool = True
146
- ) -> list:
147
- """
148
- Get all projects where a user or service account has a specific role.
149
- Searches recursively through all folders in the organization.
150
-
151
- Args:
152
- user_email: Email of the user or service account to check
153
- (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
154
- organization_id: GCP organization ID (e.g., '111111111111')
155
- role: Role to check (e.g., 'roles/owner', 'roles/editor')
156
- expand_groups: Whether to expand group memberships (default: True)
157
-
158
- Returns:
159
- list: List of project IDs where the user/service account has the specified role
160
-
161
- Example:
162
- >>> projects = get_projects_with_role(
163
- ... user_email='oshasha10@gcporg.com',
164
- ... organization_id='111111111111',
165
- ... role='roles/owner'
166
- ... )
167
- """
168
- logger.info(f"Fetching all projects in organization {organization_id} (including folders)")
169
-
170
- try:
171
- # Initialize clients
172
- projects_client = resourcemanager_v3.ProjectsClient()
173
- folders_client = resourcemanager_v3.FoldersClient()
174
-
175
- # Get all folders recursively
176
- org_parent = f"organizations/{organization_id}"
177
- all_folders = _get_all_folders_recursive(org_parent, folders_client)
178
- logger.info(f"Found {len(all_folders)} folder(s) in organization")
179
-
180
- # Create list of all parents to search (organization + all folders)
181
- parents_to_search = [org_parent] + all_folders
182
-
183
- # Collect all projects from all parents
184
- all_projects = []
185
- for parent in parents_to_search:
186
- request = resourcemanager_v3.ListProjectsRequest(parent=parent)
187
- projects = projects_client.list_projects(request=request)
188
- for project in projects:
189
- all_projects.append(project.project_id)
190
-
191
- logger.info(f"Found {len(all_projects)} total project(s) across organization and folders")
192
-
193
- # Filter projects where user has the specified role
194
- matching_projects = []
195
-
196
- for project_id in all_projects:
197
- # Check if user has the role in this project
198
- if check_user_has_role_in_project(
199
- project_id=project_id,
200
- user_email=user_email,
201
- organization_id=organization_id,
202
- role=role,
203
- expand_groups=expand_groups
204
- ):
205
- matching_projects.append(project_id)
206
-
207
- logger.info(f"Checked {len(all_projects)} projects, found {len(matching_projects)} where {user_email} has role {role}")
208
- return sorted(matching_projects)
209
-
210
- except Exception as e:
211
- logger.exception(f"Error fetching projects: {e}")
212
- raise