gcp-platforms-auto 0.8.2__py3-none-any.whl → 0.8.4__py3-none-any.whl

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.
@@ -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
 
gcp_platforms_auto/iam.py CHANGED
@@ -1,8 +1,9 @@
1
1
  """IAM access management utilities for GCP."""
2
2
 
3
3
  import logging
4
+ import os
4
5
  import google.cloud.logging
5
- from google.cloud import asset_v1, resourcemanager_v3
6
+ from google.cloud import asset_v1
6
7
  from typing import Optional
7
8
 
8
9
  # Initialize Google Cloud Logging
@@ -14,49 +15,106 @@ logger = logging.getLogger("uvicorn")
14
15
  logger.setLevel(logging.INFO)
15
16
 
16
17
 
17
- def _get_identity_string(email: str) -> str:
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:
18
25
  """
19
- Helper function to construct the proper identity string.
20
- Automatically detects if the email is a service account or user.
26
+ Check if a user has a specific role in a GCP project.
21
27
 
22
28
  Args:
23
- email: Email address (user or service account)
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)
24
34
 
25
35
  Returns:
26
- str: Properly formatted identity string
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
+ ... )
27
45
  """
28
- if ".gserviceaccount.com" in email.lower():
29
- return f"serviceAccount:{email}"
30
- else:
31
- return f"user:{email}"
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
32
89
 
33
90
 
34
- def check_user_has_role_in_project(
91
+ def check_service_account_has_role_in_project(
35
92
  project_id: str,
36
- user_email: str,
93
+ service_account_email: str,
37
94
  organization_id: str,
38
95
  role: str = "roles/owner",
39
96
  expand_groups: bool = True
40
97
  ) -> bool:
41
98
  """
42
- Check if a user or service account has a specific role in a GCP project.
99
+ Check if a service account has a specific role in a GCP project.
43
100
 
44
101
  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
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')
49
105
  organization_id: GCP organization ID (e.g., '111111111111')
106
+ role: Role to check (e.g., 'roles/owner', 'roles/editor')
50
107
  expand_groups: Whether to expand group memberships (default: True)
51
108
 
52
109
  Returns:
53
- bool: True if the user/service account has the role in the project, False otherwise
110
+ bool: True if the service account has the role in the project, False otherwise
54
111
 
55
112
  Example:
56
- >>> has_access = check_user_has_role_in_project(
57
- ... user_email='oshasha10@gcporg.com',
58
- ... role='roles/owner',
113
+ >>> has_access = check_service_account_has_role_in_project(
59
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'
60
118
  ... )
61
119
  """
62
120
  client = asset_v1.AssetServiceClient()
@@ -64,7 +122,7 @@ def check_user_has_role_in_project(
64
122
  # Construct the full resource name
65
123
  scope = f"organizations/{organization_id}"
66
124
  full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
67
- identity = _get_identity_string(user_email)
125
+ identity = f"serviceAccount:{service_account_email}"
68
126
 
69
127
  # Build the request
70
128
  request = asset_v1.AnalyzeIamPolicyRequest(
@@ -82,23 +140,21 @@ def check_user_has_role_in_project(
82
140
  options=asset_v1.IamPolicyAnalysisQuery.Options(
83
141
  expand_groups=expand_groups,
84
142
  expand_roles=True,
85
- # expand_resources=True
86
143
  )
87
144
  )
88
145
  )
89
146
 
90
147
  try:
91
148
  # Execute the analysis
92
- logger.info(f"Checking if {user_email} has role {role} in project {project_id}")
149
+ logger.info(f"Checking if service account {service_account_email} has role {role} in project {project_id}")
93
150
  response = client.analyze_iam_policy(request=request)
94
151
 
95
152
  # Check if any results were returned
96
- # If the user has the role, there will be analysis results
97
153
  if response.main_analysis and response.main_analysis.analysis_results:
98
- logger.info(f"{user_email} has role {role} in project {project_id}")
154
+ logger.info(f"Service account {service_account_email} has role {role} in project {project_id}")
99
155
  return True
100
156
 
101
- logger.info(f"{user_email} does not have role {role} in project {project_id}")
157
+ logger.info(f"Service account {service_account_email} does not have role {role} in project {project_id}")
102
158
  return False
103
159
 
104
160
  except Exception as e:
@@ -106,47 +162,112 @@ def check_user_has_role_in_project(
106
162
  raise
107
163
 
108
164
 
109
- def _get_all_folders_recursive(parent: str, folders_client) -> list:
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:
110
172
  """
111
- Recursively get all folders under a parent (organization or folder).
173
+ Check if a group has a specific role in a GCP project.
112
174
 
113
175
  Args:
114
- parent: Parent resource (e.g., 'organizations/123' or 'folders/456')
115
- folders_client: FoldersClient instance
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)
116
181
 
117
182
  Returns:
118
- list: List of all folder resource names
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
+ ... )
119
192
  """
120
- all_folders = []
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
+ )
121
219
 
122
220
  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
-
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
+
135
233
  except Exception as e:
136
- logger.warning(f"Error listing folders under {parent}: {e}")
137
-
138
- return all_folders
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))
139
258
 
