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
|
@@ -84,7 +84,7 @@ def _get_teams_repos_inner_func(
|
|
|
84
84
|
repo_urls: list[str],
|
|
85
85
|
repo_permissions: list[str],
|
|
86
86
|
) -> None:
|
|
87
|
-
logger.info(f"
|
|
87
|
+
logger.info(f"Retrieving team repos for {team_name}.")
|
|
88
88
|
team_repos = _get_team_repos(org, api_url, token, team_name)
|
|
89
89
|
|
|
90
90
|
# The `or []` is because `.nodes` can be None. See:
|
|
@@ -192,7 +192,7 @@ def _get_teams_users_inner_func(
|
|
|
192
192
|
user_urls: List[str],
|
|
193
193
|
user_roles: List[str],
|
|
194
194
|
) -> None:
|
|
195
|
-
logger.info(f"
|
|
195
|
+
logger.info(f"Retrieving team users for {team_name}.")
|
|
196
196
|
team_users = _get_team_users(org, api_url, token, team_name)
|
|
197
197
|
# The `or []` is because `.nodes` can be None. See:
|
|
198
198
|
# https://docs.github.com/en/graphql/reference/objects#teammemberconnection
|
|
@@ -299,7 +299,7 @@ def _get_child_teams_inner_func(
|
|
|
299
299
|
team_name: str,
|
|
300
300
|
team_urls: List[str],
|
|
301
301
|
) -> None:
|
|
302
|
-
logger.info(f"
|
|
302
|
+
logger.info(f"Retrieving child teams for {team_name}.")
|
|
303
303
|
child_teams = _get_child_teams(org, api_url, token, team_name)
|
|
304
304
|
# The `or []` is because `.nodes` can be None. See:
|
|
305
305
|
# https://docs.github.com/en/graphql/reference/objects#teammemberconnection
|
|
@@ -37,6 +37,7 @@ GITHUB_ORG_USERS_PAGINATED_GRAPHQL = """
|
|
|
37
37
|
isSiteAdmin
|
|
38
38
|
email
|
|
39
39
|
company
|
|
40
|
+
organizationVerifiedDomainEmails(login: $login)
|
|
40
41
|
}
|
|
41
42
|
role
|
|
42
43
|
}
|
|
@@ -64,6 +65,7 @@ GITHUB_ENTERPRISE_OWNER_USERS_PAGINATED_GRAPHQL = """
|
|
|
64
65
|
isSiteAdmin
|
|
65
66
|
email
|
|
66
67
|
company
|
|
68
|
+
organizationVerifiedDomainEmails(login: $login)
|
|
67
69
|
}
|
|
68
70
|
organizationRole
|
|
69
71
|
}
|
|
@@ -154,6 +156,9 @@ def transform_users(
|
|
|
154
156
|
for user in user_data:
|
|
155
157
|
# all members get the 'MEMBER_OF' relationship
|
|
156
158
|
processed_user = deepcopy(user["node"])
|
|
159
|
+
# GitHub returns empty strings for email addresses, so we convert them to None
|
|
160
|
+
if "email" in processed_user and not processed_user["email"]:
|
|
161
|
+
processed_user["email"] = None
|
|
157
162
|
processed_user["hasTwoFactorEnabled"] = user["hasTwoFactorEnabled"]
|
|
158
163
|
processed_user["MEMBER_OF"] = org_data["url"]
|
|
159
164
|
# admins get a second relationship expressing them as such
|
cartography/intel/github/util.py
CHANGED
|
@@ -157,6 +157,18 @@ def fetch_all(
|
|
|
157
157
|
retry += 1
|
|
158
158
|
exc = err
|
|
159
159
|
except requests.exceptions.HTTPError as err:
|
|
160
|
+
if (
|
|
161
|
+
err.response is not None
|
|
162
|
+
and err.response.status_code == 502
|
|
163
|
+
and kwargs.get("count")
|
|
164
|
+
and kwargs["count"] > 1
|
|
165
|
+
):
|
|
166
|
+
kwargs["count"] = max(1, kwargs["count"] // 2)
|
|
167
|
+
logger.warning(
|
|
168
|
+
"GitHub: Received 502 response. Reducing page size to %s and retrying.",
|
|
169
|
+
kwargs["count"],
|
|
170
|
+
)
|
|
171
|
+
continue
|
|
160
172
|
retry += 1
|
|
161
173
|
exc = err
|
|
162
174
|
except requests.exceptions.ChunkedEncodingError as err:
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from collections import namedtuple
|
|
6
|
+
|
|
7
|
+
import googleapiclient.discovery
|
|
8
|
+
import neo4j
|
|
9
|
+
from google.auth import default
|
|
10
|
+
from google.auth.exceptions import DefaultCredentialsError
|
|
11
|
+
from google.auth.transport.requests import Request
|
|
12
|
+
from google.oauth2 import credentials
|
|
13
|
+
from google.oauth2 import service_account
|
|
14
|
+
from google.oauth2.credentials import Credentials as OAuth2Credentials
|
|
15
|
+
from google.oauth2.service_account import Credentials as ServiceAccountCredentials
|
|
16
|
+
|
|
17
|
+
from cartography.config import Config
|
|
18
|
+
from cartography.intel.googleworkspace import devices
|
|
19
|
+
from cartography.intel.googleworkspace import groups
|
|
20
|
+
from cartography.intel.googleworkspace import oauth_apps
|
|
21
|
+
from cartography.intel.googleworkspace import tenant
|
|
22
|
+
from cartography.intel.googleworkspace import users
|
|
23
|
+
from cartography.util import timeit
|
|
24
|
+
|
|
25
|
+
OAUTH_SCOPES = [
|
|
26
|
+
"https://www.googleapis.com/auth/admin.directory.customer.readonly",
|
|
27
|
+
"https://www.googleapis.com/auth/admin.directory.user.readonly",
|
|
28
|
+
"https://www.googleapis.com/auth/admin.directory.user.security",
|
|
29
|
+
"https://www.googleapis.com/auth/cloud-identity.devices.readonly",
|
|
30
|
+
"https://www.googleapis.com/auth/cloud-identity.groups.readonly",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
Resources = namedtuple("Resources", ["admin", "cloudidentity"])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _initialize_resources(
|
|
39
|
+
creds: OAuth2Credentials | ServiceAccountCredentials,
|
|
40
|
+
) -> Resources:
|
|
41
|
+
"""
|
|
42
|
+
Create namedtuple of all resource objects necessary for Google API data gathering.
|
|
43
|
+
:param credentials: The credentials object
|
|
44
|
+
:return: namedtuple of all resource objects
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
return Resources(
|
|
48
|
+
googleapiclient.discovery.build(
|
|
49
|
+
"admin",
|
|
50
|
+
"directory_v1",
|
|
51
|
+
credentials=creds,
|
|
52
|
+
cache_discovery=False,
|
|
53
|
+
),
|
|
54
|
+
googleapiclient.discovery.build(
|
|
55
|
+
"cloudidentity",
|
|
56
|
+
"v1",
|
|
57
|
+
credentials=creds,
|
|
58
|
+
cache_discovery=False,
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@timeit
|
|
64
|
+
def start_googleworkspace_ingestion(
|
|
65
|
+
neo4j_session: neo4j.Session, config: Config
|
|
66
|
+
) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Starts the Google Workspace ingestion process by initializing
|
|
69
|
+
|
|
70
|
+
:param neo4j_session: The Neo4j session
|
|
71
|
+
:param config: A `cartography.config` object
|
|
72
|
+
:return: Nothing
|
|
73
|
+
"""
|
|
74
|
+
common_job_parameters = {
|
|
75
|
+
"UPDATE_TAG": config.update_tag,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
creds: OAuth2Credentials | ServiceAccountCredentials
|
|
79
|
+
if config.googleworkspace_auth_method == "delegated": # Legacy delegated method
|
|
80
|
+
if config.googleworkspace_config is None or not os.path.isfile(
|
|
81
|
+
config.googleworkspace_config
|
|
82
|
+
):
|
|
83
|
+
logger.warning(
|
|
84
|
+
(
|
|
85
|
+
"The Google Workspace config file is not set or is not a valid file."
|
|
86
|
+
"Skipping Google Workspace ingestion."
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
return
|
|
90
|
+
logger.info(
|
|
91
|
+
"Attempting to authenticate to Google Workspace using legacy delegated method"
|
|
92
|
+
)
|
|
93
|
+
try:
|
|
94
|
+
creds = service_account.Credentials.from_service_account_file(
|
|
95
|
+
config.googleworkspace_config,
|
|
96
|
+
scopes=OAUTH_SCOPES,
|
|
97
|
+
)
|
|
98
|
+
creds = creds.with_subject(os.environ.get("GOOGLE_DELEGATED_ADMIN"))
|
|
99
|
+
|
|
100
|
+
except DefaultCredentialsError as e:
|
|
101
|
+
logger.error(
|
|
102
|
+
(
|
|
103
|
+
"Unable to initialize Google Workspace creds. If you don't have Google Workspace data or don't want to load "
|
|
104
|
+
"Google Workspace data then you can ignore this message. Otherwise, the error code is: %s "
|
|
105
|
+
"Make sure your Google Workspace credentials file (if any) is valid. "
|
|
106
|
+
"For more details see documentation."
|
|
107
|
+
),
|
|
108
|
+
e,
|
|
109
|
+
)
|
|
110
|
+
return
|
|
111
|
+
elif config.googleworkspace_auth_method == "oauth":
|
|
112
|
+
auth_tokens = json.loads(
|
|
113
|
+
str(base64.b64decode(config.googleworkspace_config).decode())
|
|
114
|
+
)
|
|
115
|
+
logger.info("Attempting to authenticate to Google Workspace using OAuth")
|
|
116
|
+
try:
|
|
117
|
+
creds = credentials.Credentials(
|
|
118
|
+
token=None,
|
|
119
|
+
client_id=auth_tokens["client_id"],
|
|
120
|
+
client_secret=auth_tokens["client_secret"],
|
|
121
|
+
refresh_token=auth_tokens["refresh_token"],
|
|
122
|
+
expiry=None,
|
|
123
|
+
token_uri=auth_tokens["token_uri"],
|
|
124
|
+
scopes=OAUTH_SCOPES,
|
|
125
|
+
)
|
|
126
|
+
creds.refresh(Request())
|
|
127
|
+
except DefaultCredentialsError as e:
|
|
128
|
+
logger.error(
|
|
129
|
+
(
|
|
130
|
+
"Unable to initialize Google Workspace creds. If you don't have Google Workspace data or don't want to load "
|
|
131
|
+
"Google Workspace data then you can ignore this message. Otherwise, the error code is: %s "
|
|
132
|
+
"Make sure your Google Workspace credentials are configured correctly, your credentials are valid. "
|
|
133
|
+
"For more details see documentation."
|
|
134
|
+
),
|
|
135
|
+
e,
|
|
136
|
+
)
|
|
137
|
+
return
|
|
138
|
+
elif config.googleworkspace_auth_method == "default":
|
|
139
|
+
logger.info(
|
|
140
|
+
"Attempting to authenticate to Google Workspace using default credentials"
|
|
141
|
+
)
|
|
142
|
+
try:
|
|
143
|
+
creds, _ = default(scopes=OAUTH_SCOPES)
|
|
144
|
+
except DefaultCredentialsError as e:
|
|
145
|
+
logger.error(
|
|
146
|
+
(
|
|
147
|
+
"Unable to initialize Google Workspace creds using default credentials. If you don't have Google Workspace data or "
|
|
148
|
+
"don't want to load Google Workspace data then you can ignore this message. Otherwise, the error code is: %s "
|
|
149
|
+
"Make sure you have valid application default credentials configured. "
|
|
150
|
+
"For more details see documentation."
|
|
151
|
+
),
|
|
152
|
+
e,
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
resources = _initialize_resources(creds)
|
|
157
|
+
customer_id = tenant.sync_googleworkspace_tenant(
|
|
158
|
+
neo4j_session,
|
|
159
|
+
resources.admin,
|
|
160
|
+
config.update_tag,
|
|
161
|
+
common_job_parameters,
|
|
162
|
+
)
|
|
163
|
+
common_job_parameters["CUSTOMER_ID"] = customer_id
|
|
164
|
+
|
|
165
|
+
# Sync users and get the list of transformed users for OAuth token sync
|
|
166
|
+
user_ids = users.sync_googleworkspace_users(
|
|
167
|
+
neo4j_session,
|
|
168
|
+
resources.admin,
|
|
169
|
+
config.update_tag,
|
|
170
|
+
common_job_parameters,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Sync OAuth apps for all users
|
|
174
|
+
oauth_apps.sync_googleworkspace_oauth_apps(
|
|
175
|
+
neo4j_session,
|
|
176
|
+
resources.admin,
|
|
177
|
+
user_ids,
|
|
178
|
+
config.update_tag,
|
|
179
|
+
common_job_parameters,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
groups.sync_googleworkspace_groups(
|
|
183
|
+
neo4j_session,
|
|
184
|
+
resources.cloudidentity,
|
|
185
|
+
config.update_tag,
|
|
186
|
+
common_job_parameters,
|
|
187
|
+
)
|
|
188
|
+
devices.sync_googleworkspace_devices(
|
|
189
|
+
neo4j_session,
|
|
190
|
+
resources.cloudidentity,
|
|
191
|
+
config.update_tag,
|
|
192
|
+
common_job_parameters,
|
|
193
|
+
)
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from googleapiclient.discovery import Resource
|
|
8
|
+
from googleapiclient.errors import HttpError
|
|
9
|
+
|
|
10
|
+
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
12
|
+
from cartography.models.googleworkspace.device import GoogleWorkspaceDeviceSchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
GOOGLE_API_NUM_RETRIES = 5
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@timeit
|
|
21
|
+
def get_devices(
|
|
22
|
+
cloudidentity: Resource,
|
|
23
|
+
) -> list[dict[str, Any]]:
|
|
24
|
+
"""
|
|
25
|
+
Fetch all devices from Google Cloud Identity API.
|
|
26
|
+
"""
|
|
27
|
+
# Only fetch user synced in the last 90 days
|
|
28
|
+
from_date = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(
|
|
29
|
+
days=90
|
|
30
|
+
)
|
|
31
|
+
request = cloudidentity.devices().list(
|
|
32
|
+
customer="customers/my_customer",
|
|
33
|
+
pageSize=100,
|
|
34
|
+
orderBy="last_sync_time desc",
|
|
35
|
+
filter=f"sync:{from_date.strftime('%Y-%m-%dT%H:%M:%S')}..",
|
|
36
|
+
)
|
|
37
|
+
response_objects = []
|
|
38
|
+
while request is not None:
|
|
39
|
+
try:
|
|
40
|
+
resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
|
|
41
|
+
response_objects.extend(resp.get("devices", []))
|
|
42
|
+
request = cloudidentity.devices().list_next(request, resp)
|
|
43
|
+
except HttpError as e:
|
|
44
|
+
if (
|
|
45
|
+
e.resp.status == 403
|
|
46
|
+
and "Request had insufficient authentication scopes" in str(e)
|
|
47
|
+
):
|
|
48
|
+
logger.error(
|
|
49
|
+
"Missing required Google Workspace scopes. If using the gcloud CLI, "
|
|
50
|
+
"run: gcloud auth application-default login --scopes="
|
|
51
|
+
"https://www.googleapis.com/auth/admin.directory.customer.readonly,"
|
|
52
|
+
"https://www.googleapis.com/auth/admin.directory.user.readonly,"
|
|
53
|
+
"https://www.googleapis.com/auth/admin.directory.user.security,"
|
|
54
|
+
"https://www.googleapis.com/auth/cloud-identity.devices.readonly,"
|
|
55
|
+
"https://www.googleapis.com/auth/cloud-identity.groups.readonly,"
|
|
56
|
+
"https://www.googleapis.com/auth/cloud-platform"
|
|
57
|
+
)
|
|
58
|
+
raise
|
|
59
|
+
return response_objects
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@timeit
|
|
63
|
+
def get_device_users(
|
|
64
|
+
cloudidentity: Resource,
|
|
65
|
+
) -> list[dict[str, Any]]:
|
|
66
|
+
"""
|
|
67
|
+
Fetch all device users from Google Cloud Identity API.
|
|
68
|
+
"""
|
|
69
|
+
# Only fetch user synced in the last 90 days
|
|
70
|
+
from_date = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(
|
|
71
|
+
days=90
|
|
72
|
+
)
|
|
73
|
+
request = (
|
|
74
|
+
cloudidentity.devices()
|
|
75
|
+
.deviceUsers()
|
|
76
|
+
.list(
|
|
77
|
+
customer="customers/my_customer",
|
|
78
|
+
parent="devices/-",
|
|
79
|
+
pageSize=100,
|
|
80
|
+
orderBy="last_sync_time desc",
|
|
81
|
+
filter=f"sync:{from_date.strftime('%Y-%m-%dT%H:%M:%S')}..",
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
response_objects = []
|
|
85
|
+
while request is not None:
|
|
86
|
+
try:
|
|
87
|
+
resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
|
|
88
|
+
response_objects.extend(resp.get("deviceUsers", []))
|
|
89
|
+
request = cloudidentity.devices().deviceUsers().list_next(request, resp)
|
|
90
|
+
except HttpError as e:
|
|
91
|
+
if (
|
|
92
|
+
e.resp.status == 403
|
|
93
|
+
and "Request had insufficient authentication scopes" in str(e)
|
|
94
|
+
):
|
|
95
|
+
logger.error(
|
|
96
|
+
"Missing required Google Workspace scopes. If using the gcloud CLI, "
|
|
97
|
+
"run: gcloud auth application-default login --scopes="
|
|
98
|
+
"https://www.googleapis.com/auth/admin.directory.customer.readonly,"
|
|
99
|
+
"https://www.googleapis.com/auth/admin.directory.user.readonly,"
|
|
100
|
+
"https://www.googleapis.com/auth/admin.directory.user.security,"
|
|
101
|
+
"https://www.googleapis.com/auth/cloud-identity.devices.readonly,"
|
|
102
|
+
"https://www.googleapis.com/auth/cloud-identity.groups.readonly,"
|
|
103
|
+
"https://www.googleapis.com/auth/cloud-platform"
|
|
104
|
+
)
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
return response_objects
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def transform_devices(
|
|
111
|
+
devices: list[dict[str, Any]], device_users: list[dict[str, Any]]
|
|
112
|
+
) -> list[dict[str, Any]]:
|
|
113
|
+
"""
|
|
114
|
+
Transform device data for Neo4j ingestion.
|
|
115
|
+
"""
|
|
116
|
+
result = []
|
|
117
|
+
|
|
118
|
+
# First we need to create a mapping of device ID to its users
|
|
119
|
+
device_user_map: dict[str, str] = {}
|
|
120
|
+
for device_user in device_users:
|
|
121
|
+
# Ignore non approved device
|
|
122
|
+
if device_user.get("managementState") != "APPROVED":
|
|
123
|
+
continue
|
|
124
|
+
if device_user.get("userEmail") is None:
|
|
125
|
+
continue
|
|
126
|
+
# Extract device name from device user name path
|
|
127
|
+
device_name = device_user["name"].split("/deviceUsers/")[0]
|
|
128
|
+
if device_name not in device_user_map:
|
|
129
|
+
device_user_map[device_name] = device_user["userEmail"]
|
|
130
|
+
else:
|
|
131
|
+
logger.debug(
|
|
132
|
+
"Multiple users found for device %s, the most recent was kept",
|
|
133
|
+
device_name,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Now transform each device, adding its users
|
|
137
|
+
for device in devices:
|
|
138
|
+
# Extract device ID from name path (devices/EiRlNzYzZjYyNC1...)
|
|
139
|
+
device_name = device["name"]
|
|
140
|
+
|
|
141
|
+
transformed = {
|
|
142
|
+
# Required fields
|
|
143
|
+
"deviceId": device.get("deviceId"),
|
|
144
|
+
"hostname": device.get("hostname"),
|
|
145
|
+
# Owner
|
|
146
|
+
"owner_email": device_user_map.get(device_name),
|
|
147
|
+
# Device information
|
|
148
|
+
"model": device.get("model"),
|
|
149
|
+
"manufacturer": device.get("manufacturer"),
|
|
150
|
+
"releaseVersion": device.get("releaseVersion"),
|
|
151
|
+
"brand": device.get("brand"),
|
|
152
|
+
"buildNumber": device.get("buildNumber"),
|
|
153
|
+
"kernelVersion": device.get("kernelVersion"),
|
|
154
|
+
"basebandVersion": device.get("basebandVersion"),
|
|
155
|
+
"deviceType": device.get("deviceType"),
|
|
156
|
+
"osVersion": device.get("osVersion"),
|
|
157
|
+
"ownerType": device.get("ownerType"),
|
|
158
|
+
# Hardware identifiers
|
|
159
|
+
"serialNumber": device.get("serialNumber"),
|
|
160
|
+
"assetTag": device.get("assetTag"),
|
|
161
|
+
"imei": device.get("imei"),
|
|
162
|
+
"meid": device.get("meid"),
|
|
163
|
+
"wifiMacAddresses": device.get("wifiMacAddresses"),
|
|
164
|
+
"networkOperator": device.get("networkOperator"),
|
|
165
|
+
# Security and state
|
|
166
|
+
"encryptionState": device.get("encryptionState"),
|
|
167
|
+
"compromisedState": device.get("compromisedState"),
|
|
168
|
+
"managementState": device.get("managementState"),
|
|
169
|
+
# Timestamps
|
|
170
|
+
"createTime": device.get("createTime"),
|
|
171
|
+
"lastSyncTime": device.get("lastSyncTime"),
|
|
172
|
+
"securityPatchTime": device.get("securityPatchTime"),
|
|
173
|
+
# Android specific attributes (stored as JSON string if present)
|
|
174
|
+
"androidSpecificAttributes": (
|
|
175
|
+
json.dumps(device.get("androidSpecificAttributes"))
|
|
176
|
+
if device.get("androidSpecificAttributes")
|
|
177
|
+
else None
|
|
178
|
+
),
|
|
179
|
+
"enabledDeveloperOptions": device.get("enabledDeveloperOptions"),
|
|
180
|
+
"enabledUsbDebugging": device.get("enabledUsbDebugging"),
|
|
181
|
+
"bootloaderVersion": device.get("bootloaderVersion"),
|
|
182
|
+
"otherAccounts": device.get("otherAccounts"),
|
|
183
|
+
# Additional identifiers
|
|
184
|
+
"unifiedDeviceId": device.get("unifiedDeviceId"),
|
|
185
|
+
"endpointVerificationSpecificAttributes": (
|
|
186
|
+
json.dumps(device.get("endpointVerificationSpecificAttributes"))
|
|
187
|
+
if device.get("endpointVerificationSpecificAttributes")
|
|
188
|
+
else None
|
|
189
|
+
),
|
|
190
|
+
}
|
|
191
|
+
result.append(transformed)
|
|
192
|
+
|
|
193
|
+
return result
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def load_devices(
|
|
197
|
+
neo4j_session: neo4j.Session,
|
|
198
|
+
devices: list[dict[str, Any]],
|
|
199
|
+
customer_id: str,
|
|
200
|
+
update_tag: int,
|
|
201
|
+
) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Load device data into Neo4j.
|
|
204
|
+
"""
|
|
205
|
+
logger.info("Loading %d Google Workspace devices", len(devices))
|
|
206
|
+
load(
|
|
207
|
+
neo4j_session,
|
|
208
|
+
GoogleWorkspaceDeviceSchema(),
|
|
209
|
+
devices,
|
|
210
|
+
lastupdated=update_tag,
|
|
211
|
+
CUSTOMER_ID=customer_id,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def cleanup_devices(
|
|
216
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
217
|
+
) -> None:
|
|
218
|
+
"""
|
|
219
|
+
Remove devices that weren't updated in this sync run.
|
|
220
|
+
"""
|
|
221
|
+
logger.debug("Running Google Workspace devices cleanup job")
|
|
222
|
+
GraphJob.from_node_schema(GoogleWorkspaceDeviceSchema(), common_job_parameters).run(
|
|
223
|
+
neo4j_session
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@timeit
|
|
228
|
+
def sync_googleworkspace_devices(
|
|
229
|
+
neo4j_session: neo4j.Session,
|
|
230
|
+
cloudidentity: Resource,
|
|
231
|
+
update_tag: int,
|
|
232
|
+
common_job_parameters: dict[str, Any],
|
|
233
|
+
) -> None:
|
|
234
|
+
"""
|
|
235
|
+
Sync Google Workspace devices and device users.
|
|
236
|
+
"""
|
|
237
|
+
logger.info("Starting Google Workspace devices sync")
|
|
238
|
+
|
|
239
|
+
customer_id = common_job_parameters["CUSTOMER_ID"]
|
|
240
|
+
|
|
241
|
+
# 1. GET - Fetch devices data
|
|
242
|
+
raw_devices = get_devices(cloudidentity)
|
|
243
|
+
raw_device_users = get_device_users(cloudidentity)
|
|
244
|
+
|
|
245
|
+
# 2. TRANSFORM - Shape data for ingestion
|
|
246
|
+
transformed_devices = transform_devices(raw_devices, raw_device_users)
|
|
247
|
+
|
|
248
|
+
# 3. LOAD - Ingest to Neo4j
|
|
249
|
+
load_devices(neo4j_session, transformed_devices, customer_id, update_tag)
|
|
250
|
+
|
|
251
|
+
# 4. CLEANUP - Remove stale data
|
|
252
|
+
cleanup_devices(neo4j_session, common_job_parameters)
|
|
253
|
+
|
|
254
|
+
logger.info("Completed Google Workspace devices sync")
|