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.
Files changed (642) hide show
  1. cartography/_version.py +16 -3
  2. cartography/cli.py +466 -5
  3. cartography/client/aws/__init__.py +19 -0
  4. cartography/client/aws/ecr.py +51 -0
  5. cartography/client/core/tx.py +357 -8
  6. cartography/config.py +153 -0
  7. cartography/data/azure_permission_relationships.yaml +20 -0
  8. cartography/data/gcp_permission_relationships.yaml +21 -0
  9. cartography/data/indexes.cypher +0 -186
  10. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  11. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  12. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  13. cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
  14. cartography/driftdetect/cli.py +3 -2
  15. cartography/graph/cleanupbuilder.py +198 -41
  16. cartography/graph/job.py +54 -6
  17. cartography/graph/querybuilder.py +528 -27
  18. cartography/graph/statement.py +5 -1
  19. cartography/intel/airbyte/__init__.py +105 -0
  20. cartography/intel/airbyte/connections.py +120 -0
  21. cartography/intel/airbyte/destinations.py +81 -0
  22. cartography/intel/airbyte/organizations.py +59 -0
  23. cartography/intel/airbyte/sources.py +78 -0
  24. cartography/intel/airbyte/tags.py +64 -0
  25. cartography/intel/airbyte/users.py +106 -0
  26. cartography/intel/airbyte/util.py +122 -0
  27. cartography/intel/airbyte/workspaces.py +63 -0
  28. cartography/intel/aws/__init__.py +24 -9
  29. cartography/intel/aws/acm.py +124 -0
  30. cartography/intel/aws/apigateway.py +253 -22
  31. cartography/intel/aws/apigatewayv2.py +116 -0
  32. cartography/intel/aws/cloudtrail.py +17 -39
  33. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  34. cartography/intel/aws/cloudwatch.py +150 -4
  35. cartography/intel/aws/codebuild.py +132 -0
  36. cartography/intel/aws/cognito.py +201 -0
  37. cartography/intel/aws/config.py +7 -3
  38. cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
  39. cartography/intel/aws/ec2/instances.py +25 -1
  40. cartography/intel/aws/ec2/internet_gateways.py +4 -2
  41. cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
  42. cartography/intel/aws/ec2/network_interfaces.py +5 -1
  43. cartography/intel/aws/ec2/reserved_instances.py +3 -1
  44. cartography/intel/aws/ec2/security_groups.py +140 -122
  45. cartography/intel/aws/ec2/snapshots.py +47 -84
  46. cartography/intel/aws/ec2/subnets.py +37 -63
  47. cartography/intel/aws/ec2/tgw.py +11 -5
  48. cartography/intel/aws/ec2/volumes.py +1 -1
  49. cartography/intel/aws/ec2/vpc.py +140 -124
  50. cartography/intel/aws/ec2/vpc_peerings.py +262 -125
  51. cartography/intel/aws/ecr.py +269 -98
  52. cartography/intel/aws/ecr_image_layers.py +923 -0
  53. cartography/intel/aws/ecs.py +251 -380
  54. cartography/intel/aws/efs.py +179 -11
  55. cartography/intel/aws/elasticache.py +102 -79
  56. cartography/intel/aws/elasticsearch.py +13 -4
  57. cartography/intel/aws/eventbridge.py +164 -0
  58. cartography/intel/aws/glue.py +181 -0
  59. cartography/intel/aws/guardduty.py +443 -0
  60. cartography/intel/aws/iam.py +750 -493
  61. cartography/intel/aws/identitycenter.py +605 -83
  62. cartography/intel/aws/inspector.py +221 -105
  63. cartography/intel/aws/kms.py +173 -201
  64. cartography/intel/aws/lambda_function.py +272 -189
  65. cartography/intel/aws/organizations.py +10 -9
  66. cartography/intel/aws/permission_relationships.py +10 -20
  67. cartography/intel/aws/rds.py +337 -446
  68. cartography/intel/aws/redshift.py +9 -4
  69. cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
  70. cartography/intel/aws/resources.py +18 -0
  71. cartography/intel/aws/route53.py +386 -332
  72. cartography/intel/aws/s3.py +322 -14
  73. cartography/intel/aws/secretsmanager.py +81 -49
  74. cartography/intel/aws/securityhub.py +3 -1
  75. cartography/intel/aws/sns.py +62 -2
  76. cartography/intel/aws/sqs.py +36 -90
  77. cartography/intel/aws/ssm.py +3 -5
  78. cartography/intel/azure/__init__.py +202 -48
  79. cartography/intel/azure/aks.py +175 -0
  80. cartography/intel/azure/app_service.py +105 -0
  81. cartography/intel/azure/compute.py +59 -112
  82. cartography/intel/azure/container_instances.py +95 -0
  83. cartography/intel/azure/cosmosdb.py +222 -361
  84. cartography/intel/azure/data_factory.py +85 -0
  85. cartography/intel/azure/data_factory_dataset.py +128 -0
  86. cartography/intel/azure/data_factory_linked_service.py +119 -0
  87. cartography/intel/azure/data_factory_pipeline.py +142 -0
  88. cartography/intel/azure/data_lake.py +124 -0
  89. cartography/intel/azure/event_grid.py +94 -0
  90. cartography/intel/azure/functions.py +124 -0
  91. cartography/intel/azure/load_balancers.py +263 -0
  92. cartography/intel/azure/logic_apps.py +101 -0
  93. cartography/intel/azure/monitor.py +105 -0
  94. cartography/intel/azure/network.py +467 -0
  95. cartography/intel/azure/permission_relationships.py +466 -0
  96. cartography/intel/azure/rbac.py +309 -0
  97. cartography/intel/azure/resource_groups.py +82 -0
  98. cartography/intel/azure/security_center.py +106 -0
  99. cartography/intel/azure/sql.py +145 -292
  100. cartography/intel/azure/storage.py +185 -262
  101. cartography/intel/azure/subscription.py +21 -43
  102. cartography/intel/azure/tenant.py +39 -30
  103. cartography/intel/azure/util/common.py +13 -0
  104. cartography/intel/azure/util/credentials.py +49 -174
  105. cartography/intel/azure/util/tag.py +41 -0
  106. cartography/intel/create_indexes.py +2 -1
  107. cartography/intel/crowdstrike/spotlight.py +5 -2
  108. cartography/intel/dns.py +5 -2
  109. cartography/intel/entra/__init__.py +100 -1
  110. cartography/intel/entra/app_role_assignments.py +284 -0
  111. cartography/intel/entra/applications.py +182 -0
  112. cartography/intel/entra/federation/__init__.py +0 -0
  113. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  114. cartography/intel/entra/groups.py +198 -0
  115. cartography/intel/entra/ou.py +48 -24
  116. cartography/intel/entra/service_principals.py +217 -0
  117. cartography/intel/entra/users.py +105 -57
  118. cartography/intel/gcp/__init__.py +334 -396
  119. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  120. cartography/intel/gcp/bigtable_backup.py +91 -0
  121. cartography/intel/gcp/bigtable_cluster.py +93 -0
  122. cartography/intel/gcp/bigtable_instance.py +86 -0
  123. cartography/intel/gcp/bigtable_table.py +87 -0
  124. cartography/intel/gcp/cai.py +292 -0
  125. cartography/intel/gcp/clients.py +112 -0
  126. cartography/intel/gcp/compute.py +128 -119
  127. cartography/intel/gcp/crm/__init__.py +0 -0
  128. cartography/intel/gcp/crm/folders.py +114 -0
  129. cartography/intel/gcp/crm/orgs.py +70 -0
  130. cartography/intel/gcp/crm/projects.py +120 -0
  131. cartography/intel/gcp/dns.py +83 -169
  132. cartography/intel/gcp/gke.py +72 -113
  133. cartography/intel/gcp/iam.py +111 -91
  134. cartography/intel/gcp/permission_relationships.py +394 -0
  135. cartography/intel/gcp/policy_bindings.py +225 -0
  136. cartography/intel/gcp/storage.py +75 -159
  137. cartography/intel/github/__init__.py +62 -25
  138. cartography/intel/github/commits.py +423 -0
  139. cartography/intel/github/repos.py +463 -85
  140. cartography/intel/github/teams.py +3 -3
  141. cartography/intel/github/users.py +5 -0
  142. cartography/intel/github/util.py +12 -0
  143. cartography/intel/googleworkspace/__init__.py +193 -0
  144. cartography/intel/googleworkspace/devices.py +254 -0
  145. cartography/intel/googleworkspace/groups.py +568 -0
  146. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  147. cartography/intel/googleworkspace/tenant.py +85 -0
  148. cartography/intel/googleworkspace/users.py +138 -0
  149. cartography/intel/gsuite/__init__.py +17 -9
  150. cartography/intel/gsuite/groups.py +291 -0
  151. cartography/intel/gsuite/users.py +142 -0
  152. cartography/intel/jamf/computers.py +7 -1
  153. cartography/intel/keycloak/__init__.py +153 -0
  154. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  155. cartography/intel/keycloak/authenticationflows.py +77 -0
  156. cartography/intel/keycloak/clients.py +187 -0
  157. cartography/intel/keycloak/groups.py +126 -0
  158. cartography/intel/keycloak/identityproviders.py +94 -0
  159. cartography/intel/keycloak/organizations.py +163 -0
  160. cartography/intel/keycloak/realms.py +61 -0
  161. cartography/intel/keycloak/roles.py +202 -0
  162. cartography/intel/keycloak/scopes.py +73 -0
  163. cartography/intel/keycloak/users.py +70 -0
  164. cartography/intel/keycloak/util.py +47 -0
  165. cartography/intel/kubernetes/__init__.py +60 -14
  166. cartography/intel/kubernetes/clusters.py +86 -0
  167. cartography/intel/kubernetes/eks.py +402 -0
  168. cartography/intel/kubernetes/namespaces.py +59 -57
  169. cartography/intel/kubernetes/pods.py +168 -75
  170. cartography/intel/kubernetes/rbac.py +597 -0
  171. cartography/intel/kubernetes/secrets.py +95 -45
  172. cartography/intel/kubernetes/services.py +131 -67
  173. cartography/intel/kubernetes/util.py +142 -14
  174. cartography/intel/oci/iam.py +23 -9
  175. cartography/intel/oci/organizations.py +3 -1
  176. cartography/intel/oci/utils.py +28 -5
  177. cartography/intel/okta/applications.py +15 -5
  178. cartography/intel/okta/awssaml.py +14 -10
  179. cartography/intel/okta/factors.py +3 -1
  180. cartography/intel/okta/groups.py +5 -2
  181. cartography/intel/okta/organization.py +3 -1
  182. cartography/intel/okta/origins.py +3 -1
  183. cartography/intel/okta/roles.py +5 -2
  184. cartography/intel/okta/users.py +10 -2
  185. cartography/intel/ontology/__init__.py +44 -0
  186. cartography/intel/ontology/devices.py +54 -0
  187. cartography/intel/ontology/users.py +54 -0
  188. cartography/intel/ontology/utils.py +176 -0
  189. cartography/intel/pagerduty/escalation_policies.py +13 -6
  190. cartography/intel/pagerduty/schedules.py +9 -4
  191. cartography/intel/pagerduty/services.py +7 -3
  192. cartography/intel/pagerduty/teams.py +5 -2
  193. cartography/intel/pagerduty/users.py +3 -1
  194. cartography/intel/pagerduty/vendors.py +3 -1
  195. cartography/intel/scaleway/__init__.py +127 -0
  196. cartography/intel/scaleway/iam/__init__.py +0 -0
  197. cartography/intel/scaleway/iam/apikeys.py +71 -0
  198. cartography/intel/scaleway/iam/applications.py +71 -0
  199. cartography/intel/scaleway/iam/groups.py +71 -0
  200. cartography/intel/scaleway/iam/users.py +71 -0
  201. cartography/intel/scaleway/instances/__init__.py +0 -0
  202. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  203. cartography/intel/scaleway/instances/instances.py +92 -0
  204. cartography/intel/scaleway/projects.py +79 -0
  205. cartography/intel/scaleway/storage/__init__.py +0 -0
  206. cartography/intel/scaleway/storage/snapshots.py +86 -0
  207. cartography/intel/scaleway/storage/volumes.py +84 -0
  208. cartography/intel/scaleway/utils.py +37 -0
  209. cartography/intel/sentinelone/__init__.py +75 -0
  210. cartography/intel/sentinelone/account.py +140 -0
  211. cartography/intel/sentinelone/agent.py +139 -0
  212. cartography/intel/sentinelone/api.py +124 -0
  213. cartography/intel/sentinelone/application.py +248 -0
  214. cartography/intel/sentinelone/cve.py +119 -0
  215. cartography/intel/sentinelone/utils.py +28 -0
  216. cartography/intel/slack/__init__.py +78 -0
  217. cartography/intel/slack/channels.py +80 -0
  218. cartography/intel/slack/groups.py +90 -0
  219. cartography/intel/slack/teams.py +65 -0
  220. cartography/intel/slack/users.py +57 -0
  221. cartography/intel/slack/utils.py +29 -0
  222. cartography/intel/spacelift/__init__.py +161 -0
  223. cartography/intel/spacelift/account.py +73 -0
  224. cartography/intel/spacelift/ec2_ownership.py +280 -0
  225. cartography/intel/spacelift/runs.py +463 -0
  226. cartography/intel/spacelift/spaces.py +112 -0
  227. cartography/intel/spacelift/stacks.py +119 -0
  228. cartography/intel/spacelift/util.py +122 -0
  229. cartography/intel/spacelift/workerpools.py +131 -0
  230. cartography/intel/spacelift/workers.py +128 -0
  231. cartography/intel/trivy/__init__.py +272 -0
  232. cartography/intel/trivy/scanner.py +386 -0
  233. cartography/models/airbyte/__init__.py +0 -0
  234. cartography/models/airbyte/connection.py +138 -0
  235. cartography/models/airbyte/destination.py +75 -0
  236. cartography/models/airbyte/organization.py +19 -0
  237. cartography/models/airbyte/source.py +75 -0
  238. cartography/models/airbyte/stream.py +74 -0
  239. cartography/models/airbyte/tag.py +69 -0
  240. cartography/models/airbyte/user.py +115 -0
  241. cartography/models/airbyte/workspace.py +46 -0
  242. cartography/models/anthropic/apikey.py +4 -0
  243. cartography/models/anthropic/user.py +4 -0
  244. cartography/models/aws/acm/__init__.py +0 -0
  245. cartography/models/aws/acm/certificate.py +75 -0
  246. cartography/models/aws/apigateway/__init__.py +0 -0
  247. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  248. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  249. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  250. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  251. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  252. cartography/models/aws/cloudtrail/management_events.py +153 -0
  253. cartography/models/aws/cloudtrail/trail.py +45 -0
  254. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  255. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  256. cartography/models/aws/codebuild/__init__.py +0 -0
  257. cartography/models/aws/codebuild/project.py +49 -0
  258. cartography/models/aws/cognito/__init__.py +0 -0
  259. cartography/models/aws/cognito/identity_pool.py +70 -0
  260. cartography/models/aws/cognito/user_pool.py +47 -0
  261. cartography/models/aws/dynamodb/tables.py +2 -0
  262. cartography/models/aws/ec2/instances.py +25 -1
  263. cartography/models/aws/ec2/networkinterfaces.py +4 -0
  264. cartography/models/aws/ec2/security_group_rules.py +109 -0
  265. cartography/models/aws/ec2/security_groups.py +90 -0
  266. cartography/models/aws/ec2/snapshots.py +58 -0
  267. cartography/models/aws/ec2/subnet_instance.py +2 -0
  268. cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
  269. cartography/models/aws/ec2/subnets.py +65 -0
  270. cartography/models/aws/ec2/volumes.py +20 -0
  271. cartography/models/aws/ec2/vpc.py +46 -0
  272. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  273. cartography/models/aws/ec2/vpc_peering.py +157 -0
  274. cartography/models/aws/ecr/__init__.py +0 -0
  275. cartography/models/aws/ecr/image.py +146 -0
  276. cartography/models/aws/ecr/image_layer.py +107 -0
  277. cartography/models/aws/ecr/repository.py +72 -0
  278. cartography/models/aws/ecr/repository_image.py +95 -0
  279. cartography/models/aws/ecs/__init__.py +0 -0
  280. cartography/models/aws/ecs/clusters.py +64 -0
  281. cartography/models/aws/ecs/container_definitions.py +93 -0
  282. cartography/models/aws/ecs/container_instances.py +84 -0
  283. cartography/models/aws/ecs/containers.py +101 -0
  284. cartography/models/aws/ecs/services.py +134 -0
  285. cartography/models/aws/ecs/task_definitions.py +135 -0
  286. cartography/models/aws/ecs/tasks.py +134 -0
  287. cartography/models/aws/efs/access_point.py +77 -0
  288. cartography/models/aws/efs/file_system.py +60 -0
  289. cartography/models/aws/efs/mount_target.py +29 -2
  290. cartography/models/aws/elasticache/__init__.py +0 -0
  291. cartography/models/aws/elasticache/cluster.py +65 -0
  292. cartography/models/aws/elasticache/topic.py +67 -0
  293. cartography/models/aws/eventbridge/__init__.py +0 -0
  294. cartography/models/aws/eventbridge/rule.py +77 -0
  295. cartography/models/aws/eventbridge/target.py +71 -0
  296. cartography/models/aws/glue/__init__.py +0 -0
  297. cartography/models/aws/glue/connection.py +51 -0
  298. cartography/models/aws/glue/job.py +69 -0
  299. cartography/models/aws/guardduty/__init__.py +1 -0
  300. cartography/models/aws/guardduty/detectors.py +50 -0
  301. cartography/models/aws/guardduty/findings.py +121 -0
  302. cartography/models/aws/iam/access_key.py +103 -0
  303. cartography/models/aws/iam/account_role.py +24 -0
  304. cartography/models/aws/iam/federated_principal.py +60 -0
  305. cartography/models/aws/iam/group.py +60 -0
  306. cartography/models/aws/iam/group_membership.py +27 -0
  307. cartography/models/aws/iam/inline_policy.py +78 -0
  308. cartography/models/aws/iam/managed_policy.py +51 -0
  309. cartography/models/aws/iam/policy_statement.py +57 -0
  310. cartography/models/aws/iam/role.py +83 -0
  311. cartography/models/aws/iam/root_principal.py +52 -0
  312. cartography/models/aws/iam/service_principal.py +30 -0
  313. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  314. cartography/models/aws/iam/user.py +59 -0
  315. cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
  316. cartography/models/aws/identitycenter/awspermissionset.py +70 -0
  317. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  318. cartography/models/aws/identitycenter/awsssouser.py +49 -9
  319. cartography/models/aws/inspector/findings.py +37 -0
  320. cartography/models/aws/inspector/packages.py +1 -31
  321. cartography/models/aws/kms/__init__.py +0 -0
  322. cartography/models/aws/kms/aliases.py +86 -0
  323. cartography/models/aws/kms/grants.py +65 -0
  324. cartography/models/aws/kms/keys.py +88 -0
  325. cartography/models/aws/lambda_function/__init__.py +0 -0
  326. cartography/models/aws/lambda_function/alias.py +74 -0
  327. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  328. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  329. cartography/models/aws/lambda_function/layer.py +72 -0
  330. cartography/models/aws/rds/__init__.py +0 -0
  331. cartography/models/aws/rds/cluster.py +91 -0
  332. cartography/models/aws/rds/event_subscription.py +146 -0
  333. cartography/models/aws/rds/instance.py +156 -0
  334. cartography/models/aws/rds/snapshot.py +108 -0
  335. cartography/models/aws/rds/subnet_group.py +101 -0
  336. cartography/models/aws/route53/__init__.py +0 -0
  337. cartography/models/aws/route53/dnsrecord.py +235 -0
  338. cartography/models/aws/route53/nameserver.py +63 -0
  339. cartography/models/aws/route53/subzone.py +40 -0
  340. cartography/models/aws/route53/zone.py +47 -0
  341. cartography/models/aws/s3/notification.py +24 -0
  342. cartography/models/aws/secretsmanager/secret.py +106 -0
  343. cartography/models/aws/secretsmanager/secret_version.py +0 -2
  344. cartography/models/aws/sns/topic_subscription.py +74 -0
  345. cartography/models/aws/sqs/__init__.py +0 -0
  346. cartography/models/aws/sqs/queue.py +89 -0
  347. cartography/models/azure/__init__.py +0 -0
  348. cartography/models/azure/aks_cluster.py +54 -0
  349. cartography/models/azure/aks_nodepool.py +54 -0
  350. cartography/models/azure/app_service.py +59 -0
  351. cartography/models/azure/container_instance.py +57 -0
  352. cartography/models/azure/cosmosdb/__init__.py +0 -0
  353. cartography/models/azure/cosmosdb/account.py +77 -0
  354. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  355. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  356. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  357. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  358. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  359. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  360. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  361. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  362. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  363. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  364. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  365. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  366. cartography/models/azure/data_factory/__init__.py +0 -0
  367. cartography/models/azure/data_factory/data_factory.py +51 -0
  368. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  369. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  370. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  371. cartography/models/azure/data_lake_filesystem.py +51 -0
  372. cartography/models/azure/event_grid_topic.py +57 -0
  373. cartography/models/azure/function_app.py +59 -0
  374. cartography/models/azure/load_balancer/__init__.py +0 -0
  375. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  376. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  377. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  378. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  379. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  380. cartography/models/azure/logic_apps.py +56 -0
  381. cartography/models/azure/monitor.py +54 -0
  382. cartography/models/azure/network_interface.py +112 -0
  383. cartography/models/azure/network_security_group.py +50 -0
  384. cartography/models/azure/permission_relationships.py +60 -0
  385. cartography/models/azure/principal.py +41 -0
  386. cartography/models/azure/public_ip_address.py +50 -0
  387. cartography/models/azure/rbac.py +268 -0
  388. cartography/models/azure/resource_groups.py +52 -0
  389. cartography/models/azure/security_center.py +50 -0
  390. cartography/models/azure/sql/__init__.py +0 -0
  391. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  392. cartography/models/azure/sql/elasticpool.py +77 -0
  393. cartography/models/azure/sql/failovergroup.py +73 -0
  394. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  395. cartography/models/azure/sql/replicationlink.py +81 -0
  396. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  397. cartography/models/azure/sql/restorepoint.py +74 -0
  398. cartography/models/azure/sql/serveradadministrator.py +74 -0
  399. cartography/models/azure/sql/serverdnsalias.py +71 -0
  400. cartography/models/azure/sql/sqldatabase.py +85 -0
  401. cartography/models/azure/sql/sqlserver.py +50 -0
  402. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  403. cartography/models/azure/storage/__init__.py +0 -0
  404. cartography/models/azure/storage/account.py +59 -0
  405. cartography/models/azure/storage/blobcontainer.py +85 -0
  406. cartography/models/azure/storage/blobservice.py +71 -0
  407. cartography/models/azure/storage/fileservice.py +71 -0
  408. cartography/models/azure/storage/fileshare.py +82 -0
  409. cartography/models/azure/storage/queue.py +71 -0
  410. cartography/models/azure/storage/queueservice.py +73 -0
  411. cartography/models/azure/storage/table.py +72 -0
  412. cartography/models/azure/storage/tableservice.py +73 -0
  413. cartography/models/azure/subnet.py +101 -0
  414. cartography/models/azure/subscription.py +47 -0
  415. cartography/models/azure/tags/__init__.py +0 -0
  416. cartography/models/azure/tags/storage_tag.py +40 -0
  417. cartography/models/azure/tags/tag.py +37 -0
  418. cartography/models/azure/tenant.py +17 -0
  419. cartography/models/azure/virtual_network.py +49 -0
  420. cartography/models/azure/vm/__init__.py +0 -0
  421. cartography/models/azure/vm/datadisk.py +80 -0
  422. cartography/models/azure/vm/disk.py +55 -0
  423. cartography/models/azure/vm/snapshot.py +56 -0
  424. cartography/models/azure/vm/virtualmachine.py +59 -0
  425. cartography/models/bigfix/bigfix_computer.py +1 -1
  426. cartography/models/cloudflare/member.py +4 -0
  427. cartography/models/core/common.py +1 -0
  428. cartography/models/core/nodes.py +15 -2
  429. cartography/models/core/relationships.py +44 -0
  430. cartography/models/crowdstrike/hosts.py +1 -1
  431. cartography/models/digitalocean/droplet.py +2 -0
  432. cartography/models/duo/endpoint.py +1 -1
  433. cartography/models/duo/phone.py +2 -2
  434. cartography/models/duo/user.py +4 -0
  435. cartography/models/entra/app_role_assignment.py +115 -0
  436. cartography/models/entra/application.py +49 -0
  437. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  438. cartography/models/entra/group.py +117 -0
  439. cartography/models/entra/service_principal.py +104 -0
  440. cartography/models/entra/user.py +42 -51
  441. cartography/models/gcp/__init__.py +0 -0
  442. cartography/models/gcp/bigtable/__init__.py +0 -0
  443. cartography/models/gcp/bigtable/app_profile.py +94 -0
  444. cartography/models/gcp/bigtable/backup.py +91 -0
  445. cartography/models/gcp/bigtable/cluster.py +73 -0
  446. cartography/models/gcp/bigtable/instance.py +52 -0
  447. cartography/models/gcp/bigtable/table.py +69 -0
  448. cartography/models/gcp/compute/__init__.py +0 -0
  449. cartography/models/gcp/compute/subnet.py +74 -0
  450. cartography/models/gcp/compute/vpc.py +50 -0
  451. cartography/models/gcp/crm/__init__.py +0 -0
  452. cartography/models/gcp/crm/folders.py +98 -0
  453. cartography/models/gcp/crm/organizations.py +21 -0
  454. cartography/models/gcp/crm/projects.py +100 -0
  455. cartography/models/gcp/dns.py +109 -0
  456. cartography/models/gcp/gke.py +69 -0
  457. cartography/models/gcp/iam.py +3 -0
  458. cartography/models/gcp/permission_relationships.py +61 -0
  459. cartography/models/gcp/policy_bindings.py +93 -0
  460. cartography/models/gcp/storage/__init__.py +0 -0
  461. cartography/models/gcp/storage/bucket.py +119 -0
  462. cartography/models/github/commits.py +63 -0
  463. cartography/models/github/dependencies.py +73 -0
  464. cartography/models/github/manifests.py +49 -0
  465. cartography/models/github/users.py +10 -0
  466. cartography/models/googleworkspace/__init__.py +0 -0
  467. cartography/models/googleworkspace/device.py +132 -0
  468. cartography/models/googleworkspace/group.py +382 -0
  469. cartography/models/googleworkspace/oauth_app.py +124 -0
  470. cartography/models/googleworkspace/tenant.py +30 -0
  471. cartography/models/googleworkspace/user.py +113 -0
  472. cartography/models/gsuite/__init__.py +0 -0
  473. cartography/models/gsuite/group.py +218 -0
  474. cartography/models/gsuite/tenant.py +29 -0
  475. cartography/models/gsuite/user.py +107 -0
  476. cartography/models/kandji/device.py +1 -2
  477. cartography/models/keycloak/__init__.py +0 -0
  478. cartography/models/keycloak/authenticationexecution.py +160 -0
  479. cartography/models/keycloak/authenticationflow.py +54 -0
  480. cartography/models/keycloak/client.py +179 -0
  481. cartography/models/keycloak/group.py +101 -0
  482. cartography/models/keycloak/identityprovider.py +89 -0
  483. cartography/models/keycloak/organization.py +116 -0
  484. cartography/models/keycloak/organizationdomain.py +73 -0
  485. cartography/models/keycloak/realm.py +173 -0
  486. cartography/models/keycloak/role.py +126 -0
  487. cartography/models/keycloak/scope.py +73 -0
  488. cartography/models/keycloak/user.py +55 -0
  489. cartography/models/kubernetes/__init__.py +0 -0
  490. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  491. cartography/models/kubernetes/clusterroles.py +52 -0
  492. cartography/models/kubernetes/clusters.py +26 -0
  493. cartography/models/kubernetes/containers.py +133 -0
  494. cartography/models/kubernetes/groups.py +107 -0
  495. cartography/models/kubernetes/namespaces.py +51 -0
  496. cartography/models/kubernetes/oidc.py +51 -0
  497. cartography/models/kubernetes/pods.py +80 -0
  498. cartography/models/kubernetes/rolebindings.py +159 -0
  499. cartography/models/kubernetes/roles.py +76 -0
  500. cartography/models/kubernetes/secrets.py +79 -0
  501. cartography/models/kubernetes/serviceaccounts.py +77 -0
  502. cartography/models/kubernetes/services.py +108 -0
  503. cartography/models/kubernetes/users.py +105 -0
  504. cartography/models/lastpass/user.py +4 -0
  505. cartography/models/ontology/__init__.py +0 -0
  506. cartography/models/ontology/device.py +137 -0
  507. cartography/models/ontology/mapping/__init__.py +76 -0
  508. cartography/models/ontology/mapping/data/__init__.py +0 -0
  509. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  510. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  511. cartography/models/ontology/mapping/data/containers.py +88 -0
  512. cartography/models/ontology/mapping/data/databases.py +182 -0
  513. cartography/models/ontology/mapping/data/devices.py +194 -0
  514. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  515. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  516. cartography/models/ontology/mapping/data/users.py +63 -0
  517. cartography/models/ontology/mapping/specs.py +85 -0
  518. cartography/models/ontology/user.py +51 -0
  519. cartography/models/openai/adminapikey.py +4 -0
  520. cartography/models/openai/apikey.py +4 -0
  521. cartography/models/openai/user.py +4 -0
  522. cartography/models/scaleway/__init__.py +0 -0
  523. cartography/models/scaleway/iam/__init__.py +0 -0
  524. cartography/models/scaleway/iam/apikey.py +100 -0
  525. cartography/models/scaleway/iam/application.py +52 -0
  526. cartography/models/scaleway/iam/group.py +95 -0
  527. cartography/models/scaleway/iam/user.py +64 -0
  528. cartography/models/scaleway/instance/__init__.py +0 -0
  529. cartography/models/scaleway/instance/flexibleip.py +52 -0
  530. cartography/models/scaleway/instance/instance.py +120 -0
  531. cartography/models/scaleway/organization.py +19 -0
  532. cartography/models/scaleway/project.py +48 -0
  533. cartography/models/scaleway/storage/__init__.py +0 -0
  534. cartography/models/scaleway/storage/snapshot.py +78 -0
  535. cartography/models/scaleway/storage/volume.py +51 -0
  536. cartography/models/sentinelone/__init__.py +1 -0
  537. cartography/models/sentinelone/account.py +40 -0
  538. cartography/models/sentinelone/agent.py +50 -0
  539. cartography/models/sentinelone/application.py +44 -0
  540. cartography/models/sentinelone/application_version.py +96 -0
  541. cartography/models/sentinelone/cve.py +73 -0
  542. cartography/models/slack/__init__.py +0 -0
  543. cartography/models/slack/channels.py +92 -0
  544. cartography/models/slack/group.py +129 -0
  545. cartography/models/slack/team.py +22 -0
  546. cartography/models/slack/user.py +62 -0
  547. cartography/models/snipeit/asset.py +2 -0
  548. cartography/models/snipeit/user.py +4 -0
  549. cartography/models/spacelift/__init__.py +0 -0
  550. cartography/models/spacelift/cloudtrailevent.py +120 -0
  551. cartography/models/spacelift/run.py +162 -0
  552. cartography/models/spacelift/space.py +131 -0
  553. cartography/models/spacelift/spaceliftaccount.py +31 -0
  554. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  555. cartography/models/spacelift/stack.py +96 -0
  556. cartography/models/spacelift/user.py +63 -0
  557. cartography/models/spacelift/worker.py +97 -0
  558. cartography/models/spacelift/workerpool.py +90 -0
  559. cartography/models/tailscale/device.py +2 -1
  560. cartography/models/tailscale/user.py +6 -1
  561. cartography/models/trivy/__init__.py +0 -0
  562. cartography/models/trivy/findings.py +66 -0
  563. cartography/models/trivy/fix.py +66 -0
  564. cartography/models/trivy/package.py +71 -0
  565. cartography/rules/README.md +1 -0
  566. cartography/rules/__init__.py +0 -0
  567. cartography/rules/cli.py +261 -0
  568. cartography/rules/data/__init__.py +0 -0
  569. cartography/rules/data/rules/__init__.py +46 -0
  570. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  571. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  572. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  573. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  574. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  575. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  576. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  577. cartography/rules/data/rules/mfa_missing.py +46 -0
  578. cartography/rules/data/rules/object_storage_public.py +100 -0
  579. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  580. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  581. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  582. cartography/rules/formatters.py +108 -0
  583. cartography/rules/runners.py +216 -0
  584. cartography/rules/spec/__init__.py +0 -0
  585. cartography/rules/spec/model.py +267 -0
  586. cartography/rules/spec/result.py +38 -0
  587. cartography/sync.py +25 -5
  588. cartography/util.py +101 -31
  589. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
  590. cartography-0.123.0.dist-info/RECORD +856 -0
  591. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  592. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  593. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  594. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  595. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  596. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
  597. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  598. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  599. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  600. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  601. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  602. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  603. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  604. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  605. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  606. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  607. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  608. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  609. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  610. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  611. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  612. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  613. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  614. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  615. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  616. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  617. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  618. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  619. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  620. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  621. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  622. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  623. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  624. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  625. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  626. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  627. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  628. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  629. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  630. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  631. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  632. cartography/intel/gcp/crm.py +0 -355
  633. cartography/intel/gsuite/api.py +0 -342
  634. cartography-0.104.0rc2.dist-info/RECORD +0 -455
  635. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  636. /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
  637. /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
  638. /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
  639. /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
  640. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
  641. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
  642. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
