cartography 0.99.0rc1__py3-none-any.whl → 0.100.0rc2__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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

@@ -0,0 +1,222 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ from googleapiclient.discovery import Resource
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.models.gcp.iam import GCPRoleSchema
12
+ from cartography.models.gcp.iam import GCPServiceAccountSchema
13
+ from cartography.util import timeit
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # GCP API can be subject to rate limiting, so add small delays between calls
18
+ LIST_SLEEP = 1
19
+ DESCRIBE_SLEEP = 1
20
+
21
+
22
+ @timeit
23
+ def get_gcp_service_accounts(iam_client: Resource, project_id: str) -> List[Dict[str, Any]]:
24
+ """
25
+ Retrieve a list of GCP service accounts for a given project.
26
+
27
+ :param iam_client: The IAM resource object created by googleapiclient.discovery.build().
28
+ :param project_id: The GCP Project ID to retrieve service accounts from.
29
+ :return: A list of dictionaries representing GCP service accounts.
30
+ """
31
+ service_accounts: List[Dict[str, Any]] = []
32
+ try:
33
+ request = iam_client.projects().serviceAccounts().list(
34
+ name=f'projects/{project_id}',
35
+ )
36
+ while request is not None:
37
+ response = request.execute()
38
+ if 'accounts' in response:
39
+ service_accounts.extend(response['accounts'])
40
+ request = iam_client.projects().serviceAccounts().list_next(
41
+ previous_request=request,
42
+ previous_response=response,
43
+ )
44
+ except Exception as e:
45
+ logger.warning(f"Error retrieving service accounts for project {project_id}: {e}")
46
+ return service_accounts
47
+
48
+
49
+ @timeit
50
+ def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
51
+ """
52
+ Retrieve custom and predefined roles from GCP for a given project.
53
+
54
+ :param iam_client: The IAM resource object created by googleapiclient.discovery.build().
55
+ :param project_id: The GCP Project ID to retrieve roles from.
56
+ :return: A list of dictionaries representing GCP roles.
57
+ """
58
+ try:
59
+ roles = []
60
+
61
+ # Get custom roles
62
+ custom_req = iam_client.projects().roles().list(parent=f'projects/{project_id}')
63
+ while custom_req is not None:
64
+ resp = custom_req.execute()
65
+ roles.extend(resp.get('roles', []))
66
+ custom_req = iam_client.projects().roles().list_next(custom_req, resp)
67
+
68
+ # Get predefined roles
69
+ predefined_req = iam_client.roles().list(view='FULL')
70
+ while predefined_req is not None:
71
+ resp = predefined_req.execute()
72
+ roles.extend(resp.get('roles', []))
73
+ predefined_req = iam_client.roles().list_next(predefined_req, resp)
74
+
75
+ return roles
76
+ except Exception as e:
77
+ logger.warning(f"Error getting GCP roles - {e}")
78
+ return []
79
+
80
+
81
+ @timeit
82
+ def load_gcp_service_accounts(
83
+ neo4j_session: neo4j.Session,
84
+ service_accounts: List[Dict[str, Any]],
85
+ project_id: str,
86
+ gcp_update_tag: int,
87
+ ) -> None:
88
+ """
89
+ Load GCP service account data into Neo4j.
90
+
91
+ :param neo4j_session: The Neo4j session.
92
+ :param service_accounts: A list of service account data to load.
93
+ :param project_id: The GCP Project ID associated with the service accounts.
94
+ :param gcp_update_tag: The timestamp of the current sync run.
95
+ """
96
+ logger.debug(f"Loading {len(service_accounts)} service accounts for project {project_id}")
97
+ transformed_service_accounts = []
98
+ for sa in service_accounts:
99
+ transformed_sa = {
100
+ 'id': sa['uniqueId'],
101
+ 'email': sa.get('email'),
102
+ 'displayName': sa.get('displayName'),
103
+ 'oauth2ClientId': sa.get('oauth2ClientId'),
104
+ 'uniqueId': sa.get('uniqueId'),
105
+ 'disabled': sa.get('disabled', False),
106
+ 'projectId': project_id,
107
+ }
108
+ transformed_service_accounts.append(transformed_sa)
109
+
110
+ load(
111
+ neo4j_session,
112
+ GCPServiceAccountSchema(),
113
+ transformed_service_accounts,
114
+ lastupdated=gcp_update_tag,
115
+ projectId=project_id,
116
+ additional_labels=['GCPPrincipal'],
117
+ )
118
+
119
+
120
+ @timeit
121
+ def load_gcp_roles(
122
+ neo4j_session: neo4j.Session,
123
+ roles: List[Dict[str, Any]],
124
+ project_id: str,
125
+ gcp_update_tag: int,
126
+ ) -> None:
127
+ """
128
+ Load GCP role data into Neo4j.
129
+
130
+ :param neo4j_session: The Neo4j session.
131
+ :param roles: A list of role data to load.
132
+ :param project_id: The GCP Project ID associated with the roles.
133
+ :param gcp_update_tag: The timestamp of the current sync run.
134
+ """
135
+ logger.debug(f"Loading {len(roles)} roles for project {project_id}")
136
+ transformed_roles = []
137
+ for role in roles:
138
+ role_name = role['name']
139
+ if role_name.startswith('roles/'):
140
+ if role_name in ['roles/owner', 'roles/editor', 'roles/viewer']:
141
+ role_type = 'BASIC'
142
+ else:
143
+ role_type = 'PREDEFINED'
144
+ else:
145
+ role_type = 'CUSTOM'
146
+
147
+ transformed_role = {
148
+ 'id': role_name,
149
+ 'name': role_name,
150
+ 'title': role.get('title'),
151
+ 'description': role.get('description'),
152
+ 'deleted': role.get('deleted', False),
153
+ 'etag': role.get('etag'),
154
+ 'includedPermissions': role.get('includedPermissions', []),
155
+ 'roleType': role_type,
156
+ 'projectId': project_id,
157
+ }
158
+ transformed_roles.append(transformed_role)
159
+
160
+ load(
161
+ neo4j_session,
162
+ GCPRoleSchema(),
163
+ transformed_roles,
164
+ lastupdated=gcp_update_tag,
165
+ projectId=project_id,
166
+ )
167
+
168
+
169
+ @timeit
170
+ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
171
+ """
172
+ Run cleanup jobs for GCP IAM data in Neo4j.
173
+
174
+ :param neo4j_session: The Neo4j session.
175
+ :param common_job_parameters: Common job parameters for cleanup.
176
+ """
177
+ logger.debug("Running GCP IAM cleanup job")
178
+ job_params = {
179
+ **common_job_parameters,
180
+ 'projectId': common_job_parameters.get('PROJECT_ID'),
181
+ }
182
+
183
+ cleanup_jobs = [
184
+ GraphJob.from_node_schema(GCPServiceAccountSchema(), job_params),
185
+ GraphJob.from_node_schema(GCPRoleSchema(), job_params),
186
+ ]
187
+
188
+ for cleanup_job in cleanup_jobs:
189
+ cleanup_job.run(neo4j_session)
190
+
191
+
192
+ @timeit
193
+ def sync(
194
+ neo4j_session: neo4j.Session,
195
+ iam_client: Resource,
196
+ project_id: str,
197
+ gcp_update_tag: int,
198
+ common_job_parameters: Dict[str, Any],
199
+ ) -> None:
200
+ """
201
+ Sync GCP IAM resources for a given project.
202
+
203
+ :param neo4j_session: The Neo4j session.
204
+ :param iam_client: The IAM resource object created by googleapiclient.discovery.build().
205
+ :param project_id: The GCP Project ID to sync.
206
+ :param gcp_update_tag: The timestamp of the current sync run.
207
+ :param common_job_parameters: Common job parameters for the sync.
208
+ """
209
+ logger.info(f"Syncing GCP IAM for project {project_id}")
210
+
211
+ # Get and load service accounts
212
+ service_accounts = get_gcp_service_accounts(iam_client, project_id)
213
+ logger.info(f"Found {len(service_accounts)} service accounts in project {project_id}")
214
+ load_gcp_service_accounts(neo4j_session, service_accounts, project_id, gcp_update_tag)
215
+
216
+ # Get and load roles
217
+ roles = get_gcp_roles(iam_client, project_id)
218
+ logger.info(f"Found {len(roles)} roles in project {project_id}")
219
+ load_gcp_roles(neo4j_session, roles, project_id, gcp_update_tag)
220
+
221
+ # Run cleanup
222
+ cleanup(neo4j_session, common_job_parameters)
@@ -6,6 +6,7 @@ from collections import namedtuple
6
6
 
