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
@@ -0,0 +1,198 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import AsyncGenerator
4
+ from typing import Generator
5
+
6
+ import neo4j
7
+ from azure.identity import ClientSecretCredential
8
+ from msgraph import GraphServiceClient
9
+ from msgraph.generated.models.directory_object import DirectoryObject
10
+ from msgraph.generated.models.group import Group
11
+
12
+ from cartography.client.core.tx import load
13
+ from cartography.graph.job import GraphJob
14
+ from cartography.models.entra.group import EntraGroupSchema
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @timeit
21
+ async def get_entra_groups(client: GraphServiceClient) -> AsyncGenerator[Group, None]:
22
+ """Get all groups from Microsoft Graph API with pagination using a generator."""
23
+ request_configuration = client.groups.GroupsRequestBuilderGetRequestConfiguration(
24
+ query_parameters=client.groups.GroupsRequestBuilderGetQueryParameters(top=999)
25
+ )
26
+ page = await client.groups.get(request_configuration=request_configuration)
27
+ while page:
28
+ if page.value:
29
+ for group in page.value:
30
+ yield group
31
+ if not page.odata_next_link:
32
+ break
33
+ page = await client.groups.with_url(page.odata_next_link).get()
34
+
35
+
36
+ @timeit
37
+ async def get_group_members(
38
+ client: GraphServiceClient, group_id: str
39
+ ) -> tuple[list[str], list[str]]:
40
+ """Get member user IDs and subgroup IDs for a given group."""
41
+ user_ids: list[str] = []
42
+ group_ids: list[str] = []
43
+ request_builder = client.groups.by_group_id(group_id).members
44
+ page = await request_builder.get()
45
+ while page:
46
+ if page.value:
47
+ for obj in page.value:
48
+ if isinstance(obj, DirectoryObject):
49
+ odata_type = getattr(obj, "odata_type", "")
50
+ if odata_type == "#microsoft.graph.user":
51
+ user_ids.append(obj.id)
52
+ elif odata_type == "#microsoft.graph.group":
53
+ group_ids.append(obj.id)
54
+ if not page.odata_next_link:
55
+ break
56
+ page = await request_builder.with_url(page.odata_next_link).get()
57
+ return user_ids, group_ids
58
+
59
+
60
+ @timeit
61
+ async def get_group_owners(client: GraphServiceClient, group_id: str) -> list[str]:
62
+ """Get owner user IDs for a given group."""
63
+ owner_ids: list[str] = []
64
+ request_builder = client.groups.by_group_id(group_id).owners
65
+ page = await request_builder.get()
66
+ while page:
67
+ if page.value:
68
+ for obj in page.value:
69
+ odata_type = getattr(obj, "odata_type", "")
70
+ if odata_type == "#microsoft.graph.user":
71
+ owner_ids.append(obj.id)
72
+ if not page.odata_next_link:
73
+ break
74
+ page = await request_builder.with_url(page.odata_next_link).get()
75
+ return owner_ids
76
+
77
+
78
+ def transform_groups(
79
+ groups: list[Group],
80
+ user_member_map: dict[str, list[str]],
81
+ group_member_map: dict[str, list[str]],
82
+ group_owner_map: dict[str, list[str]],
83
+ ) -> Generator[dict[str, Any], None, None]:
84
+ """Transform API responses into dictionaries for ingestion using a generator."""
85
+ for g in groups:
86
+ yield {
87
+ "id": g.id,
88
+ "display_name": g.display_name,
89
+ "description": g.description,
90
+ "mail": g.mail,
91
+ "mail_nickname": g.mail_nickname,
92
+ "mail_enabled": g.mail_enabled,
93
+ "security_enabled": g.security_enabled,
94
+ "group_types": g.group_types,
95
+ "visibility": g.visibility,
96
+ "is_assignable_to_role": g.is_assignable_to_role,
97
+ "created_date_time": g.created_date_time,
98
+ "deleted_date_time": g.deleted_date_time,
99
+ "member_ids": user_member_map.get(g.id, []),
100
+ "member_group_ids": group_member_map.get(g.id, []),
101
+ "owner_ids": group_owner_map.get(g.id, []),
102
+ }
103
+
104
+
105
+ @timeit
106
+ def load_groups(
107
+ neo4j_session: neo4j.Session,
108
+ groups: list[dict[str, Any]],
109
+ update_tag: int,
110
+ tenant_id: str,
111
+ ) -> None:
112
+ logger.info(f"Loading {len(groups)} Entra groups")
113
+ load(
114
+ neo4j_session,
115
+ EntraGroupSchema(),
116
+ groups,
117
+ lastupdated=update_tag,
118
+ TENANT_ID=tenant_id,
119
+ )
120
+
121
+
122
+ @timeit
123
+ def cleanup_groups(
124
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
125
+ ) -> None:
126
+ GraphJob.from_node_schema(EntraGroupSchema(), common_job_parameters).run(
127
+ neo4j_session
128
+ )
129
+
130
+
131
+ @timeit
132
+ async def sync_entra_groups(
133
+ neo4j_session: neo4j.Session,
134
+ tenant_id: str,
135
+ client_id: str,
136
+ client_secret: str,
137
+ update_tag: int,
138
+ common_job_parameters: dict[str, Any],
139
+ ) -> None:
140
+ """Sync Entra groups."""
141
+ credential = ClientSecretCredential(
142
+ tenant_id=tenant_id, client_id=client_id, client_secret=client_secret
143
+ )
144
+ client = GraphServiceClient(
145
+ credential, scopes=["https://graph.microsoft.com/.default"]
146
+ )
147
+
148
+ # Collect groups in batches to avoid loading all at once
149
+ groups_batch = []
150
+ batch_size = 100 # Process groups in batches
151
+
152
+ user_member_map: dict[str, list[str]] = {}
153
+ group_member_map: dict[str, list[str]] = {}
154
+ group_owner_map: dict[str, list[str]] = {}
155
+
156
+ # First pass: collect groups and their owners/members
157
+ async for group in get_entra_groups(client):
158
+ groups_batch.append(group)
159
+
160
+ # Fetch owners and members for this group
161
+ owners = await get_group_owners(client, group.id)
162
+ group_owner_map[group.id] = owners
163
+
164
+ try:
165
+ users, subgroups = await get_group_members(client, group.id)
166
+ user_member_map[group.id] = users
167
+ group_member_map[group.id] = subgroups
168
+ except Exception as e:
169
+ logger.error(f"Failed to fetch members for group {group.id}: {e}")
170
+ user_member_map[group.id] = []
171
+ group_member_map[group.id] = []
172
+
173
+ # Process batch when it reaches the size limit
174
+ if len(groups_batch) >= batch_size:
175
+ transformed_groups = list(
176
+ transform_groups(
177
+ groups_batch, user_member_map, group_member_map, group_owner_map
178
+ )
179
+ )
180
+ load_groups(neo4j_session, transformed_groups, update_tag, tenant_id)
181
+
182
+ # Clear the batch and maps for processed groups
183
+ for g in groups_batch:
184
+ user_member_map.pop(g.id, None)
185
+ group_member_map.pop(g.id, None)
186
+ group_owner_map.pop(g.id, None)
187
+ groups_batch.clear()
188
+
189
+ # Process any remaining groups
190
+ if groups_batch:
191
+ transformed_groups = list(
192
+ transform_groups(
193
+ groups_batch, user_member_map, group_member_map, group_owner_map
194
+ )
195
+ )
196
+ load_groups(neo4j_session, transformed_groups, update_tag, tenant_id)
197
+
198
+ cleanup_groups(neo4j_session, common_job_parameters)
@@ -1,6 +1,8 @@
1
1
  # cartography/intel/entra/ou.py