@@ -2,15 +2,57 @@ import asyncio
2
2
  import logging
3
3
 
4
4
  import neo4j
5
+ from azure.identity import ClientSecretCredential
6
+ from msgraph import GraphServiceClient
5
7
 
6
8
  from cartography.config import Config
9
+ from cartography.intel.entra.app_role_assignments import sync_app_role_assignments
10
+ from cartography.intel.entra.applications import sync_entra_applications
11
+ from cartography.intel.entra.federation.aws_identity_center import sync_entra_federation
12
+ from cartography.intel.entra.groups import sync_entra_groups
7
13
  from cartography.intel.entra.ou import sync_entra_ous
14
+ from cartography.intel.entra.service_principals import sync_service_principals
15
+ from cartography.intel.entra.users import get_tenant
16
+ from cartography.intel.entra.users import load_tenant
8
17
  from cartography.intel.entra.users import sync_entra_users
18
+ from cartography.intel.entra.users import transform_tenant
9
19
  from cartography.util import timeit
10
20
 
11
21
  logger = logging.getLogger(__name__)
12
22
 
13
23
 
24
+ @timeit
25
+ async def sync_tenant(
26
+ neo4j_session: neo4j.Session,
27
+ tenant_id: str,
28
+ client_id: str,
29
+ client_secret: str,
30
+ update_tag: int,
31
+ ) -> None:
32
+ """
33
+ Sync tenant information as a prerequisite for all other Entra resource syncs.
34
+
35
+ :param neo4j_session: Neo4j session
36
+ :param tenant_id: Entra tenant ID
37
+ :param client_id: Azure application client ID
38
+ :param client_secret: Azure application client secret
39
+ :param update_tag: Update tag for tracking data freshness
40
+ """
41
+ credential = ClientSecretCredential(
42
+ tenant_id=tenant_id,
43
+ client_id=client_id,
44
+ client_secret=client_secret,
45
+ )
46
+ client = GraphServiceClient(
47
+ credential, scopes=["https://graph.microsoft.com/.default"]
48
+ )
49
+
50
+ # Fetch tenant and load it
51
+ tenant = await get_tenant(client)
52
+ transformed_tenant = transform_tenant(tenant, tenant_id)
53
+ load_tenant(neo4j_session, transformed_tenant, update_tag)
54
+
55
+
14
56
  @timeit