7
7
  import googleapiclient.discovery
8
8
  import neo4j
9
+ from google.auth import default
9
10
  from google.auth.exceptions import DefaultCredentialsError
10
11
  from google.auth.transport.requests import Request
11
12
  from google.oauth2 import credentials
@@ -18,10 +19,11 @@ from cartography.config import Config
18
19
  from cartography.intel.gsuite import api
19
20
  from cartography.util import timeit
20
21
 
21
- OAUTH_SCOPE = [
22
+ OAUTH_SCOPES = [
22
23
  'https://www.googleapis.com/auth/admin.directory.user.readonly',
23
24
  'https://www.googleapis.com/auth/admin.directory.group.readonly',
24
25
  'https://www.googleapis.com/auth/admin.directory.group.member',
26
+ 'https://www.googleapis.com/auth/cloud-platform',
25
27
  ]
26
28
 
27
29
  logger = logging.getLogger(__name__)
@@ -70,7 +72,7 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
70
72
  try:
71
73
  creds = service_account.Credentials.from_service_account_file(
72
74
  config.gsuite_config,
73
- scopes=OAUTH_SCOPE,
75
+ scopes=OAUTH_SCOPES,
74
76
  )
75
77
  creds = creds.with_subject(os.environ.get('GSUITE_DELEGATED_ADMIN'))
76
78
 
@@ -96,10 +98,10 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
96
98
  refresh_token=auth_tokens['refresh_token'],
97
99
  expiry=None,
98
100
  token_uri=auth_tokens['token_uri'],
99
- scopes=OAUTH_SCOPE,
101
+ scopes=OAUTH_SCOPES,
100
102
  )
