cartography 0.102.0rc1__py3-none-any.whl → 0.103.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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (297) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +376 -249
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +28 -0
  6. cartography/driftdetect/__main__.py +1 -2
  7. cartography/driftdetect/add_shortcut.py +10 -2
  8. cartography/driftdetect/cli.py +71 -75
  9. cartography/driftdetect/detect_deviations.py +7 -3
  10. cartography/driftdetect/get_states.py +20 -8
  11. cartography/driftdetect/model.py +5 -5
  12. cartography/driftdetect/serializers.py +8 -6
  13. cartography/driftdetect/storage.py +2 -2
  14. cartography/graph/cleanupbuilder.py +35 -15
  15. cartography/graph/job.py +46 -17
  16. cartography/graph/querybuilder.py +165 -80
  17. cartography/graph/statement.py +35 -26
  18. cartography/intel/analysis.py +4 -1
  19. cartography/intel/aws/__init__.py +114 -55
  20. cartography/intel/aws/apigateway.py +134 -63
  21. cartography/intel/aws/cloudtrail.py +127 -0
  22. cartography/intel/aws/cloudwatch.py +93 -0
  23. cartography/intel/aws/config.py +56 -20
  24. cartography/intel/aws/dynamodb.py +108 -40
  25. cartography/intel/aws/ec2/__init__.py +2 -2
  26. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  27. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  28. cartography/intel/aws/ec2/images.py +49 -20
  29. cartography/intel/aws/ec2/instances.py +234 -136
  30. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  31. cartography/intel/aws/ec2/key_pairs.py +44 -20
  32. cartography/intel/aws/ec2/launch_templates.py +101 -59
  33. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  34. cartography/intel/aws/ec2/load_balancers.py +82 -42
  35. cartography/intel/aws/ec2/network_acls.py +89 -65
  36. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  37. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  38. cartography/intel/aws/ec2/route_tables.py +327 -0
  39. cartography/intel/aws/ec2/security_groups.py +71 -21
  40. cartography/intel/aws/ec2/snapshots.py +61 -22
  41. cartography/intel/aws/ec2/subnets.py +54 -18
  42. cartography/intel/aws/ec2/tgw.py +100 -34
  43. cartography/intel/aws/ec2/util.py +1 -1
  44. cartography/intel/aws/ec2/volumes.py +69 -41
  45. cartography/intel/aws/ec2/vpc.py +37 -12
  46. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  47. cartography/intel/aws/ecr.py +88 -32
  48. cartography/intel/aws/ecs.py +83 -47
  49. cartography/intel/aws/efs.py +93 -0
  50. cartography/intel/aws/eks.py +55 -29
  51. cartography/intel/aws/elasticache.py +42 -18
  52. cartography/intel/aws/elasticsearch.py +57 -20
  53. cartography/intel/aws/emr.py +61 -23
  54. cartography/intel/aws/iam.py +401 -145
  55. cartography/intel/aws/iam_instance_profiles.py +22 -22
  56. cartography/intel/aws/identitycenter.py +71 -37
  57. cartography/intel/aws/inspector.py +159 -89
  58. cartography/intel/aws/kms.py +92 -38
  59. cartography/intel/aws/lambda_function.py +103 -34
  60. cartography/intel/aws/organizations.py +30 -10
  61. cartography/intel/aws/permission_relationships.py +133 -51
  62. cartography/intel/aws/rds.py +249 -85
  63. cartography/intel/aws/redshift.py +107 -46
  64. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  65. cartography/intel/aws/resources.py +57 -44
  66. cartography/intel/aws/route53.py +108 -61
  67. cartography/intel/aws/s3.py +168 -83
  68. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  69. cartography/intel/aws/secretsmanager.py +24 -12
  70. cartography/intel/aws/securityhub.py +20 -9
  71. cartography/intel/aws/sns.py +166 -0
  72. cartography/intel/aws/sqs.py +60 -28
  73. cartography/intel/aws/ssm.py +70 -30
  74. cartography/intel/aws/util/arns.py +7 -7
  75. cartography/intel/aws/util/common.py +31 -4
  76. cartography/intel/azure/__init__.py +78 -19
  77. cartography/intel/azure/compute.py +101 -27
  78. cartography/intel/azure/cosmosdb.py +496 -170
  79. cartography/intel/azure/sql.py +296 -105
  80. cartography/intel/azure/storage.py +322 -113
  81. cartography/intel/azure/subscription.py +39 -23
  82. cartography/intel/azure/tenant.py +13 -4
  83. cartography/intel/azure/util/credentials.py +95 -55
  84. cartography/intel/bigfix/__init__.py +2 -2
  85. cartography/intel/bigfix/computers.py +93 -65
  86. cartography/intel/cloudflare/__init__.py +74 -0
  87. cartography/intel/cloudflare/accounts.py +57 -0
  88. cartography/intel/cloudflare/dnsrecords.py +64 -0
  89. cartography/intel/cloudflare/members.py +75 -0
  90. cartography/intel/cloudflare/roles.py +65 -0
  91. cartography/intel/cloudflare/zones.py +64 -0
  92. cartography/intel/create_indexes.py +3 -2
  93. cartography/intel/crowdstrike/__init__.py +11 -9
  94. cartography/intel/crowdstrike/endpoints.py +5 -1
  95. cartography/intel/crowdstrike/spotlight.py +8 -3
  96. cartography/intel/cve/__init__.py +46 -13
  97. cartography/intel/cve/feed.py +48 -12
  98. cartography/intel/digitalocean/__init__.py +22 -13
  99. cartography/intel/digitalocean/compute.py +75 -108
  100. cartography/intel/digitalocean/management.py +44 -80
  101. cartography/intel/digitalocean/platform.py +48 -43
  102. cartography/intel/dns.py +36 -10
  103. cartography/intel/duo/__init__.py +21 -16
  104. cartography/intel/duo/api_host.py +14 -9
  105. cartography/intel/duo/endpoints.py +50 -45
  106. cartography/intel/duo/groups.py +18 -14
  107. cartography/intel/duo/phones.py +37 -34
  108. cartography/intel/duo/tokens.py +26 -23
  109. cartography/intel/duo/users.py +54 -50
  110. cartography/intel/duo/web_authn_credentials.py +30 -25
  111. cartography/intel/entra/__init__.py +25 -7
  112. cartography/intel/entra/ou.py +112 -0
  113. cartography/intel/entra/users.py +69 -63
  114. cartography/intel/gcp/__init__.py +185 -49
  115. cartography/intel/gcp/compute.py +418 -231
  116. cartography/intel/gcp/crm.py +96 -43
  117. cartography/intel/gcp/dns.py +60 -19
  118. cartography/intel/gcp/gke.py +72 -38
  119. cartography/intel/gcp/iam.py +61 -41
  120. cartography/intel/gcp/storage.py +84 -55
  121. cartography/intel/github/__init__.py +13 -11
  122. cartography/intel/github/repos.py +270 -137
  123. cartography/intel/github/teams.py +170 -88
  124. cartography/intel/github/users.py +70 -39
  125. cartography/intel/github/util.py +36 -34
  126. cartography/intel/gsuite/__init__.py +47 -26
  127. cartography/intel/gsuite/api.py +73 -30
  128. cartography/intel/jamf/__init__.py +19 -1
  129. cartography/intel/jamf/computers.py +30 -7
  130. cartography/intel/jamf/util.py +7 -2
  131. cartography/intel/kandji/__init__.py +6 -3
  132. cartography/intel/kandji/devices.py +14 -8
  133. cartography/intel/kubernetes/namespaces.py +7 -4
  134. cartography/intel/kubernetes/pods.py +7 -4
  135. cartography/intel/kubernetes/services.py +8 -4
  136. cartography/intel/lastpass/__init__.py +2 -2
  137. cartography/intel/lastpass/users.py +23 -12
  138. cartography/intel/oci/__init__.py +44 -11
  139. cartography/intel/oci/iam.py +134 -38
  140. cartography/intel/oci/organizations.py +13 -6
  141. cartography/intel/oci/utils.py +43 -20
  142. cartography/intel/okta/__init__.py +66 -15
  143. cartography/intel/okta/applications.py +42 -20
  144. cartography/intel/okta/awssaml.py +93 -33
  145. cartography/intel/okta/factors.py +16 -4
  146. cartography/intel/okta/groups.py +56 -29
  147. cartography/intel/okta/organization.py +5 -1
  148. cartography/intel/okta/origins.py +6 -2
  149. cartography/intel/okta/roles.py +15 -5
  150. cartography/intel/okta/users.py +20 -8
  151. cartography/intel/okta/utils.py +6 -4
  152. cartography/intel/openai/__init__.py +86 -0
  153. cartography/intel/openai/adminapikeys.py +90 -0
  154. cartography/intel/openai/apikeys.py +96 -0
  155. cartography/intel/openai/projects.py +94 -0
  156. cartography/intel/openai/serviceaccounts.py +82 -0
  157. cartography/intel/openai/users.py +78 -0
  158. cartography/intel/openai/util.py +29 -0
  159. cartography/intel/pagerduty/__init__.py +8 -7
  160. cartography/intel/pagerduty/escalation_policies.py +18 -6
  161. cartography/intel/pagerduty/schedules.py +12 -4
  162. cartography/intel/pagerduty/services.py +11 -4
  163. cartography/intel/pagerduty/teams.py +8 -3
  164. cartography/intel/pagerduty/users.py +3 -1
  165. cartography/intel/pagerduty/vendors.py +3 -1
  166. cartography/intel/semgrep/__init__.py +24 -6
  167. cartography/intel/semgrep/dependencies.py +50 -28
  168. cartography/intel/semgrep/deployment.py +3 -1
  169. cartography/intel/semgrep/findings.py +42 -18
  170. cartography/intel/snipeit/__init__.py +17 -3
  171. cartography/intel/snipeit/asset.py +12 -6
  172. cartography/intel/snipeit/user.py +8 -5
  173. cartography/intel/snipeit/util.py +9 -4
  174. cartography/intel/tailscale/__init__.py +77 -0
  175. cartography/intel/tailscale/acls.py +146 -0
  176. cartography/intel/tailscale/devices.py +127 -0
  177. cartography/intel/tailscale/postureintegrations.py +81 -0
  178. cartography/intel/tailscale/tailnets.py +76 -0
  179. cartography/intel/tailscale/users.py +80 -0
  180. cartography/intel/tailscale/utils.py +132 -0
  181. cartography/models/aws/apigateway.py +21 -17
  182. cartography/models/aws/apigatewaycertificate.py +28 -22
  183. cartography/models/aws/apigatewayresource.py +28 -20
  184. cartography/models/aws/apigatewaystage.py +33 -25
  185. cartography/models/aws/cloudtrail/__init__.py +0 -0
  186. cartography/models/aws/cloudtrail/trail.py +61 -0
  187. cartography/models/aws/cloudwatch/__init__.py +0 -0
  188. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  189. cartography/models/aws/dynamodb/gsi.py +30 -22
  190. cartography/models/aws/dynamodb/tables.py +25 -17
  191. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  192. cartography/models/aws/ec2/images.py +36 -34
  193. cartography/models/aws/ec2/instances.py +51 -45
  194. cartography/models/aws/ec2/keypair.py +21 -16
  195. cartography/models/aws/ec2/keypair_instance.py +28 -21
  196. cartography/models/aws/ec2/launch_configurations.py +30 -26
  197. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  198. cartography/models/aws/ec2/launch_templates.py +21 -17
  199. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  200. cartography/models/aws/ec2/load_balancers.py +47 -37
  201. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  202. cartography/models/aws/ec2/network_acls.py +38 -29
  203. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  204. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  205. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  206. cartography/models/aws/ec2/reservations.py +18 -14
  207. cartography/models/aws/ec2/route_table_associations.py +97 -0
  208. cartography/models/aws/ec2/route_tables.py +128 -0
  209. cartography/models/aws/ec2/routes.py +85 -0
  210. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  211. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  212. cartography/models/aws/ec2/subnet_instance.py +24 -19
  213. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  214. cartography/models/aws/ec2/volumes.py +47 -40
  215. cartography/models/aws/efs/__init__.py +0 -0
  216. cartography/models/aws/efs/mount_target.py +52 -0
  217. cartography/models/aws/eks/clusters.py +23 -21
  218. cartography/models/aws/emr.py +32 -30
  219. cartography/models/aws/iam/instanceprofile.py +33 -24
  220. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  221. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  222. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  223. cartography/models/aws/inspector/findings.py +77 -65
  224. cartography/models/aws/inspector/packages.py +35 -29
  225. cartography/models/aws/s3/__init__.py +0 -0
  226. cartography/models/aws/s3/account_public_access_block.py +51 -0
  227. cartography/models/aws/sns/__init__.py +0 -0
  228. cartography/models/aws/sns/topic.py +50 -0
  229. cartography/models/aws/ssm/instance_information.py +51 -39
  230. cartography/models/aws/ssm/instance_patch.py +32 -26
  231. cartography/models/bigfix/bigfix_computer.py +42 -38
  232. cartography/models/bigfix/bigfix_root.py +3 -3
  233. cartography/models/cloudflare/__init__.py +0 -0
  234. cartography/models/cloudflare/account.py +25 -0
  235. cartography/models/cloudflare/dnsrecord.py +55 -0
  236. cartography/models/cloudflare/member.py +82 -0
  237. cartography/models/cloudflare/role.py +44 -0
  238. cartography/models/cloudflare/zone.py +59 -0
  239. cartography/models/core/common.py +12 -10
  240. cartography/models/core/nodes.py +5 -2
  241. cartography/models/core/relationships.py +14 -6
  242. cartography/models/crowdstrike/hosts.py +37 -35
  243. cartography/models/cve/cve.py +34 -32
  244. cartography/models/cve/cve_feed.py +6 -6
  245. cartography/models/digitalocean/__init__.py +0 -0
  246. cartography/models/digitalocean/account.py +21 -0
  247. cartography/models/digitalocean/droplet.py +56 -0
  248. cartography/models/digitalocean/project.py +48 -0
  249. cartography/models/duo/api_host.py +3 -3
  250. cartography/models/duo/endpoint.py +43 -41
  251. cartography/models/duo/group.py +14 -14
  252. cartography/models/duo/phone.py +27 -27
  253. cartography/models/duo/token.py +16 -16
  254. cartography/models/duo/user.py +46 -44
  255. cartography/models/duo/web_authn_credential.py +27 -19
  256. cartography/models/entra/ou.py +48 -0
  257. cartography/models/entra/tenant.py +24 -18
  258. cartography/models/entra/user.py +64 -48
  259. cartography/models/gcp/iam.py +23 -23
  260. cartography/models/github/orgs.py +5 -4
  261. cartography/models/github/teams.py +37 -31
  262. cartography/models/github/users.py +34 -23
  263. cartography/models/kandji/device.py +22 -16
  264. cartography/models/kandji/tenant.py +6 -4
  265. cartography/models/lastpass/tenant.py +3 -3
  266. cartography/models/lastpass/user.py +32 -28
  267. cartography/models/openai/__init__.py +0 -0
  268. cartography/models/openai/adminapikey.py +90 -0
  269. cartography/models/openai/apikey.py +84 -0
  270. cartography/models/openai/organization.py +17 -0
  271. cartography/models/openai/project.py +70 -0
  272. cartography/models/openai/serviceaccount.py +50 -0
  273. cartography/models/openai/user.py +49 -0
  274. cartography/models/semgrep/dependencies.py +36 -24
  275. cartography/models/semgrep/deployment.py +5 -5
  276. cartography/models/semgrep/findings.py +58 -42
  277. cartography/models/semgrep/locations.py +27 -21
  278. cartography/models/snipeit/asset.py +30 -21
  279. cartography/models/snipeit/tenant.py +6 -4
  280. cartography/models/snipeit/user.py +19 -12
  281. cartography/models/tailscale/__init__.py +0 -0
  282. cartography/models/tailscale/device.py +95 -0
  283. cartography/models/tailscale/group.py +86 -0
  284. cartography/models/tailscale/postureintegration.py +58 -0
  285. cartography/models/tailscale/tag.py +102 -0
  286. cartography/models/tailscale/tailnet.py +29 -0
  287. cartography/models/tailscale/user.py +52 -0
  288. cartography/stats.py +3 -3
  289. cartography/sync.py +113 -31
  290. cartography/util.py +84 -62
  291. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
  292. cartography-0.103.0.dist-info/RECORD +442 -0
  293. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc1.dist-info/RECORD +0 -377
  295. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -26,7 +26,7 @@ def _get_user_roles(api_client: ApiClient, user_id: str, okta_org_id: str) -> st