15
57
  def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
16
58
  """
@@ -37,6 +79,15 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
37
79
  }
38
80
 
39
81
  async def main() -> None:
82
+ # Load tenant first as a prerequisite for all resource syncs
83
+ await sync_tenant(
84
+ neo4j_session,
85
+ config.entra_tenant_id,
86
+ config.entra_client_id,
87
+ config.entra_client_secret,
88
+ config.update_tag,
89
+ )
90
+
40
91
  # Run user sync
41
92
  await sync_entra_users(
42
93
  neo4j_session,
@@ -47,6 +98,16 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
47
98
  common_job_parameters,
48
99
  )
49
100
 
101
+ # Run group sync
102
+ await sync_entra_groups(
103
+ neo4j_session,
104
+ config.entra_tenant_id,
105
+ config.entra_client_id,
106
+ config.entra_client_secret,
107
+ config.update_tag,
108
+ common_job_parameters,
109
+ )
110
+
50
111
  # Run OU sync
51
112
  await sync_entra_ous(
52
113
  neo4j_session,
@@ -57,5 +118,43 @@ def start_entra_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
57
118
  common_job_parameters,
58
119
  )
59
120
 
60
- # Execute both syncs in sequence
121
+ # Run application sync
122
+ await sync_entra_applications(
123
+ neo4j_session,
124
+ config.entra_tenant_id,
125
+ config.entra_client_id,
126
+ config.entra_client_secret,
127
+ config.update_tag,
128
+ common_job_parameters,
129
+ )
130
+
131
+ # Run service principals sync
132
+ await sync_service_principals(
133
+ neo4j_session,
134
+ config.entra_tenant_id,
135
+ config.entra_client_id,
136
+ config.entra_client_secret,
137
+ config.update_tag,
138
+ common_job_parameters,
139
+ )
140
+
141
+ # Run app role assignments sync
142
+ await sync_app_role_assignments(
143
+ neo4j_session,
144
+ config.entra_tenant_id,
145
+ config.entra_client_id,
146
+ config.entra_client_secret,
147
+ config.update_tag,
148
+ common_job_parameters,
149
+ )
150
+
151
+ # Run federation sync (after all resources are synced)
152
+ await sync_entra_federation(
153
+ neo4j_session,
154
+ config.update_tag,
155
+ config.entra_tenant_id,
156
+ common_job_parameters,
157
+ )
158
+
159
+ # Execute syncs in sequence
61
160
  asyncio.run(main())
@@ -0,0 +1,284 @@
1
+ import gc
2
+ from typing import Any
3
+ from typing import AsyncGenerator
4
+
5
+ import neo4j
6
+ from azure.identity import ClientSecretCredential
7
+ from msgraph import GraphServiceClient
8
+ from msgraph.generated.models.app_role_assignment_collection_response import (
9
+ AppRoleAssignmentCollectionResponse,
10
+ )
11
+
12
+ from cartography.client.core.tx import load
13
+ from cartography.client.core.tx import read_list_of_values_tx
14
+ from cartography.client.core.tx import read_single_value_tx
15
+ from cartography.graph.job import GraphJob
16
+ from cartography.intel.entra.applications import APP_ROLE_ASSIGNMENTS_PAGE_SIZE
17
+ from cartography.intel.entra.applications import logger
18
+ from cartography.models.entra.app_role_assignment import EntraAppRoleAssignmentSchema
19
+ from cartography.util import timeit
20
+
21
+
22
+ @timeit
23
+ async def get_app_role_assignments_for_app(
24
+ client: GraphServiceClient, neo4j_session: neo4j.Session, app_id: str
25
+ ) -> AsyncGenerator[dict[str, Any], None]:
26
+ """
27
+ Gets app role assignments for a single application by querying the graph for service principal ID.
28
+
29
+ :param client: GraphServiceClient
30
+ :param neo4j_session: Neo4j session for querying service principal
31
+ :param app_id: Application ID
32
+ :return: Generator of app role assignment data as dicts
33
+ """
34
+ logger.info(f"Fetching role assignments for application: {app_id}")
35
+
36
+ # Query the graph to get the service principal ID for this application
37
+ query = """
38
+ MATCH (sp:EntraServicePrincipal {app_id: $app_id})
39
+ RETURN sp.id as service_principal_id
40
+ """
41
+ service_principal_id = neo4j_session.execute_read(
42
+ read_single_value_tx, query, app_id=app_id
43
+ )
44
+
45
+ if not service_principal_id:
46
+ logger.warning(
47
+ f"No service principal found in graph for application {app_id}. Continuing."
48
+ )
49
+ return
50
+
51
+ # Get assignments for this service principal with pagination and limits
52
+ # Use maximum page size (999) to get more data per request
53
+ # Memory is managed through streaming and batching, not page size
54
+ request_config = client.service_principals.by_service_principal_id(
55
+ service_principal_id
56
+ ).app_role_assigned_to.AppRoleAssignedToRequestBuilderGetRequestConfiguration(
57
+ query_parameters=client.service_principals.by_service_principal_id(
58
+ service_principal_id
59
+ ).app_role_assigned_to.AppRoleAssignedToRequestBuilderGetQueryParameters(
60
+ top=APP_ROLE_ASSIGNMENTS_PAGE_SIZE # Maximum allowed by Microsoft Graph API
61
+ )
62
+ )
63
+
64
+ assignments_page: AppRoleAssignmentCollectionResponse | None = (
65
+ await client.service_principals.by_service_principal_id(
66
+ service_principal_id
67
+ ).app_role_assigned_to.get(request_configuration=request_config)
68
+ )
69
+
70
+ assignment_count = 0
71
+ page_count = 0
72
+
73
+ while assignments_page:
74
+ page_count += 1
75
+
76
+ if assignments_page.value:
77
+ page_valid_count = 0
78
+ page_skipped_count = 0
79
+
80
+ # Process assignments and immediately yield to avoid accumulation
81
+ for assignment in assignments_page.value:
82
+ # Only yield if we have valid data since it's possible (but unlikely) for assignment.id to be None
83
+ if assignment.principal_id:
84
+ assignment_count += 1
85
+ page_valid_count += 1
86
+ yield {
87
+ "id": assignment.id,
88
+ "app_role_id": assignment.app_role_id,
89
+ "created_date_time": assignment.created_date_time,
90
+ "principal_id": assignment.principal_id,
91
+ "principal_display_name": assignment.principal_display_name,
92
+ "principal_type": assignment.principal_type,
93
+ "resource_display_name": assignment.resource_display_name,
94
+ "resource_id": assignment.resource_id,
95
+ "application_app_id": app_id,
96
+ }
97
+ else:
98
+ page_skipped_count += 1
99
+
100
+ # Log page results with details about skipped objects
101
+ if page_skipped_count > 0:
102
+ logger.warning(
103
+ f"Page {page_count} for {app_id}: {page_valid_count} valid assignments, "
104
+ f"{page_skipped_count} skipped objects. Total valid: {assignment_count}"
105
+ )
106
+ else:
107
+ logger.debug(
108
+ f"Page {page_count} for {app_id}: {page_valid_count} assignments. "
109
+ f"Total: {assignment_count}"
110
+ )
111
+
112
+ # Force garbage collection after each page
113
+ gc.collect()
114
+
115
+ # Check if we have more pages to fetch
116
+ if not assignments_page.odata_next_link:
117
+ break
118
+
119
+ # Clear previous page before fetching next
120
+ assignments_page.value = None
121
+
122
+ # Fetch next page using the SAME request builder to preserve response typing
123
+ # Using the root service_principals builder here can return ServicePrincipal objects,
124
+ # which lack AppRoleAssignment fields like principal_id. Stay on the
125
+ # app_role_assigned_to builder to ensure AppRoleAssignmentCollectionResponse typing.
126
+ logger.debug(f"Fetching page {page_count + 1} of assignments for {app_id}")
127
+ next_page_url = assignments_page.odata_next_link
128
+ assignments_page = await (
129
+ client.service_principals.by_service_principal_id(service_principal_id)
130
+ .app_role_assigned_to.with_url(next_page_url)
131
+ .get()
132
+ )
133
+
134
+ logger.info(
135
+ f"Successfully retrieved {assignment_count} assignments for application {app_id} (pages: {page_count})"
136
+ )
137
+
138
+
139
+ def transform_app_role_assignments(
140
+ assignments: list[dict[str, Any]],
141
+ ) -> list[dict[str, Any]]:
142
+ """
143
+ Transform app role assignment data for graph loading.
144
+
145
+ :param assignments: Raw app role assignment data as dicts
146
+ :return: Transformed assignment data for graph loading
147
+ """
148
+ transformed = []
149
+ for assign in assignments:
150
+ transformed.append(
151
+ {
152
+ "id": assign["id"],
153
+ "app_role_id": (
154
+ str(assign["app_role_id"]) if assign["app_role_id"] else None
155
+ ),
156
+ "created_date_time": assign["created_date_time"],
157
+ "principal_id": (
158
+ str(assign["principal_id"]) if assign["principal_id"] else None
159
+ ),
160
+ "principal_display_name": assign["principal_display_name"],
161
+ "principal_type": assign["principal_type"],
162
+ "resource_display_name": assign["resource_display_name"],
163
+ "resource_id": (
164
+ str(assign["resource_id"]) if assign["resource_id"] else None
165
+ ),
166
+ "application_app_id": assign["application_app_id"],
167
+ }
168
+ )
169
+ return transformed
170
+
171
+
172
+ @timeit
173
+ def load_app_role_assignments(
174
+ neo4j_session: neo4j.Session,
175
+ assignments_data: list[dict[str, Any]],
176
+ update_tag: int,
177
+ tenant_id: str,
178
+ ) -> None:
179
+ """
180
+ Load Entra app role assignments to the graph.
181
+
182
+ :param neo4j_session: Neo4j session
183
+ :param assignments_data: Assignment data to load
184
+ :param update_tag: Update tag for tracking data freshness
185
+ :param tenant_id: Entra tenant ID
186
+ """
187
+ load(
188
+ neo4j_session,
189
+ EntraAppRoleAssignmentSchema(),
190
+ assignments_data,
191
+ lastupdated=update_tag,
192
+ TENANT_ID=tenant_id,
193
+ )
194
+
195
+
196
+ @timeit
197
+ def cleanup_app_role_assignments(
198
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
199
+ ) -> None:
200
+ """
201
+ Delete Entra app role assignments and their relationships from the graph if they were not updated in the last sync.
202
+
203
+ :param neo4j_session: Neo4j session
204
+ :param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
205
+ """
206
+ GraphJob.from_node_schema(
207
+ EntraAppRoleAssignmentSchema(), common_job_parameters
208
+ ).run(neo4j_session)
209
+
210
+
211
+ @timeit
212
+ async def sync_app_role_assignments(
213
+ neo4j_session: neo4j.Session,
214
+ tenant_id: str,
215
+ client_id: str,
216
+ client_secret: str,
217
+ update_tag: int,
218
+ common_job_parameters: dict[str, Any],
219
+ ) -> None:
220
+ """
221
+ Sync Entra app role assignments to the graph.
222
+
223
+ :param neo4j_session: Neo4j session
224
+ :param tenant_id: Entra tenant ID
225
+ :param client_id: Azure application client ID
226
+ :param client_secret: Azure application client secret
227
+ :param update_tag: Update tag for tracking data freshness
228
+ :param common_job_parameters: Common job parameters for cleanup
229
+ """
230
+ # Create credentials and client
231
+ credential = ClientSecretCredential(
232
+ tenant_id=tenant_id,
233
+ client_id=client_id,
234
+ client_secret=client_secret,
235
+ )
236
+
237
+ client = GraphServiceClient(
238
+ credential,
239
+ scopes=["https://graph.microsoft.com/.default"],
240
+ )
241
+ assignment_batch_size = 200 # Batch size for assignments
242
+ assignments_batch = []
243
+ total_assignment_count = 0
244
+
245
+ # Get app_ids from graph instead of streaming from API again
246
+ query = "MATCH (app:EntraApplication) RETURN app.app_id"
247
+ app_ids = neo4j_session.execute_read(read_list_of_values_tx, query)
248
+
249
+ for app_id in app_ids:
250
+ # Stream app role assignments (now using graph query for service principal ID)
251
+ async for assignment in get_app_role_assignments_for_app(
252
+ client, neo4j_session, app_id
253
+ ):
254
+ assignments_batch.append(assignment)
255
+ total_assignment_count += 1
256
+
257
+ # Transform and load assignments in batches
258
+ if len(assignments_batch) >= assignment_batch_size:
259
+ transformed_assignments = transform_app_role_assignments(
260
+ assignments_batch
261
+ )
262
+ load_app_role_assignments(
263
+ neo4j_session, transformed_assignments, update_tag, tenant_id
264
+ )
265
+ logger.debug(f"Loaded batch of {len(assignments_batch)} assignments")
266
+ assignments_batch.clear()
267
+ transformed_assignments.clear()
268
+
269
+ # Force garbage collection after batch load
270
+ gc.collect()
271
+
272
+ # Process remaining assignments
273
+ if assignments_batch:
274
+ transformed_assignments = transform_app_role_assignments(assignments_batch)
275
+ load_app_role_assignments(
276
+ neo4j_session, transformed_assignments, update_tag, tenant_id
277
+ )
278
+ assignments_batch.clear()
279
+ transformed_assignments.clear()
280
+
281
+ cleanup_app_role_assignments(neo4j_session, common_job_parameters)
282
+ logger.info(f"Completed syncing {total_assignment_count} app role assignments")
283
+ # Final garbage collection
284
+ gc.collect()
@@ -0,0 +1,182 @@
1
+ import gc
2
+ import logging
3
+ from typing import Any
4
+ from typing import AsyncGenerator
5
+ from typing import Generator
6
+
7
+ import neo4j
8
+ from azure.identity import ClientSecretCredential
9
+ from msgraph.generated.models.application import Application
10
+ from msgraph.graph_service_client import GraphServiceClient
11
+
12
+ from cartography.client.core.tx import load
13
+ from cartography.graph.job import GraphJob
14
+ from cartography.models.entra.application import EntraApplicationSchema
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Configurable constants for API pagination
20
+ # Microsoft Graph API recommends page sizes up to 999 for most resources
21
+ # Set to 999 by default, but can be adjusted if needed
22
+ #
23
+ # Adjust these values if:
24
+ # - You have performance issues (decrease values)
25
+ # - You want to minimize API calls (increase values up to 999)
26
+ # - You're hitting rate limits (decrease values)
27
+ APPLICATIONS_PAGE_SIZE = 999
28
+ APP_ROLE_ASSIGNMENTS_PAGE_SIZE = 999
29
+
30
+
31
+ @timeit
32
+ async def get_entra_applications(
33
+ client: GraphServiceClient,
34
+ ) -> AsyncGenerator[Application, None]:
35
+ """
36
+ Gets Entra applications using the Microsoft Graph API with a generator.
37
+
38
+ :param client: GraphServiceClient
39
+ :return: Generator of raw Application objects from Microsoft Graph
40
+ """
41
+ count = 0
42
+ # Get all applications with pagination
43
+ request_configuration = client.applications.ApplicationsRequestBuilderGetRequestConfiguration(
44
+ query_parameters=client.applications.ApplicationsRequestBuilderGetQueryParameters(
45
+ top=APPLICATIONS_PAGE_SIZE
46
+ )
47
+ )
48
+ page = await client.applications.get(request_configuration=request_configuration)
49
+
50
+ while page:
51
+ if page.value:
52
+ for app in page.value:
53
+ count += 1
54
+ yield app
55
+
56
+ if not page.odata_next_link:
57
+ break
58
+ page = await client.applications.with_url(page.odata_next_link).get()
59
+
60
+ logger.info(f"Retrieved {count} Entra applications total")
61
+
62
+
63
+ def transform_applications(
64
+ applications: list[Application],
65
+ ) -> Generator[dict[str, Any], None, None]:
66
+ """
67
+ Transform application data for graph loading using a generator.
68
+
69
+ :param applications: Raw Application objects from Microsoft Graph API
70
+ :return: Generator of transformed application data for graph loading
71
+ """
72
+ for app in applications:
73
+ yield {
74
+ "id": app.id,
75
+ "app_id": app.app_id,
76
+ "display_name": app.display_name,
77
+ "publisher_domain": app.publisher_domain,
78
+ "sign_in_audience": app.sign_in_audience,
79
+ }
80
+
81
+
82
+ @timeit
83
+ def load_applications(
84
+ neo4j_session: neo4j.Session,
85
+ applications_data: list[dict[str, Any]],
86
+ update_tag: int,
87
+ tenant_id: str,
88
+ ) -> None:
89
+ """
90
+ Load Entra applications to the graph.
91
+
92
+ :param neo4j_session: Neo4j session
93
+ :param applications_data: Application data to load
94
+ :param update_tag: Update tag for tracking data freshness
95
+ :param tenant_id: Entra tenant ID
96
+ """
97
+ load(
98
+ neo4j_session,
99
+ EntraApplicationSchema(),
100
+ applications_data,
101
+ lastupdated=update_tag,
102
+ TENANT_ID=tenant_id,
103
+ )
104
+
105
+
106
+ @timeit
107
+ def cleanup_applications(
108
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
109
+ ) -> None:
110
+ """
111
+ Delete Entra applications and their relationships from the graph if they were not updated in the last sync.
112
+
113
+ :param neo4j_session: Neo4j session
114
+ :param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
115
+ """
116
+ GraphJob.from_node_schema(EntraApplicationSchema(), common_job_parameters).run(
117
+ neo4j_session
118
+ )
119
+
120
+
121
+ @timeit
122
+ async def sync_entra_applications(
123
+ neo4j_session: neo4j.Session,
124
+ tenant_id: str,
125
+ client_id: str,
126
+ client_secret: str,
127
+ update_tag: int,
128
+ common_job_parameters: dict[str, Any],
129
+ ) -> None:
130
+ """
131
+ Sync Entra applications and their app role assignments to the graph.
132
+
133
+ :param neo4j_session: Neo4j session
134
+ :param tenant_id: Entra tenant ID
135
+ :param client_id: Azure application client ID
136
+ :param client_secret: Azure application client secret
137
+ :param update_tag: Update tag for tracking data freshness
138
+ :param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
139
+ """
140
+ # Create credentials and client
141
+ credential = ClientSecretCredential(
142
+ tenant_id=tenant_id,
143
+ client_id=client_id,
144
+ client_secret=client_secret,
145
+ )
146
+
147
+ client = GraphServiceClient(
148
+ credential,
149
+ scopes=["https://graph.microsoft.com/.default"],
150
+ )
151
+
152
+ # Step 1: Sync applications
153
+ app_batch_size = 10 # Batch size for applications
154
+ apps_batch = []
155
+ total_app_count = 0
156
+
157
+ # Stream and load applications
158
+ async for app in get_entra_applications(client):
159
+ total_app_count += 1
160
+ apps_batch.append(app)
161
+
162
+ # Transform and load applications in batches
163
+ if len(apps_batch) >= app_batch_size:
164
+ transformed_apps = list(transform_applications(apps_batch))
165
+ load_applications(neo4j_session, transformed_apps, update_tag, tenant_id)
166
+ logger.info(
167
+ f"Loaded batch of {len(apps_batch)} applications (total: {total_app_count})"
168
+ )
169
+ apps_batch.clear()
170
+ transformed_apps.clear()
171
+ gc.collect() # Force garbage collection
172
+
173
+ # Process remaining applications
174
+ if apps_batch:
175
+ transformed_apps = list(transform_applications(apps_batch))
176
+ load_applications(neo4j_session, transformed_apps, update_tag, tenant_id)
177
+ apps_batch.clear()
178
+ transformed_apps.clear()
179
+ cleanup_applications(neo4j_session, common_job_parameters)
180
+ logger.info(f"Completed syncing {total_app_count} applications")
181
+ # Final garbage collection
182
+ gc.collect()
File without changes
@@ -0,0 +1,77 @@
1
+ from typing import Any
2
+
3
+ import neo4j
4
+
5
+ from cartography.client.core.tx import load_matchlinks
6
+ from cartography.client.core.tx import read_list_of_dicts_tx
7
+ from cartography.graph.job import GraphJob
8
+ from cartography.models.entra.entra_user_to_aws_sso import (
9
+ EntraUserToAWSSSOUserMatchLink,
10
+ )
11
+ from cartography.util import timeit
12
+
13
+
14
+ @timeit
15
+ def sync_entra_to_aws_identity_center(
16
+ neo4j_session: neo4j.Session,
17
+ update_tag: int,
18
+ tenant_id: str,
19
+ common_job_parameters: dict[str, Any],
20
+ ) -> None:
21
+ query = """
22
+ MATCH (:EntraTenant{id: $TENANT_ID})-[:RESOURCE]->(e:EntraUser)
23
+ -[:HAS_APP_ROLE]->(ar:EntraAppRoleAssignment)
24
+ -[:ASSIGNED_TO]->(n:EntraApplication)
25
+ -[:SERVICE_PRINCIPAL]->(spn:EntraServicePrincipal)
26
+ -[:FEDERATES_TO]->(ic:AWSIdentityCenter)
27
+ MATCH (sso:AWSSSOUser{identity_store_id:ic.identity_store_id})
28
+ WHERE e.user_principal_name = sso.user_name
29
+ RETURN e.user_principal_name as entra_user_principal_name, sso.user_name as aws_user_name, sso.identity_store_id as identity_store_id
30
+ """
31
+ entrauser_to_awssso_users = neo4j_session.execute_read(
32
+ read_list_of_dicts_tx, query, TENANT_ID=tenant_id
33
+ )
34
+
35
+ # Load MatchLink relationships from Entra users to AWS SSO users
36
+ load_matchlinks(
37
+ neo4j_session,
38
+ EntraUserToAWSSSOUserMatchLink(),
39
+ entrauser_to_awssso_users,
40
+ lastupdated=update_tag,
41
+ _sub_resource_label="EntraTenant",
42
+ _sub_resource_id=tenant_id,
43
+ )
44
+
45
+ cleanup_entra_user_to_aws_sso_user_matchlinks(neo4j_session, common_job_parameters)
46
+
47
+
48
+ @timeit
49
+ async def sync_entra_federation(
50
+ neo4j_session: neo4j.Session,
51
+ update_tag: int,
52
+ tenant_id: str,
53
+ common_job_parameters: dict[str, Any],
54
+ ) -> None:
55
+ """
56
+ Sync Entra federation relationships to the graph.
57
+
58
+ :param neo4j_session: Neo4j session
59
+ :param update_tag: Update tag for tracking data freshness
60
+ :param tenant_id: Entra tenant ID
61
+ :param common_job_parameters: Common job parameters for cleanup
62
+ """
63
+ sync_entra_to_aws_identity_center(
64
+ neo4j_session, update_tag, tenant_id, common_job_parameters
65
+ )
66
+
67
+
68
+ @timeit
69
+ def cleanup_entra_user_to_aws_sso_user_matchlinks(
70
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
71
+ ) -> None:
72
+ GraphJob.from_matchlink(
73
+ EntraUserToAWSSSOUserMatchLink(),
74
+ "EntraTenant",
75
+ common_job_parameters["TENANT_ID"],
76
+ common_job_parameters["UPDATE_TAG"],
77
+ ).run(neo4j_session)