cartography 0.102.0rc1__py3-none-any.whl → 0.103.0rc1__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/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +302 -253
- cartography/client/core/tx.py +39 -18
- cartography/config.py +4 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +327 -0
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +53 -44
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/create_indexes.py +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +36 -10
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- cartography/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/bigfix/bigfix_computer.py +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +56 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/stats.py +3 -3
- cartography/sync.py +107 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
- cartography-0.103.0rc1.dist-info/RECORD +396 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc1.dist-info/RECORD +0 -377
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
cartography/intel/oci/iam.py
CHANGED
|
@@ -10,9 +10,10 @@ from typing import List
|
|
|
10
10
|
import neo4j
|
|
11
11
|
import oci
|
|
12
12
|
|
|
13
|
-
from . import utils
|
|
14
13
|
from cartography.util import run_cleanup_job
|
|
15
14
|
|
|
15
|
+
from . import utils
|
|
16
|
+
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
@@ -25,8 +26,17 @@ def sync_compartments(
|
|
|
25
26
|
) -> None:
|
|
26
27
|
logger.debug("Syncing IAM compartments for account '%s'.", current_tenancy_id)
|
|
27
28
|
data = get_compartment_list_data(iam, current_tenancy_id)
|
|
28
|
-
load_compartments(
|
|
29
|
-
|
|
29
|
+
load_compartments(
|
|
30
|
+
neo4j_session,
|
|
31
|
+
data["Compartments"],
|
|
32
|
+
current_tenancy_id,
|
|
33
|
+
oci_update_tag,
|
|
34
|
+
)
|
|
35
|
+
run_cleanup_job(
|
|
36
|
+
"oci_import_compartments_cleanup.json",
|
|
37
|
+
neo4j_session,
|
|
38
|
+
common_job_parameters,
|
|
39
|
+
)
|
|
30
40
|
|
|
31
41
|
|
|
32
42
|
def get_compartment_list_data_recurse(
|
|
@@ -35,11 +45,17 @@ def get_compartment_list_data_recurse(
|
|
|
35
45
|
compartment_id: str,
|
|
36
46
|
) -> None:
|
|
37
47
|
|
|
38
|
-
response = oci.pagination.list_call_get_all_results(
|
|
48
|
+
response = oci.pagination.list_call_get_all_results(
|
|
49
|
+
iam.list_compartments,
|
|
50
|
+
compartment_id,
|
|
51
|
+
)
|
|
39
52
|
if not response.data:
|
|
40
53
|
return
|
|
41
54
|
compartment_list.update(
|
|
42
|
-
{
|
|
55
|
+
{
|
|
56
|
+
"Compartments": list(compartment_list["Compartments"])
|
|
57
|
+
+ utils.oci_object_to_json(response.data),
|
|
58
|
+
},
|
|
43
59
|
)
|
|
44
60
|
for compartment in response.data:
|
|
45
61
|
get_compartment_list_data_recurse(iam, compartment_list, compartment.id)
|
|
@@ -122,7 +138,9 @@ def load_users(
|
|
|
122
138
|
CAN_USE_API_KEYS=user["capabilities"]["can-use-api-keys"],
|
|
123
139
|
CAN_USE_AUTH_TOKENS=user["capabilities"]["can-use-auth-tokens"],
|
|
124
140
|
CAN_USE_CONSOLE_PASSWORD=user["capabilities"]["can-use-console-password"],
|
|
125
|
-
CAN_USE_CUSTOMER_SECRET_KEYS=user["capabilities"][
|
|
141
|
+
CAN_USE_CUSTOMER_SECRET_KEYS=user["capabilities"][
|
|
142
|
+
"can-use-customer-secret-keys"
|
|
143
|
+
],
|
|
126
144
|
CAN_USE_SMTP_CREDENTIALS=user["capabilities"]["can-use-smtp-credentials"],
|
|
127
145
|
COMPARTMENT_ID=user["compartment-id"],
|
|
128
146
|
OCI_TENANCY_ID=current_oci_tenancy_id,
|
|
@@ -134,8 +152,11 @@ def get_user_list_data(
|
|
|
134
152
|
iam: oci.identity.identity_client.IdentityClient,
|
|
135
153
|
current_tenancy_id: str,
|
|
136
154
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
137
|
-
response = oci.pagination.list_call_get_all_results(
|
|
138
|
-
|
|
155
|
+
response = oci.pagination.list_call_get_all_results(
|
|
156
|
+
iam.list_users,
|
|
157
|
+
current_tenancy_id,
|
|
158
|
+
)
|
|
159
|
+
return {"Users": utils.oci_object_to_json(response.data)}
|
|
139
160
|
|
|
140
161
|
|
|
141
162
|
def sync_users(
|
|
@@ -147,16 +168,23 @@ def sync_users(
|
|
|
147
168
|
) -> None:
|
|
148
169
|
logger.debug("Syncing IAM users for account '%s'.", current_tenancy_id)
|
|
149
170
|
data = get_user_list_data(iam, current_tenancy_id)
|
|
150
|
-
load_users(neo4j_session, data[
|
|
151
|
-
run_cleanup_job(
|
|
171
|
+
load_users(neo4j_session, data["Users"], current_tenancy_id, oci_update_tag)
|
|
172
|
+
run_cleanup_job(
|
|
173
|
+
"oci_import_users_cleanup.json",
|
|
174
|
+
neo4j_session,
|
|
175
|
+
common_job_parameters,
|
|
176
|
+
)
|
|
152
177
|
|
|
153
178
|
|
|
154
179
|
def get_group_list_data(
|
|
155
180
|
iam: oci.identity.identity_client.IdentityClient,
|
|
156
181
|
current_tenancy_id: str,
|
|
157
182
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
158
|
-
response = oci.pagination.list_call_get_all_results(
|
|
159
|
-
|
|
183
|
+
response = oci.pagination.list_call_get_all_results(
|
|
184
|
+
iam.list_groups,
|
|
185
|
+
current_tenancy_id,
|
|
186
|
+
)
|
|
187
|
+
return {"Groups": utils.oci_object_to_json(response.data)}
|
|
160
188
|
|
|
161
189
|
|
|
162
190
|
def load_groups(
|
|
@@ -200,7 +228,11 @@ def sync_groups(
|
|
|
200
228
|
logger.debug("Syncing IAM groups for account '%s'.", current_tenancy_id)
|
|
201
229
|
data = get_group_list_data(iam, current_tenancy_id)
|
|
202
230
|
load_groups(neo4j_session, data["Groups"], current_tenancy_id, oci_update_tag)
|
|
203
|
-
run_cleanup_job(
|
|
231
|
+
run_cleanup_job(
|
|
232
|
+
"oci_import_groups_cleanup.json",
|
|
233
|
+
neo4j_session,
|
|
234
|
+
common_job_parameters,
|
|
235
|
+
)
|
|
204
236
|
|
|
205
237
|
|
|
206
238
|
def get_group_membership_data(
|
|
@@ -209,9 +241,11 @@ def get_group_membership_data(
|
|
|
209
241
|
current_tenancy_id: str,
|
|
210
242
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
211
243
|
response = oci.pagination.list_call_get_all_results(
|
|
212
|
-
iam.list_user_group_memberships,
|
|
244
|
+
iam.list_user_group_memberships,
|
|
245
|
+
compartment_id=current_tenancy_id,
|
|
246
|
+
group_id=group_id,
|
|
213
247
|
)
|
|
214
|
-
return {
|
|
248
|
+
return {"GroupMemberships": utils.oci_object_to_json(response.data)}
|
|
215
249
|
|
|
216
250
|
|
|
217
251
|
def sync_group_memberships(
|
|
@@ -222,15 +256,18 @@ def sync_group_memberships(
|
|
|
222
256
|
common_job_parameters: Dict[str, Any],
|
|
223
257
|
) -> None:
|
|
224
258
|
logger.debug("Syncing IAM group membership for account '%s'.", current_tenancy_id)
|
|
225
|
-
query =
|
|
226
|
-
|
|
259
|
+
query = (
|
|
260
|
+
"MATCH (group:OCIGroup)<-[:RESOURCE]-(OCITenancy{ocid: $OCI_TENANCY_ID}) "
|
|
261
|
+
"return group.name as name, group.ocid as ocid;"
|
|
262
|
+
)
|
|
227
263
|
groups = neo4j_session.run(query, OCI_TENANCY_ID=current_tenancy_id)
|
|
228
264
|
groups_membership = {
|
|
229
|
-
group["ocid"]: get_group_membership_data(iam, group[
|
|
265
|
+
group["ocid"]: get_group_membership_data(iam, group["ocid"], current_tenancy_id)
|
|
266
|
+
for group in groups
|
|
230
267
|
}
|
|
231
268
|
load_group_memberships(neo4j_session, groups_membership, oci_update_tag)
|
|
232
269
|
run_cleanup_job(
|
|
233
|
-
|
|
270
|
+
"oci_import_groups_membership_cleanup.json",
|
|
234
271
|
neo4j_session,
|
|
235
272
|
common_job_parameters,
|
|
236
273
|
)
|
|
@@ -298,8 +335,11 @@ def get_policy_list_data(
|
|
|
298
335
|
iam: oci.identity.identity_client.IdentityClient,
|
|
299
336
|
current_tenancy_id: str,
|
|
300
337
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
301
|
-
response = oci.pagination.list_call_get_all_results(
|
|
302
|
-
|
|
338
|
+
response = oci.pagination.list_call_get_all_results(
|
|
339
|
+
iam.list_policies,
|
|
340
|
+
compartment_id=current_tenancy_id,
|
|
341
|
+
)
|
|
342
|
+
return {"Policies": utils.oci_object_to_json(response.data)}
|
|
303
343
|
|
|
304
344
|
|
|
305
345
|
def sync_policies(
|
|
@@ -313,12 +353,23 @@ def sync_policies(
|
|
|
313
353
|
compartments = utils.get_compartments_in_tenancy(neo4j_session, current_tenancy_id)
|
|
314
354
|
for compartment in compartments:
|
|
315
355
|
logger.debug(
|
|
316
|
-
"Syncing OCI policies for compartment '%s' in account '%s'.",
|
|
356
|
+
"Syncing OCI policies for compartment '%s' in account '%s'.",
|
|
357
|
+
compartment["ocid"],
|
|
358
|
+
current_tenancy_id,
|
|
317
359
|
)
|
|
318
360
|
data = get_policy_list_data(iam, compartment["ocid"])
|
|
319
|
-
if
|
|
320
|
-
load_policies(
|
|
321
|
-
|
|
361
|
+
if data["Policies"]:
|
|
362
|
+
load_policies(
|
|
363
|
+
neo4j_session,
|
|
364
|
+
data["Policies"],
|
|
365
|
+
current_tenancy_id,
|
|
366
|
+
oci_update_tag,
|
|
367
|
+
)
|
|
368
|
+
run_cleanup_job(
|
|
369
|
+
"oci_import_policies_cleanup.json",
|
|
370
|
+
neo4j_session,
|
|
371
|
+
common_job_parameters,
|
|
372
|
+
)
|
|
322
373
|
|
|
323
374
|
|
|
324
375
|
def load_oci_policy_group_reference(
|
|
@@ -378,22 +429,33 @@ def sync_oci_policy_references(
|
|
|
378
429
|
for policy in policies:
|
|
379
430
|
check_compart = policy["compartmentid"]
|
|
380
431
|
for statement in policy["statements"]:
|
|
381
|
-
m = re.search(
|
|
432
|
+
m = re.search("(?<=group\\s)[^ ]*(?=\\s)", statement)
|
|
382
433
|
if m:
|
|
383
434
|
for group in groups:
|
|
384
435
|
if group["name"].lower() == m.group(0).lower():
|
|
385
436
|
load_oci_policy_group_reference(
|
|
386
|
-
neo4j_session,
|
|
437
|
+
neo4j_session,
|
|
438
|
+
policy["ocid"],
|
|
439
|
+
group["ocid"],
|
|
440
|
+
tenancy_id,
|
|
441
|
+
oci_update_tag,
|
|
387
442
|
)
|
|
388
|
-
m = re.search(
|
|
443
|
+
m = re.search("(?<=compartment\\s)[^ ]*(?=$)", statement)
|
|
389
444
|
if m:
|
|
390
445
|
for compartment in compartments:
|
|
391
446
|
# Only look at the compartment or subcompartment name referenced in the policy statement
|
|
392
447
|
# in which the policy is a member of.
|
|
393
|
-
if
|
|
448
|
+
if (
|
|
449
|
+
compartment["ocid"] == check_compart
|
|
450
|
+
or compartment["compartmentid"] == check_compart
|
|
451
|
+
):
|
|
394
452
|
if compartment["name"].lower() == m.group(0).lower():
|
|
395
453
|
load_oci_policy_compartment_reference(
|
|
396
|
-
neo4j_session,
|
|
454
|
+
neo4j_session,
|
|
455
|
+
policy["ocid"],
|
|
456
|
+
compartment["ocid"],
|
|
457
|
+
tenancy_id,
|
|
458
|
+
oci_update_tag,
|
|
397
459
|
)
|
|
398
460
|
|
|
399
461
|
|
|
@@ -401,8 +463,11 @@ def get_region_subscriptions_list_data(
|
|
|
401
463
|
iam: oci.identity.identity_client.IdentityClient,
|
|
402
464
|
current_tenancy_id: str,
|
|
403
465
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
404
|
-
response = oci.pagination.list_call_get_all_results(
|
|
405
|
-
|
|
466
|
+
response = oci.pagination.list_call_get_all_results(
|
|
467
|
+
iam.list_region_subscriptions,
|
|
468
|
+
current_tenancy_id,
|
|
469
|
+
)
|
|
470
|
+
return {"RegionSubscriptions": utils.oci_object_to_json(response.data)}
|
|
406
471
|
|
|
407
472
|
|
|
408
473
|
def load_region_subscriptions(
|
|
@@ -438,9 +503,17 @@ def sync_region_subscriptions(
|
|
|
438
503
|
oci_update_tag: int,
|
|
439
504
|
common_job_parameters: Dict[str, Any],
|
|
440
505
|
) -> None:
|
|
441
|
-
logger.debug(
|
|
506
|
+
logger.debug(
|
|
507
|
+
"Syncing IAM region subscriptions for account '%s'.",
|
|
508
|
+
current_tenancy_id,
|
|
509
|
+
)
|
|
442
510
|
data = get_region_subscriptions_list_data(iam, current_tenancy_id)
|
|
443
|
-
load_region_subscriptions(
|
|
511
|
+
load_region_subscriptions(
|
|
512
|
+
neo4j_session,
|
|
513
|
+
data["RegionSubscriptions"],
|
|
514
|
+
current_tenancy_id,
|
|
515
|
+
oci_update_tag,
|
|
516
|
+
)
|
|
444
517
|
# run_cleanup_job('oci_import_region_subscriptions_cleanup.json', neo4j_session, common_job_parameters)
|
|
445
518
|
|
|
446
519
|
|
|
@@ -454,8 +527,31 @@ def sync(
|
|
|
454
527
|
logger.info("Syncing IAM for account '%s'.", tenancy_id)
|
|
455
528
|
sync_users(neo4j_session, iam, tenancy_id, oci_update_tag, common_job_parameters)
|
|
456
529
|
sync_groups(neo4j_session, iam, tenancy_id, oci_update_tag, common_job_parameters)
|
|
457
|
-
sync_group_memberships(
|
|
458
|
-
|
|
530
|
+
sync_group_memberships(
|
|
531
|
+
neo4j_session,
|
|
532
|
+
iam,
|
|
533
|
+
tenancy_id,
|
|
534
|
+
oci_update_tag,
|
|
535
|
+
common_job_parameters,
|
|
536
|
+
)
|
|
537
|
+
sync_compartments(
|
|
538
|
+
neo4j_session,
|
|
539
|
+
iam,
|
|
540
|
+
tenancy_id,
|
|
541
|
+
oci_update_tag,
|
|
542
|
+
common_job_parameters,
|
|
543
|
+
)
|
|
459
544
|
sync_policies(neo4j_session, iam, tenancy_id, oci_update_tag, common_job_parameters)
|
|
460
|
-
sync_oci_policy_references(
|
|
461
|
-
|
|
545
|
+
sync_oci_policy_references(
|
|
546
|
+
neo4j_session,
|
|
547
|
+
tenancy_id,
|
|
548
|
+
oci_update_tag,
|
|
549
|
+
common_job_parameters,
|
|
550
|
+
)
|
|
551
|
+
sync_region_subscriptions(
|
|
552
|
+
neo4j_session,
|
|
553
|
+
iam,
|
|
554
|
+
tenancy_id,
|
|
555
|
+
oci_update_tag,
|
|
556
|
+
common_job_parameters,
|
|
557
|
+
)
|
|
@@ -41,7 +41,7 @@ def get_oci_account_default() -> Dict[str, Any]:
|
|
|
41
41
|
def get_oci_profile_names_from_config() -> List[Any]:
|
|
42
42
|
config_path = oci.config._get_config_path_with_fallback("~/.oci/config")
|
|
43
43
|
config = open(config_path).read()
|
|
44
|
-
pattern = r
|
|
44
|
+
pattern = r"\[(.*)\]"
|
|
45
45
|
m = re.findall(pattern, config)
|
|
46
46
|
return m
|
|
47
47
|
|
|
@@ -52,16 +52,20 @@ def get_oci_accounts_from_config() -> Dict[str, Any]:
|
|
|
52
52
|
|
|
53
53
|
d = {}
|
|
54
54
|
for profile_name in available_profiles:
|
|
55
|
-
if profile_name ==
|
|
55
|
+
if profile_name == "DEFAULT":
|
|
56
56
|
logger.debug("Skipping OCI profile 'DEFAULT'.")
|
|
57
57
|
continue
|
|
58
58
|
try:
|
|
59
|
-
profile_oci_credentials = oci.config.from_file(
|
|
59
|
+
profile_oci_credentials = oci.config.from_file(
|
|
60
|
+
"~/.oci/config",
|
|
61
|
+
profile_name,
|
|
62
|
+
)
|
|
60
63
|
oci.config.validate_config(profile_oci_credentials)
|
|
61
64
|
except (ConfigFileNotFound, ProfileNotFound, InvalidConfig) as e:
|
|
62
65
|
logger.debug(
|
|
63
66
|
"Error occurred calling oci.config.from_file with profile_name '%s'.",
|
|
64
|
-
profile_name,
|
|
67
|
+
profile_name,
|
|
68
|
+
exc_info=True,
|
|
65
69
|
)
|
|
66
70
|
logger.error(
|
|
67
71
|
(
|
|
@@ -104,8 +108,11 @@ def load_oci_accounts(
|
|
|
104
108
|
)
|
|
105
109
|
|
|
106
110
|
|
|
107
|
-
def cleanup(
|
|
108
|
-
|
|
111
|
+
def cleanup(
|
|
112
|
+
neo4j_session: neo4j.Session,
|
|
113
|
+
common_job_parameters: Dict[str, Any],
|
|
114
|
+
) -> None:
|
|
115
|
+
run_cleanup_job("oci_tenancy_cleanup.json", neo4j_session, common_job_parameters)
|
|
109
116
|
|
|
110
117
|
|
|
111
118
|
def sync(
|
cartography/intel/oci/utils.py
CHANGED
|
@@ -22,47 +22,70 @@ def replace_char_in_dict(in_dict: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
22
22
|
for dict_key, dict_val in in_dict.items():
|
|
23
23
|
if isinstance(dict_val, dict):
|
|
24
24
|
dict_val = replace_char_in_dict(dict_val)
|
|
25
|
-
out_dict[dict_key.replace(
|
|
25
|
+
out_dict[dict_key.replace("_", "-")] = dict_val
|
|
26
26
|
return out_dict
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
# Grab list of all compartments and sub-compartments in neo4j already populated by iam.
|
|
30
|
-
def get_compartments_in_tenancy(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
def get_compartments_in_tenancy(
|
|
31
|
+
neo4j_session: neo4j.Session,
|
|
32
|
+
tenancy_id: str,
|
|
33
|
+
) -> neo4j.Result:
|
|
34
|
+
query = (
|
|
35
|
+
"MATCH (OCITenancy{ocid: $OCI_TENANCY_ID})-[*]->(compartment:OCICompartment) "
|
|
36
|
+
"return DISTINCT compartment.name as name, compartment.ocid as ocid, "
|
|
37
|
+
"compartment.compartmentid as compartmentid;"
|
|
38
|
+
)
|
|
34
39
|
return neo4j_session.run(query, OCI_TENANCY_ID=tenancy_id)
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
# Grab list of all groups in neo4j already populated by iam.
|
|
38
|
-
def get_groups_in_tenancy(
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
def get_groups_in_tenancy(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
tenancy_id: str,
|
|
46
|
+
) -> neo4j.Result:
|
|
47
|
+
query = (
|
|
48
|
+
"MATCH (OCITenancy{ocid: $OCI_TENANCY_ID})-[*]->(group:OCIGroup)"
|
|
49
|
+
"return DISTINCT group.name as name, group.ocid as ocid;"
|
|
50
|
+
)
|
|
41
51
|
return neo4j_session.run(query, OCI_TENANCY_ID=tenancy_id)
|
|
42
52
|
|
|
43
53
|
|
|
44
54
|
# Grab list of all policies in neo4j already populated by iam.
|
|
45
|
-
def get_policies_in_tenancy(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
def get_policies_in_tenancy(
|
|
56
|
+
neo4j_session: neo4j.Session,
|
|
57
|
+
tenancy_id: str,
|
|
58
|
+
) -> neo4j.Result:
|
|
59
|
+
query = (
|
|
60
|
+
"MATCH (OCITenancy{ocid: $OCI_TENANCY_ID})-[*]->(policy:OCIPolicy)"
|
|
61
|
+
"return DISTINCT policy.name as name, policy.ocid as ocid, policy.statements as statements, "
|
|
62
|
+
"policy.compartmentid as compartmentid;"
|
|
63
|
+
)
|
|
49
64
|
return neo4j_session.run(query, OCI_TENANCY_ID=tenancy_id)
|
|
50
65
|
|
|
51
66
|
|
|
52
67
|
# Grab list of all regions in neo4j already populated by iam.
|
|
53
|
-
def get_regions_in_tenancy(
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
def get_regions_in_tenancy(
|
|
69
|
+
neo4j_session: neo4j.Session,
|
|
70
|
+
tenancy_id: str,
|
|
71
|
+
) -> neo4j.Result:
|
|
72
|
+
query = (
|
|
73
|
+
"MATCH (OCITenancy{ocid: $OCI_TENANCY_ID})-->(region:OCIRegion)"
|
|
74
|
+
"return DISTINCT region.name as name, region.key as key;"
|
|
75
|
+
)
|
|
56
76
|
return neo4j_session.run(query, OCI_TENANCY_ID=tenancy_id)
|
|
57
77
|
|
|
58
78
|
|
|
59
79
|
# Grab list of all security groups in neo4j already populated by network. Need to handle regions for this one.
|
|
60
80
|
def get_security_groups_in_tenancy(
|
|
61
81
|
neo4j_session: neo4j.Session,
|
|
62
|
-
tenancy_id: str,
|
|
82
|
+
tenancy_id: str,
|
|
83
|
+
region: str,
|
|
63
84
|
) -> neo4j.Result:
|
|
64
|
-
query =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
query = (
|
|
86
|
+
"MATCH (OCITenancy{ocid: $OCI_TENANCY_ID})-[*]->(security_group:OCINetworkSecurityGroup)-[OCI_REGION]->"
|
|
87
|
+
"(region:OCIRegion{name: $OCI_REGION})"
|
|
88
|
+
"return DISTINCT security_group.name as name, security_group.ocid as ocid, security_group.compartmentid "
|
|
89
|
+
"as compartmentid;"
|
|
90
|
+
)
|
|
68
91
|
return neo4j_session.run(query, OCI_TENANCY_ID=tenancy_id, OCI_REGION=region)
|
|
@@ -24,19 +24,25 @@ stat_handler = get_stats_client(__name__)
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@timeit
|
|
27
|
-
def _cleanup_okta_organizations(
|
|
27
|
+
def _cleanup_okta_organizations(
|
|
28
|
+
neo4j_session: neo4j.Session,
|
|
29
|
+
common_job_parameters: Dict,
|
|
30
|
+
) -> None:
|
|
28
31
|
"""
|
|
29
32
|
Remove stale Okta organization
|
|
30
33
|
:param neo4j_session: The Neo4j session
|
|
31
34
|
:param common_job_parameters: Parameters to carry to the cleanup job
|
|
32
35
|
:return: Nothing
|
|
33
36
|
"""
|
|
34
|
-
run_cleanup_job(
|
|
37
|
+
run_cleanup_job("okta_import_cleanup.json", neo4j_session, common_job_parameters)
|
|
35
38
|
cleanup_okta_groups(neo4j_session, common_job_parameters)
|
|
36
39
|
|
|
37
40
|
|
|
38
|
-
def cleanup_okta_groups(
|
|
39
|
-
|
|
41
|
+
def cleanup_okta_groups(
|
|
42
|
+
neo4j_session: neo4j.Session,
|
|
43
|
+
common_job_parameters: Dict,
|
|
44
|
+
) -> None:
|
|
45
|
+
run_cleanup_job("okta_groups_cleanup.json", neo4j_session, common_job_parameters)
|
|
40
46
|
|
|
41
47
|
|
|
42
48
|
@timeit
|
|
@@ -62,34 +68,79 @@ def start_okta_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
62
68
|
|
|
63
69
|
state = OktaSyncState()
|
|
64
70
|
|
|
65
|
-
organization.create_okta_organization(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
organization.create_okta_organization(
|
|
72
|
+
neo4j_session,
|
|
73
|
+
config.okta_org_id,
|
|
74
|
+
config.update_tag,
|
|
75
|
+
)
|
|
76
|
+
users.sync_okta_users(
|
|
77
|
+
neo4j_session,
|
|
78
|
+
config.okta_org_id,
|
|
79
|
+
config.update_tag,
|
|
80
|
+
config.okta_api_key,
|
|
81
|
+
state,
|
|
82
|
+
)
|
|
83
|
+
groups.sync_okta_groups(
|
|
84
|
+
neo4j_session,
|
|
85
|
+
config.okta_org_id,
|
|
86
|
+
config.update_tag,
|
|
87
|
+
config.okta_api_key,
|
|
88
|
+
state,
|
|
89
|
+
)
|
|
90
|
+
applications.sync_okta_applications(
|
|
91
|
+
neo4j_session,
|
|
92
|
+
config.okta_org_id,
|
|
93
|
+
config.update_tag,
|
|
94
|
+
config.okta_api_key,
|
|
95
|
+
)
|
|
96
|
+
factors.sync_users_factors(
|
|
97
|
+
neo4j_session,
|
|
98
|
+
config.okta_org_id,
|
|
99
|
+
config.update_tag,
|
|
100
|
+
config.okta_api_key,
|
|
101
|
+
state,
|
|
102
|
+
)
|
|
103
|
+
origins.sync_trusted_origins(
|
|
104
|
+
neo4j_session,
|
|
105
|
+
config.okta_org_id,
|
|
106
|
+
config.update_tag,
|
|
107
|
+
config.okta_api_key,
|
|
108
|
+
)
|
|
109
|
+
awssaml.sync_okta_aws_saml(
|
|
110
|
+
neo4j_session,
|
|
111
|
+
config.okta_saml_role_regex,
|
|
112
|
+
config.update_tag,
|
|
113
|
+
config.okta_org_id,
|
|
114
|
+
)
|
|
72
115
|
|
|
73
116
|
# need creds with permission
|
|
74
117
|
# soft fail as some won't be able to get such high priv token
|
|
75
118
|
# when we get the E0000006 error
|
|
76
119
|
# see https://developer.okta.com/docs/reference/error-codes/
|
|
77
120
|
try:
|
|
78
|
-
roles.sync_roles(
|
|
121
|
+
roles.sync_roles(
|
|
122
|
+
neo4j_session,
|
|
123
|
+
config.okta_org_id,
|
|
124
|
+
config.update_tag,
|
|
125
|
+
config.okta_api_key,
|
|
126
|
+
state,
|
|
127
|
+
)
|
|
79
128
|
except OktaError as okta_error:
|
|
80
129
|
logger.warning(f"Unable to pull admin roles got {okta_error}")
|
|
81
130
|
|
|
82
131
|
# Getting roles requires super admin which most won't be able to get easily
|
|
83
132
|
if okta_error.error_code == "E0000006":
|
|
84
|
-
logger.warning(
|
|
133
|
+
logger.warning(
|
|
134
|
+
"Unable to sync admin roles - api token needs admin rights to pull admin roles data",
|
|
135
|
+
)
|
|
85
136
|
|
|
86
137
|
_cleanup_okta_organizations(neo4j_session, common_job_parameters)
|
|
87
138
|
|
|
88
139
|
merge_module_sync_metadata(
|
|
89
140
|
neo4j_session,
|
|
90
|
-
group_type=
|
|
141
|
+
group_type="OktaOrganization",
|
|
91
142
|
group_id=config.okta_org_id,
|
|
92
|
-
synced_type=
|
|
143
|
+
synced_type="OktaOrganization",
|
|
93
144
|
update_tag=config.update_tag,
|
|
94
145
|
stat_handler=stat_handler,
|
|
95
146
|
)
|