26
26
  """
27
27
 
28
28
  # https://developer.okta.com/docs/reference/api/roles/#list-roles
29
- response = api_client.get_path(f'/{user_id}/roles')
29
+ response = api_client.get_path(f"/{user_id}/roles")
30
30
  check_rate_limit(response)
31
31
  return response.text
32
32
 
@@ -42,7 +42,7 @@ def _get_group_roles(api_client: ApiClient, group_id: str, okta_org_id: str) ->
42
42
  """
43
43
 
44
44
  # https://developer.okta.com/docs/reference/api/roles/#list-roles-assigned-to-group
45
- response = api_client.get_path(f'/{group_id}/roles')
45
+ response = api_client.get_path(f"/{group_id}/roles")
46
46
  check_rate_limit(response)
47
47
  return response.text
48
48
 
@@ -94,7 +94,12 @@ def transform_group_roles_data(data: str, okta_org_id: str) -> List[Dict]:
94
94
 
95
95
 
96
96
  @timeit
97
- def _load_user_role(neo4j_session: neo4j.Session, user_id: str, roles_data: List[Dict], okta_update_tag: int) -> None:
97
+ def _load_user_role(
98
+ neo4j_session: neo4j.Session,
99
+ user_id: str,
100
+ roles_data: List[Dict],
101
+ okta_update_tag: int,
102
+ ) -> None:
98
103
  ingest = """
99
104
  MATCH (user:OktaUser{id: $USER_ID})<-[:RESOURCE]-(org:OktaOrganization)
100
105
  WITH user,org
@@ -122,7 +127,9 @@ def _load_user_role(neo4j_session: neo4j.Session, user_id: str, roles_data: List
122
127
 
123
128
  @timeit
124
129
  def _load_group_role(
125
- neo4j_session: neo4j.Session, group_id: str, roles_data: List[Dict],
130
+ neo4j_session: neo4j.Session,
131
+ group_id: str,
132
+ roles_data: List[Dict],
126
133
  okta_update_tag: int,
127
134
  ) -> None:
128
135
  ingest = """
