cartography 0.104.0rc2__py3-none-any.whl → 0.123.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.
- cartography/_version.py +16 -3
- cartography/cli.py +466 -5
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +357 -8
- cartography/config.py +153 -0
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +0 -186
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
- cartography/driftdetect/cli.py +3 -2
- cartography/graph/cleanupbuilder.py +198 -41
- cartography/graph/job.py +54 -6
- cartography/graph/querybuilder.py +528 -27
- cartography/graph/statement.py +5 -1
- cartography/intel/airbyte/__init__.py +105 -0
- cartography/intel/airbyte/connections.py +120 -0
- cartography/intel/airbyte/destinations.py +81 -0
- cartography/intel/airbyte/organizations.py +59 -0
- cartography/intel/airbyte/sources.py +78 -0
- cartography/intel/airbyte/tags.py +64 -0
- cartography/intel/airbyte/users.py +106 -0
- cartography/intel/airbyte/util.py +122 -0
- cartography/intel/airbyte/workspaces.py +63 -0
- cartography/intel/aws/__init__.py +24 -9
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +253 -22
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +17 -39
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +150 -4
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +7 -3
- cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
- cartography/intel/aws/ec2/instances.py +25 -1
- cartography/intel/aws/ec2/internet_gateways.py +4 -2
- cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
- cartography/intel/aws/ec2/network_interfaces.py +5 -1
- cartography/intel/aws/ec2/reserved_instances.py +3 -1
- cartography/intel/aws/ec2/security_groups.py +140 -122
- cartography/intel/aws/ec2/snapshots.py +47 -84
- cartography/intel/aws/ec2/subnets.py +37 -63
- cartography/intel/aws/ec2/tgw.py +11 -5
- cartography/intel/aws/ec2/volumes.py +1 -1
- cartography/intel/aws/ec2/vpc.py +140 -124
- cartography/intel/aws/ec2/vpc_peerings.py +262 -125
- cartography/intel/aws/ecr.py +269 -98
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +251 -380
- cartography/intel/aws/efs.py +179 -11
- cartography/intel/aws/elasticache.py +102 -79
- cartography/intel/aws/elasticsearch.py +13 -4
- cartography/intel/aws/eventbridge.py +164 -0
- cartography/intel/aws/glue.py +181 -0
- cartography/intel/aws/guardduty.py +443 -0
- cartography/intel/aws/iam.py +750 -493
- cartography/intel/aws/identitycenter.py +605 -83
- cartography/intel/aws/inspector.py +221 -105
- cartography/intel/aws/kms.py +173 -201
- cartography/intel/aws/lambda_function.py +272 -189
- cartography/intel/aws/organizations.py +10 -9
- cartography/intel/aws/permission_relationships.py +10 -20
- cartography/intel/aws/rds.py +337 -446
- cartography/intel/aws/redshift.py +9 -4
- cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
- cartography/intel/aws/resources.py +18 -0
- cartography/intel/aws/route53.py +386 -332
- cartography/intel/aws/s3.py +322 -14
- cartography/intel/aws/secretsmanager.py +81 -49
- cartography/intel/aws/securityhub.py +3 -1
- cartography/intel/aws/sns.py +62 -2
- cartography/intel/aws/sqs.py +36 -90
- cartography/intel/aws/ssm.py +3 -5
- cartography/intel/azure/__init__.py +202 -48
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +59 -112
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +222 -361
- cartography/intel/azure/data_factory.py +85 -0
- cartography/intel/azure/data_factory_dataset.py +128 -0
- cartography/intel/azure/data_factory_linked_service.py +119 -0
- cartography/intel/azure/data_factory_pipeline.py +142 -0
- cartography/intel/azure/data_lake.py +124 -0
- cartography/intel/azure/event_grid.py +94 -0
- cartography/intel/azure/functions.py +124 -0
- cartography/intel/azure/load_balancers.py +263 -0
- cartography/intel/azure/logic_apps.py +101 -0
- cartography/intel/azure/monitor.py +105 -0
- cartography/intel/azure/network.py +467 -0
- cartography/intel/azure/permission_relationships.py +466 -0
- cartography/intel/azure/rbac.py +309 -0
- cartography/intel/azure/resource_groups.py +82 -0
- cartography/intel/azure/security_center.py +106 -0
- cartography/intel/azure/sql.py +145 -292
- cartography/intel/azure/storage.py +185 -262
- cartography/intel/azure/subscription.py +21 -43
- cartography/intel/azure/tenant.py +39 -30
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +49 -174
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/create_indexes.py +2 -1
- cartography/intel/crowdstrike/spotlight.py +5 -2
- cartography/intel/dns.py +5 -2
- cartography/intel/entra/__init__.py +100 -1
- cartography/intel/entra/app_role_assignments.py +284 -0
- cartography/intel/entra/applications.py +182 -0
- cartography/intel/entra/federation/__init__.py +0 -0
- cartography/intel/entra/federation/aws_identity_center.py +77 -0
- cartography/intel/entra/groups.py +198 -0
- cartography/intel/entra/ou.py +48 -24
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +105 -57
- cartography/intel/gcp/__init__.py +334 -396
- cartography/intel/gcp/bigtable_app_profile.py +101 -0
- cartography/intel/gcp/bigtable_backup.py +91 -0
- cartography/intel/gcp/bigtable_cluster.py +93 -0
- cartography/intel/gcp/bigtable_instance.py +86 -0
- cartography/intel/gcp/bigtable_table.py +87 -0
- cartography/intel/gcp/cai.py +292 -0
- cartography/intel/gcp/clients.py +112 -0
- cartography/intel/gcp/compute.py +128 -119
- cartography/intel/gcp/crm/__init__.py +0 -0
- cartography/intel/gcp/crm/folders.py +114 -0
- cartography/intel/gcp/crm/orgs.py +70 -0
- cartography/intel/gcp/crm/projects.py +120 -0
- cartography/intel/gcp/dns.py +83 -169
- cartography/intel/gcp/gke.py +72 -113
- cartography/intel/gcp/iam.py +111 -91
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +75 -159
- cartography/intel/github/__init__.py +62 -25
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +463 -85
- cartography/intel/github/teams.py +3 -3
- cartography/intel/github/users.py +5 -0
- cartography/intel/github/util.py +12 -0
- cartography/intel/googleworkspace/__init__.py +193 -0
- cartography/intel/googleworkspace/devices.py +254 -0
- cartography/intel/googleworkspace/groups.py +568 -0
- cartography/intel/googleworkspace/oauth_apps.py +259 -0
- cartography/intel/googleworkspace/tenant.py +85 -0
- cartography/intel/googleworkspace/users.py +138 -0
- cartography/intel/gsuite/__init__.py +17 -9
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/computers.py +7 -1
- cartography/intel/keycloak/__init__.py +153 -0
- cartography/intel/keycloak/authenticationexecutions.py +322 -0
- cartography/intel/keycloak/authenticationflows.py +77 -0
- cartography/intel/keycloak/clients.py +187 -0
- cartography/intel/keycloak/groups.py +126 -0
- cartography/intel/keycloak/identityproviders.py +94 -0
- cartography/intel/keycloak/organizations.py +163 -0
- cartography/intel/keycloak/realms.py +61 -0
- cartography/intel/keycloak/roles.py +202 -0
- cartography/intel/keycloak/scopes.py +73 -0
- cartography/intel/keycloak/users.py +70 -0
- cartography/intel/keycloak/util.py +47 -0
- cartography/intel/kubernetes/__init__.py +60 -14
- cartography/intel/kubernetes/clusters.py +86 -0
- cartography/intel/kubernetes/eks.py +402 -0
- cartography/intel/kubernetes/namespaces.py +59 -57
- cartography/intel/kubernetes/pods.py +168 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -67
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/oci/iam.py +23 -9
- cartography/intel/oci/organizations.py +3 -1
- cartography/intel/oci/utils.py +28 -5
- cartography/intel/okta/applications.py +15 -5
- cartography/intel/okta/awssaml.py +14 -10
- cartography/intel/okta/factors.py +3 -1
- cartography/intel/okta/groups.py +5 -2
- cartography/intel/okta/organization.py +3 -1
- cartography/intel/okta/origins.py +3 -1
- cartography/intel/okta/roles.py +5 -2
- cartography/intel/okta/users.py +10 -2
- cartography/intel/ontology/__init__.py +44 -0
- cartography/intel/ontology/devices.py +54 -0
- cartography/intel/ontology/users.py +54 -0
- cartography/intel/ontology/utils.py +176 -0
- cartography/intel/pagerduty/escalation_policies.py +13 -6
- cartography/intel/pagerduty/schedules.py +9 -4
- cartography/intel/pagerduty/services.py +7 -3
- cartography/intel/pagerduty/teams.py +5 -2
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/scaleway/__init__.py +127 -0
- cartography/intel/scaleway/iam/__init__.py +0 -0
- cartography/intel/scaleway/iam/apikeys.py +71 -0
- cartography/intel/scaleway/iam/applications.py +71 -0
- cartography/intel/scaleway/iam/groups.py +71 -0
- cartography/intel/scaleway/iam/users.py +71 -0
- cartography/intel/scaleway/instances/__init__.py +0 -0
- cartography/intel/scaleway/instances/flexibleips.py +86 -0
- cartography/intel/scaleway/instances/instances.py +92 -0
- cartography/intel/scaleway/projects.py +79 -0
- cartography/intel/scaleway/storage/__init__.py +0 -0
- cartography/intel/scaleway/storage/snapshots.py +86 -0
- cartography/intel/scaleway/storage/volumes.py +84 -0
- cartography/intel/scaleway/utils.py +37 -0
- cartography/intel/sentinelone/__init__.py +75 -0
- cartography/intel/sentinelone/account.py +140 -0
- cartography/intel/sentinelone/agent.py +139 -0
- cartography/intel/sentinelone/api.py +124 -0
- cartography/intel/sentinelone/application.py +248 -0
- cartography/intel/sentinelone/cve.py +119 -0
- cartography/intel/sentinelone/utils.py +28 -0
- cartography/intel/slack/__init__.py +78 -0
- cartography/intel/slack/channels.py +80 -0
- cartography/intel/slack/groups.py +90 -0
- cartography/intel/slack/teams.py +65 -0
- cartography/intel/slack/users.py +57 -0
- cartography/intel/slack/utils.py +29 -0
- cartography/intel/spacelift/__init__.py +161 -0
- cartography/intel/spacelift/account.py +73 -0
- cartography/intel/spacelift/ec2_ownership.py +280 -0
- cartography/intel/spacelift/runs.py +463 -0
- cartography/intel/spacelift/spaces.py +112 -0
- cartography/intel/spacelift/stacks.py +119 -0
- cartography/intel/spacelift/util.py +122 -0
- cartography/intel/spacelift/workerpools.py +131 -0
- cartography/intel/spacelift/workers.py +128 -0
- cartography/intel/trivy/__init__.py +272 -0
- cartography/intel/trivy/scanner.py +386 -0
- cartography/models/airbyte/__init__.py +0 -0
- cartography/models/airbyte/connection.py +138 -0
- cartography/models/airbyte/destination.py +75 -0
- cartography/models/airbyte/organization.py +19 -0
- cartography/models/airbyte/source.py +75 -0
- cartography/models/airbyte/stream.py +74 -0
- cartography/models/airbyte/tag.py +69 -0
- cartography/models/airbyte/user.py +115 -0
- cartography/models/airbyte/workspace.py +46 -0
- cartography/models/anthropic/apikey.py +4 -0
- cartography/models/anthropic/user.py +4 -0
- cartography/models/aws/acm/__init__.py +0 -0
- cartography/models/aws/acm/certificate.py +75 -0
- cartography/models/aws/apigateway/__init__.py +0 -0
- cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
- cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
- cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +45 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
- cartography/models/aws/codebuild/__init__.py +0 -0
- cartography/models/aws/codebuild/project.py +49 -0
- cartography/models/aws/cognito/__init__.py +0 -0
- cartography/models/aws/cognito/identity_pool.py +70 -0
- cartography/models/aws/cognito/user_pool.py +47 -0
- cartography/models/aws/dynamodb/tables.py +2 -0
- cartography/models/aws/ec2/instances.py +25 -1
- cartography/models/aws/ec2/networkinterfaces.py +4 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +2 -0
- cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +20 -0
- cartography/models/aws/ec2/vpc.py +46 -0
- cartography/models/aws/ec2/vpc_cidr.py +102 -0
- cartography/models/aws/ec2/vpc_peering.py +157 -0
- cartography/models/aws/ecr/__init__.py +0 -0
- cartography/models/aws/ecr/image.py +146 -0
- cartography/models/aws/ecr/image_layer.py +107 -0
- cartography/models/aws/ecr/repository.py +72 -0
- cartography/models/aws/ecr/repository_image.py +95 -0
- cartography/models/aws/ecs/__init__.py +0 -0
- cartography/models/aws/ecs/clusters.py +64 -0
- cartography/models/aws/ecs/container_definitions.py +93 -0
- cartography/models/aws/ecs/container_instances.py +84 -0
- cartography/models/aws/ecs/containers.py +101 -0
- cartography/models/aws/ecs/services.py +134 -0
- cartography/models/aws/ecs/task_definitions.py +135 -0
- cartography/models/aws/ecs/tasks.py +134 -0
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/aws/efs/file_system.py +60 -0
- cartography/models/aws/efs/mount_target.py +29 -2
- cartography/models/aws/elasticache/__init__.py +0 -0
- cartography/models/aws/elasticache/cluster.py +65 -0
- cartography/models/aws/elasticache/topic.py +67 -0
- cartography/models/aws/eventbridge/__init__.py +0 -0
- cartography/models/aws/eventbridge/rule.py +77 -0
- cartography/models/aws/eventbridge/target.py +71 -0
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/guardduty/__init__.py +1 -0
- cartography/models/aws/guardduty/detectors.py +50 -0
- cartography/models/aws/guardduty/findings.py +121 -0
- cartography/models/aws/iam/access_key.py +103 -0
- cartography/models/aws/iam/account_role.py +24 -0
- cartography/models/aws/iam/federated_principal.py +60 -0
- cartography/models/aws/iam/group.py +60 -0
- cartography/models/aws/iam/group_membership.py +27 -0
- cartography/models/aws/iam/inline_policy.py +78 -0
- cartography/models/aws/iam/managed_policy.py +51 -0
- cartography/models/aws/iam/policy_statement.py +57 -0
- cartography/models/aws/iam/role.py +83 -0
- cartography/models/aws/iam/root_principal.py +52 -0
- cartography/models/aws/iam/service_principal.py +30 -0
- cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
- cartography/models/aws/iam/user.py +59 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
- cartography/models/aws/identitycenter/awspermissionset.py +70 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +49 -9
- cartography/models/aws/inspector/findings.py +37 -0
- cartography/models/aws/inspector/packages.py +1 -31
- cartography/models/aws/kms/__init__.py +0 -0
- cartography/models/aws/kms/aliases.py +86 -0
- cartography/models/aws/kms/grants.py +65 -0
- cartography/models/aws/kms/keys.py +88 -0
- cartography/models/aws/lambda_function/__init__.py +0 -0
- cartography/models/aws/lambda_function/alias.py +74 -0
- cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
- cartography/models/aws/lambda_function/lambda_function.py +91 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +91 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/rds/instance.py +156 -0
- cartography/models/aws/rds/snapshot.py +108 -0
- cartography/models/aws/rds/subnet_group.py +101 -0
- cartography/models/aws/route53/__init__.py +0 -0
- cartography/models/aws/route53/dnsrecord.py +235 -0
- cartography/models/aws/route53/nameserver.py +63 -0
- cartography/models/aws/route53/subzone.py +40 -0
- cartography/models/aws/route53/zone.py +47 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +0 -2
- cartography/models/aws/sns/topic_subscription.py +74 -0
- cartography/models/aws/sqs/__init__.py +0 -0
- cartography/models/aws/sqs/queue.py +89 -0
- cartography/models/azure/__init__.py +0 -0
- cartography/models/azure/aks_cluster.py +54 -0
- cartography/models/azure/aks_nodepool.py +54 -0
- cartography/models/azure/app_service.py +59 -0
- cartography/models/azure/container_instance.py +57 -0
- cartography/models/azure/cosmosdb/__init__.py +0 -0
- cartography/models/azure/cosmosdb/account.py +77 -0
- cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
- cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
- cartography/models/azure/cosmosdb/cassandratable.py +81 -0
- cartography/models/azure/cosmosdb/corspolicy.py +74 -0
- cartography/models/azure/cosmosdb/dblocation.py +120 -0
- cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
- cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
- cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
- cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
- cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
- cartography/models/azure/cosmosdb/tableresource.py +76 -0
- cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
- cartography/models/azure/data_factory/__init__.py +0 -0
- cartography/models/azure/data_factory/data_factory.py +51 -0
- cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
- cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
- cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
- cartography/models/azure/data_lake_filesystem.py +51 -0
- cartography/models/azure/event_grid_topic.py +57 -0
- cartography/models/azure/function_app.py +59 -0
- cartography/models/azure/load_balancer/__init__.py +0 -0
- cartography/models/azure/load_balancer/load_balancer.py +49 -0
- cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
- cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
- cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
- cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
- cartography/models/azure/logic_apps.py +56 -0
- cartography/models/azure/monitor.py +54 -0
- cartography/models/azure/network_interface.py +112 -0
- cartography/models/azure/network_security_group.py +50 -0
- cartography/models/azure/permission_relationships.py +60 -0
- cartography/models/azure/principal.py +41 -0
- cartography/models/azure/public_ip_address.py +50 -0
- cartography/models/azure/rbac.py +268 -0
- cartography/models/azure/resource_groups.py +52 -0
- cartography/models/azure/security_center.py +50 -0
- cartography/models/azure/sql/__init__.py +0 -0
- cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
- cartography/models/azure/sql/elasticpool.py +77 -0
- cartography/models/azure/sql/failovergroup.py +73 -0
- cartography/models/azure/sql/recoverabledatabase.py +75 -0
- cartography/models/azure/sql/replicationlink.py +81 -0
- cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
- cartography/models/azure/sql/restorepoint.py +74 -0
- cartography/models/azure/sql/serveradadministrator.py +74 -0
- cartography/models/azure/sql/serverdnsalias.py +71 -0
- cartography/models/azure/sql/sqldatabase.py +85 -0
- cartography/models/azure/sql/sqlserver.py +50 -0
- cartography/models/azure/sql/transparentdataencryption.py +76 -0
- cartography/models/azure/storage/__init__.py +0 -0
- cartography/models/azure/storage/account.py +59 -0
- cartography/models/azure/storage/blobcontainer.py +85 -0
- cartography/models/azure/storage/blobservice.py +71 -0
- cartography/models/azure/storage/fileservice.py +71 -0
- cartography/models/azure/storage/fileshare.py +82 -0
- cartography/models/azure/storage/queue.py +71 -0
- cartography/models/azure/storage/queueservice.py +73 -0
- cartography/models/azure/storage/table.py +72 -0
- cartography/models/azure/storage/tableservice.py +73 -0
- cartography/models/azure/subnet.py +101 -0
- cartography/models/azure/subscription.py +47 -0
- cartography/models/azure/tags/__init__.py +0 -0
- cartography/models/azure/tags/storage_tag.py +40 -0
- cartography/models/azure/tags/tag.py +37 -0
- cartography/models/azure/tenant.py +17 -0
- cartography/models/azure/virtual_network.py +49 -0
- cartography/models/azure/vm/__init__.py +0 -0
- cartography/models/azure/vm/datadisk.py +80 -0
- cartography/models/azure/vm/disk.py +55 -0
- cartography/models/azure/vm/snapshot.py +56 -0
- cartography/models/azure/vm/virtualmachine.py +59 -0
- cartography/models/bigfix/bigfix_computer.py +1 -1
- cartography/models/cloudflare/member.py +4 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/nodes.py +15 -2
- cartography/models/core/relationships.py +44 -0
- cartography/models/crowdstrike/hosts.py +1 -1
- cartography/models/digitalocean/droplet.py +2 -0
- cartography/models/duo/endpoint.py +1 -1
- cartography/models/duo/phone.py +2 -2
- cartography/models/duo/user.py +4 -0
- cartography/models/entra/app_role_assignment.py +115 -0
- cartography/models/entra/application.py +49 -0
- cartography/models/entra/entra_user_to_aws_sso.py +41 -0
- cartography/models/entra/group.py +117 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/user.py +42 -51
- cartography/models/gcp/__init__.py +0 -0
- cartography/models/gcp/bigtable/__init__.py +0 -0
- cartography/models/gcp/bigtable/app_profile.py +94 -0
- cartography/models/gcp/bigtable/backup.py +91 -0
- cartography/models/gcp/bigtable/cluster.py +73 -0
- cartography/models/gcp/bigtable/instance.py +52 -0
- cartography/models/gcp/bigtable/table.py +69 -0
- cartography/models/gcp/compute/__init__.py +0 -0
- cartography/models/gcp/compute/subnet.py +74 -0
- cartography/models/gcp/compute/vpc.py +50 -0
- cartography/models/gcp/crm/__init__.py +0 -0
- cartography/models/gcp/crm/folders.py +98 -0
- cartography/models/gcp/crm/organizations.py +21 -0
- cartography/models/gcp/crm/projects.py +100 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/gke.py +69 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/permission_relationships.py +61 -0
- cartography/models/gcp/policy_bindings.py +93 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- cartography/models/github/commits.py +63 -0
- cartography/models/github/dependencies.py +73 -0
- cartography/models/github/manifests.py +49 -0
- cartography/models/github/users.py +10 -0
- cartography/models/googleworkspace/__init__.py +0 -0
- cartography/models/googleworkspace/device.py +132 -0
- cartography/models/googleworkspace/group.py +382 -0
- cartography/models/googleworkspace/oauth_app.py +124 -0
- cartography/models/googleworkspace/tenant.py +30 -0
- cartography/models/googleworkspace/user.py +113 -0
- cartography/models/gsuite/__init__.py +0 -0
- cartography/models/gsuite/group.py +218 -0
- cartography/models/gsuite/tenant.py +29 -0
- cartography/models/gsuite/user.py +107 -0
- cartography/models/kandji/device.py +1 -2
- cartography/models/keycloak/__init__.py +0 -0
- cartography/models/keycloak/authenticationexecution.py +160 -0
- cartography/models/keycloak/authenticationflow.py +54 -0
- cartography/models/keycloak/client.py +179 -0
- cartography/models/keycloak/group.py +101 -0
- cartography/models/keycloak/identityprovider.py +89 -0
- cartography/models/keycloak/organization.py +116 -0
- cartography/models/keycloak/organizationdomain.py +73 -0
- cartography/models/keycloak/realm.py +173 -0
- cartography/models/keycloak/role.py +126 -0
- cartography/models/keycloak/scope.py +73 -0
- cartography/models/keycloak/user.py +55 -0
- cartography/models/kubernetes/__init__.py +0 -0
- cartography/models/kubernetes/clusterrolebindings.py +138 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/clusters.py +26 -0
- cartography/models/kubernetes/containers.py +133 -0
- cartography/models/kubernetes/groups.py +107 -0
- cartography/models/kubernetes/namespaces.py +51 -0
- cartography/models/kubernetes/oidc.py +51 -0
- cartography/models/kubernetes/pods.py +80 -0
- cartography/models/kubernetes/rolebindings.py +159 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/secrets.py +79 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- cartography/models/kubernetes/services.py +108 -0
- cartography/models/kubernetes/users.py +105 -0
- cartography/models/lastpass/user.py +4 -0
- cartography/models/ontology/__init__.py +0 -0
- cartography/models/ontology/device.py +137 -0
- cartography/models/ontology/mapping/__init__.py +76 -0
- cartography/models/ontology/mapping/data/__init__.py +0 -0
- cartography/models/ontology/mapping/data/apikeys.py +93 -0
- cartography/models/ontology/mapping/data/computeinstance.py +95 -0
- cartography/models/ontology/mapping/data/containers.py +88 -0
- cartography/models/ontology/mapping/data/databases.py +182 -0
- cartography/models/ontology/mapping/data/devices.py +194 -0
- cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
- cartography/models/ontology/mapping/data/useraccounts.py +416 -0
- cartography/models/ontology/mapping/data/users.py +63 -0
- cartography/models/ontology/mapping/specs.py +85 -0
- cartography/models/ontology/user.py +51 -0
- cartography/models/openai/adminapikey.py +4 -0
- cartography/models/openai/apikey.py +4 -0
- cartography/models/openai/user.py +4 -0
- cartography/models/scaleway/__init__.py +0 -0
- cartography/models/scaleway/iam/__init__.py +0 -0
- cartography/models/scaleway/iam/apikey.py +100 -0
- cartography/models/scaleway/iam/application.py +52 -0
- cartography/models/scaleway/iam/group.py +95 -0
- cartography/models/scaleway/iam/user.py +64 -0
- cartography/models/scaleway/instance/__init__.py +0 -0
- cartography/models/scaleway/instance/flexibleip.py +52 -0
- cartography/models/scaleway/instance/instance.py +120 -0
- cartography/models/scaleway/organization.py +19 -0
- cartography/models/scaleway/project.py +48 -0
- cartography/models/scaleway/storage/__init__.py +0 -0
- cartography/models/scaleway/storage/snapshot.py +78 -0
- cartography/models/scaleway/storage/volume.py +51 -0
- cartography/models/sentinelone/__init__.py +1 -0
- cartography/models/sentinelone/account.py +40 -0
- cartography/models/sentinelone/agent.py +50 -0
- cartography/models/sentinelone/application.py +44 -0
- cartography/models/sentinelone/application_version.py +96 -0
- cartography/models/sentinelone/cve.py +73 -0
- cartography/models/slack/__init__.py +0 -0
- cartography/models/slack/channels.py +92 -0
- cartography/models/slack/group.py +129 -0
- cartography/models/slack/team.py +22 -0
- cartography/models/slack/user.py +62 -0
- cartography/models/snipeit/asset.py +2 -0
- cartography/models/snipeit/user.py +4 -0
- cartography/models/spacelift/__init__.py +0 -0
- cartography/models/spacelift/cloudtrailevent.py +120 -0
- cartography/models/spacelift/run.py +162 -0
- cartography/models/spacelift/space.py +131 -0
- cartography/models/spacelift/spaceliftaccount.py +31 -0
- cartography/models/spacelift/spaceliftgitcommit.py +157 -0
- cartography/models/spacelift/stack.py +96 -0
- cartography/models/spacelift/user.py +63 -0
- cartography/models/spacelift/worker.py +97 -0
- cartography/models/spacelift/workerpool.py +90 -0
- cartography/models/tailscale/device.py +2 -1
- cartography/models/tailscale/user.py +6 -1
- cartography/models/trivy/__init__.py +0 -0
- cartography/models/trivy/findings.py +66 -0
- cartography/models/trivy/fix.py +66 -0
- cartography/models/trivy/package.py +71 -0
- cartography/rules/README.md +1 -0
- cartography/rules/__init__.py +0 -0
- cartography/rules/cli.py +261 -0
- cartography/rules/data/__init__.py +0 -0
- cartography/rules/data/rules/__init__.py +46 -0
- cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
- cartography/rules/data/rules/compute_instance_exposed.py +51 -0
- cartography/rules/data/rules/database_instance_exposed.py +53 -0
- cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
- cartography/rules/data/rules/identity_administration_privileges.py +100 -0
- cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
- cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
- cartography/rules/data/rules/mfa_missing.py +46 -0
- cartography/rules/data/rules/object_storage_public.py +100 -0
- cartography/rules/data/rules/policy_administration_privileges.py +104 -0
- cartography/rules/data/rules/unmanaged_accounts.py +43 -0
- cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
- cartography/rules/formatters.py +108 -0
- cartography/rules/runners.py +216 -0
- cartography/rules/spec/__init__.py +0 -0
- cartography/rules/spec/model.py +267 -0
- cartography/rules/spec/result.py +38 -0
- cartography/sync.py +25 -5
- cartography/util.py +101 -31
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
- cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
- cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
- cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
- cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
- cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
- cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
- cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
- cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
- cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
- cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
- cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
- cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
- cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
- cartography/intel/gcp/crm.py +0 -355
- cartography/intel/gsuite/api.py +0 -342
- cartography-0.104.0rc2.dist-info/RECORD +0 -455
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
- /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
- /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
- /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
|
@@ -2,15 +2,57 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
4
|
import neo4j
|
|
5
|
+
from azure.identity import ClientSecretCredential
|
|
6
|
+
from msgraph import GraphServiceClient
|
|
5
7
|
|
|
6
8
|
from cartography.config import Config
|
|
9
|
+
from cartography.intel.entra.app_role_assignments import sync_app_role_assignments
|
|
10
|
+
from cartography.intel.entra.applications import sync_entra_applications
|
|
11
|
+
from cartography.intel.entra.federation.aws_identity_center import sync_entra_federation
|
|
12
|
+
from cartography.intel.entra.groups import sync_entra_groups
|
|
7
13
|
from cartography.intel.entra.ou import sync_entra_ous
|
|
14
|
+
from cartography.intel.entra.service_principals import sync_service_principals
|
|
15
|
+
from cartography.intel.entra.users import get_tenant
|
|
16
|
+
from cartography.intel.entra.users import load_tenant
|
|
8
17
|
from cartography.intel.entra.users import sync_entra_users
|
|
18
|
+
from cartography.intel.entra.users import transform_tenant
|
|
9
19
|
from cartography.util import timeit
|
|
10
20
|
|
|
11
21
|
logger = logging.getLogger(__name__)
|
|
12
22
|
|
|
13
23
|
|
|
24
|
+
@timeit
|
|
25
|
+
async def sync_tenant(
|
|
26
|
+
neo4j_session: neo4j.Session,
|
|
27
|
+
tenant_id: str,
|
|
28
|
+
client_id: str,
|
|
29
|
+
client_secret: str,
|
|
30
|
+
update_tag: int,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Sync tenant information as a prerequisite for all other Entra resource syncs.
|
|
34
|
+
|
|
35
|
+
:param neo4j_session: Neo4j session
|
|
36
|
+
:param tenant_id: Entra tenant ID
|
|
37
|
+
:param client_id: Azure application client ID
|
|
38
|
+
:param client_secret: Azure application client secret
|
|
39
|
+
:param update_tag: Update tag for tracking data freshness
|
|
40
|
+
"""
|
|
41
|
+
credential = ClientSecretCredential(
|
|
42
|
+
tenant_id=tenant_id,
|
|
43
|
+
client_id=client_id,
|
|
44
|
+
client_secret=client_secret,
|
|
45
|
+
)
|
|
46
|
+
client = GraphServiceClient(
|
|
47
|
+
credential, scopes=["https://graph.microsoft.com/.default"]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Fetch tenant and load it
|
|
51
|
+
tenant = await get_tenant(client)
|
|
52
|
+
transformed_tenant = transform_tenant(tenant, tenant_id)
|
|
53
|
+
load_tenant(neo4j_session, transformed_tenant, update_tag)
|
|
54
|
+
|
|
55
|
+
|
|
14
56
|
@timeit
|
|
15
57
|
def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
16
58
|
"""
|
|
@@ -37,6 +79,15 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
37
79
|
}
|
|
38
80
|
|
|
39
81
|
async def main() -> None:
|
|
82
|
+
# Load tenant first as a prerequisite for all resource syncs
|
|
83
|
+
await sync_tenant(
|
|
84
|
+
neo4j_session,
|
|
85
|
+
config.entra_tenant_id,
|
|
86
|
+
config.entra_client_id,
|
|
87
|
+
config.entra_client_secret,
|
|
88
|
+
config.update_tag,
|
|
89
|
+
)
|
|
90
|
+
|
|
40
91
|
# Run user sync
|
|
41
92
|
await sync_entra_users(
|
|
42
93
|
neo4j_session,
|
|
@@ -47,6 +98,16 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
47
98
|
common_job_parameters,
|
|
48
99
|
)
|
|
49
100
|
|
|
101
|
+
# Run group sync
|
|
102
|
+
await sync_entra_groups(
|
|
103
|
+
neo4j_session,
|
|
104
|
+
config.entra_tenant_id,
|
|
105
|
+
config.entra_client_id,
|
|
106
|
+
config.entra_client_secret,
|
|
107
|
+
config.update_tag,
|
|
108
|
+
common_job_parameters,
|
|
109
|
+
)
|
|
110
|
+
|
|
50
111
|
# Run OU sync
|
|
51
112
|
await sync_entra_ous(
|
|
52
113
|
neo4j_session,
|
|
@@ -57,5 +118,43 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
57
118
|
common_job_parameters,
|
|
58
119
|
)
|
|
59
120
|
|
|
60
|
-
|
|
121
|
+
# Run application sync
|
|
122
|
+
await sync_entra_applications(
|
|
123
|
+
neo4j_session,
|
|
124
|
+
config.entra_tenant_id,
|
|
125
|
+
config.entra_client_id,
|
|
126
|
+
config.entra_client_secret,
|
|
127
|
+
config.update_tag,
|
|
128
|
+
common_job_parameters,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Run service principals sync
|
|
132
|
+
await sync_service_principals(
|
|
133
|
+
neo4j_session,
|
|
134
|
+
config.entra_tenant_id,
|
|
135
|
+
config.entra_client_id,
|
|
136
|
+
config.entra_client_secret,
|
|
137
|
+
config.update_tag,
|
|
138
|
+
common_job_parameters,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Run app role assignments sync
|
|
142
|
+
await sync_app_role_assignments(
|
|
143
|
+
neo4j_session,
|
|
144
|
+
config.entra_tenant_id,
|
|
145
|
+
config.entra_client_id,
|
|
146
|
+
config.entra_client_secret,
|
|
147
|
+
config.update_tag,
|
|
148
|
+
common_job_parameters,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Run federation sync (after all resources are synced)
|
|
152
|
+
await sync_entra_federation(
|
|
153
|
+
neo4j_session,
|
|
154
|
+
config.update_tag,
|
|
155
|
+
config.entra_tenant_id,
|
|
156
|
+
common_job_parameters,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Execute syncs in sequence
|
|
61
160
|
asyncio.run(main())
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import AsyncGenerator
|
|
4
|
+
|
|
5
|
+
import neo4j
|
|
6
|
+
from azure.identity import ClientSecretCredential
|
|
7
|
+
from msgraph import GraphServiceClient
|
|
8
|
+
from msgraph.generated.models.app_role_assignment_collection_response import (
|
|
9
|
+
AppRoleAssignmentCollectionResponse,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from cartography.client.core.tx import load
|
|
13
|
+
from cartography.client.core.tx import read_list_of_values_tx
|
|
14
|
+
from cartography.client.core.tx import read_single_value_tx
|
|
15
|
+
from cartography.graph.job import GraphJob
|
|
16
|
+
from cartography.intel.entra.applications import APP_ROLE_ASSIGNMENTS_PAGE_SIZE
|
|
17
|
+
from cartography.intel.entra.applications import logger
|
|
18
|
+
from cartography.models.entra.app_role_assignment import EntraAppRoleAssignmentSchema
|
|
19
|
+
from cartography.util import timeit
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@timeit
|
|
23
|
+
async def get_app_role_assignments_for_app(
|
|
24
|
+
client: GraphServiceClient, neo4j_session: neo4j.Session, app_id: str
|
|
25
|
+
) -> AsyncGenerator[dict[str, Any], None]:
|
|
26
|
+
"""
|
|
27
|
+
Gets app role assignments for a single application by querying the graph for service principal ID.
|
|
28
|
+
|
|
29
|
+
:param client: GraphServiceClient
|
|
30
|
+
:param neo4j_session: Neo4j session for querying service principal
|
|
31
|
+
:param app_id: Application ID
|
|
32
|
+
:return: Generator of app role assignment data as dicts
|
|
33
|
+
"""
|
|
34
|
+
logger.info(f"Fetching role assignments for application: {app_id}")
|
|
35
|
+
|
|
36
|
+
# Query the graph to get the service principal ID for this application
|
|
37
|
+
query = """
|
|
38
|
+
MATCH (sp:EntraServicePrincipal {app_id: $app_id})
|
|
39
|
+
RETURN sp.id as service_principal_id
|
|
40
|
+
"""
|
|
41
|
+
service_principal_id = neo4j_session.execute_read(
|
|
42
|
+
read_single_value_tx, query, app_id=app_id
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if not service_principal_id:
|
|
46
|
+
logger.warning(
|
|
47
|
+
f"No service principal found in graph for application {app_id}. Continuing."
|
|
48
|
+
)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# Get assignments for this service principal with pagination and limits
|
|
52
|
+
# Use maximum page size (999) to get more data per request
|
|
53
|
+
# Memory is managed through streaming and batching, not page size
|
|
54
|
+
request_config = client.service_principals.by_service_principal_id(
|
|
55
|
+
service_principal_id
|
|
56
|
+
).app_role_assigned_to.AppRoleAssignedToRequestBuilderGetRequestConfiguration(
|
|
57
|
+
query_parameters=client.service_principals.by_service_principal_id(
|
|
58
|
+
service_principal_id
|
|
59
|
+
).app_role_assigned_to.AppRoleAssignedToRequestBuilderGetQueryParameters(
|
|
60
|
+
top=APP_ROLE_ASSIGNMENTS_PAGE_SIZE # Maximum allowed by Microsoft Graph API
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assignments_page: AppRoleAssignmentCollectionResponse | None = (
|
|
65
|
+
await client.service_principals.by_service_principal_id(
|
|
66
|
+
service_principal_id
|
|
67
|
+
).app_role_assigned_to.get(request_configuration=request_config)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
assignment_count = 0
|
|
71
|
+
page_count = 0
|
|
72
|
+
|
|
73
|
+
while assignments_page:
|
|
74
|
+
page_count += 1
|
|
75
|
+
|
|
76
|
+
if assignments_page.value:
|
|
77
|
+
page_valid_count = 0
|
|
78
|
+
page_skipped_count = 0
|
|
79
|
+
|
|
80
|
+
# Process assignments and immediately yield to avoid accumulation
|
|
81
|
+
for assignment in assignments_page.value:
|
|
82
|
+
# Only yield if we have valid data since it's possible (but unlikely) for assignment.id to be None
|
|
83
|
+
if assignment.principal_id:
|
|
84
|
+
assignment_count += 1
|
|
85
|
+
page_valid_count += 1
|
|
86
|
+
yield {
|
|
87
|
+
"id": assignment.id,
|
|
88
|
+
"app_role_id": assignment.app_role_id,
|
|
89
|
+
"created_date_time": assignment.created_date_time,
|
|
90
|
+
"principal_id": assignment.principal_id,
|
|
91
|
+
"principal_display_name": assignment.principal_display_name,
|
|
92
|
+
"principal_type": assignment.principal_type,
|
|
93
|
+
"resource_display_name": assignment.resource_display_name,
|
|
94
|
+
"resource_id": assignment.resource_id,
|
|
95
|
+
"application_app_id": app_id,
|
|
96
|
+
}
|
|
97
|
+
else:
|
|
98
|
+
page_skipped_count += 1
|
|
99
|
+
|
|
100
|
+
# Log page results with details about skipped objects
|
|
101
|
+
if page_skipped_count > 0:
|
|
102
|
+
logger.warning(
|
|
103
|
+
f"Page {page_count} for {app_id}: {page_valid_count} valid assignments, "
|
|
104
|
+
f"{page_skipped_count} skipped objects. Total valid: {assignment_count}"
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
logger.debug(
|
|
108
|
+
f"Page {page_count} for {app_id}: {page_valid_count} assignments. "
|
|
109
|
+
f"Total: {assignment_count}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Force garbage collection after each page
|
|
113
|
+
gc.collect()
|
|
114
|
+
|
|
115
|
+
# Check if we have more pages to fetch
|
|
116
|
+
if not assignments_page.odata_next_link:
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
# Clear previous page before fetching next
|
|
120
|
+
assignments_page.value = None
|
|
121
|
+
|
|
122
|
+
# Fetch next page using the SAME request builder to preserve response typing
|
|
123
|
+
# Using the root service_principals builder here can return ServicePrincipal objects,
|
|
124
|
+
# which lack AppRoleAssignment fields like principal_id. Stay on the
|
|
125
|
+
# app_role_assigned_to builder to ensure AppRoleAssignmentCollectionResponse typing.
|
|
126
|
+
logger.debug(f"Fetching page {page_count + 1} of assignments for {app_id}")
|
|
127
|
+
next_page_url = assignments_page.odata_next_link
|
|
128
|
+
assignments_page = await (
|
|
129
|
+
client.service_principals.by_service_principal_id(service_principal_id)
|
|
130
|
+
.app_role_assigned_to.with_url(next_page_url)
|
|
131
|
+
.get()
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
logger.info(
|
|
135
|
+
f"Successfully retrieved {assignment_count} assignments for application {app_id} (pages: {page_count})"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def transform_app_role_assignments(
|
|
140
|
+
assignments: list[dict[str, Any]],
|
|
141
|
+
) -> list[dict[str, Any]]:
|
|
142
|
+
"""
|
|
143
|
+
Transform app role assignment data for graph loading.
|
|
144
|
+
|
|
145
|
+
:param assignments: Raw app role assignment data as dicts
|
|
146
|
+
:return: Transformed assignment data for graph loading
|
|
147
|
+
"""
|
|
148
|
+
transformed = []
|
|
149
|
+
for assign in assignments:
|
|
150
|
+
transformed.append(
|
|
151
|
+
{
|
|
152
|
+
"id": assign["id"],
|
|
153
|
+
"app_role_id": (
|
|
154
|
+
str(assign["app_role_id"]) if assign["app_role_id"] else None
|
|
155
|
+
),
|
|
156
|
+
"created_date_time": assign["created_date_time"],
|
|
157
|
+
"principal_id": (
|
|
158
|
+
str(assign["principal_id"]) if assign["principal_id"] else None
|
|
159
|
+
),
|
|
160
|
+
"principal_display_name": assign["principal_display_name"],
|
|
161
|
+
"principal_type": assign["principal_type"],
|
|
162
|
+
"resource_display_name": assign["resource_display_name"],
|
|
163
|
+
"resource_id": (
|
|
164
|
+
str(assign["resource_id"]) if assign["resource_id"] else None
|
|
165
|
+
),
|
|
166
|
+
"application_app_id": assign["application_app_id"],
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
return transformed
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@timeit
|
|
173
|
+
def load_app_role_assignments(
|
|
174
|
+
neo4j_session: neo4j.Session,
|
|
175
|
+
assignments_data: list[dict[str, Any]],
|
|
176
|
+
update_tag: int,
|
|
177
|
+
tenant_id: str,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""
|
|
180
|
+
Load Entra app role assignments to the graph.
|
|
181
|
+
|
|
182
|
+
:param neo4j_session: Neo4j session
|
|
183
|
+
:param assignments_data: Assignment data to load
|
|
184
|
+
:param update_tag: Update tag for tracking data freshness
|
|
185
|
+
:param tenant_id: Entra tenant ID
|
|
186
|
+
"""
|
|
187
|
+
load(
|
|
188
|
+
neo4j_session,
|
|
189
|
+
EntraAppRoleAssignmentSchema(),
|
|
190
|
+
assignments_data,
|
|
191
|
+
lastupdated=update_tag,
|
|
192
|
+
TENANT_ID=tenant_id,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@timeit
|
|
197
|
+
def cleanup_app_role_assignments(
|
|
198
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
199
|
+
) -> None:
|
|
200
|
+
"""
|
|
201
|
+
Delete Entra app role assignments and their relationships from the graph if they were not updated in the last sync.
|
|
202
|
+
|
|
203
|
+
:param neo4j_session: Neo4j session
|
|
204
|
+
:param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
|
|
205
|
+
"""
|
|
206
|
+
GraphJob.from_node_schema(
|
|
207
|
+
EntraAppRoleAssignmentSchema(), common_job_parameters
|
|
208
|
+
).run(neo4j_session)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@timeit
|
|
212
|
+
async def sync_app_role_assignments(
|
|
213
|
+
neo4j_session: neo4j.Session,
|
|
214
|
+
tenant_id: str,
|
|
215
|
+
client_id: str,
|
|
216
|
+
client_secret: str,
|
|
217
|
+
update_tag: int,
|
|
218
|
+
common_job_parameters: dict[str, Any],
|
|
219
|
+
) -> None:
|
|
220
|
+
"""
|
|
221
|
+
Sync Entra app role assignments to the graph.
|
|
222
|
+
|
|
223
|
+
:param neo4j_session: Neo4j session
|
|
224
|
+
:param tenant_id: Entra tenant ID
|
|
225
|
+
:param client_id: Azure application client ID
|
|
226
|
+
:param client_secret: Azure application client secret
|
|
227
|
+
:param update_tag: Update tag for tracking data freshness
|
|
228
|
+
:param common_job_parameters: Common job parameters for cleanup
|
|
229
|
+
"""
|
|
230
|
+
# Create credentials and client
|
|
231
|
+
credential = ClientSecretCredential(
|
|
232
|
+
tenant_id=tenant_id,
|
|
233
|
+
client_id=client_id,
|
|
234
|
+
client_secret=client_secret,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
client = GraphServiceClient(
|
|
238
|
+
credential,
|
|
239
|
+
scopes=["https://graph.microsoft.com/.default"],
|
|
240
|
+
)
|
|
241
|
+
assignment_batch_size = 200 # Batch size for assignments
|
|
242
|
+
assignments_batch = []
|
|
243
|
+
total_assignment_count = 0
|
|
244
|
+
|
|
245
|
+
# Get app_ids from graph instead of streaming from API again
|
|
246
|
+
query = "MATCH (app:EntraApplication) RETURN app.app_id"
|
|
247
|
+
app_ids = neo4j_session.execute_read(read_list_of_values_tx, query)
|
|
248
|
+
|
|
249
|
+
for app_id in app_ids:
|
|
250
|
+
# Stream app role assignments (now using graph query for service principal ID)
|
|
251
|
+
async for assignment in get_app_role_assignments_for_app(
|
|
252
|
+
client, neo4j_session, app_id
|
|
253
|
+
):
|
|
254
|
+
assignments_batch.append(assignment)
|
|
255
|
+
total_assignment_count += 1
|
|
256
|
+
|
|
257
|
+
# Transform and load assignments in batches
|
|
258
|
+
if len(assignments_batch) >= assignment_batch_size:
|
|
259
|
+
transformed_assignments = transform_app_role_assignments(
|
|
260
|
+
assignments_batch
|
|
261
|
+
)
|
|
262
|
+
load_app_role_assignments(
|
|
263
|
+
neo4j_session, transformed_assignments, update_tag, tenant_id
|
|
264
|
+
)
|
|
265
|
+
logger.debug(f"Loaded batch of {len(assignments_batch)} assignments")
|
|
266
|
+
assignments_batch.clear()
|
|
267
|
+
transformed_assignments.clear()
|
|
268
|
+
|
|
269
|
+
# Force garbage collection after batch load
|
|
270
|
+
gc.collect()
|
|
271
|
+
|
|
272
|
+
# Process remaining assignments
|
|
273
|
+
if assignments_batch:
|
|
274
|
+
transformed_assignments = transform_app_role_assignments(assignments_batch)
|
|
275
|
+
load_app_role_assignments(
|
|
276
|
+
neo4j_session, transformed_assignments, update_tag, tenant_id
|
|
277
|
+
)
|
|
278
|
+
assignments_batch.clear()
|
|
279
|
+
transformed_assignments.clear()
|
|
280
|
+
|
|
281
|
+
cleanup_app_role_assignments(neo4j_session, common_job_parameters)
|
|
282
|
+
logger.info(f"Completed syncing {total_assignment_count} app role assignments")
|
|
283
|
+
# Final garbage collection
|
|
284
|
+
gc.collect()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import AsyncGenerator
|
|
5
|
+
from typing import Generator
|
|
6
|
+
|
|
7
|
+
import neo4j
|
|
8
|
+
from azure.identity import ClientSecretCredential
|
|
9
|
+
from msgraph.generated.models.application import Application
|
|
10
|
+
from msgraph.graph_service_client import GraphServiceClient
|
|
11
|
+
|
|
12
|
+
from cartography.client.core.tx import load
|
|
13
|
+
from cartography.graph.job import GraphJob
|
|
14
|
+
from cartography.models.entra.application import EntraApplicationSchema
|
|
15
|
+
from cartography.util import timeit
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Configurable constants for API pagination
|
|
20
|
+
# Microsoft Graph API recommends page sizes up to 999 for most resources
|
|
21
|
+
# Set to 999 by default, but can be adjusted if needed
|
|
22
|
+
#
|
|
23
|
+
# Adjust these values if:
|
|
24
|
+
# - You have performance issues (decrease values)
|
|
25
|
+
# - You want to minimize API calls (increase values up to 999)
|
|
26
|
+
# - You're hitting rate limits (decrease values)
|
|
27
|
+
APPLICATIONS_PAGE_SIZE = 999
|
|
28
|
+
APP_ROLE_ASSIGNMENTS_PAGE_SIZE = 999
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@timeit
|
|
32
|
+
async def get_entra_applications(
|
|
33
|
+
client: GraphServiceClient,
|
|
34
|
+
) -> AsyncGenerator[Application, None]:
|
|
35
|
+
"""
|
|
36
|
+
Gets Entra applications using the Microsoft Graph API with a generator.
|
|
37
|
+
|
|
38
|
+
:param client: GraphServiceClient
|
|
39
|
+
:return: Generator of raw Application objects from Microsoft Graph
|
|
40
|
+
"""
|
|
41
|
+
count = 0
|
|
42
|
+
# Get all applications with pagination
|
|
43
|
+
request_configuration = client.applications.ApplicationsRequestBuilderGetRequestConfiguration(
|
|
44
|
+
query_parameters=client.applications.ApplicationsRequestBuilderGetQueryParameters(
|
|
45
|
+
top=APPLICATIONS_PAGE_SIZE
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
page = await client.applications.get(request_configuration=request_configuration)
|
|
49
|
+
|
|
50
|
+
while page:
|
|
51
|
+
if page.value:
|
|
52
|
+
for app in page.value:
|
|
53
|
+
count += 1
|
|
54
|
+
yield app
|
|
55
|
+
|
|
56
|
+
if not page.odata_next_link:
|
|
57
|
+
break
|
|
58
|
+
page = await client.applications.with_url(page.odata_next_link).get()
|
|
59
|
+
|
|
60
|
+
logger.info(f"Retrieved {count} Entra applications total")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def transform_applications(
|
|
64
|
+
applications: list[Application],
|
|
65
|
+
) -> Generator[dict[str, Any], None, None]:
|
|
66
|
+
"""
|
|
67
|
+
Transform application data for graph loading using a generator.
|
|
68
|
+
|
|
69
|
+
:param applications: Raw Application objects from Microsoft Graph API
|
|
70
|
+
:return: Generator of transformed application data for graph loading
|
|
71
|
+
"""
|
|
72
|
+
for app in applications:
|
|
73
|
+
yield {
|
|
74
|
+
"id": app.id,
|
|
75
|
+
"app_id": app.app_id,
|
|
76
|
+
"display_name": app.display_name,
|
|
77
|
+
"publisher_domain": app.publisher_domain,
|
|
78
|
+
"sign_in_audience": app.sign_in_audience,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@timeit
|
|
83
|
+
def load_applications(
|
|
84
|
+
neo4j_session: neo4j.Session,
|
|
85
|
+
applications_data: list[dict[str, Any]],
|
|
86
|
+
update_tag: int,
|
|
87
|
+
tenant_id: str,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Load Entra applications to the graph.
|
|
91
|
+
|
|
92
|
+
:param neo4j_session: Neo4j session
|
|
93
|
+
:param applications_data: Application data to load
|
|
94
|
+
:param update_tag: Update tag for tracking data freshness
|
|
95
|
+
:param tenant_id: Entra tenant ID
|
|
96
|
+
"""
|
|
97
|
+
load(
|
|
98
|
+
neo4j_session,
|
|
99
|
+
EntraApplicationSchema(),
|
|
100
|
+
applications_data,
|
|
101
|
+
lastupdated=update_tag,
|
|
102
|
+
TENANT_ID=tenant_id,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@timeit
|
|
107
|
+
def cleanup_applications(
|
|
108
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
109
|
+
) -> None:
|
|
110
|
+
"""
|
|
111
|
+
Delete Entra applications and their relationships from the graph if they were not updated in the last sync.
|
|
112
|
+
|
|
113
|
+
:param neo4j_session: Neo4j session
|
|
114
|
+
:param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
|
|
115
|
+
"""
|
|
116
|
+
GraphJob.from_node_schema(EntraApplicationSchema(), common_job_parameters).run(
|
|
117
|
+
neo4j_session
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@timeit
|
|
122
|
+
async def sync_entra_applications(
|
|
123
|
+
neo4j_session: neo4j.Session,
|
|
124
|
+
tenant_id: str,
|
|
125
|
+
client_id: str,
|
|
126
|
+
client_secret: str,
|
|
127
|
+
update_tag: int,
|
|
128
|
+
common_job_parameters: dict[str, Any],
|
|
129
|
+
) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Sync Entra applications and their app role assignments to the graph.
|
|
132
|
+
|
|
133
|
+
:param neo4j_session: Neo4j session
|
|
134
|
+
:param tenant_id: Entra tenant ID
|
|
135
|
+
:param client_id: Azure application client ID
|
|
136
|
+
:param client_secret: Azure application client secret
|
|
137
|
+
:param update_tag: Update tag for tracking data freshness
|
|
138
|
+
:param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
|
|
139
|
+
"""
|
|
140
|
+
# Create credentials and client
|
|
141
|
+
credential = ClientSecretCredential(
|
|
142
|
+
tenant_id=tenant_id,
|
|
143
|
+
client_id=client_id,
|
|
144
|
+
client_secret=client_secret,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
client = GraphServiceClient(
|
|
148
|
+
credential,
|
|
149
|
+
scopes=["https://graph.microsoft.com/.default"],
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Step 1: Sync applications
|
|
153
|
+
app_batch_size = 10 # Batch size for applications
|
|
154
|
+
apps_batch = []
|
|
155
|
+
total_app_count = 0
|
|
156
|
+
|
|
157
|
+
# Stream and load applications
|
|
158
|
+
async for app in get_entra_applications(client):
|
|
159
|
+
total_app_count += 1
|
|
160
|
+
apps_batch.append(app)
|
|
161
|
+
|
|
162
|
+
# Transform and load applications in batches
|
|
163
|
+
if len(apps_batch) >= app_batch_size:
|
|
164
|
+
transformed_apps = list(transform_applications(apps_batch))
|
|
165
|
+
load_applications(neo4j_session, transformed_apps, update_tag, tenant_id)
|
|
166
|
+
logger.info(
|
|
167
|
+
f"Loaded batch of {len(apps_batch)} applications (total: {total_app_count})"
|
|
168
|
+
)
|
|
169
|
+
apps_batch.clear()
|
|
170
|
+
transformed_apps.clear()
|
|
171
|
+
gc.collect() # Force garbage collection
|
|
172
|
+
|
|
173
|
+
# Process remaining applications
|
|
174
|
+
if apps_batch:
|
|
175
|
+
transformed_apps = list(transform_applications(apps_batch))
|
|
176
|
+
load_applications(neo4j_session, transformed_apps, update_tag, tenant_id)
|
|
177
|
+
apps_batch.clear()
|
|
178
|
+
transformed_apps.clear()
|
|
179
|
+
cleanup_applications(neo4j_session, common_job_parameters)
|
|
180
|
+
logger.info(f"Completed syncing {total_app_count} applications")
|
|
181
|
+
# Final garbage collection
|
|
182
|
+
gc.collect()
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
|
|
5
|
+
from cartography.client.core.tx import load_matchlinks
|
|
6
|
+
from cartography.client.core.tx import read_list_of_dicts_tx
|
|
7
|
+
from cartography.graph.job import GraphJob
|
|
8
|
+
from cartography.models.entra.entra_user_to_aws_sso import (
|
|
9
|
+
EntraUserToAWSSSOUserMatchLink,
|
|
10
|
+
)
|
|
11
|
+
from cartography.util import timeit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@timeit
|
|
15
|
+
def sync_entra_to_aws_identity_center(
|
|
16
|
+
neo4j_session: neo4j.Session,
|
|
17
|
+
update_tag: int,
|
|
18
|
+
tenant_id: str,
|
|
19
|
+
common_job_parameters: dict[str, Any],
|
|
20
|
+
) -> None:
|
|
21
|
+
query = """
|
|
22
|
+
MATCH (:EntraTenant{id: $TENANT_ID})-[:RESOURCE]->(e:EntraUser)
|
|
23
|
+
-[:HAS_APP_ROLE]->(ar:EntraAppRoleAssignment)
|
|
24
|
+
-[:ASSIGNED_TO]->(n:EntraApplication)
|
|
25
|
+
-[:SERVICE_PRINCIPAL]->(spn:EntraServicePrincipal)
|
|
26
|
+
-[:FEDERATES_TO]->(ic:AWSIdentityCenter)
|
|
27
|
+
MATCH (sso:AWSSSOUser{identity_store_id:ic.identity_store_id})
|
|
28
|
+
WHERE e.user_principal_name = sso.user_name
|
|
29
|
+
RETURN e.user_principal_name as entra_user_principal_name, sso.user_name as aws_user_name, sso.identity_store_id as identity_store_id
|
|
30
|
+
"""
|
|
31
|
+
entrauser_to_awssso_users = neo4j_session.execute_read(
|
|
32
|
+
read_list_of_dicts_tx, query, TENANT_ID=tenant_id
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Load MatchLink relationships from Entra users to AWS SSO users
|
|
36
|
+
load_matchlinks(
|
|
37
|
+
neo4j_session,
|
|
38
|
+
EntraUserToAWSSSOUserMatchLink(),
|
|
39
|
+
entrauser_to_awssso_users,
|
|
40
|
+
lastupdated=update_tag,
|
|
41
|
+
_sub_resource_label="EntraTenant",
|
|
42
|
+
_sub_resource_id=tenant_id,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
cleanup_entra_user_to_aws_sso_user_matchlinks(neo4j_session, common_job_parameters)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
async def sync_entra_federation(
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
update_tag: int,
|
|
52
|
+
tenant_id: str,
|
|
53
|
+
common_job_parameters: dict[str, Any],
|
|
54
|
+
) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Sync Entra federation relationships to the graph.
|
|
57
|
+
|
|
58
|
+
:param neo4j_session: Neo4j session
|
|
59
|
+
:param update_tag: Update tag for tracking data freshness
|
|
60
|
+
:param tenant_id: Entra tenant ID
|
|
61
|
+
:param common_job_parameters: Common job parameters for cleanup
|
|
62
|
+
"""
|
|
63
|
+
sync_entra_to_aws_identity_center(
|
|
64
|
+
neo4j_session, update_tag, tenant_id, common_job_parameters
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@timeit
|
|
69
|
+
def cleanup_entra_user_to_aws_sso_user_matchlinks(
|
|
70
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
71
|
+
) -> None:
|
|
72
|
+
GraphJob.from_matchlink(
|
|
73
|
+
EntraUserToAWSSSOUserMatchLink(),
|
|
74
|
+
"EntraTenant",
|
|
75
|
+
common_job_parameters["TENANT_ID"],
|
|
76
|
+
common_job_parameters["UPDATE_TAG"],
|
|
77
|
+
).run(neo4j_session)
|