gcp-platforms-auto 0.7.1__tar.gz → 0.7.5__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.7.1
3
+ Version: 0.7.5
4
4
  Summary: A brief description of your package
5
5
  Author-email: ofir4858 <ofirshasha10@gmail.com>
6
6
  License: MIT
@@ -12,6 +12,8 @@ Description-Content-Type: text/markdown
12
12
  Requires-Dist: requests
13
13
  Requires-Dist: pyjwt
14
14
  Requires-Dist: google-cloud-logging
15
+ Requires-Dist: google-cloud-asset
16
+ Requires-Dist: google-cloud-resource-manager
15
17
  Requires-Dist: gitpython
16
18
  Requires-Dist: sqlalchemy
17
19
  Requires-Dist: pg8000
@@ -0,0 +1,212 @@
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(
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@dev.sky320.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(
57
+ ... user_email='oshasha10@dev.sky320.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@dev.sky320.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@dev.sky320.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(
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gcp_platforms_auto
3
- Version: 0.7.1
3
+ Version: 0.7.5
4
4
  Summary: A brief description of your package
5
5
  Author-email: ofir4858 <ofirshasha10@gmail.com>
6
6
  License: MIT
@@ -12,6 +12,8 @@ Description-Content-Type: text/markdown
12
12
  Requires-Dist: requests
13
13
  Requires-Dist: pyjwt
14
14
  Requires-Dist: google-cloud-logging
15
+ Requires-Dist: google-cloud-asset
16
+ Requires-Dist: google-cloud-resource-manager
15
17
  Requires-Dist: gitpython
16
18
  Requires-Dist: sqlalchemy
17
19
  Requires-Dist: pg8000
@@ -3,6 +3,7 @@ pyproject.toml
3
3
  gcp_platforms_auto/__init__.py
4
4
  gcp_platforms_auto/db.py
5
5
  gcp_platforms_auto/git.py
6
+ gcp_platforms_auto/iam.py
6
7
  gcp_platforms_auto.egg-info/PKG-INFO
7
8
  gcp_platforms_auto.egg-info/SOURCES.txt
8
9
  gcp_platforms_auto.egg-info/dependency_links.txt
@@ -1,6 +1,8 @@
1
1
  requests
2
2
  pyjwt
3
3
  google-cloud-logging
4
+ google-cloud-asset
5
+ google-cloud-resource-manager
4
6
  gitpython
5
7
  sqlalchemy
6
8
  pg8000
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gcp_platforms_auto"
7
- version = "0.7.1"
7
+ version = "0.7.5"
8
8
  description = "A brief description of your package"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -15,6 +15,8 @@ dependencies = [
15
15
  "requests",
16
16
  "pyjwt",
17
17
  "google-cloud-logging",
18
+ "google-cloud-asset",
19
+ "google-cloud-resource-manager",
18
20
  "gitpython",
19
21
  "sqlalchemy",
20
22
  "pg8000"