@@ -152,7 +159,10 @@ def _load_group_role(
152
159
 
153
160
  @timeit
154
161
  def sync_roles(
155
- neo4j_session: str, okta_org_id: str, okta_update_tag: int, okta_api_key: str,
162
+ neo4j_session: str,
163
+ okta_org_id: str,
164
+ okta_update_tag: int,
165
+ okta_api_key: str,
156
166
  sync_state: OktaSyncState,
157
167
  ) -> None:
158
168
  """
@@ -12,7 +12,6 @@ from cartography.intel.okta.sync_state import OktaSyncState
12
12
  from cartography.intel.okta.utils import check_rate_limit
13
13
  from cartography.util import timeit
14
14
 
15
-
16
15
  logger = logging.getLogger(__name__)
17
16
 
18
17
 
@@ -56,7 +55,9 @@ def _get_okta_users(user_client: UsersClient) -> List[Dict]:
56
55
 
57
56
 
58
57
  @timeit
59
- def transform_okta_user_list(okta_user_list: List[User]) -> Tuple[List[Dict], List[str]]:
58
+ def transform_okta_user_list(
59
+ okta_user_list: List[User],
60
+ ) -> Tuple[List[Dict], List[str]]:
60
61
  users: List[Dict] = []
61
62
  user_ids: List[str] = []
62
63
 
@@ -91,7 +92,9 @@ def transform_okta_user(okta_user: User) -> Dict:
91
92
  user_props["activated"] = None
92
93
 
93
94
  if okta_user.statusChanged:
94
- user_props["status_changed"] = okta_user.statusChanged.strftime("%m/%d/%Y, %H:%M:%S")
95
+ user_props["status_changed"] = okta_user.statusChanged.strftime(
96
+ "%m/%d/%Y, %H:%M:%S",
97
+ )
95
98
  else:
96
99
  user_props["status_changed"] = None
97
100
 
@@ -101,12 +104,16 @@ def transform_okta_user(okta_user: User) -> Dict:
101
104
  user_props["last_login"] = None
102
105
 
103
106
  if okta_user.lastUpdated:
104
- user_props["okta_last_updated"] = okta_user.lastUpdated.strftime("%m/%d/%Y, %H:%M:%S")
107
+ user_props["okta_last_updated"] = okta_user.lastUpdated.strftime(
108
+ "%m/%d/%Y, %H:%M:%S",
109
+ )
105
110
  else:
106
111
  user_props["okta_last_updated"] = None
107
112
 
108
113
  if okta_user.passwordChanged:
109
- user_props["password_changed"] = okta_user.passwordChanged.strftime("%m/%d/%Y, %H:%M:%S")
114
+ user_props["password_changed"] = okta_user.passwordChanged.strftime(
115
+ "%m/%d/%Y, %H:%M:%S",
116
+ )
110
117
  else:
111
118
  user_props["password_changed"] = None
112
119
 
@@ -120,7 +127,9 @@ def transform_okta_user(okta_user: User) -> Dict:
120
127
 
121
128
  @timeit
122
129
  def _load_okta_users(
123
- neo4j_session: neo4j.Session, okta_org_id: str, user_list: List[Dict],
130
+ neo4j_session: neo4j.Session,
131
+ okta_org_id: str,
132
+ user_list: List[Dict],
124
133
  okta_update_tag: int,
125
134
  ) -> None:
126
135
  """
@@ -175,8 +184,11 @@ def _load_okta_users(
175
184
 
176
185
  @timeit
177
186
  def sync_okta_users(
178
- neo4j_session: neo4j.Session, okta_org_id: str, okta_update_tag: int,
179
- okta_api_key: str, sync_state: OktaSyncState,
187
+ neo4j_session: neo4j.Session,
188
+ okta_org_id: str,
189
+ okta_update_tag: int,
190
+ okta_api_key: str,
191
+ sync_state: OktaSyncState,
180
192
  ) -> None:
181
193
  """
182
194
  Sync okta users
@@ -43,9 +43,9 @@ def check_rate_limit(response: Response) -> None:
43
43
  """
44
44
  rate_limit_threshold = 0.1
45
45
 
46
- remaining = response.headers.get('x-rate-limit-remaining')
47
- limit = response.headers.get('x-rate-limit-limit')
48
- reset_time = response.headers.get('x-rate-limit-reset')
46
+ remaining = response.headers.get("x-rate-limit-remaining")
47
+ limit = response.headers.get("x-rate-limit-limit")
48
+ reset_time = response.headers.get("x-rate-limit-reset")
49
49
 
50
50
  if remaining and limit and reset_time:
51
51
  if (int(remaining) / int(limit)) < rate_limit_threshold:
@@ -58,5 +58,7 @@ def check_rate_limit(response: Response) -> None:
58
58
  f"Okta API limit exceeded. Sleep time of {sleep_time_seconds} would exceed one minute. Crashing "
59
59
  f"Okta sync to avoid blocking.",
60
60
  )
61
- logger.warning(f"Okta rate limit threshold reached. Waiting {sleep_time_seconds} seconds.")
61
+ logger.warning(
62
+ f"Okta rate limit threshold reached. Waiting {sleep_time_seconds} seconds.",
63
+ )
62
64
  time.sleep(sleep_time_seconds)
@@ -0,0 +1,86 @@
1
+ import logging
2
+
3
+ import neo4j
4
+ import requests
5
+
6
+ import cartography.intel.openai.adminapikeys
7
+ import cartography.intel.openai.apikeys
8
+ import cartography.intel.openai.projects
9
+ import cartography.intel.openai.serviceaccounts
10
+ import cartography.intel.openai.users
11
+ from cartography.config import Config
12
+ from cartography.util import timeit
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @timeit
18
+ def start_openai_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
19
+ """
20
+ If this module is configured, perform ingestion of OpenAI data. Otherwise warn and exit
21
+ :param neo4j_session: Neo4J session for database interface
22
+ :param config: A cartography.config object
23
+ :return: None
24
+ """
25
+
26
+ if not config.openai_apikey or not config.openai_org_id:
27
+ logger.info(
28
+ "OpenAI import is not configured - skipping this module. "
29
+ "See docs to configure.",
30
+ )
31
+ return
32
+
33
+ # Create requests sessions
34
+ api_session = requests.session()
35
+ api_session.headers.update(
36
+ {
37
+ "Authorization": f"Bearer {config.openai_apikey}",
38
+ "OpenAI-Organization": config.openai_org_id,
39
+ }
40
+ )
41
+
42
+ common_job_parameters = {
43
+ "UPDATE_TAG": config.update_tag,
44
+ "BASE_URL": "https://api.openai.com/v1",
45
+ "ORG_ID": config.openai_org_id,
46
+ }
47
+
48
+ # Organization node is created during the users sync
49
+ cartography.intel.openai.users.sync(
50
+ neo4j_session,
51
+ api_session,
52
+ common_job_parameters,
53
+ ORG_ID=config.openai_org_id,
54
+ )
55
+
56
+ for project in cartography.intel.openai.projects.sync(
57
+ neo4j_session,
58
+ api_session,
59
+ common_job_parameters,
60
+ ORG_ID=config.openai_org_id,
61
+ ):
62
+ project_job_parameters = {
63
+ "UPDATE_TAG": config.update_tag,
64
+ "BASE_URL": "https://api.openai.com/v1",
65
+ "ORG_ID": config.openai_org_id,
66
+ "project_id": project["id"],
67
+ }
68
+ cartography.intel.openai.serviceaccounts.sync(
69
+ neo4j_session,
70
+ api_session,
71
+ project_job_parameters,
72
+ project_id=project["id"],
73
+ )
74
+ cartography.intel.openai.apikeys.sync(
75
+ neo4j_session,
76
+ api_session,
77
+ project_job_parameters,
78
+ project_id=project["id"],
79
+ )
80
+
81
+ cartography.intel.openai.adminapikeys.sync(
82
+ neo4j_session,
83
+ api_session,
84
+ common_job_parameters,
85
+ ORG_ID=config.openai_org_id,
86
+ )
@@ -0,0 +1,90 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ import requests
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.openai.util import paginated_get
12
+ from cartography.models.openai.adminapikey import OpenAIAdminApiKeySchema
13
+ from cartography.util import timeit
14
+
15
+ logger = logging.getLogger(__name__)
16
+ # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
17
+ _TIMEOUT = (60, 60)
18
+
19
+
20
+ @timeit
21
+ def sync(
22
+ neo4j_session: neo4j.Session,
23
+ api_session: requests.Session,
24
+ common_job_parameters: Dict[str, Any],
25
+ ORG_ID: str,
26
+ ) -> List[Dict]:
27
+ adminapikeys = get(
28
+ api_session,
29
+ common_job_parameters["BASE_URL"],
30
+ )
31
+ transformed_adminapikeys = transform(adminapikeys)
32
+ load_adminapikeys(
33
+ neo4j_session,
34
+ transformed_adminapikeys,
35
+ ORG_ID,
36
+ common_job_parameters["UPDATE_TAG"],
37
+ )
38
+ cleanup(neo4j_session, common_job_parameters)
39
+ return adminapikeys
40
+
41
+
42
+ @timeit
43
+ def get(
44
+ api_session: requests.Session,
45
+ base_url: str,
46
+ ) -> List[Dict[str, Any]]:
47
+ return list(
48
+ paginated_get(
49
+ api_session, f"{base_url}/organization/admin_api_keys", timeout=_TIMEOUT
50
+ )
51
+ )
52
+
53
+
54
+ def transform(
55
+ adminapikeys: List[Dict[str, Any]],
56
+ ) -> List[Dict[str, Any]]:
57
+ result: List[Dict[str, Any]] = []
58
+ for adminapikey in adminapikeys:
59
+ if adminapikey["owner"]["type"] == "user":
60
+ adminapikey["owner_user_id"] = adminapikey["owner"]["id"]
61
+ else:
62
+ adminapikey["owner_sa_id"] = adminapikey["owner"]["id"]
63
+ result.append(adminapikey)
64
+ return result
65
+
66
+
67
+ @timeit
68
+ def load_adminapikeys(
69
+ neo4j_session: neo4j.Session,
70
+ data: List[Dict[str, Any]],
71
+ ORG_ID: str,
72
+ update_tag: int,
73
+ ) -> None:
74
+ logger.info("Loading %d OpenAI AdminApiKey into Neo4j.", len(data))
75
+ load(
76
+ neo4j_session,
77
+ OpenAIAdminApiKeySchema(),
78
+ data,
79
+ lastupdated=update_tag,
80
+ ORG_ID=ORG_ID,
81
+ )
82
+
83
+
84
+ @timeit
85
+ def cleanup(
86
+ neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
87
+ ) -> None:
88
+ GraphJob.from_node_schema(OpenAIAdminApiKeySchema(), common_job_parameters).run(
89
+ neo4j_session
90
+ )
@@ -0,0 +1,96 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ import requests
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.openai.util import paginated_get
12
+ from cartography.models.openai.apikey import OpenAIApiKeySchema
13
+ from cartography.util import timeit
14
+
15
+ logger = logging.getLogger(__name__)
16
+ # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
17
+ _TIMEOUT = (60, 60)
18
+
19
+
20
+ @timeit
21
+ def sync(
22
+ neo4j_session: neo4j.Session,
23
+ api_session: requests.Session,
24
+ common_job_parameters: Dict[str, Any],
25
+ project_id: str,
26
+ ) -> None:
27
+ apikeys = get(
28
+ api_session,
29
+ common_job_parameters["BASE_URL"],
30
+ project_id,
31
+ )
32
+ transformed_apikeys = transform(apikeys)
33
+ load_apikeys(
34
+ neo4j_session,
35
+ transformed_apikeys,
36
+ project_id,
37
+ common_job_parameters["UPDATE_TAG"],
38
+ )
39
+ cleanup(neo4j_session, common_job_parameters)
40
+
41
+
42
+ @timeit
43
+ def get(
44
+ api_session: requests.Session,
45
+ base_url: str,
46
+ project_id: str,
47
+ ) -> List[Dict[str, Any]]:
48
+ return list(
49
+ paginated_get(
50
+ api_session,
51
+ "{base_url}/organization/projects/{project_id}/api_keys".format(
52
+ base_url=base_url,
53
+ project_id=project_id,
54
+ ),
55
+ timeout=_TIMEOUT,
56
+ )
57
+ )
58
+
59
+
60
+ def transform(
61
+ apikeys: List[Dict[str, Any]],
62
+ ) -> List[Dict[str, Any]]:
63
+ result: List[Dict[str, Any]] = []
64
+ for apikey in apikeys:
65
+ if apikey["owner"]["type"] == "user":
66
+ apikey["owner_user_id"] = apikey["owner"]["user"]["id"]
67
+ else:
68
+ apikey["owner_sa_id"] = apikey["owner"]["service_account"]["id"]
69
+ result.append(apikey)
70
+ return result
71
+
72
+
73
+ @timeit
74
+ def load_apikeys(
75
+ neo4j_session: neo4j.Session,
76
+ data: List[Dict[str, Any]],
77
+ project_id: str,
78
+ update_tag: int,
79
+ ) -> None:
80
+ logger.info("Loading %d OpenAI Project APIKey into Neo4j.", len(data))
81
+ load(
82
+ neo4j_session,
83
+ OpenAIApiKeySchema(),
84
+ data,
85
+ lastupdated=update_tag,
86
+ project_id=project_id,
87
+ )
88
+
89
+
90
+ @timeit
91
+ def cleanup(
92
+ neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
93
+ ) -> None:
94
+ GraphJob.from_node_schema(OpenAIApiKeySchema(), common_job_parameters).run(
95
+ neo4j_session
96
+ )
@@ -0,0 +1,94 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ import requests
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.openai.util import paginated_get
12
+ from cartography.models.openai.project import OpenAIProjectSchema
13
+ from cartography.util import timeit
14
+
15
+ logger = logging.getLogger(__name__)
16
+ # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
17
+ _TIMEOUT = (60, 60)
18
+
19
+
20
+ @timeit
21
+ def sync(
22
+ neo4j_session: neo4j.Session,
23
+ api_session: requests.Session,
24
+ common_job_parameters: Dict[str, Any],
25
+ ORG_ID: str,
26
+ ) -> List[Dict]:
27
+ projects = get(
28
+ api_session,
29
+ common_job_parameters["BASE_URL"],
30
+ )
31
+ for project in projects:
32
+ project["users"] = []
33
+ for user in get_project_users(
34
+ api_session,
35
+ common_job_parameters["BASE_URL"],
36
+ project["id"],
37
+ ):
38
+ project["users"].append(user["id"])
39
+ load_projects(neo4j_session, projects, ORG_ID, common_job_parameters["UPDATE_TAG"])
40
+ cleanup(neo4j_session, common_job_parameters)
41
+ return projects
42
+
43
+
44
+ @timeit
45
+ def get(
46
+ api_session: requests.Session,
47
+ base_url: str,
48
+ ) -> List[Dict[str, Any]]:
49
+ return list(
50
+ paginated_get(
51
+ api_session, f"{base_url}/organization/projects", timeout=_TIMEOUT
52
+ )
53
+ )
54
+
55
+
56
+ @timeit
57
+ def get_project_users(
58
+ api_session: requests.Session,
59
+ base_url: str,
60
+ project_id: str,
61
+ ) -> List[Dict[str, Any]]:
62
+ return list(
63
+ paginated_get(
64
+ api_session,
65
+ f"{base_url}/organization/projects/{project_id}/users",
66
+ timeout=_TIMEOUT,
67
+ )
68
+ )
69
+
70
+
71
+ @timeit
72
+ def load_projects(
73
+ neo4j_session: neo4j.Session,
74
+ data: List[Dict[str, Any]],
75
+ ORG_ID: str,
76
+ update_tag: int,
77
+ ) -> None:
78
+ logger.info("Loading %d OpenAIProjectSchema into Neo4j.", len(data))
79
+ load(
80
+ neo4j_session,
81
+ OpenAIProjectSchema(),
82
+ data,
83
+ lastupdated=update_tag,
84
+ ORG_ID=ORG_ID,
85
+ )
86
+
87
+
88
+ @timeit
89
+ def cleanup(
90
+ neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
91
+ ) -> None:
92
+ GraphJob.from_node_schema(OpenAIProjectSchema(), common_job_parameters).run(
93
+ neo4j_session
94
+ )
@@ -0,0 +1,82 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ import requests
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.openai.util import paginated_get
12
+ from cartography.models.openai.serviceaccount import OpenAIServiceAccountSchema
13
+ from cartography.util import timeit
14
+
15
+ logger = logging.getLogger(__name__)
16
+ # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
17
+ _TIMEOUT = (60, 60)
18
+
19
+
20
+ @timeit
21
+ def sync(
22
+ neo4j_session: neo4j.Session,
23
+ api_session: requests.Session,
24
+ common_job_parameters: Dict[str, Any],
25
+ project_id: str,
26
+ ) -> None:
27
+ serviceaccountss = get(
28
+ api_session,
29
+ common_job_parameters["BASE_URL"],
30
+ project_id,
31
+ )
32
+ load_serviceaccounts(
33
+ neo4j_session,
34
+ serviceaccountss,
35
+ project_id,
36
+ common_job_parameters["UPDATE_TAG"],
37
+ )
38
+ cleanup(neo4j_session, common_job_parameters)
39
+
40
+
41
+ @timeit
42
+ def get(
43
+ api_session: requests.Session,
44
+ base_url: str,
45
+ project_id: str,
46
+ ) -> List[Dict[str, Any]]:
47
+ return list(
48
+ paginated_get(
49
+ api_session,
50
+ "{base_url}/organization/projects/{project_id}/service_accounts".format(
51
+ base_url=base_url,
52
+ project_id=project_id,
53
+ ),
54
+ timeout=_TIMEOUT,
55
+ )
56
+ )
57
+
58
+
59
+ @timeit
60
+ def load_serviceaccounts(
61
+ neo4j_session: neo4j.Session,
62
+ data: List[Dict[str, Any]],
63
+ project_id: str,
64
+ update_tag: int,
65
+ ) -> None:
66
+ logger.info("Loading %d OpenAI ProjectServiceAccount into Neo4j.", len(data))
67
+ load(
68
+ neo4j_session,
69
+ OpenAIServiceAccountSchema(),
70
+ data,
71
+ lastupdated=update_tag,
72
+ project_id=project_id,
73
+ )
74
+
75
+
76
+ @timeit
77
+ def cleanup(
78
+ neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
79
+ ) -> None:
80
+ GraphJob.from_node_schema(OpenAIServiceAccountSchema(), common_job_parameters).run(
81
+ neo4j_session
82
+ )
@@ -0,0 +1,78 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import neo4j
7
+ import requests
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.openai.util import paginated_get
12
+ from cartography.models.openai.organization import OpenAIOrganizationSchema
13
+ from cartography.models.openai.user import OpenAIUserSchema
14
+ from cartography.util import timeit
15
+
16
+ logger = logging.getLogger(__name__)
17
+ # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
18
+ _TIMEOUT = (60, 60)
19
+
20
+
21
+ @timeit
22
+ def sync(
23
+ neo4j_session: neo4j.Session,
24
+ api_session: requests.Session,
25
+ common_job_parameters: Dict[str, Any],
26
+ ORG_ID: str,
27
+ ) -> None:
28
+ users = get(
29
+ api_session,
30
+ common_job_parameters["BASE_URL"],
31
+ )
32
+ load_users(neo4j_session, users, ORG_ID, common_job_parameters["UPDATE_TAG"])
33
+ cleanup(neo4j_session, common_job_parameters)
34
+
35
+
36
+ @timeit
37
+ def get(
38
+ api_session: requests.Session,
39
+ base_url: str,
40
+ ) -> List[Dict[str, Any]]:
41
+ return list(
42
+ paginated_get(api_session, f"{base_url}/organization/users", timeout=_TIMEOUT)
43
+ )
44
+
45
+
46
+ @timeit
47
+ def load_users(
48
+ neo4j_session: neo4j.Session,
49
+ data: List[Dict[str, Any]],
50
+ ORG_ID: str,
51
+ update_tag: int,
52
+ ) -> None:
53
+ load(
54
+ neo4j_session,
55
+ OpenAIOrganizationSchema(),
56
+ [{"id": ORG_ID}],
57
+ lastupdated=update_tag,
58
+ )
59
+ logger.info("Loading %d OpenAI User into Neo4j.", len(data))
60
+ load(
61
+ neo4j_session,
62
+ OpenAIUserSchema(),
63
+ data,
64
+ lastupdated=update_tag,
65
+ ORG_ID=ORG_ID,
66
+ )
67
+
68
+
69
+ @timeit
70
+ def cleanup(
71
+ neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
72
+ ) -> None:
73
+ GraphJob.from_node_schema(OpenAIOrganizationSchema(), common_job_parameters).run(
74
+ neo4j_session
75
+ )
76
+ GraphJob.from_node_schema(OpenAIUserSchema(), common_job_parameters).run(
77
+ neo4j_session
78
+ )