140
259
 
141
260
  def get_projects_with_role(
142
261
  user_email: str,
143
262
  organization_id: str,
144
263
  role: str = "roles/owner",
145
- expand_groups: bool = True
264
+ expand_groups: bool = True,
265
+ project_prefix: Optional[str] = None,
266
+ projects_base_paths: Optional[list] = []
146
267
  ) -> list:
147
268
  """
148
269
  Get all projects where a user or service account has a specific role.
149
- Searches recursively through all folders in the organization.
270
+ Searches for all service projects in the organization.
150
271
 
151
272
  Args:
152
273
  user_email: Email of the user or service account to check
@@ -168,27 +289,12 @@ def get_projects_with_role(
168
289
  logger.info(f"Fetching all projects in organization {organization_id} (including folders)")
169
290
 
170
291
  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")
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}")
192
298
 
193
299
  # Filter projects where user has the specified role
194
300
  matching_projects = []
@@ -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
@@ -0,0 +1,9 @@
1
+ gcp_platforms_auto/__init__.py,sha256=pKouDuMclA8r93k5H9TjGbxNsFwqIYfymjO-owuGGws,526
2
+ gcp_platforms_auto/db.py,sha256=jE5nwmqVHcxT4m6-meUgUz4V4DM8M_sMmeTpvKr2Z2Y,8768
3
+ gcp_platforms_auto/git.py,sha256=NnLDfRzzrzbm9yekepc-qgu8ejYmjNxQ4VDlW46gG2o,5508
4
+ gcp_platforms_auto/iam.py,sha256=wnMZ_jl2YM5dLY8LqtPfEyh_0Osqs1ypsxakJGmwHVw,11614
5
+ gcp_platforms_auto/models.py,sha256=mVg8NKV25kqdTuazqenAp7Ay03N5D8GIh3F_TWP0zyI,853
6
+ gcp_platforms_auto-0.8.4.dist-info/METADATA,sha256=UU0EYfRtyhRgQW_1867j05FBs16hAAan5w_UWKQn15w,600
7
+ gcp_platforms_auto-0.8.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
+ gcp_platforms_auto-0.8.4.dist-info/top_level.txt,sha256=4q-ofPMmvBaTnIbAzs-Wp_OwheAVxxmJ1fW9vl3-kyE,19
9
+ gcp_platforms_auto-0.8.4.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- gcp_platforms_auto/__init__.py,sha256=MhphRLBFhpZSik83woVgJi3NpyGjBRZ4RyOcXu8dqAk,443
2
- gcp_platforms_auto/db.py,sha256=jE5nwmqVHcxT4m6-meUgUz4V4DM8M_sMmeTpvKr2Z2Y,8768
3
- gcp_platforms_auto/git.py,sha256=NnLDfRzzrzbm9yekepc-qgu8ejYmjNxQ4VDlW46gG2o,5508
4
- gcp_platforms_auto/iam.py,sha256=BFf4LitUXzHrDETZHO_sf2zw6XGrR9a_Vg-TBIqE77k,7671
5
- gcp_platforms_auto/models.py,sha256=mVg8NKV25kqdTuazqenAp7Ay03N5D8GIh3F_TWP0zyI,853
6
- gcp_platforms_auto-0.8.2.dist-info/METADATA,sha256=_Kn33l2ax-3uMpd3x96La8euXPO3wqv0xKpD25KUT1s,645
7
- gcp_platforms_auto-0.8.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
- gcp_platforms_auto-0.8.2.dist-info/top_level.txt,sha256=4q-ofPMmvBaTnIbAzs-Wp_OwheAVxxmJ1fW9vl3-kyE,19
9
- gcp_platforms_auto-0.8.2.dist-info/RECORD,,