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,568 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import neo4j
5
+ from googleapiclient.discovery import Resource
6
+ from googleapiclient.errors import HttpError
7
+
8
+ from cartography.client.core.tx import load
9
+ from cartography.client.core.tx import load_matchlinks
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.models.googleworkspace.group import GoogleWorkspaceGroupSchema
12
+ from cartography.models.googleworkspace.group import (
13
+ GoogleWorkspaceGroupToGroupInheritedMemberRel,
14
+ )
15
+ from cartography.models.googleworkspace.group import (
16
+ GoogleWorkspaceGroupToGroupInheritedOwnerRel,
17
+ )
18
+ from cartography.models.googleworkspace.group import (
19
+ GoogleWorkspaceGroupToGroupMemberRel,
20
+ )
21
+ from cartography.models.googleworkspace.group import GoogleWorkspaceGroupToGroupOwnerRel
22
+ from cartography.models.googleworkspace.group import (
23
+ GoogleWorkspaceUserToGroupInheritedMemberRel,
24
+ )
25
+ from cartography.models.googleworkspace.group import (
26
+ GoogleWorkspaceUserToGroupInheritedOwnerRel,
27
+ )
28
+ from cartography.util import timeit
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ GOOGLE_API_NUM_RETRIES = 5
33
+
34
+
35
+ @timeit
36
+ def get_all_groups(cloudidentity: Resource, customer_id: str) -> list[dict[str, Any]]:
37
+ """
38
+ Return list of Google Groups in your organization using Cloud Identity API.
39
+ Returns empty list if we are unable to enumerate the groups for any reasons.
40
+
41
+ :param cloudidentity: Google Cloud Identity resource object from googleapiclient.discovery.build
42
+ See https://googleapis.github.io/google-api-python-client/docs/epy/googleapiclient.discovery-module.html#build.
43
+ :param customer_id: The customer ID for the organization
44
+ :return: list of Google groups in domain (transformed to match expected schema)
45
+ """
46
+ request = cloudidentity.groups().list(
47
+ parent=f"customers/{customer_id}",
48
+ pageSize=100,
49
+ view="FULL",
50
+ )
51
+ response_objects = []
52
+ while request is not None:
53
+ try:
54
+ resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
55
+ response_objects.extend(resp.get("groups", []))
56
+ request = cloudidentity.groups().list_next(request, resp)
57
+ except HttpError as e:
58
+ if (
59
+ e.resp.status == 403
60
+ and "Request had insufficient authentication scopes" in str(e)
61
+ ):
62
+ logger.error(
63
+ "Missing required Google Workspace scopes. If using the gcloud CLI, "
64
+ "run: gcloud auth application-default login --scopes="
65
+ "https://www.googleapis.com/auth/admin.directory.customer.readonly,"
66
+ "https://www.googleapis.com/auth/admin.directory.user.readonly,"
67
+ "https://www.googleapis.com/auth/admin.directory.user.security,"
68
+ "https://www.googleapis.com/auth/cloud-identity.devices.readonly,"
69
+ "https://www.googleapis.com/auth/cloud-identity.groups.readonly,"
70
+ "https://www.googleapis.com/auth/cloud-platform"
71
+ )
72
+ raise
73
+ return response_objects
74
+
75
+
76
+ @timeit
77
+ def get_members_for_groups(
78
+ cloudidentity: Resource, group_names: list[str]
79
+ ) -> dict[str, list[dict[str, Any]]]:
80
+ """Get all members for given groups
81
+
82
+ Args:
83
+ cloudidentity (Resource): google's apiclient discovery resource object. From googleapiclient.discovery.build
84
+ See https://googleapis.github.io/google-api-python-client/docs/epy/googleapiclient.discovery-module.html#build.
85
+ groups_email (list[str]): List of group email addresses to get members for
86
+
87
+
88
+ :return: list of dictionaries representing Users or Groups grouped by group email
89
+ """
90
+ results: dict[str, list[dict]] = {}
91
+ for group_name in group_names:
92
+ request = (
93
+ cloudidentity.groups()
94
+ .memberships()
95
+ .list(
96
+ parent=group_name,
97
+ pageSize=100,
98
+ view="FULL",
99
+ )
100
+ )
101
+ members: list[dict] = []
102
+ while request is not None:
103
+ resp = request.execute(num_retries=GOOGLE_API_NUM_RETRIES)
104
+ members = members + resp.get("memberships", [])
105
+ request = cloudidentity.groups().memberships().list_next(request, resp)
106
+ results[group_name] = members
107
+ return results
108
+
109
+
110
+ @timeit
111
+ def get_user_inherited_member_relationships(
112
+ neo4j_session: neo4j.Session,
113
+ customer_id: str,
114
+ ) -> list[dict[str, Any]]:
115
+ """
116
+ Query Neo4j to find User -> INHERITED_MEMBER_OF -> Group relationships.
117
+
118
+ Finds users who are indirectly members of groups through the group hierarchy:
119
+ User -[:MEMBER_OF]-> SubGroup -[:MEMBER_OF*1..]-> ParentGroup
120
+
121
+ Returns list of relationship data for load_matchlinks():
122
+ [
123
+ {"user_id": "user-1", "group_id": "group-parent"},
124
+ ...
125
+ ]
126
+ """
127
+ query = """
128
+ MATCH (t:GoogleWorkspaceTenant {id: $CUSTOMER_ID})-[:RESOURCE]->(u:GoogleWorkspaceUser),
129
+ (u)-[:MEMBER_OF]->(g1:GoogleWorkspaceGroup)-[:MEMBER_OF*1..]->(g2:GoogleWorkspaceGroup)
130
+ RETURN DISTINCT u.id AS user_id, g2.id AS group_id
131
+ """
132
+ result = neo4j_session.run(query, CUSTOMER_ID=customer_id)
133
+ relationships = [dict(record) for record in result]
134
+ logger.debug(
135
+ "Found %d User INHERITED_MEMBER_OF Group relationships",
136
+ len(relationships),
137
+ )
138
+ return relationships
139
+
140
+
141
+ @timeit
142
+ def get_user_inherited_owner_relationships(
143
+ neo4j_session: neo4j.Session,
144
+ customer_id: str,
145
+ ) -> list[dict[str, Any]]:
146
+ """
147
+ Query Neo4j to find User -> INHERITED_OWNER_OF -> Group relationships.
148
+
149
+ Finds users who are indirectly owners of groups through the group hierarchy:
150
+ User -[:OWNER_OF]-> SubGroup -[:MEMBER_OF*1..]-> ParentGroup
151
+
152
+ Returns list of relationship data for load_matchlinks():
153
+ [
154
+ {"user_id": "user-1", "group_id": "group-parent"},
155
+ ...
156
+ ]
157
+ """
158
+ query = """
159
+ MATCH (t:GoogleWorkspaceTenant {id: $CUSTOMER_ID})-[:RESOURCE]->(u:GoogleWorkspaceUser),
160
+ (u)-[:OWNER_OF]->(g1:GoogleWorkspaceGroup)-[:MEMBER_OF*1..]->(g2:GoogleWorkspaceGroup)
161
+ RETURN DISTINCT u.id AS user_id, g2.id AS group_id
162
+ """
163
+ result = neo4j_session.run(query, CUSTOMER_ID=customer_id)
164
+ relationships = [dict(record) for record in result]
165
+ logger.debug(
166
+ "Found %d User INHERITED_OWNER_OF Group relationships",
167
+ len(relationships),
168
+ )
169
+ return relationships
170
+
171
+
172
+ @timeit
173
+ def get_group_inherited_member_relationships(
174
+ neo4j_session: neo4j.Session,
175
+ customer_id: str,
176
+ ) -> list[dict[str, Any]]:
177
+ """
178
+ Query Neo4j to find Group -> INHERITED_MEMBER_OF -> Group relationships.
179
+
180
+ Finds groups that are indirectly members of other groups through hierarchy:
181
+ SubGroup1 -[:MEMBER_OF]-> SubGroup2 -[:MEMBER_OF*1..]-> ParentGroup
182
+
183
+ Returns list of relationship data for load_matchlinks():
184
+ [
185
+ {"source_group_id": "group-sub1", "target_group_id": "group-parent"},
186
+ ...
187
+ ]
188
+ """
189
+ query = """
190
+ MATCH (t:GoogleWorkspaceTenant {id: $CUSTOMER_ID})-[:RESOURCE]->(g1:GoogleWorkspaceGroup),
191
+ (g1)-[:MEMBER_OF]->(g2:GoogleWorkspaceGroup)-[:MEMBER_OF*1..]->(g3:GoogleWorkspaceGroup)
192
+ RETURN DISTINCT g1.id AS source_group_id, g3.id AS target_group_id
193
+ """
194
+ result = neo4j_session.run(query, CUSTOMER_ID=customer_id)
195
+ relationships = [dict(record) for record in result]
196
+ logger.debug(
197
+ "Found %d Group INHERITED_MEMBER_OF Group relationships",
198
+ len(relationships),
199
+ )
200
+ return relationships
201
+
202
+
203
+ @timeit
204
+ def get_group_inherited_owner_relationships(
205
+ neo4j_session: neo4j.Session,
206
+ customer_id: str,
207
+ ) -> list[dict[str, Any]]:
208
+ """
209
+ Query Neo4j to find Group -> INHERITED_OWNER_OF -> Group relationships.
210
+
211
+ Finds groups that are indirectly owners of other groups through hierarchy:
212
+ SubGroup1 -[:OWNER_OF]-> SubGroup2 -[:MEMBER_OF*1..]-> ParentGroup
213
+
214
+ Returns list of relationship data for load_matchlinks():
215
+ [
216
+ {"source_group_id": "group-sub1", "target_group_id": "group-parent"},
217
+ ...
218
+ ]
219
+ """
220
+ query = """
221
+ MATCH (t:GoogleWorkspaceTenant {id: $CUSTOMER_ID})-[:RESOURCE]->(g1:GoogleWorkspaceGroup),
222
+ (g1)-[:OWNER_OF]->(g2:GoogleWorkspaceGroup)-[:MEMBER_OF*1..]->(g3:GoogleWorkspaceGroup)
223
+ RETURN DISTINCT g1.id AS source_group_id, g3.id AS target_group_id
224
+ """
225
+ result = neo4j_session.run(query, CUSTOMER_ID=customer_id)
226
+ relationships = [dict(record) for record in result]
227
+ logger.debug(
228
+ "Found %d Group INHERITED_OWNER_OF Group relationships",
229
+ len(relationships),
230
+ )
231
+ return relationships
232
+
233
+
234
+ @timeit
235
+ def transform_groups(
236
+ groups: list[dict], group_memberships: dict[str, list[dict[str, Any]]]
237
+ ) -> tuple[list[dict], list[dict], list[dict]]:
238
+ """Strips list of API response objects to return list of group objects only and lists of subgroup relationships
239
+
240
+ :param groups: Raw groups from Google API
241
+ :param group_memberships: Group memberships data
242
+ :return: tuple of (groups, group_member_relationships, group_owner_relationships)
243
+ """
244
+ transformed_groups: list[dict] = []
245
+ group_member_relationships: list[dict] = []
246
+ group_owner_relationships: list[dict] = []
247
+
248
+ for group in groups:
249
+ transformed_group = group.copy()
250
+ # GroupKey.id can contains email address or not (for imported groups)
251
+ if (
252
+ "groupKey" in group
253
+ and "id" in group["groupKey"]
254
+ and "@" in group["groupKey"]["id"]
255
+ ):
256
+ transformed_group["email"] = group["groupKey"]["id"]
257
+ # Serialize labels
258
+ formated_labels: list[str] = []
259
+ for key, value in group.get("labels", {}).items():
260
+ if value:
261
+ formated_labels.append(f"{key}:{value}")
262
+ else:
263
+ formated_labels.append(key)
264
+ transformed_group["labels"] = formated_labels
265
+
266
+ transformed_group["member_ids"] = []
267
+ transformed_group["owner_ids"] = []
268
+
269
+ for member in group_memberships.get(group["name"], []):
270
+ is_owner: bool = False
271
+ member_key_id = member.get("preferredMemberKey", {}).get("id")
272
+ if member_key_id is None:
273
+ continue
274
+
275
+ for role_obj in member.get("roles", []):
276
+ if role_obj.get("name") == "OWNER":
277
+ is_owner = True
278
+ break
279
+
280
+ if member["type"] == "GROUP":
281
+ # Create group-to-group relationships
282
+ relationship_data = {
283
+ "parent_group_id": group["name"],
284
+ "subgroup_email": member_key_id,
285
+ }
286
+
287
+ if is_owner:
288
+ group_owner_relationships.append(relationship_data)
289
+ else:
290
+ group_member_relationships.append(relationship_data)
291
+ continue
292
+
293
+ # Handle user memberships
294
+ if is_owner:
295
+ transformed_group["owner_ids"].append(member_key_id)
296
+ transformed_group["member_ids"].append(member_key_id)
297
+
298
+ transformed_groups.append(transformed_group)
299
+
300
+ return transformed_groups, group_member_relationships, group_owner_relationships
301
+
302
+
303
+ @timeit
304
+ def load_googleworkspace_groups(
305
+ neo4j_session: neo4j.Session,
306
+ groups: list[dict],
307
+ customer_id: str,
308
+ googleworkspace_update_tag: int,
309
+ ) -> None:
310
+ """
311
+ Load Google Workspace groups using the modern data model
312
+ """
313
+ logger.info(
314
+ "Ingesting %d Google Workspace groups for customer %s", len(groups), customer_id
315
+ )
316
+
317
+ # Load groups with relationship to tenant
318
+ load(
319
+ neo4j_session,
320
+ GoogleWorkspaceGroupSchema(),
321
+ groups,
322
+ lastupdated=googleworkspace_update_tag,
323
+ CUSTOMER_ID=customer_id,
324
+ )
325
+
326
+
327
+ @timeit
328
+ def load_googleworkspace_group_to_group_relationships(
329
+ neo4j_session: neo4j.Session,
330
+ group_member_relationships: list[dict],
331
+ group_owner_relationships: list[dict],
332
+ customer_id: str,
333
+ googleworkspace_update_tag: int,
334
+ ) -> None:
335
+ """
336
+ Load Google Workspace group-to-group relationships using MatchLinks
337
+ """
338
+ logger.info(
339
+ "Ingesting %d group member relationships", len(group_member_relationships)
340
+ )
341
+ logger.info(
342
+ "Ingesting %d group owner relationships", len(group_owner_relationships)
343
+ )
344
+
345
+ # Load group member relationships (Group -> Group MEMBER)
346
+ if group_member_relationships:
347
+ load_matchlinks(
348
+ neo4j_session,
349
+ GoogleWorkspaceGroupToGroupMemberRel(),
350
+ group_member_relationships,
351
+ lastupdated=googleworkspace_update_tag,
352
+ _sub_resource_label="GoogleWorkspaceTenant",
353
+ _sub_resource_id=customer_id,
354
+ )
355
+
356
+ # Load group owner relationships (Group -> Group OWNER)
357
+ if group_owner_relationships:
358
+ load_matchlinks(
359
+ neo4j_session,
360
+ GoogleWorkspaceGroupToGroupOwnerRel(),
361
+ group_owner_relationships,
362
+ lastupdated=googleworkspace_update_tag,
363
+ _sub_resource_label="GoogleWorkspaceTenant",
364
+ _sub_resource_id=customer_id,
365
+ )
366
+
367
+
368
+ @timeit
369
+ def load_googleworkspace_inherited_relationships(
370
+ neo4j_session: neo4j.Session,
371
+ user_member_rels: list[dict],
372
+ user_owner_rels: list[dict],
373
+ group_member_rels: list[dict],
374
+ group_owner_rels: list[dict],
375
+ customer_id: str,
376
+ googleworkspace_update_tag: int,
377
+ ) -> None:
378
+ """
379
+ Load inherited Google Workspace group relationships.
380
+
381
+ These relationships represent indirect memberships and ownerships
382
+ through the group hierarchy, computed via graph traversal.
383
+ """
384
+ logger.info("Computing inherited group relationships for customer %s", customer_id)
385
+
386
+ if user_member_rels:
387
+ load_matchlinks(
388
+ neo4j_session,
389
+ GoogleWorkspaceUserToGroupInheritedMemberRel(),
390
+ user_member_rels,
391
+ lastupdated=googleworkspace_update_tag,
392
+ _sub_resource_label="GoogleWorkspaceTenant",
393
+ _sub_resource_id=customer_id,
394
+ )
395
+
396
+ if user_owner_rels:
397
+ load_matchlinks(
398
+ neo4j_session,
399
+ GoogleWorkspaceUserToGroupInheritedOwnerRel(),
400
+ user_owner_rels,
401
+ lastupdated=googleworkspace_update_tag,
402
+ _sub_resource_label="GoogleWorkspaceTenant",
403
+ _sub_resource_id=customer_id,
404
+ )
405
+
406
+ if group_member_rels:
407
+ load_matchlinks(
408
+ neo4j_session,
409
+ GoogleWorkspaceGroupToGroupInheritedMemberRel(),
410
+ group_member_rels,
411
+ lastupdated=googleworkspace_update_tag,
412
+ _sub_resource_label="GoogleWorkspaceTenant",
413
+ _sub_resource_id=customer_id,
414
+ )
415
+
416
+ if group_owner_rels:
417
+ load_matchlinks(
418
+ neo4j_session,
419
+ GoogleWorkspaceGroupToGroupInheritedOwnerRel(),
420
+ group_owner_rels,
421
+ lastupdated=googleworkspace_update_tag,
422
+ _sub_resource_label="GoogleWorkspaceTenant",
423
+ _sub_resource_id=customer_id,
424
+ )
425
+
426
+ logger.info("Finished computing inherited group relationships")
427
+
428
+
429
+ @timeit
430
+ def cleanup_googleworkspace_groups(
431
+ neo4j_session: neo4j.Session,
432
+ common_job_parameters: dict[str, Any],
433
+ customer_id: str,
434
+ googleworkspace_update_tag: int,
435
+ ) -> None:
436
+ """
437
+ Clean up Google Workspace groups and group-to-group relationships using the modern data model
438
+ """
439
+ logger.debug("Running Google Workspace groups cleanup job")
440
+
441
+ # Cleanup group nodes
442
+ GraphJob.from_node_schema(GoogleWorkspaceGroupSchema(), common_job_parameters).run(
443
+ neo4j_session
444
+ )
445
+
446
+ # Cleanup group-to-group member relationships
447
+ GraphJob.from_matchlink(
448
+ GoogleWorkspaceGroupToGroupMemberRel(),
449
+ "GoogleWorkspaceTenant",
450
+ customer_id,
451
+ googleworkspace_update_tag,
452
+ ).run(neo4j_session)
453
+
454
+ # Cleanup group-to-group owner relationships
455
+ GraphJob.from_matchlink(
456
+ GoogleWorkspaceGroupToGroupOwnerRel(),
457
+ "GoogleWorkspaceTenant",
458
+ customer_id,
459
+ googleworkspace_update_tag,
460
+ ).run(neo4j_session)
461
+
462
+ # Cleanup inherited relationships
463
+ logger.debug("Cleaning up inherited group relationships")
464
+
465
+ # Cleanup User -> Group inherited relationships
466
+ GraphJob.from_matchlink(
467
+ GoogleWorkspaceUserToGroupInheritedMemberRel(),
468
+ "GoogleWorkspaceTenant",
469
+ customer_id,
470
+ googleworkspace_update_tag,
471
+ ).run(neo4j_session)
472
+
473
+ GraphJob.from_matchlink(
474
+ GoogleWorkspaceUserToGroupInheritedOwnerRel(),
475
+ "GoogleWorkspaceTenant",
476
+ customer_id,
477
+ googleworkspace_update_tag,
478
+ ).run(neo4j_session)
479
+
480
+ # Cleanup Group -> Group inherited relationships
481
+ GraphJob.from_matchlink(
482
+ GoogleWorkspaceGroupToGroupInheritedMemberRel(),
483
+ "GoogleWorkspaceTenant",
484
+ customer_id,
485
+ googleworkspace_update_tag,
486
+ ).run(neo4j_session)
487
+
488
+ GraphJob.from_matchlink(
489
+ GoogleWorkspaceGroupToGroupInheritedOwnerRel(),
490
+ "GoogleWorkspaceTenant",
491
+ customer_id,
492
+ googleworkspace_update_tag,
493
+ ).run(neo4j_session)
494
+
495
+
496
+ @timeit
497
+ def sync_googleworkspace_groups(
498
+ neo4j_session: neo4j.Session,
499
+ cloudidentity: Resource,
500
+ googleworkspace_update_tag: int,
501
+ common_job_parameters: dict[str, Any],
502
+ ) -> None:
503
+ """
504
+ GET Google Workspace group objects using the Cloud Identity API,
505
+ load the data into Neo4j and clean up stale nodes.
506
+
507
+ :param neo4j_session: The Neo4j session
508
+ :param cloudidentity: Google Cloud Identity resource object created by `googleapiclient.discovery.build()`.
509
+ Used for fetching groups and memberships via Cloud Identity API.
510
+ See https://googleapis.github.io/google-api-python-client/docs/epy/googleapiclient.discovery-module.html#build.
511
+ :param googleworkspace_update_tag: The timestamp value to set our new Neo4j nodes with
512
+ :param common_job_parameters: Parameters to carry to the Neo4j jobs
513
+ :return: Nothing
514
+ """
515
+ logger.debug("Syncing Google Workspace Groups")
516
+ customer_id = common_job_parameters["CUSTOMER_ID"]
517
+
518
+ # 1. GET - Fetch data from API
519
+ resp_objs = get_all_groups(cloudidentity, customer_id)
520
+ group_members = get_members_for_groups(
521
+ cloudidentity, [resp["name"] for resp in resp_objs]
522
+ )
523
+
524
+ # 2. TRANSFORM - Shape data for ingestion
525
+ groups, group_member_relationships, group_owner_relationships = transform_groups(
526
+ resp_objs, group_members
527
+ )
528
+
529
+ # 3. LOAD - Ingest to Neo4j using data model
530
+ load_googleworkspace_groups(
531
+ neo4j_session, groups, customer_id, googleworkspace_update_tag
532
+ )
533
+
534
+ # Load group-to-group relationships after groups are loaded
535
+ load_googleworkspace_group_to_group_relationships(
536
+ neo4j_session,
537
+ group_member_relationships,
538
+ group_owner_relationships,
539
+ customer_id,
540
+ googleworkspace_update_tag,
541
+ )
542
+
543
+ # Load inherited relationships (computed via graph traversal)
544
+ user_member_rels = get_user_inherited_member_relationships(
545
+ neo4j_session, customer_id
546
+ )
547
+ user_owner_rels = get_user_inherited_owner_relationships(neo4j_session, customer_id)
548
+ group_member_rels = get_group_inherited_member_relationships(
549
+ neo4j_session, customer_id
550
+ )
551
+ group_owner_rels = get_group_inherited_owner_relationships(
552
+ neo4j_session, customer_id
553
+ )
554
+ load_googleworkspace_inherited_relationships(
555
+ neo4j_session,
556
+ user_member_rels,
557
+ user_owner_rels,
558
+ group_member_rels,
559
+ group_owner_rels,
560
+ customer_id,
561
+ googleworkspace_update_tag,
562
+ )
563
+
564
+ # 4. CLEANUP - Remove stale data
565
+ cleanup_params = {**common_job_parameters, "CUSTOMER_ID": customer_id}
566
+ cleanup_googleworkspace_groups(
567
+ neo4j_session, cleanup_params, customer_id, googleworkspace_update_tag
568
+ )