cartography 0.112.0__py3-none-any.whl → 0.113.0__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.
- cartography/_version.py +2 -2
- cartography/data/indexes.cypher +0 -4
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/resources.py +2 -0
- cartography/intel/gcp/__init__.py +7 -3
- cartography/intel/gcp/dns.py +82 -169
- cartography/intel/gcp/iam.py +66 -54
- cartography/intel/gcp/storage.py +75 -159
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/METADATA +1 -1
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/RECORD +20 -16
- cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/WHEEL +0 -0
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.112.0.dist-info → cartography-0.113.0.dist-info}/top_level.txt +0 -0
cartography/intel/gcp/storage.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Dict
|
|
3
3
|
from typing import List
|
|
4
|
+
from typing import Tuple
|
|
4
5
|
|
|
5
6
|
import neo4j
|
|
6
7
|
from googleapiclient.discovery import HttpError
|
|
7
8
|
from googleapiclient.discovery import Resource
|
|
8
9
|
|
|
10
|
+
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
9
12
|
from cartography.intel.gcp import compute
|
|
10
|
-
from cartography.
|
|
13
|
+
from cartography.models.gcp.storage.bucket import GCPBucketLabelSchema
|
|
14
|
+
from cartography.models.gcp.storage.bucket import GCPBucketSchema
|
|
11
15
|
from cartography.util import timeit
|
|
12
16
|
|
|
13
17
|
logger = logging.getLogger(__name__)
|
|
@@ -58,165 +62,85 @@ def get_gcp_buckets(storage: Resource, project_id: str) -> Dict:
|
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
@timeit
|
|
61
|
-
def
|
|
65
|
+
def transform_gcp_buckets_and_labels(bucket_res: Dict) -> Tuple[List[Dict], List[Dict]]:
|
|
62
66
|
"""
|
|
63
|
-
Transform the GCP Storage Bucket response object for Neo4j ingestion
|
|
67
|
+
Transform the GCP Storage Bucket response object for Neo4j ingestion.
|
|
64
68
|
|
|
65
|
-
:
|
|
66
|
-
:
|
|
67
|
-
|
|
68
|
-
:rtype: list
|
|
69
|
-
:return: List of buckets ready for ingestion to Neo4j
|
|
69
|
+
:param bucket_res: The raw GCP bucket response.
|
|
70
|
+
:return: A tuple of (buckets, bucket_labels) ready for ingestion to Neo4j.
|
|
70
71
|
"""
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
buckets: List[Dict] = []
|
|
74
|
+
labels: List[Dict] = []
|
|
73
75
|
for b in bucket_res.get("items", []):
|
|
74
|
-
bucket = {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
76
|
+
bucket = {
|
|
77
|
+
"iam_config_bucket_policy_only": (
|
|
78
|
+
b.get("iamConfiguration", {}).get("bucketPolicyOnly", {}).get("enabled")
|
|
79
|
+
),
|
|
80
|
+
"id": b["id"],
|
|
81
|
+
# Preserve legacy bucket_id field for compatibility
|
|
82
|
+
"bucket_id": b["id"],
|
|
83
|
+
"owner_entity": b.get("owner", {}).get("entity"),
|
|
84
|
+
"owner_entity_id": b.get("owner", {}).get("entityId"),
|
|
85
|
+
"kind": b.get("kind"),
|
|
86
|
+
"location": b.get("location"),
|
|
87
|
+
"location_type": b.get("locationType"),
|
|
88
|
+
"meta_generation": b.get("metageneration"),
|
|
89
|
+
"project_number": b.get("projectNumber"),
|
|
90
|
+
"self_link": b.get("selfLink"),
|
|
91
|
+
"storage_class": b.get("storageClass"),
|
|
92
|
+
"time_created": b.get("timeCreated"),
|
|
93
|
+
"versioning_enabled": b.get("versioning", {}).get("enabled"),
|
|
94
|
+
"retention_period": b.get("retentionPolicy", {}).get("retentionPeriod"),
|
|
95
|
+
"default_kms_key_name": b.get("encryption", {}).get("defaultKmsKeyName"),
|
|
96
|
+
"log_bucket": b.get("logging", {}).get("logBucket"),
|
|
97
|
+
"requester_pays": b.get("billing", {}).get("requesterPays"),
|
|
98
|
+
}
|
|
99
|
+
buckets.append(bucket)
|
|
100
|
+
for key, val in b.get("labels", {}).items():
|
|
101
|
+
labels.append(
|
|
102
|
+
{
|
|
103
|
+
"id": f"GCPBucket_{key}",
|
|
104
|
+
"key": key,
|
|
105
|
+
"value": val,
|
|
106
|
+
"bucket_id": b["id"],
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
return buckets, labels
|
|
107
110
|
|
|
108
111
|
|
|
109
112
|
@timeit
|
|
110
113
|
def load_gcp_buckets(
|
|
111
114
|
neo4j_session: neo4j.Session,
|
|
112
115
|
buckets: List[Dict],
|
|
116
|
+
project_id: str,
|
|
113
117
|
gcp_update_tag: int,
|
|
114
118
|
) -> None:
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
:type gcp_update_tag: timestamp
|
|
125
|
-
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
126
|
-
|
|
127
|
-
:rtype: NoneType
|
|
128
|
-
:return: Nothing
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
query = """
|
|
132
|
-
MERGE(p:GCPProject{projectnumber:$ProjectNumber})
|
|
133
|
-
ON CREATE SET p.firstseen = timestamp()
|
|
134
|
-
SET p.lastupdated = $gcp_update_tag
|
|
135
|
-
|
|
136
|
-
MERGE(bucket:GCPBucket{id:$BucketId})
|
|
137
|
-
ON CREATE SET bucket.firstseen = timestamp(),
|
|
138
|
-
bucket.bucket_id = $BucketId
|
|
139
|
-
SET bucket.self_link = $SelfLink,
|
|
140
|
-
bucket.project_number = $ProjectNumber,
|
|
141
|
-
bucket.kind = $Kind,
|
|
142
|
-
bucket.location = $Location,
|
|
143
|
-
bucket.location_type = $LocationType,
|
|
144
|
-
bucket.meta_generation = $MetaGeneration,
|
|
145
|
-
bucket.storage_class = $StorageClass,
|
|
146
|
-
bucket.time_created = $TimeCreated,
|
|
147
|
-
bucket.retention_period = $RetentionPeriod,
|
|
148
|
-
bucket.iam_config_bucket_policy_only = $IamConfigBucketPolicyOnly,
|
|
149
|
-
bucket.owner_entity = $OwnerEntity,
|
|
150
|
-
bucket.owner_entity_id = $OwnerEntityId,
|
|
151
|
-
bucket.lastupdated = $gcp_update_tag,
|
|
152
|
-
bucket.versioning_enabled = $VersioningEnabled,
|
|
153
|
-
bucket.log_bucket = $LogBucket,
|
|
154
|
-
bucket.requester_pays = $RequesterPays,
|
|
155
|
-
bucket.default_kms_key_name = $DefaultKmsKeyName
|
|
156
|
-
|
|
157
|
-
MERGE (p)-[r:RESOURCE]->(bucket)
|
|
158
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
159
|
-
SET r.lastupdated = $gcp_update_tag
|
|
160
|
-
"""
|
|
161
|
-
for bucket in buckets:
|
|
162
|
-
neo4j_session.run(
|
|
163
|
-
query,
|
|
164
|
-
ProjectNumber=bucket["project_number"],
|
|
165
|
-
BucketId=bucket["id"],
|
|
166
|
-
SelfLink=bucket["self_link"],
|
|
167
|
-
Kind=bucket["kind"],
|
|
168
|
-
Location=bucket["location"],
|
|
169
|
-
LocationType=bucket["location_type"],
|
|
170
|
-
MetaGeneration=bucket["meta_generation"],
|
|
171
|
-
StorageClass=bucket["storage_class"],
|
|
172
|
-
TimeCreated=bucket["time_created"],
|
|
173
|
-
RetentionPeriod=bucket["retention_period"],
|
|
174
|
-
IamConfigBucketPolicyOnly=bucket["iam_config_bucket_policy_only"],
|
|
175
|
-
OwnerEntity=bucket["owner_entity"],
|
|
176
|
-
OwnerEntityId=bucket["owner_entity_id"],
|
|
177
|
-
VersioningEnabled=bucket["versioning_enabled"],
|
|
178
|
-
LogBucket=bucket["log_bucket"],
|
|
179
|
-
RequesterPays=bucket["requester_pays"],
|
|
180
|
-
DefaultKmsKeyName=bucket["default_kms_key_name"],
|
|
181
|
-
gcp_update_tag=gcp_update_tag,
|
|
182
|
-
)
|
|
183
|
-
_attach_gcp_bucket_labels(neo4j_session, bucket, gcp_update_tag)
|
|
119
|
+
"""Ingest GCP Storage Buckets to Neo4j."""
|
|
120
|
+
load(
|
|
121
|
+
neo4j_session,
|
|
122
|
+
GCPBucketSchema(),
|
|
123
|
+
buckets,
|
|
124
|
+
lastupdated=gcp_update_tag,
|
|
125
|
+
PROJECT_ID=project_id,
|
|
126
|
+
)
|
|
184
127
|
|
|
185
128
|
|
|
186
129
|
@timeit
|
|
187
|
-
def
|
|
130
|
+
def load_gcp_bucket_labels(
|
|
188
131
|
neo4j_session: neo4j.Session,
|
|
189
|
-
|
|
132
|
+
bucket_labels: List[Dict],
|
|
133
|
+
project_id: str,
|
|
190
134
|
gcp_update_tag: int,
|
|
191
135
|
) -> None:
|
|
192
|
-
"""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
MERGE (l:Label:GCPBucketLabel{id: $BucketLabelId})
|
|
201
|
-
ON CREATE SET l.firstseen = timestamp(),
|
|
202
|
-
l.key = $Key
|
|
203
|
-
SET l.value = $Value,
|
|
204
|
-
l.lastupdated = $gcp_update_tag
|
|
205
|
-
WITH l
|
|
206
|
-
MATCH (bucket:GCPBucket{id:$BucketId})
|
|
207
|
-
MERGE (l)<-[r:LABELED]-(bucket)
|
|
208
|
-
ON CREATE SET r.firstseen = timestamp()
|
|
209
|
-
SET r.lastupdated = $gcp_update_tag
|
|
210
|
-
"""
|
|
211
|
-
for key, val in bucket.get("labels", []):
|
|
212
|
-
neo4j_session.run(
|
|
213
|
-
query,
|
|
214
|
-
BucketLabelId=f"GCPBucket_{key}",
|
|
215
|
-
Key=key,
|
|
216
|
-
Value=val,
|
|
217
|
-
BucketId=bucket["id"],
|
|
218
|
-
gcp_update_tag=gcp_update_tag,
|
|
219
|
-
)
|
|
136
|
+
"""Ingest GCP Storage Bucket labels and attach them to buckets."""
|
|
137
|
+
load(
|
|
138
|
+
neo4j_session,
|
|
139
|
+
GCPBucketLabelSchema(),
|
|
140
|
+
bucket_labels,
|
|
141
|
+
lastupdated=gcp_update_tag,
|
|
142
|
+
PROJECT_ID=project_id,
|
|
143
|
+
)
|
|
220
144
|
|
|
221
145
|
|
|
222
146
|
@timeit
|
|
@@ -224,22 +148,14 @@ def cleanup_gcp_buckets(
|
|
|
224
148
|
neo4j_session: neo4j.Session,
|
|
225
149
|
common_job_parameters: Dict,
|
|
226
150
|
) -> None:
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
:param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
|
|
235
|
-
|
|
236
|
-
:rtype: NoneType
|
|
237
|
-
:return: Nothing
|
|
238
|
-
"""
|
|
239
|
-
run_cleanup_job(
|
|
240
|
-
"gcp_storage_bucket_cleanup.json",
|
|
151
|
+
"""Delete out-of-date GCP Storage Bucket nodes and relationships."""
|
|
152
|
+
# Bucket labels depend on buckets, so we must remove labels first to avoid
|
|
153
|
+
# dangling references before deleting the buckets themselves.
|
|
154
|
+
GraphJob.from_node_schema(GCPBucketLabelSchema(), common_job_parameters).run(
|
|
155
|
+
neo4j_session,
|
|
156
|
+
)
|
|
157
|
+
GraphJob.from_node_schema(GCPBucketSchema(), common_job_parameters).run(
|
|
241
158
|
neo4j_session,
|
|
242
|
-
common_job_parameters,
|
|
243
159
|
)
|
|
244
160
|
|
|
245
161
|
|
|
@@ -274,7 +190,7 @@ def sync_gcp_buckets(
|
|
|
274
190
|
"""
|
|
275
191
|
logger.info("Syncing Storage objects for project %s.", project_id)
|
|
276
192
|
storage_res = get_gcp_buckets(storage, project_id)
|
|
277
|
-
|
|
278
|
-
load_gcp_buckets(neo4j_session,
|
|
279
|
-
|
|
193
|
+
buckets, bucket_labels = transform_gcp_buckets_and_labels(storage_res)
|
|
194
|
+
load_gcp_buckets(neo4j_session, buckets, project_id, gcp_update_tag)
|
|
195
|
+
load_gcp_bucket_labels(neo4j_session, bucket_labels, project_id, gcp_update_tag)
|
|
280
196
|
cleanup_gcp_buckets(neo4j_session, common_job_parameters)
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
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 APIGatewayV2APINodeProperties(CartographyNodeProperties):
|
|
15
|
+
id: PropertyRef = PropertyRef("id", extra_index=True)
|
|
16
|
+
name: PropertyRef = PropertyRef("name")
|
|
17
|
+
protocoltype: PropertyRef = PropertyRef("protocolType")
|
|
18
|
+
routeselectionexpression: PropertyRef = PropertyRef("routeSelectionExpression")
|
|
19
|
+
apikeyselectionexpression: PropertyRef = PropertyRef("apiKeySelectionExpression")
|
|
20
|
+
apiendpoint: PropertyRef = PropertyRef("apiEndpoint")
|
|
21
|
+
version: PropertyRef = PropertyRef("version")
|
|
22
|
+
createddate: PropertyRef = PropertyRef("createdDate")
|
|
23
|
+
description: PropertyRef = PropertyRef("description")
|
|
24
|
+
region: PropertyRef = PropertyRef("region", set_in_kwargs=True)
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class APIGatewayV2APIToAWSAccountRelProperties(CartographyRelProperties):
|
|
30
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
# (:APIGatewayV2API)<-[:RESOURCE]-(:AWSAccount)
|
|
35
|
+
class APIGatewayV2APIToAWSAccountRel(CartographyRelSchema):
|
|
36
|
+
target_node_label: str = "AWSAccount"
|
|
37
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
38
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
39
|
+
)
|
|
40
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
41
|
+
rel_label: str = "RESOURCE"
|
|
42
|
+
properties: APIGatewayV2APIToAWSAccountRelProperties = (
|
|
43
|
+
APIGatewayV2APIToAWSAccountRelProperties()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class APIGatewayV2APISchema(CartographyNodeSchema):
|
|
49
|
+
label: str = "APIGatewayV2API"
|
|
50
|
+
properties: APIGatewayV2APINodeProperties = APIGatewayV2APINodeProperties()
|
|
51
|
+
sub_resource_relationship: APIGatewayV2APIToAWSAccountRel = (
|
|
52
|
+
APIGatewayV2APIToAWSAccountRel()
|
|
53
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
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.nodes import ExtraNodeLabels
|
|
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 OtherRelationships
|
|
12
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class GCPDNSZoneNodeProperties(CartographyNodeProperties):
|
|
17
|
+
id: PropertyRef = PropertyRef("id", extra_index=True)
|
|
18
|
+
name: PropertyRef = PropertyRef("name", extra_index=True)
|
|
19
|
+
dns_name: PropertyRef = PropertyRef("dns_name")
|
|
20
|
+
description: PropertyRef = PropertyRef("description")
|
|
21
|
+
visibility: PropertyRef = PropertyRef("visibility")
|
|
22
|
+
kind: PropertyRef = PropertyRef("kind")
|
|
23
|
+
nameservers: PropertyRef = PropertyRef("nameservers")
|
|
24
|
+
created_at: PropertyRef = PropertyRef("created_at")
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class GCPDNSZoneToProjectRelProperties(CartographyRelProperties):
|
|
30
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
# (:GCPProject)-[:RESOURCE]->(:GCPDNSZone)
|
|
35
|
+
class GCPDNSZoneToProjectRel(CartographyRelSchema):
|
|
36
|
+
target_node_label: str = "GCPProject"
|
|
37
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
38
|
+
{"id": PropertyRef("PROJECT_ID", set_in_kwargs=True)}
|
|
39
|
+
)
|
|
40
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
41
|
+
rel_label: str = "RESOURCE"
|
|
42
|
+
properties: GCPDNSZoneToProjectRelProperties = GCPDNSZoneToProjectRelProperties()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class GCPDNSZoneSchema(CartographyNodeSchema):
|
|
47
|
+
label: str = "GCPDNSZone"
|
|
48
|
+
properties: GCPDNSZoneNodeProperties = GCPDNSZoneNodeProperties()
|
|
49
|
+
extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(["DNSZone"])
|
|
50
|
+
sub_resource_relationship: GCPDNSZoneToProjectRel = GCPDNSZoneToProjectRel()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class GCPRecordSetNodeProperties(CartographyNodeProperties):
|
|
55
|
+
id: PropertyRef = PropertyRef("id", extra_index=True)
|
|
56
|
+
name: PropertyRef = PropertyRef("name", extra_index=True)
|
|
57
|
+
type: PropertyRef = PropertyRef("type")
|
|
58
|
+
ttl: PropertyRef = PropertyRef("ttl")
|
|
59
|
+
data: PropertyRef = PropertyRef("data")
|
|
60
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class GCPRecordSetToProjectRelProperties(CartographyRelProperties):
|
|
65
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
# (:GCPProject)-[:RESOURCE]->(:GCPRecordSet)
|
|
70
|
+
class GCPRecordSetToProjectRel(CartographyRelSchema):
|
|
71
|
+
target_node_label: str = "GCPProject"
|
|
72
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
73
|
+
{"id": PropertyRef("PROJECT_ID", set_in_kwargs=True)}
|
|
74
|
+
)
|
|
75
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
76
|
+
rel_label: str = "RESOURCE"
|
|
77
|
+
properties: GCPRecordSetToProjectRelProperties = (
|
|
78
|
+
GCPRecordSetToProjectRelProperties()
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class GCPRecordSetToZoneRelProperties(CartographyRelProperties):
|
|
84
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
# (:GCPDNSZone)-[:HAS_RECORD]->(:GCPRecordSet)
|
|
89
|
+
class GCPRecordSetToZoneRel(CartographyRelSchema):
|
|
90
|
+
target_node_label: str = "GCPDNSZone"
|
|
91
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
92
|
+
{"id": PropertyRef("zone_id")}
|
|
93
|
+
)
|
|
94
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
95
|
+
rel_label: str = "HAS_RECORD"
|
|
96
|
+
properties: GCPRecordSetToZoneRelProperties = GCPRecordSetToZoneRelProperties()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class GCPRecordSetSchema(CartographyNodeSchema):
|
|
101
|
+
label: str = "GCPRecordSet"
|
|
102
|
+
properties: GCPRecordSetNodeProperties = GCPRecordSetNodeProperties()
|
|
103
|
+
extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(["DNSRecord"])
|
|
104
|
+
sub_resource_relationship: GCPRecordSetToProjectRel = GCPRecordSetToProjectRel()
|
|
105
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
106
|
+
[
|
|
107
|
+
GCPRecordSetToZoneRel(),
|
|
108
|
+
]
|
|
109
|
+
)
|
cartography/models/gcp/iam.py
CHANGED
|
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
from cartography.models.core.common import PropertyRef
|
|
5
5
|
from cartography.models.core.nodes import CartographyNodeProperties
|
|
6
6
|
from cartography.models.core.nodes import CartographyNodeSchema
|
|
7
|
+
from cartography.models.core.nodes import ExtraNodeLabels
|
|
7
8
|
from cartography.models.core.relationships import CartographyRelProperties
|
|
8
9
|
from cartography.models.core.relationships import CartographyRelSchema
|
|
9
10
|
from cartography.models.core.relationships import LinkDirection
|
|
@@ -61,6 +62,8 @@ class GCPServiceAccountSchema(CartographyNodeSchema):
|
|
|
61
62
|
label: str = "GCPServiceAccount"
|
|
62
63
|
properties: GCPServiceAccountNodeProperties = GCPServiceAccountNodeProperties()
|
|
63
64
|
sub_resource_relationship: GCPPrincipalToProject = GCPPrincipalToProject()
|
|
65
|
+
# Service accounts are principals; add shared label for cross-module queries
|
|
66
|
+
extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(["GCPPrincipal"])
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
@dataclass(frozen=True)
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
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 GCPBucketNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("id", extra_index=True)
|
|
17
|
+
# Preserve legacy field for compatibility with existing queries
|
|
18
|
+
bucket_id: PropertyRef = PropertyRef("bucket_id")
|
|
19
|
+
project_number: PropertyRef = PropertyRef("project_number")
|
|
20
|
+
self_link: PropertyRef = PropertyRef("self_link")
|
|
21
|
+
kind: PropertyRef = PropertyRef("kind")
|
|
22
|
+
location: PropertyRef = PropertyRef("location")
|
|
23
|
+
location_type: PropertyRef = PropertyRef("location_type")
|
|
24
|
+
meta_generation: PropertyRef = PropertyRef("meta_generation")
|
|
25
|
+
storage_class: PropertyRef = PropertyRef("storage_class")
|
|
26
|
+
time_created: PropertyRef = PropertyRef("time_created")
|
|
27
|
+
retention_period: PropertyRef = PropertyRef("retention_period")
|
|
28
|
+
iam_config_bucket_policy_only: PropertyRef = PropertyRef(
|
|
29
|
+
"iam_config_bucket_policy_only"
|
|
30
|
+
)
|
|
31
|
+
owner_entity: PropertyRef = PropertyRef("owner_entity")
|
|
32
|
+
owner_entity_id: PropertyRef = PropertyRef("owner_entity_id")
|
|
33
|
+
versioning_enabled: PropertyRef = PropertyRef("versioning_enabled")
|
|
34
|
+
log_bucket: PropertyRef = PropertyRef("log_bucket")
|
|
35
|
+
requester_pays: PropertyRef = PropertyRef("requester_pays")
|
|
36
|
+
default_kms_key_name: PropertyRef = PropertyRef("default_kms_key_name")
|
|
37
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class GCPBucketToProjectRelProperties(CartographyRelProperties):
|
|
42
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
# (:GCPProject)-[:RESOURCE]->(:GCPBucket)
|
|
47
|
+
class GCPBucketToProjectRel(CartographyRelSchema):
|
|
48
|
+
target_node_label: str = "GCPProject"
|
|
49
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
50
|
+
{"id": PropertyRef("PROJECT_ID", set_in_kwargs=True)}
|
|
51
|
+
)
|
|
52
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
53
|
+
rel_label: str = "RESOURCE"
|
|
54
|
+
properties: GCPBucketToProjectRelProperties = GCPBucketToProjectRelProperties()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class GCPBucketSchema(CartographyNodeSchema):
|
|
59
|
+
label: str = "GCPBucket"
|
|
60
|
+
properties: GCPBucketNodeProperties = GCPBucketNodeProperties()
|
|
61
|
+
sub_resource_relationship: GCPBucketToProjectRel = GCPBucketToProjectRel()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class GCPBucketLabelNodeProperties(CartographyNodeProperties):
|
|
66
|
+
id: PropertyRef = PropertyRef("id", extra_index=True)
|
|
67
|
+
key: PropertyRef = PropertyRef("key", extra_index=True)
|
|
68
|
+
value: PropertyRef = PropertyRef("value")
|
|
69
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class GCPBucketLabelToProjectRelProperties(CartographyRelProperties):
|
|
74
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
# (:GCPProject)-[:RESOURCE]->(:GCPBucketLabel)
|
|
79
|
+
class GCPBucketLabelToProjectRel(CartographyRelSchema):
|
|
80
|
+
target_node_label: str = "GCPProject"
|
|
81
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
82
|
+
{"id": PropertyRef("PROJECT_ID", set_in_kwargs=True)}
|
|
83
|
+
)
|
|
84
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
85
|
+
rel_label: str = "RESOURCE"
|
|
86
|
+
properties: GCPBucketLabelToProjectRelProperties = (
|
|
87
|
+
GCPBucketLabelToProjectRelProperties()
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class GCPBucketLabelToBucketRelProperties(CartographyRelProperties):
|
|
93
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
# (:GCPBucket)-[:LABELED]->(:GCPBucketLabel)
|
|
98
|
+
class GCPBucketLabelToBucketRel(CartographyRelSchema):
|
|
99
|
+
target_node_label: str = "GCPBucket"
|
|
100
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
101
|
+
{"id": PropertyRef("bucket_id")}
|
|
102
|
+
)
|
|
103
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
104
|
+
rel_label: str = "LABELED"
|
|
105
|
+
properties: GCPBucketLabelToBucketRelProperties = (
|
|
106
|
+
GCPBucketLabelToBucketRelProperties()
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True)
|
|
111
|
+
class GCPBucketLabelSchema(CartographyNodeSchema):
|
|
112
|
+
label: str = "GCPBucketLabel"
|
|
113
|
+
properties: GCPBucketLabelNodeProperties = GCPBucketLabelNodeProperties()
|
|
114
|
+
sub_resource_relationship: GCPBucketLabelToProjectRel = GCPBucketLabelToProjectRel()
|
|
115
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
116
|
+
[
|
|
117
|
+
GCPBucketLabelToBucketRel(),
|
|
118
|
+
]
|
|
119
|
+
)
|