2
2
  import logging
3
3
  from typing import Any
4
+ from typing import AsyncGenerator
5
+ from typing import Generator
4
6
 
5
7
  import neo4j
6
8
  from azure.identity import ClientSecretCredential
@@ -9,7 +11,6 @@ from msgraph.generated.models.administrative_unit import AdministrativeUnit
9
11
 
10
12
  from cartography.client.core.tx import load
11
13
  from cartography.graph.job import GraphJob
12
- from cartography.intel.entra.users import load_tenant
13
14
  from cartography.models.entra.ou import EntraOUSchema
14
15
  from cartography.util import timeit
15
16
 
@@ -17,30 +18,44 @@ logger = logging.getLogger(__name__)
17
18
 
18
19
 
19
20
  @timeit
20
- async def get_entra_ous(client: GraphServiceClient) -> list[AdministrativeUnit]:
21
+ async def get_entra_ous(
22
+ client: GraphServiceClient,
23
+ ) -> AsyncGenerator[AdministrativeUnit, None]:
21
24
  """
22
- Get all OUs from Microsoft Graph API with pagination support
25
+ Get all OUs from Microsoft Graph API with pagination support using a generator
23
26
  """
24
- all_units: list[AdministrativeUnit] = []
25
- request = client.directory.administrative_units.request()
26
-
27
- while request:
28
- response = await request.get()
29
- all_units.extend(response.value)
30
- request = response.odata_next_link if response.odata_next_link else None
31
-
32
- return all_units
27
+ # Initialize first page request
28
+ current_request = client.directory.administrative_units
29
+
30
+ while current_request:
31
+ try:
32
+ response = await current_request.get()
33
+ if response and response.value:
34
+ for unit in response.value:
35
+ yield unit
36
+
37
+ # Handle next page using OData link
38
+ if response.odata_next_link:
39
+ current_request = client.directory.administrative_units.with_url(
40
+ response.odata_next_link
41
+ )
42
+ else:
43
+ current_request = None
44
+ else:
45
+ current_request = None
46
+ except Exception as e:
47
+ logger.error(f"Failed to retrieve administrative units: {str(e)}")
48
+ current_request = None
33
49
 
