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
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Any
|
|
3
|
-
from typing import Dict
|
|
4
|
-
from typing import List
|
|
5
3
|
|
|
6
4
|
import boto3
|
|
5
|
+
import botocore.exceptions
|
|
7
6
|
import neo4j
|
|
8
7
|
|
|
9
8
|
from cartography.client.core.tx import load
|
|
9
|
+
from cartography.client.core.tx import load_matchlinks
|
|
10
|
+
from cartography.client.core.tx import read_list_of_dicts_tx
|
|
10
11
|
from cartography.graph.job import GraphJob
|
|
11
12
|
from cartography.models.aws.identitycenter.awsidentitycenter import (
|
|
12
13
|
AWSIdentityCenterInstanceSchema,
|
|
@@ -14,20 +15,38 @@ from cartography.models.aws.identitycenter.awsidentitycenter import (
|
|
|
14
15
|
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
15
16
|
AWSPermissionSetSchema,
|
|
16
17
|
)
|
|
18
|
+
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
19
|
+
AWSRoleToSSOGroupMatchLink,
|
|
20
|
+
)
|
|
21
|
+
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
22
|
+
AWSRoleToSSOUserMatchLink,
|
|
23
|
+
)
|
|
24
|
+
from cartography.models.aws.identitycenter.awssogroup import AWSSSOGroupSchema
|
|
17
25
|
from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
|
|
18
26
|
from cartography.util import aws_handle_regions
|
|
19
|
-
from cartography.util import run_cleanup_job
|
|
20
27
|
from cartography.util import timeit
|
|
21
28
|
|
|
22
29
|
logger = logging.getLogger(__name__)
|
|
23
30
|
|
|
24
31
|
|
|
32
|
+
def _is_permission_set_sync_unsupported_error(
|
|
33
|
+
error: botocore.exceptions.ClientError,
|
|
34
|
+
) -> bool:
|
|
35
|
+
"""Return True when the Identity Center instance does not support permission sets."""
|
|
36
|
+
error_info = error.response.get("Error", {})
|
|
37
|
+
if error_info.get("Code") != "ValidationException":
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
message = error_info.get("Message", "").lower()
|
|
41
|
+
return "not supported for this identity center instance" in message
|
|
42
|
+
|
|
43
|
+
|
|
25
44
|
@timeit
|
|
26
45
|
@aws_handle_regions
|
|
27
46
|
def get_identity_center_instances(
|
|
28
47
|
boto3_session: boto3.session.Session,
|
|
29
48
|
region: str,
|
|
30
|
-
) ->
|
|
49
|
+
) -> list[dict[str, Any]]:
|
|
31
50
|
"""
|
|
32
51
|
Get all AWS IAM Identity Center instances in the current region
|
|
33
52
|
"""
|
|
@@ -44,7 +63,7 @@ def get_identity_center_instances(
|
|
|
44
63
|
@timeit
|
|
45
64
|
def load_identity_center_instances(
|
|
46
65
|
neo4j_session: neo4j.Session,
|
|
47
|
-
instance_data:
|
|
66
|
+
instance_data: list[dict[str, Any]],
|
|
48
67
|
region: str,
|
|
49
68
|
current_aws_account_id: str,
|
|
50
69
|
aws_update_tag: int,
|
|
@@ -71,7 +90,7 @@ def get_permission_sets(
|
|
|
71
90
|
boto3_session: boto3.session.Session,
|
|
72
91
|
instance_arn: str,
|
|
73
92
|
region: str,
|
|
74
|
-
) ->
|
|
93
|
+
) -> list[dict[str, Any]]:
|
|
75
94
|
"""
|
|
76
95
|
Get all permission sets for a given Identity Center instance
|
|
77
96
|
"""
|
|
@@ -88,18 +107,37 @@ def get_permission_sets(
|
|
|
88
107
|
)
|
|
89
108
|
permission_set = details.get("PermissionSet", {})
|
|
90
109
|
if permission_set:
|
|
91
|
-
permission_set["RoleHint"] = (
|
|
92
|
-
f":role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_{permission_set.get('Name')}"
|
|
93
|
-
)
|
|
94
110
|
permission_sets.append(permission_set)
|
|
95
111
|
|
|
96
112
|
return permission_sets
|
|
97
113
|
|
|
98
114
|
|
|
115
|
+
def transform_permission_sets(
|
|
116
|
+
permission_sets: list[dict[str, Any]],
|
|
117
|
+
region: str,
|
|
118
|
+
) -> list[dict[str, Any]]:
|
|
119
|
+
"""
|
|
120
|
+
Transform permission sets by adding the RoleHint based on region.
|
|
121
|
+
|
|
122
|
+
AWS SSO roles in us-east-1 don't include region in the ARN path,
|
|
123
|
+
but roles in other regions do: /aws-reserved/sso.amazonaws.com/{region}/AWSReservedSSO_*
|
|
124
|
+
"""
|
|
125
|
+
for permission_set in permission_sets:
|
|
126
|
+
if region == "us-east-1":
|
|
127
|
+
permission_set["RoleHint"] = (
|
|
128
|
+
f":role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_{permission_set.get('Name')}"
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
permission_set["RoleHint"] = (
|
|
132
|
+
f":role/aws-reserved/sso.amazonaws.com/{region}/AWSReservedSSO_{permission_set.get('Name')}"
|
|
133
|
+
)
|
|
134
|
+
return permission_sets
|
|
135
|
+
|
|
136
|
+
|
|
99
137
|
@timeit
|
|
100
138
|
def load_permission_sets(
|
|
101
139
|
neo4j_session: neo4j.Session,
|
|
102
|
-
permission_sets:
|
|
140
|
+
permission_sets: list[dict[str, Any]],
|
|
103
141
|
instance_arn: str,
|
|
104
142
|
region: str,
|
|
105
143
|
aws_account_id: str,
|
|
@@ -120,6 +158,8 @@ def load_permission_sets(
|
|
|
120
158
|
InstanceArn=instance_arn,
|
|
121
159
|
Region=region,
|
|
122
160
|
AWS_ID=aws_account_id,
|
|
161
|
+
_sub_resource_label="AWSAccount",
|
|
162
|
+
_sub_resource_id=aws_account_id,
|
|
123
163
|
)
|
|
124
164
|
|
|
125
165
|
|
|
@@ -129,7 +169,7 @@ def get_sso_users(
|
|
|
129
169
|
boto3_session: boto3.session.Session,
|
|
130
170
|
identity_store_id: str,
|
|
131
171
|
region: str,
|
|
132
|
-
) ->
|
|
172
|
+
) -> list[dict[str, Any]]:
|
|
133
173
|
"""
|
|
134
174
|
Get all SSO users for a given Identity Store
|
|
135
175
|
"""
|
|
@@ -140,17 +180,117 @@ def get_sso_users(
|
|
|
140
180
|
for page in paginator.paginate(IdentityStoreId=identity_store_id):
|
|
141
181
|
user_page = page.get("Users", [])
|
|
142
182
|
for user in user_page:
|
|
143
|
-
if user.get("ExternalIds", None):
|
|
144
|
-
user["ExternalId"] = user.get("ExternalIds")[0].get("Id")
|
|
145
183
|
users.append(user)
|
|
146
184
|
|
|
147
185
|
return users
|
|
148
186
|
|
|
149
187
|
|
|
188
|
+
@timeit
|
|
189
|
+
@aws_handle_regions
|
|
190
|
+
def get_sso_groups(
|
|
191
|
+
boto3_session: boto3.session.Session,
|
|
192
|
+
identity_store_id: str,
|
|
193
|
+
region: str,
|
|
194
|
+
) -> list[dict[str, Any]]:
|
|
195
|
+
"""
|
|
196
|
+
Get all SSO groups for a given Identity Store
|
|
197
|
+
"""
|
|
198
|
+
client = boto3_session.client("identitystore", region_name=region)
|
|
199
|
+
groups: list[dict[str, Any]] = []
|
|
200
|
+
|
|
201
|
+
paginator = client.get_paginator("list_groups")
|
|
202
|
+
for page in paginator.paginate(IdentityStoreId=identity_store_id):
|
|
203
|
+
group_page = page.get("Groups", [])
|
|
204
|
+
for group in group_page:
|
|
205
|
+
groups.append(group)
|
|
206
|
+
|
|
207
|
+
return groups
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def transform_sso_users(
|
|
211
|
+
users: list[dict[str, Any]],
|
|
212
|
+
user_group_memberships: dict[str, list[str]] | None = None,
|
|
213
|
+
user_permissionsets: list[dict[str, Any]] | None = None,
|
|
214
|
+
) -> list[dict[str, Any]]:
|
|
215
|
+
"""
|
|
216
|
+
Transform SSO users to match the expected schema, optionally including group memberships
|
|
217
|
+
and permission set assignments.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
users: List of SSO users from AWS API
|
|
221
|
+
user_group_memberships: Optional mapping of UserId -> [GroupIds]
|
|
222
|
+
user_permissionsets: Optional list of permission set assignments with shape:
|
|
223
|
+
[{UserId: str, PermissionSetArn: str, AccountId: str}, ...]
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Transformed users with MemberOfGroups and AssignedPermissionSets fields added
|
|
227
|
+
"""
|
|
228
|
+
# Build mapping from UserId to list of PermissionSetArns
|
|
229
|
+
user_permission_sets: dict[str, list[str]] = {}
|
|
230
|
+
if user_permissionsets:
|
|
231
|
+
for assignment in user_permissionsets:
|
|
232
|
+
user_id = assignment["UserId"]
|
|
233
|
+
perm_set = assignment["PermissionSetArn"]
|
|
234
|
+
user_permission_sets.setdefault(user_id, []).append(perm_set)
|
|
235
|
+
|
|
236
|
+
# Transform users
|
|
237
|
+
transformed_users = []
|
|
238
|
+
for user in users:
|
|
239
|
+
if user.get("ExternalIds"):
|
|
240
|
+
user["ExternalId"] = user["ExternalIds"][0].get("Id")
|
|
241
|
+
# Add group memberships if provided
|
|
242
|
+
if user_group_memberships:
|
|
243
|
+
user["MemberOfGroups"] = user_group_memberships.get(user["UserId"], [])
|
|
244
|
+
# Add direct permission set assignments if available
|
|
245
|
+
if user_permission_sets:
|
|
246
|
+
user["AssignedPermissionSets"] = user_permission_sets.get(
|
|
247
|
+
user["UserId"], []
|
|
248
|
+
)
|
|
249
|
+
transformed_users.append(user)
|
|
250
|
+
return transformed_users
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def transform_sso_groups(
|
|
254
|
+
groups: list[dict[str, Any]],
|
|
255
|
+
group_role_assignments: list[dict[str, Any]] | None = None,
|
|
256
|
+
) -> list[dict[str, Any]]:
|
|
257
|
+
"""
|
|
258
|
+
Transform SSO groups to match the expected schema, optionally including permission set assignments.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
groups: List of SSO groups from AWS API
|
|
262
|
+
group_role_assignments: Optional list of role assignments with shape:
|
|
263
|
+
[{GroupId: str, PermissionSetArn: str, AccountId: str}, ...]
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Transformed groups with AssignedPermissionSets field added
|
|
267
|
+
"""
|
|
268
|
+
# Build mapping from GroupId to list of PermissionSetArns
|
|
269
|
+
group_permission_sets: dict[str, list[str]] = {}
|
|
270
|
+
if group_role_assignments:
|
|
271
|
+
for assignment in group_role_assignments:
|
|
272
|
+
group_id = assignment["GroupId"]
|
|
273
|
+
perm_set = assignment["PermissionSetArn"]
|
|
274
|
+
group_permission_sets.setdefault(group_id, []).append(perm_set)
|
|
275
|
+
|
|
276
|
+
# Transform groups
|
|
277
|
+
transformed_groups: list[dict[str, Any]] = []
|
|
278
|
+
for group in groups:
|
|
279
|
+
if group.get("ExternalIds"):
|
|
280
|
+
group["ExternalId"] = group["ExternalIds"][0].get("Id")
|
|
281
|
+
# Add permission set assignments if available
|
|
282
|
+
if group_permission_sets:
|
|
283
|
+
group["AssignedPermissionSets"] = group_permission_sets.get(
|
|
284
|
+
group["GroupId"], []
|
|
285
|
+
)
|
|
286
|
+
transformed_groups.append(group)
|
|
287
|
+
return transformed_groups
|
|
288
|
+
|
|
289
|
+
|
|
150
290
|
@timeit
|
|
151
291
|
def load_sso_users(
|
|
152
292
|
neo4j_session: neo4j.Session,
|
|
153
|
-
users:
|
|
293
|
+
users: list[dict[str, Any]],
|
|
154
294
|
identity_store_id: str,
|
|
155
295
|
region: str,
|
|
156
296
|
aws_account_id: str,
|
|
@@ -174,21 +314,49 @@ def load_sso_users(
|
|
|
174
314
|
)
|
|
175
315
|
|
|
176
316
|
|
|
317
|
+
@timeit
|
|
318
|
+
def load_sso_groups(
|
|
319
|
+
neo4j_session: neo4j.Session,
|
|
320
|
+
groups: list[dict[str, Any]],
|
|
321
|
+
identity_store_id: str,
|
|
322
|
+
region: str,
|
|
323
|
+
aws_account_id: str,
|
|
324
|
+
aws_update_tag: int,
|
|
325
|
+
) -> None:
|
|
326
|
+
"""
|
|
327
|
+
Load SSO groups into the graph
|
|
328
|
+
"""
|
|
329
|
+
logger.info(
|
|
330
|
+
f"Loading {len(groups)} SSO groups for identity store {identity_store_id} in region {region}",
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
load(
|
|
334
|
+
neo4j_session,
|
|
335
|
+
AWSSSOGroupSchema(),
|
|
336
|
+
groups,
|
|
337
|
+
lastupdated=aws_update_tag,
|
|
338
|
+
IdentityStoreId=identity_store_id,
|
|
339
|
+
AWS_ID=aws_account_id,
|
|
340
|
+
Region=region,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
177
344
|
@timeit
|
|
178
345
|
@aws_handle_regions
|
|
179
|
-
def
|
|
346
|
+
def get_user_permissionsets(
|
|
180
347
|
boto3_session: boto3.session.Session,
|
|
181
|
-
users:
|
|
348
|
+
users: list[dict[str, Any]],
|
|
182
349
|
instance_arn: str,
|
|
183
350
|
region: str,
|
|
184
|
-
) ->
|
|
351
|
+
) -> list[dict[str, Any]]:
|
|
185
352
|
"""
|
|
186
|
-
Get
|
|
353
|
+
Get permissionsets for SSO users, taking into account which accounts the user is assigned to.
|
|
354
|
+
Although a permissionset can be assigned to multiple accounts, it is possible for the user
|
|
355
|
+
to be assigned to just a subset of those!
|
|
187
356
|
"""
|
|
188
|
-
|
|
189
|
-
logger.info(f"Getting role assignments for {len(users)} users")
|
|
357
|
+
logger.info(f"Getting permissionsets for {len(users)} users")
|
|
190
358
|
client = boto3_session.client("sso-admin", region_name=region)
|
|
191
|
-
|
|
359
|
+
user_permissionsets = []
|
|
192
360
|
|
|
193
361
|
for user in users:
|
|
194
362
|
user_id = user["UserId"]
|
|
@@ -199,7 +367,7 @@ def get_role_assignments(
|
|
|
199
367
|
PrincipalType="USER",
|
|
200
368
|
):
|
|
201
369
|
for assignment in page.get("AccountAssignments", []):
|
|
202
|
-
|
|
370
|
+
user_permissionsets.append(
|
|
203
371
|
{
|
|
204
372
|
"UserId": user_id,
|
|
205
373
|
"PermissionSetArn": assignment.get("PermissionSetArn"),
|
|
@@ -207,40 +375,198 @@ def get_role_assignments(
|
|
|
207
375
|
},
|
|
208
376
|
)
|
|
209
377
|
|
|
210
|
-
return
|
|
378
|
+
return user_permissionsets
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@timeit
|
|
382
|
+
@aws_handle_regions
|
|
383
|
+
def get_group_permissionsets(
|
|
384
|
+
boto3_session: boto3.session.Session,
|
|
385
|
+
groups: list[dict[str, Any]],
|
|
386
|
+
instance_arn: str,
|
|
387
|
+
region: str,
|
|
388
|
+
) -> list[dict[str, Any]]:
|
|
389
|
+
"""
|
|
390
|
+
Get permissionsets for SSO groups, taking into account which accounts the group is assigned to.
|
|
391
|
+
"""
|
|
392
|
+
logger.info(f"Getting permissionsets for {len(groups)} groups")
|
|
393
|
+
client = boto3_session.client("sso-admin", region_name=region)
|
|
394
|
+
group_permissionsets: list[dict[str, Any]] = []
|
|
395
|
+
|
|
396
|
+
for group in groups:
|
|
397
|
+
group_id = group["GroupId"]
|
|
398
|
+
paginator = client.get_paginator("list_account_assignments_for_principal")
|
|
399
|
+
for page in paginator.paginate(
|
|
400
|
+
InstanceArn=instance_arn,
|
|
401
|
+
PrincipalId=group_id,
|
|
402
|
+
PrincipalType="GROUP",
|
|
403
|
+
):
|
|
404
|
+
for assignment in page.get("AccountAssignments", []):
|
|
405
|
+
group_permissionsets.append(
|
|
406
|
+
{
|
|
407
|
+
"GroupId": group_id,
|
|
408
|
+
"PermissionSetArn": assignment.get("PermissionSetArn"),
|
|
409
|
+
"AccountId": assignment.get("AccountId"),
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
return group_permissionsets
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@timeit
|
|
417
|
+
@aws_handle_regions
|
|
418
|
+
def get_user_group_memberships(
|
|
419
|
+
boto3_session: boto3.session.Session,
|
|
420
|
+
identity_store_id: str,
|
|
421
|
+
groups: list[dict[str, Any]],
|
|
422
|
+
region: str,
|
|
423
|
+
) -> dict[str, list[str]]:
|
|
424
|
+
"""
|
|
425
|
+
Return a mapping of UserId -> [GroupIds] for all group memberships in the identity store.
|
|
426
|
+
"""
|
|
427
|
+
client = boto3_session.client("identitystore", region_name=region)
|
|
428
|
+
user_groups: dict[str, list[str]] = {}
|
|
429
|
+
|
|
430
|
+
for group in groups:
|
|
431
|
+
group_id = group["GroupId"]
|
|
432
|
+
paginator = client.get_paginator("list_group_memberships")
|
|
433
|
+
for page in paginator.paginate(
|
|
434
|
+
IdentityStoreId=identity_store_id, GroupId=group_id
|
|
435
|
+
):
|
|
436
|
+
for membership in page.get("GroupMemberships", []):
|
|
437
|
+
member = membership.get("MemberId", {})
|
|
438
|
+
user_id = member.get("UserId")
|
|
439
|
+
if user_id:
|
|
440
|
+
user_groups.setdefault(user_id, []).append(group_id)
|
|
441
|
+
|
|
442
|
+
return user_groups
|
|
211
443
|
|
|
212
444
|
|
|
213
445
|
@timeit
|
|
214
|
-
def
|
|
446
|
+
def _get_permset_roles(
|
|
215
447
|
neo4j_session: neo4j.Session,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
448
|
+
permset_ids: list[str],
|
|
449
|
+
) -> dict[tuple[str, str], str]:
|
|
450
|
+
"""
|
|
451
|
+
Given a list of permission set ARNs, return a mapping of (permission set ARN, account ID) to role ARN
|
|
452
|
+
based on the ASSIGNED_TO_ROLE relationship in the graph.
|
|
219
453
|
"""
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
454
|
+
query = """
|
|
455
|
+
MATCH (role:AWSRole)<-[:ASSIGNED_TO_ROLE]-(permset:AWSPermissionSet)
|
|
456
|
+
MATCH (account:AWSAccount)-[:RESOURCE]->(role)
|
|
457
|
+
WHERE permset.arn IN $PermSetIds
|
|
458
|
+
RETURN permset.arn AS PermissionSetArn, role.arn AS RoleArn, account.id AS AccountId
|
|
459
|
+
"""
|
|
460
|
+
permset_to_role = neo4j_session.execute_read(
|
|
461
|
+
read_list_of_dicts_tx,
|
|
462
|
+
query,
|
|
463
|
+
PermSetIds=permset_ids,
|
|
464
|
+
)
|
|
465
|
+
return {
|
|
466
|
+
(entry["PermissionSetArn"], entry["AccountId"]): entry["RoleArn"]
|
|
467
|
+
for entry in permset_to_role
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
@timeit
|
|
472
|
+
def get_principal_roles(
|
|
473
|
+
neo4j_session: neo4j.Session,
|
|
474
|
+
principal_assignments: list[dict[str, Any]],
|
|
475
|
+
) -> list[dict[str, Any]]:
|
|
476
|
+
"""
|
|
477
|
+
At this point we've established that the principal has access to a given account
|
|
478
|
+
via a given permission set, and now we need to find the exact role in the account
|
|
479
|
+
it has access to.
|
|
480
|
+
:param neo4j_session: neo4j.Session
|
|
481
|
+
:param principal_assignments: either a list of {
|
|
482
|
+
"UserId": str
|
|
483
|
+
"AccountId": str,
|
|
484
|
+
"PermissionSetArn": str,
|
|
485
|
+
}, or a list of {
|
|
486
|
+
"GroupId": str,
|
|
487
|
+
"AccountId": str,
|
|
488
|
+
"PermissionSetArn": str,
|
|
489
|
+
}
|
|
490
|
+
:return: either a list of {
|
|
491
|
+
"UserId": str,
|
|
492
|
+
"AccountId": str,
|
|
493
|
+
"PermissionSetArn": str,
|
|
494
|
+
"RoleArn": str
|
|
495
|
+
} or,
|
|
496
|
+
a list of {
|
|
497
|
+
"GroupId": str,
|
|
498
|
+
"AccountId": str,
|
|
499
|
+
"PermissionSetArn": str,
|
|
500
|
+
"RoleArn": str
|
|
501
|
+
}
|
|
502
|
+
"""
|
|
503
|
+
# Get unique permission set ARNs from role assignments
|
|
504
|
+
permset_ids = list({pa["PermissionSetArn"] for pa in principal_assignments})
|
|
505
|
+
permset_to_role = _get_permset_roles(neo4j_session, permset_ids)
|
|
506
|
+
|
|
507
|
+
unmatched = 0
|
|
508
|
+
# Use the lookup table to enrich assignments with exact role ARNs
|
|
509
|
+
principal_roles = []
|
|
510
|
+
for assignment in principal_assignments:
|
|
511
|
+
lookup_key = (assignment["PermissionSetArn"], assignment["AccountId"])
|
|
512
|
+
role_arn = permset_to_role.get(lookup_key)
|
|
513
|
+
if not role_arn:
|
|
514
|
+
unmatched += 1
|
|
515
|
+
principal_roles.append(
|
|
516
|
+
{
|
|
517
|
+
**assignment,
|
|
518
|
+
"RoleArn": role_arn,
|
|
519
|
+
}
|
|
237
520
|
)
|
|
521
|
+
if unmatched > 0:
|
|
522
|
+
logger.info(
|
|
523
|
+
f"Identity Center: {unmatched} of {len(principal_assignments)} principal assignments "
|
|
524
|
+
"did not match a role. This usually means IAM roles for some permission sets/accounts "
|
|
525
|
+
"have not been ingested yet. Re-run the IAM sync, and then the Identity Center sync to fix."
|
|
526
|
+
)
|
|
527
|
+
return principal_roles
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
@timeit
|
|
531
|
+
def load_user_roles(
|
|
532
|
+
neo4j_session: neo4j.Session,
|
|
533
|
+
user_roles: list[dict[str, Any]],
|
|
534
|
+
aws_account_id: str,
|
|
535
|
+
aws_update_tag: int,
|
|
536
|
+
) -> None:
|
|
537
|
+
logger.info(f"Loading {len(user_roles)} user roles")
|
|
538
|
+
load_matchlinks(
|
|
539
|
+
neo4j_session,
|
|
540
|
+
AWSRoleToSSOUserMatchLink(),
|
|
541
|
+
user_roles,
|
|
542
|
+
lastupdated=aws_update_tag,
|
|
543
|
+
_sub_resource_label="AWSAccount",
|
|
544
|
+
_sub_resource_id=aws_account_id,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
@timeit
|
|
549
|
+
def load_group_roles(
|
|
550
|
+
neo4j_session: neo4j.Session,
|
|
551
|
+
group_roles: list[dict[str, Any]],
|
|
552
|
+
aws_account_id: str,
|
|
553
|
+
aws_update_tag: int,
|
|
554
|
+
) -> None:
|
|
555
|
+
logger.info(f"Loading {len(group_roles)} group roles")
|
|
556
|
+
load_matchlinks(
|
|
557
|
+
neo4j_session,
|
|
558
|
+
AWSRoleToSSOGroupMatchLink(),
|
|
559
|
+
group_roles,
|
|
560
|
+
lastupdated=aws_update_tag,
|
|
561
|
+
_sub_resource_label="AWSAccount",
|
|
562
|
+
_sub_resource_id=aws_account_id,
|
|
563
|
+
)
|
|
238
564
|
|
|
239
565
|
|
|
240
566
|
@timeit
|
|
241
567
|
def cleanup(
|
|
242
568
|
neo4j_session: neo4j.Session,
|
|
243
|
-
common_job_parameters:
|
|
569
|
+
common_job_parameters: dict[str, Any],
|
|
244
570
|
) -> None:
|
|
245
571
|
GraphJob.from_node_schema(
|
|
246
572
|
AWSIdentityCenterInstanceSchema(),
|
|
@@ -252,24 +578,249 @@ def cleanup(
|
|
|
252
578
|
GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(
|
|
253
579
|
neo4j_session,
|
|
254
580
|
)
|
|
255
|
-
|
|
256
|
-
"aws_import_identity_center_cleanup.json",
|
|
581
|
+
GraphJob.from_node_schema(AWSSSOGroupSchema(), common_job_parameters).run(
|
|
257
582
|
neo4j_session,
|
|
258
|
-
common_job_parameters,
|
|
259
583
|
)
|
|
260
584
|
|
|
585
|
+
# Clean up role assignment MatchLinks
|
|
586
|
+
GraphJob.from_matchlink(
|
|
587
|
+
AWSRoleToSSOUserMatchLink(),
|
|
588
|
+
"AWSAccount",
|
|
589
|
+
common_job_parameters["AWS_ID"],
|
|
590
|
+
common_job_parameters["UPDATE_TAG"],
|
|
591
|
+
).run(neo4j_session)
|
|
592
|
+
GraphJob.from_matchlink(
|
|
593
|
+
AWSRoleToSSOGroupMatchLink(),
|
|
594
|
+
"AWSAccount",
|
|
595
|
+
common_job_parameters["AWS_ID"],
|
|
596
|
+
common_job_parameters["UPDATE_TAG"],
|
|
597
|
+
).run(neo4j_session)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _sync_permission_sets(
|
|
601
|
+
neo4j_session: neo4j.Session,
|
|
602
|
+
boto3_session: boto3.session.Session,
|
|
603
|
+
instance_arn: str,
|
|
604
|
+
region: str,
|
|
605
|
+
current_aws_account_id: str,
|
|
606
|
+
update_tag: int,
|
|
607
|
+
) -> bool:
|
|
608
|
+
"""
|
|
609
|
+
Sync permission sets for an Identity Center instance.
|
|
610
|
+
|
|
611
|
+
Returns:
|
|
612
|
+
True if permission set sync is supported, False for account-scoped instances
|
|
613
|
+
that don't support permission sets.
|
|
614
|
+
"""
|
|
615
|
+
try:
|
|
616
|
+
permission_sets = get_permission_sets(boto3_session, instance_arn, region)
|
|
617
|
+
# Transform permission sets to add RoleHint for fuzzy matching to IAM roles
|
|
618
|
+
permission_sets = transform_permission_sets(permission_sets, region)
|
|
619
|
+
load_permission_sets(
|
|
620
|
+
neo4j_session,
|
|
621
|
+
permission_sets,
|
|
622
|
+
instance_arn,
|
|
623
|
+
region,
|
|
624
|
+
current_aws_account_id,
|
|
625
|
+
update_tag,
|
|
626
|
+
)
|
|
627
|
+
return True
|
|
628
|
+
except botocore.exceptions.ClientError as error:
|
|
629
|
+
if _is_permission_set_sync_unsupported_error(error):
|
|
630
|
+
logger.warning(
|
|
631
|
+
"Skipping permission set sync for Identity Center instance %s in region %s "
|
|
632
|
+
"because the instance does not support permission sets. "
|
|
633
|
+
"Will attempt to sync users and groups only.",
|
|
634
|
+
instance_arn,
|
|
635
|
+
region,
|
|
636
|
+
)
|
|
637
|
+
return False
|
|
638
|
+
raise
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _sync_groups_and_users(
|
|
642
|
+
neo4j_session: neo4j.Session,
|
|
643
|
+
boto3_session: boto3.session.Session,
|
|
644
|
+
instance_arn: str,
|
|
645
|
+
identity_store_id: str,
|
|
646
|
+
region: str,
|
|
647
|
+
permission_set_sync_supported: bool,
|
|
648
|
+
current_aws_account_id: str,
|
|
649
|
+
update_tag: int,
|
|
650
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
651
|
+
"""
|
|
652
|
+
Sync groups and users for an Identity Center instance.
|
|
653
|
+
|
|
654
|
+
Groups are synced first so that user->group membership edges can be created.
|
|
655
|
+
Permission set assignments are fetched (if supported) and used to enrich
|
|
656
|
+
the user/group nodes with HAS_PERMISSION_SET relationships, then returned
|
|
657
|
+
for later role assignment creation.
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
permission_set_sync_supported: If True, fetches and enriches permission set assignments
|
|
661
|
+
|
|
662
|
+
Returns:
|
|
663
|
+
(user_permission_set_assignments, group_permission_set_assignments)
|
|
664
|
+
These are the raw assignment data needed for creating ALLOWED_BY relationships.
|
|
665
|
+
Will be empty lists if permission_set_sync_supported is False.
|
|
666
|
+
"""
|
|
667
|
+
# Fetch groups first to avoid interleaving between groups and users
|
|
668
|
+
groups = get_sso_groups(boto3_session, identity_store_id, region)
|
|
669
|
+
|
|
670
|
+
# Get permission set assignments for groups (if permission sets are supported)
|
|
671
|
+
group_permissionsets_raw: list[dict[str, Any]] = []
|
|
672
|
+
if permission_set_sync_supported:
|
|
673
|
+
group_permissionsets_raw = get_group_permissionsets(
|
|
674
|
+
boto3_session,
|
|
675
|
+
groups,
|
|
676
|
+
instance_arn,
|
|
677
|
+
region,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
# Transform and load groups with their permission set assignments FIRST
|
|
681
|
+
# so that user->group membership edges can attach in the same run.
|
|
682
|
+
transformed_groups = transform_sso_groups(groups, group_permissionsets_raw)
|
|
683
|
+
load_sso_groups(
|
|
684
|
+
neo4j_session,
|
|
685
|
+
transformed_groups,
|
|
686
|
+
identity_store_id,
|
|
687
|
+
region,
|
|
688
|
+
current_aws_account_id,
|
|
689
|
+
update_tag,
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
# Handle users AFTER groups exist
|
|
693
|
+
users = get_sso_users(boto3_session, identity_store_id, region)
|
|
694
|
+
user_group_memberships = get_user_group_memberships(
|
|
695
|
+
boto3_session,
|
|
696
|
+
identity_store_id,
|
|
697
|
+
groups,
|
|
698
|
+
region,
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
# Get direct permission set assignments for users (if permission sets are supported)
|
|
702
|
+
user_permissionsets_raw: list[dict[str, Any]] = []
|
|
703
|
+
if permission_set_sync_supported:
|
|
704
|
+
user_permissionsets_raw = get_user_permissionsets(
|
|
705
|
+
boto3_session,
|
|
706
|
+
users,
|
|
707
|
+
instance_arn,
|
|
708
|
+
region,
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
# Transform and load users with their group memberships AFTER groups exist
|
|
712
|
+
transformed_users = transform_sso_users(
|
|
713
|
+
users,
|
|
714
|
+
user_group_memberships,
|
|
715
|
+
user_permissionsets_raw,
|
|
716
|
+
)
|
|
717
|
+
load_sso_users(
|
|
718
|
+
neo4j_session,
|
|
719
|
+
transformed_users,
|
|
720
|
+
identity_store_id,
|
|
721
|
+
region,
|
|
722
|
+
current_aws_account_id,
|
|
723
|
+
update_tag,
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
# Return the raw assignment data for role assignment creation
|
|
727
|
+
return user_permissionsets_raw, group_permissionsets_raw
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def _sync_role_assignments(
|
|
731
|
+
neo4j_session: neo4j.Session,
|
|
732
|
+
user_permissionsets_raw: list[dict[str, Any]],
|
|
733
|
+
group_permissionsets_raw: list[dict[str, Any]],
|
|
734
|
+
current_aws_account_id: str,
|
|
735
|
+
update_tag: int,
|
|
736
|
+
) -> None:
|
|
737
|
+
"""
|
|
738
|
+
Create ALLOWED_BY relationships between IAM roles and SSO principals.
|
|
739
|
+
|
|
740
|
+
This enriches the raw permission set assignment data with exact role ARNs
|
|
741
|
+
from the graph (using the composite key lookup), then creates the MatchLink
|
|
742
|
+
relationships.
|
|
743
|
+
|
|
744
|
+
Note: This must be called AFTER groups and users are loaded so that the
|
|
745
|
+
MatchLinks can find the existing AWSSSOUser/AWSSSOGroup nodes when creating
|
|
746
|
+
the ALLOWED_BY edges.
|
|
747
|
+
"""
|
|
748
|
+
user_roles = get_principal_roles(neo4j_session, user_permissionsets_raw)
|
|
749
|
+
load_user_roles(neo4j_session, user_roles, current_aws_account_id, update_tag)
|
|
750
|
+
|
|
751
|
+
group_roles = get_principal_roles(neo4j_session, group_permissionsets_raw)
|
|
752
|
+
load_group_roles(neo4j_session, group_roles, current_aws_account_id, update_tag)
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def _sync_instance(
|
|
756
|
+
neo4j_session: neo4j.Session,
|
|
757
|
+
boto3_session: boto3.session.Session,
|
|
758
|
+
instance: dict[str, Any],
|
|
759
|
+
region: str,
|
|
760
|
+
current_aws_account_id: str,
|
|
761
|
+
update_tag: int,
|
|
762
|
+
) -> None:
|
|
763
|
+
"""
|
|
764
|
+
Sync a single Identity Center instance.
|
|
765
|
+
|
|
766
|
+
This syncs permission sets (if supported), groups, users, and role assignments
|
|
767
|
+
in the correct order to ensure all relationships can be created.
|
|
768
|
+
"""
|
|
769
|
+
instance_arn = instance["InstanceArn"]
|
|
770
|
+
identity_store_id = instance["IdentityStoreId"]
|
|
771
|
+
|
|
772
|
+
# Sync permission sets (may not be supported for account-scoped instances)
|
|
773
|
+
permission_set_sync_supported = _sync_permission_sets(
|
|
774
|
+
neo4j_session,
|
|
775
|
+
boto3_session,
|
|
776
|
+
instance_arn,
|
|
777
|
+
region,
|
|
778
|
+
current_aws_account_id,
|
|
779
|
+
update_tag,
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
# Sync groups and users (always happens, but enriched with permission sets if available)
|
|
783
|
+
user_assignments, group_assignments = _sync_groups_and_users(
|
|
784
|
+
neo4j_session,
|
|
785
|
+
boto3_session,
|
|
786
|
+
instance_arn,
|
|
787
|
+
identity_store_id,
|
|
788
|
+
region,
|
|
789
|
+
permission_set_sync_supported,
|
|
790
|
+
current_aws_account_id,
|
|
791
|
+
update_tag,
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
# Create role assignment relationships (only if permission sets are supported)
|
|
795
|
+
if permission_set_sync_supported:
|
|
796
|
+
_sync_role_assignments(
|
|
797
|
+
neo4j_session,
|
|
798
|
+
user_assignments,
|
|
799
|
+
group_assignments,
|
|
800
|
+
current_aws_account_id,
|
|
801
|
+
update_tag,
|
|
802
|
+
)
|
|
803
|
+
|
|
261
804
|
|
|
262
805
|
@timeit
|
|
263
806
|
def sync_identity_center_instances(
|
|
264
807
|
neo4j_session: neo4j.Session,
|
|
265
808
|
boto3_session: boto3.session.Session,
|
|
266
|
-
regions:
|
|
809
|
+
regions: list[str],
|
|
267
810
|
current_aws_account_id: str,
|
|
268
811
|
update_tag: int,
|
|
269
|
-
common_job_parameters:
|
|
812
|
+
common_job_parameters: dict[str, Any],
|
|
270
813
|
) -> None:
|
|
271
814
|
"""
|
|
272
|
-
Sync Identity Center instances
|
|
815
|
+
Sync Identity Center instances across all specified regions.
|
|
816
|
+
|
|
817
|
+
For each instance, syncs:
|
|
818
|
+
1. Permission sets (if supported by the instance type)
|
|
819
|
+
2. Groups and users (with permission set assignments if available)
|
|
820
|
+
3. Role assignment relationships (ALLOWED_BY edges from roles to principals)
|
|
821
|
+
|
|
822
|
+
Account-scoped Identity Center instances don't support permission sets and will
|
|
823
|
+
skip step 1 and 3, but still sync users and groups.
|
|
273
824
|
"""
|
|
274
825
|
logger.info(f"Syncing Identity Center instances for regions {regions}")
|
|
275
826
|
for region in regions:
|
|
@@ -283,42 +834,13 @@ def sync_identity_center_instances(
|
|
|
283
834
|
update_tag,
|
|
284
835
|
)
|
|
285
836
|
|
|
286
|
-
# For each instance, get and load its permission sets and SSO users
|
|
287
837
|
for instance in instances:
|
|
288
|
-
|
|
289
|
-
identity_store_id = instance["IdentityStoreId"]
|
|
290
|
-
|
|
291
|
-
permission_sets = get_permission_sets(boto3_session, instance_arn, region)
|
|
292
|
-
|
|
293
|
-
load_permission_sets(
|
|
294
|
-
neo4j_session,
|
|
295
|
-
permission_sets,
|
|
296
|
-
instance_arn,
|
|
297
|
-
region,
|
|
298
|
-
current_aws_account_id,
|
|
299
|
-
update_tag,
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
users = get_sso_users(boto3_session, identity_store_id, region)
|
|
303
|
-
load_sso_users(
|
|
838
|
+
_sync_instance(
|
|
304
839
|
neo4j_session,
|
|
305
|
-
users,
|
|
306
|
-
identity_store_id,
|
|
307
|
-
region,
|
|
308
|
-
current_aws_account_id,
|
|
309
|
-
update_tag,
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
# Get and load role assignments
|
|
313
|
-
role_assignments = get_role_assignments(
|
|
314
840
|
boto3_session,
|
|
315
|
-
|
|
316
|
-
instance_arn,
|
|
841
|
+
instance,
|
|
317
842
|
region,
|
|
318
|
-
|
|
319
|
-
load_role_assignments(
|
|
320
|
-
neo4j_session,
|
|
321
|
-
role_assignments,
|
|
843
|
+
current_aws_account_id,
|
|
322
844
|
update_tag,
|
|
323
845
|
)
|
|
324
846
|
|