101
103
  creds.refresh(Request())
102
- creds = creds.create_scoped(OAUTH_SCOPE)
104
+ creds = creds.create_scoped(OAUTH_SCOPES)
103
105
  except DefaultCredentialsError as e:
104
106
  logger.error(
105
107
  (
@@ -111,6 +113,21 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
111
113
  e,
112
114
  )
113
115
  return
116
+ elif config.gsuite_auth_method == 'default':
117
+ logger.info('Attempting to authenticate to GSuite using default credentials')
118
+ try:
119
+ creds, _ = default(scopes=OAUTH_SCOPES)
120
+ except DefaultCredentialsError as e:
121
+ logger.error(
122
+ (
123
+ "Unable to initialize GSuite creds using default credentials. If you don't have GSuite data or "
124
+ "don't want to load GSuite data then you can ignore this message. Otherwise, the error code is: %s "
125
+ "Make sure you have valid application default credentials configured. "
126
+ "For more details see README"
127
+ ),
128
+ e,
129
+ )
130
+ return
114
131
 
115
132
  resources = _initialize_resources(creds)
116
133
  api.sync_gsuite_users(neo4j_session, resources.admin, config.update_tag, common_job_parameters)
@@ -4,6 +4,7 @@ from typing import List
4
4
 
5
5
  import neo4j
6
6
  from googleapiclient.discovery import Resource
7
+ from googleapiclient.errors import HttpError
7
8
 
8
9
  from cartography.util import run_cleanup_job
9
10
  from cartography.util import timeit
@@ -30,9 +31,21 @@ def get_all_groups(admin: Resource) -> List[Dict]:
30
31
  request = admin.groups().list(customer='my_customer', maxResults=20, orderBy='email')
31
32
  response_objects = []
32
33
  while request is not None:
33
- resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
34
- response_objects.append(resp)
35
- request = admin.groups().list_next(request, resp)
34
+ try:
35
+ resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
36
+ response_objects.append(resp)
37
+ request = admin.groups().list_next(request, resp)
38
+ except HttpError as e:
39
+ if e.resp.status == 403 and "Request had insufficient authentication scopes" in str(e):
40
+ logger.error(
41
+ "Missing required GSuite scopes. If using the gcloud CLI, ",
42
+ "run: gcloud auth application-default login --scopes="
43
+ '"https://www.googleapis.com/auth/admin.directory.user.readonly,'
44
+ 'https://www.googleapis.com/auth/admin.directory.group.readonly,'
45
+ 'https://www.googleapis.com/auth/admin.directory.group.member.readonly,'
46
+ 'https://www.googleapis.com/auth/cloud-platform"',
47
+ )
48
+ raise
36
49
  return response_objects
37
50
 
38
51
 
@@ -140,6 +153,7 @@ def load_gsuite_groups(neo4j_session: neo4j.Session, groups: List[Dict], gsuite_
140
153
  g.etag = group.etag,
141
154
  g.kind = group.kind,
142
155
  g.name = group.name,
156
+ g:GCPPrincipal,
143
157
  g.lastupdated = $UpdateTag
144
158
  """
145
159
  logger.info(f'Ingesting {len(groups)} gsuite groups')
@@ -179,6 +193,7 @@ def load_gsuite_users(neo4j_session: neo4j.Session, users: List[Dict], gsuite_up
179
193
  u.suspended = user.suspended,
180
194
  u.thumbnail_photo_etag = user.thumbnailPhotoEtag,
181
195
  u.thumbnail_photo_url = user.thumbnailPhotoUrl,
196
+ u:GCPPrincipal,
182
197
  u.lastupdated = $UpdateTag
183
198
  """
184
199
  logger.info(f'Ingesting {len(users)} gsuite users')
@@ -0,0 +1,47 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import TargetNodeMatcher
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class APIGatewayRestAPINodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef('id', extra_index=True)
16
+ createddate: PropertyRef = PropertyRef('createdDate')
17
+ version: PropertyRef = PropertyRef('version')
18
+ minimumcompressionsize: PropertyRef = PropertyRef('minimumCompressionSize')
19
+ disableexecuteapiendpoint: PropertyRef = PropertyRef('disableExecuteApiEndpoint')
20
+ region: PropertyRef = PropertyRef('region', set_in_kwargs=True)
21
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
22
+ anonymous_access: PropertyRef = PropertyRef('anonymous_access')
23
+ anonymous_actions: PropertyRef = PropertyRef('anonymous_actions')
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class APIGatewayRestAPIToAwsAccountRelProperties(CartographyRelProperties):
28
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ # (:APIGatewayRestAPI)<-[:RESOURCE]-(:AWSAccount)
33
+ class APIGatewayRestAPIToAWSAccount(CartographyRelSchema):
34
+ target_node_label: str = 'AWSAccount'
35
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
36
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
37
+ )
38
+ direction: LinkDirection = LinkDirection.INWARD
39
+ rel_label: str = "RESOURCE"
40
+ properties: APIGatewayRestAPIToAwsAccountRelProperties = APIGatewayRestAPIToAwsAccountRelProperties()
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class APIGatewayRestAPISchema(CartographyNodeSchema):
45
+ label: str = 'APIGatewayRestAPI'
46
+ properties: APIGatewayRestAPINodeProperties = APIGatewayRestAPINodeProperties()
47
+ sub_resource_relationship: APIGatewayRestAPIToAWSAccount = APIGatewayRestAPIToAWSAccount()
@@ -0,0 +1,66 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class APIGatewayClientCertificateNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef('clientCertificateId')
17
+ createddate: PropertyRef = PropertyRef('createdDate')
18
+ expirationdate: PropertyRef = PropertyRef('expirationDate')
19
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class APIGatewayClientCertificateToStageRelProperties(CartographyRelProperties):
24
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class CertToStageRelProps(CartographyRelProperties):
29
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ # (:APIGatewayStage)-[:HAS_CERTIFICATE]->(:APIGatewayClientCertificate)
34
+ class APIGatewayClientCertificateToStage(CartographyRelSchema):
35
+ target_node_label: str = 'APIGatewayStage'
36
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
37
+ {'id': PropertyRef('stageArn')},
38
+ )
39
+ direction: LinkDirection = LinkDirection.INWARD
40
+ rel_label: str = "HAS_CERTIFICATE"
41
+ properties: CertToStageRelProps = CertToStageRelProps()
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class CertToAccountRelProps(CartographyRelProperties):
46
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ # (:APIGatewayClientCertificate)<-[:RESOURCE]-(:AWSAccount)
51
+ class APIGatewayClientCertificateToAWSAccount(CartographyRelSchema):
52
+ target_node_label: str = 'AWSAccount'
53
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
54
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
55
+ )
56
+ direction: LinkDirection = LinkDirection.INWARD
57
+ rel_label: str = "RESOURCE"
58
+ properties: CertToAccountRelProps = CertToAccountRelProps()
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class APIGatewayClientCertificateSchema(CartographyNodeSchema):
63
+ label: str = 'APIGatewayClientCertificate'
64
+ properties: APIGatewayClientCertificateNodeProperties = APIGatewayClientCertificateNodeProperties()
65
+ sub_resource_relationship: APIGatewayClientCertificateToAWSAccount = APIGatewayClientCertificateToAWSAccount()
66
+ other_relationships: OtherRelationships = OtherRelationships([APIGatewayClientCertificateToStage()])
@@ -0,0 +1,62 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class APIGatewayResourceNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef('id')
17
+ path: PropertyRef = PropertyRef('path')
18
+ pathpart: PropertyRef = PropertyRef('pathPart')
19
+ parentid: PropertyRef = PropertyRef('parentId')
20
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class APIGatewayResourceToRestAPIRelProperties(CartographyRelProperties):
25
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ # (:APIGatewayResource)<-[:RESOURCE]-(:APIGatewayRestAPI)
30
+ class APIGatewayResourceToRestAPI(CartographyRelSchema):
31
+ target_node_label: str = 'APIGatewayRestAPI'
32
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
33
+ {'id': PropertyRef('apiId')},
34
+ )
35
+ direction: LinkDirection = LinkDirection.INWARD
36
+ rel_label: str = "RESOURCE"
37
+ properties: APIGatewayResourceToRestAPIRelProperties = APIGatewayResourceToRestAPIRelProperties()
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class APIGatewayResourceToAwsAccountRelProperties(CartographyRelProperties):
42
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ # (:APIGatewayResource)<-[:RESOURCE]-(:AWSAccount)
47
+ class APIGatewayResourceToAWSAccount(CartographyRelSchema):
48
+ target_node_label: str = 'AWSAccount'
49
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
50
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
51
+ )
52
+ direction: LinkDirection = LinkDirection.INWARD
53
+ rel_label: str = "RESOURCE"
54
+ properties: APIGatewayResourceToAwsAccountRelProperties = APIGatewayResourceToAwsAccountRelProperties()
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class APIGatewayResourceSchema(CartographyNodeSchema):
59
+ label: str = 'APIGatewayResource'
60
+ properties: APIGatewayResourceNodeProperties = APIGatewayResourceNodeProperties()
61
+ sub_resource_relationship: APIGatewayResourceToAWSAccount = APIGatewayResourceToAWSAccount()
62
+ other_relationships: OtherRelationships = OtherRelationships([APIGatewayResourceToRestAPI()])
@@ -0,0 +1,67 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class APIGatewayStageNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef('arn')
17
+ stagename: PropertyRef = PropertyRef('stageName')
18
+ createddate: PropertyRef = PropertyRef('createdDate')
19
+ deploymentid: PropertyRef = PropertyRef('deploymentId')
20
+ clientcertificateid: PropertyRef = PropertyRef('clientCertificateId')
21
+ cacheclusterenabled: PropertyRef = PropertyRef('cacheClusterEnabled')
22
+ cacheclusterstatus: PropertyRef = PropertyRef('cacheClusterStatus')
23
+ tracingenabled: PropertyRef = PropertyRef('tracingEnabled')
24
+ webaclarn: PropertyRef = PropertyRef('webAclArn')
25
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class APIGatewayStageToRestAPIRelProperties(CartographyRelProperties):
30
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ # (:APIGatewayStage)<-[:ASSOCIATED_WITH]-(:APIGatewayRestAPI)
35
+ class APIGatewayStageToRestAPI(CartographyRelSchema):
36
+ target_node_label: str = 'APIGatewayRestAPI'
37
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
38
+ {'id': PropertyRef('apiId')},
39
+ )
40
+ direction: LinkDirection = LinkDirection.INWARD
41
+ rel_label: str = "ASSOCIATED_WITH"
42
+ properties: APIGatewayStageToRestAPIRelProperties = APIGatewayStageToRestAPIRelProperties()
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class APIGatewayStageToAwsAccountRelProperties(CartographyRelProperties):
47
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ # (:APIGatewayStage)<-[:RESOURCE]-(:AWSAccount)
52
+ class APIGatewayStageToAWSAccount(CartographyRelSchema):
53
+ target_node_label: str = 'AWSAccount'
54
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
55
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
56
+ )
57
+ direction: LinkDirection = LinkDirection.INWARD
58
+ rel_label: str = "RESOURCE"
59
+ properties: APIGatewayStageToAwsAccountRelProperties = APIGatewayStageToAwsAccountRelProperties()
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class APIGatewayStageSchema(CartographyNodeSchema):
64
+ label: str = 'APIGatewayStage'
65
+ properties: APIGatewayStageNodeProperties = APIGatewayStageNodeProperties()
66
+ sub_resource_relationship: APIGatewayStageToAWSAccount = APIGatewayStageToAWSAccount()
67
+ other_relationships: OtherRelationships = OtherRelationships([APIGatewayStageToRestAPI()])
@@ -0,0 +1,70 @@
1
+ import logging
2
+ from dataclasses import dataclass
3
+
4
+ from cartography.models.core.common import PropertyRef
5
+ from cartography.models.core.nodes import CartographyNodeProperties
6
+ from cartography.models.core.nodes import CartographyNodeSchema
7
+ from cartography.models.core.relationships import CartographyRelProperties
8
+ from cartography.models.core.relationships import CartographyRelSchema
9
+ from cartography.models.core.relationships import LinkDirection
10
+ from cartography.models.core.relationships import make_target_node_matcher
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class GCPServiceAccountNodeProperties(CartographyNodeProperties):
18
+ id: PropertyRef = PropertyRef('id', extra_index=True)
19
+ email: PropertyRef = PropertyRef('email', extra_index=True)
20
+ display_name: PropertyRef = PropertyRef('displayName')
21
+ oauth2_client_id: PropertyRef = PropertyRef('oauth2ClientId')
22
+ unique_id: PropertyRef = PropertyRef('uniqueId')
23
+ disabled: PropertyRef = PropertyRef('disabled')
24
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
25
+ project_id: PropertyRef = PropertyRef('projectId', set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class GCPRoleNodeProperties(CartographyNodeProperties):
30
+ id: PropertyRef = PropertyRef('name', extra_index=True)
31
+ name: PropertyRef = PropertyRef('name', extra_index=True)
32
+ title: PropertyRef = PropertyRef('title')
33
+ description: PropertyRef = PropertyRef('description')
34
+ deleted: PropertyRef = PropertyRef('deleted')
35
+ etag: PropertyRef = PropertyRef('etag')
36
+ permissions: PropertyRef = PropertyRef('includedPermissions')
37
+ role_type: PropertyRef = PropertyRef('roleType')
38
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
39
+ project_id: PropertyRef = PropertyRef('projectId', set_in_kwargs=True)
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class GCPIAMToProjectRelProperties(CartographyRelProperties):
44
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ # (:GCPUser|GCPServiceAccount|GCPRole)<-[:RESOURCE]-(:GCPProject)
49
+ class GCPPrincipalToProject(CartographyRelSchema):
50
+ target_node_label: str = 'GCPProject'
51
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
52
+ {'id': PropertyRef('projectId', set_in_kwargs=True)},
53
+ )
54
+ direction: LinkDirection = LinkDirection.INWARD
55
+ rel_label: str = "RESOURCE"
56
+ properties: GCPIAMToProjectRelProperties = GCPIAMToProjectRelProperties()
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class GCPServiceAccountSchema(CartographyNodeSchema):
61
+ label: str = 'GCPServiceAccount'
62
+ properties: GCPServiceAccountNodeProperties = GCPServiceAccountNodeProperties()
63
+ sub_resource_relationship: GCPPrincipalToProject = GCPPrincipalToProject()
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class GCPRoleSchema(CartographyNodeSchema):
68
+ label: str = 'GCPRole'
69
+ properties: GCPRoleNodeProperties = GCPRoleNodeProperties()
70
+ sub_resource_relationship: GCPPrincipalToProject = GCPPrincipalToProject()