34
50
 
35
51
  def transform_ous(
36
52
  units: list[AdministrativeUnit], tenant_id: str
37
- ) -> list[dict[str, Any]]:
53
+ ) -> Generator[dict[str, Any], None, None]:
38
54
  """
39
- Transform the API response into the format expected by our schema
55
+ Transform the API response into the format expected by our schema using a generator
40
56
  """
41
- result: list[dict[str, Any]] = []
42
57
  for unit in units:
43
- transformed_unit = {
58
+ yield {
44
59
  "id": unit.id,
45
60
  "display_name": unit.display_name,
46
61
  "description": unit.description,
@@ -50,8 +65,6 @@ def transform_ous(
50
65
  "deleted_date_time": unit.deleted_date_time,
51
66
  "tenant_id": tenant_id,
52
67
  }
53
- result.append(transformed_unit)
54
- return result
55
68
 
56
69
 
57
70
  @timeit
@@ -100,13 +113,24 @@ async def sync_entra_ous(
100
113
  credential, scopes=["https://graph.microsoft.com/.default"]
101
114
  )
102
115
 
103
- # Get OUs
104
- units = await get_entra_ous(client)
105
- transformed_units = transform_ous(units, tenant_id)
116
+ # Process OUs in batches
117
+ batch_size = 100 # OUs are typically fewer than users/groups
118
+ units_batch = []
119
+
120
+ async for unit in get_entra_ous(client):
121
+ units_batch.append(unit)
122
+
123
+ if len(units_batch) >= batch_size:
124
+ transformed_units = list(transform_ous(units_batch, tenant_id))
125
+ load_ous(
126
+ neo4j_session, transformed_units, update_tag, common_job_parameters
127
+ )
128
+ units_batch.clear()
106
129
 
107
- # Load data
108
- load_tenant(neo4j_session, {"id": tenant_id}, update_tag)
109
- load_ous(neo4j_session, transformed_units, update_tag, common_job_parameters)
130
+ # Process any remaining OUs
131
+ if units_batch:
132
+ transformed_units = list(transform_ous(units_batch, tenant_id))
133
+ load_ous(neo4j_session, transformed_units, update_tag, common_job_parameters)
110
134
 
111
135
  # Cleanup stale data
112
136
  cleanup_ous(neo4j_session, common_job_parameters)
@@ -0,0 +1,217 @@
1
+ import logging
2
+ import re
3
+ from typing import Any
4
+ from typing import AsyncGenerator
5
+
6
+ import neo4j
7
+ from azure.identity import ClientSecretCredential
8
+ from msgraph import GraphServiceClient
9
+ from msgraph.generated.models.service_principal import ServicePrincipal
10
+
11
+ from cartography.client.core.tx import load
12
+ from cartography.graph.job import GraphJob
13
+ from cartography.models.entra.service_principal import EntraServicePrincipalSchema
14
+ from cartography.util import timeit
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ SERVICE_PRINCIPALS_PAGE_SIZE = 999
19
+
20
+
21
+ @timeit
22
+ async def get_entra_service_principals(
23
+ client: GraphServiceClient,
24
+ ) -> AsyncGenerator[ServicePrincipal, None]:
25
+ """
26
+ Gets Entra service principals using the Microsoft Graph API with a generator.
27
+
28
+ :param client: GraphServiceClient
29
+ :return: Generator of raw ServicePrincipal objects from Microsoft Graph
30
+ """
31
+ count = 0
32
+ # Get all service principals with pagination
33
+ request_configuration = client.service_principals.ServicePrincipalsRequestBuilderGetRequestConfiguration(
34
+ query_parameters=client.service_principals.ServicePrincipalsRequestBuilderGetQueryParameters(
35
+ top=SERVICE_PRINCIPALS_PAGE_SIZE
36
+ )
37
+ )
38
+ page = await client.service_principals.get(
39
+ request_configuration=request_configuration
40
+ )
41
+
42
+ while page:
43
+ if page.value:
44
+ for spn in page.value:
45
+ count += 1
46
+ yield spn
47
+
48
+ if not page.odata_next_link:
49
+ break
50
+ page = await client.service_principals.with_url(page.odata_next_link).get()
51
+
52
+ logger.info(f"Retrieved {count} Entra service principals total")
53
+
54
+
55
+ async def get_service_principal_by_app_id(
56
+ client: GraphServiceClient, app_id: str
57
+ ) -> ServicePrincipal | None:
58
+ """
59
+ Gets a service principal by app_id using the Microsoft Graph API.
60
+ This function is extracted from the original app_role_assignments logic.
61
+
62
+ :param client: GraphServiceClient
63
+ :param app_id: Application ID to search for
64
+ :return: ServicePrincipal object or None if not found
65
+ """
66
+ service_principals_page = await client.service_principals.get(
67
+ request_configuration=client.service_principals.ServicePrincipalsRequestBuilderGetRequestConfiguration(
68
+ query_parameters=client.service_principals.ServicePrincipalsRequestBuilderGetQueryParameters(
69
+ filter=f"appId eq '{app_id}'"
70
+ )
71
+ )
72
+ )
73
+
74
+ if not service_principals_page or not service_principals_page.value:
75
+ logger.warning(
76
+ f"No service principal found for application {app_id}. Continuing."
77
+ )
78
+ return None
79
+
80
+ return service_principals_page.value[0]
81
+
82
+
83
+ def transform_service_principals(
84
+ service_principals: list[ServicePrincipal],
85
+ ) -> list[dict[str, Any]]:
86
+ result = []
87
+ for spn in service_principals:
88
+ aws_identity_center_instance_id = None
89
+ match = re.search(r"d-[a-z0-9]{10}", spn.login_url or "")
90
+ aws_identity_center_instance_id = match.group(0) if match else None
91
+ transformed = {
92
+ "id": spn.id,
93
+ "app_id": spn.app_id,
94
+ "account_enabled": spn.account_enabled,
95
+ # uuid.UUID to string
96
+ "app_owner_organization_id": (
97
+ str(spn.app_owner_organization_id)
98
+ if spn.app_owner_organization_id
99
+ else None
100
+ ),
101
+ "aws_identity_center_instance_id": aws_identity_center_instance_id,
102
+ "display_name": spn.display_name,
103
+ "login_url": spn.login_url,
104
+ "preferred_single_sign_on_mode": spn.preferred_single_sign_on_mode,
105
+ "preferred_token_signing_key_thumbprint": spn.preferred_token_signing_key_thumbprint,
106
+ "reply_urls": spn.reply_urls,
107
+ "service_principal_type": spn.service_principal_type,
108
+ "sign_in_audience": spn.sign_in_audience,
109
+ "tags": spn.tags,
110
+ # uuid.UUID to string
111
+ "token_encryption_key_id": (
112
+ str(spn.token_encryption_key_id)
113
+ if spn.token_encryption_key_id
114
+ else None
115
+ ),
116
+ }
117
+ result.append(transformed)
118
+ return result
119
+
120
+
121
+ @timeit
122
+ def load_service_principals(
123
+ neo4j_session: neo4j.Session,
124
+ service_principal_data: list[dict[str, Any]],
125
+ update_tag: int,
126
+ tenant_id: str,
127
+ ) -> None:
128
+ load(
129
+ neo4j_session,
130
+ EntraServicePrincipalSchema(),
131
+ service_principal_data,
132
+ lastupdated=update_tag,
133
+ TENANT_ID=tenant_id,
134
+ )
135
+
136
+
137
+ @timeit
138
+ def cleanup_service_principals(
139
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
140
+ ) -> None:
141
+ """
142
+ Delete Entra service principals from the graph if they were not updated in the last sync.
143
+
144
+ :param neo4j_session: Neo4j session
145
+ :param common_job_parameters: Common job parameters containing UPDATE_TAG and TENANT_ID
146
+ """
147
+ GraphJob.from_node_schema(EntraServicePrincipalSchema(), common_job_parameters).run(
148
+ neo4j_session
149
+ )
150
+
151
+
152
+ @timeit
153
+ async def sync_service_principals(
154
+ neo4j_session: neo4j.Session,
155
+ tenant_id: str,
156
+ client_id: str,
157
+ client_secret: str,
158
+ update_tag: int,
159
+ common_job_parameters: dict[str, Any],
160
+ ) -> None:
161
+ """
162
+ Sync Entra service principals to the graph.
163
+
164
+ :param neo4j_session: Neo4j session
165
+ :param tenant_id: Entra tenant ID
166
+ :param client_id: Azure application client ID
167
+ :param client_secret: Azure application client secret
168
+ :param update_tag: Update tag for tracking data freshness
169
+ :param common_job_parameters: Common job parameters for cleanup
170
+ """
171
+ # Create credentials and client
172
+ credential = ClientSecretCredential(
173
+ tenant_id=tenant_id,
174
+ client_id=client_id,
175
+ client_secret=client_secret,
176
+ )
177
+
178
+ client = GraphServiceClient(
179
+ credential,
180
+ scopes=["https://graph.microsoft.com/.default"],
181
+ )
182
+ service_principals_batch = []
183
+ batch_size = 50 # Batch size for service principals
184
+ total_count = 0
185
+
186
+ # Stream service principals and process in batches
187
+ async for spn in get_entra_service_principals(client):
188
+ service_principals_batch.append(spn)
189
+ total_count += 1
190
+
191
+ # Transform and load service principals in batches
192
+ if len(service_principals_batch) >= batch_size:
193
+ transformed_service_principals = transform_service_principals(
194
+ service_principals_batch
195
+ )
196
+ load_service_principals(
197
+ neo4j_session, transformed_service_principals, update_tag, tenant_id
198
+ )
199
+ logger.info(
200
+ f"Loaded batch of {len(service_principals_batch)} service principals (total: {total_count})"
201
+ )
202
+ service_principals_batch.clear()
203
+ transformed_service_principals.clear()
204
+
205
+ # Process remaining service principals
206
+ if service_principals_batch:
207
+ transformed_service_principals = transform_service_principals(
208
+ service_principals_batch
209
+ )
210
+ load_service_principals(
211
+ neo4j_session, transformed_service_principals, update_tag, tenant_id
212
+ )
213
+ service_principals_batch.clear()
214
+ transformed_service_principals.clear()
215
+
216
+ logger.info(f"Completed loading {total_count} service principals")
217
+ cleanup_service_principals(neo4j_session, common_job_parameters)