cartography 0.102.0rc2__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 +138 -98
  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 -46
  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 +44 -34
  208. cartography/models/aws/ec2/route_tables.py +50 -43
  209. cartography/models/aws/ec2/routes.py +45 -37
  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.0rc2.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.0rc2.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc2.dist-info/RECORD +0 -381
  295. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,6 @@ from cartography.intel.okta.utils import create_api_client
15
15
  from cartography.intel.okta.utils import is_last_page
16
16
  from cartography.util import timeit
17
17
 
18
-
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
 
@@ -36,9 +35,9 @@ def _get_okta_applications(api_client: ApiClient) -> List[Dict]:
36
35
  paged_response = api_client.get(next_url)
37
36
  else:
38
37
  params = {
39
- 'limit': 500,
38
+ "limit": 500,
40
39
  }
41
- paged_response = api_client.get_path('/', params)
40
+ paged_response = api_client.get_path("/", params)
42
41
  except OktaError as okta_error:
43
42
  logger.debug(f"Got error while listing applications {okta_error}")
44
43
  break
@@ -73,11 +72,13 @@ def _get_application_assigned_users(api_client: ApiClient, app_id: str) -> List[
73
72
  paged_response = api_client.get(next_url)
74
73
  else:
75
74
  params = {
76
- 'limit': 500,
75
+ "limit": 500,
77
76
  }
78
- paged_response = api_client.get_path(f'/{app_id}/users', params)
77
+ paged_response = api_client.get_path(f"/{app_id}/users", params)
79
78
  except OktaError as okta_error:
80
- logger.debug(f"Got error while going through list application assigned users {okta_error}")
79
+ logger.debug(
80
+ f"Got error while going through list application assigned users {okta_error}",
81
+ )
81
82
  break
82
83
 
83
84
  app_users.append(paged_response.text)
@@ -110,11 +111,13 @@ def _get_application_assigned_groups(api_client: ApiClient, app_id: str) -> List
110
111
  paged_response = api_client.get(next_url)
111
112
  else:
112
113
  params = {
113
- 'limit': 500,
114
+ "limit": 500,
114
115
  }
115
- paged_response = api_client.get_path(f'/{app_id}/groups', params)
116
+ paged_response = api_client.get_path(f"/{app_id}/groups", params)
116
117
  except OktaError as okta_error:
117
- logger.debug(f"Got error while going through list application assigned groups {okta_error}")
118
+ logger.debug(
119
+ f"Got error while going through list application assigned groups {okta_error}",
120
+ )
118
121
  break
119
122
 
120
123
  app_groups.append(paged_response.text)
@@ -130,7 +133,9 @@ def _get_application_assigned_groups(api_client: ApiClient, app_id: str) -> List
130
133
 
131
134
 
132
135
  @timeit
133
- def transform_application_assigned_users_list(assigned_user_list: List[str]) -> List[str]:
136
+ def transform_application_assigned_users_list(
137
+ assigned_user_list: List[str],
138
+ ) -> List[str]:
134
139
  """
135
140
  Transform application users Okta data
136
141
  :param assigned_user_list: Okta data on assigned users
@@ -161,7 +166,9 @@ def transform_application_assigned_users(json_app_data: str) -> List[str]:
161
166
 
162
167
 
163
168
  @timeit
164
- def transform_application_assigned_groups_list(assigned_group_list: List[str]) -> List[Dict]:
169
+ def transform_application_assigned_groups_list(
170
+ assigned_group_list: List[str],
171
+ ) -> List[Dict]:
165
172
  group_list: List[Dict] = []
166
173
 
167
174
  for current in assigned_group_list:
@@ -206,14 +213,16 @@ def transform_okta_application(okta_application: Dict) -> Dict:
206
213
  app_props["label"] = okta_application["label"]
207
214
  if "created" in okta_application and okta_application["created"]:
208
215
  app_props["created"] = datetime.strptime(
209
- okta_application["created"], "%Y-%m-%dT%H:%M:%S.%fZ",
216
+ okta_application["created"],
217
+ "%Y-%m-%dT%H:%M:%S.%fZ",
210
218
  ).strftime("%m/%d/%Y, %H:%M:%S")
211
219
  else:
212
220
  app_props["created"] = None
213
221
 
214
222
  if "lastUpdated" in okta_application and okta_application["lastUpdated"]:
215
223
  app_props["okta_last_updated"] = datetime.strptime(
216
- okta_application["lastUpdated"], "%Y-%m-%dT%H:%M:%S.%fZ",
224
+ okta_application["lastUpdated"],
225
+ "%Y-%m-%dT%H:%M:%S.%fZ",
217
226
  ).strftime("%m/%d/%Y, %H:%M:%S")
218
227
  else:
219
228
  app_props["okta_last_updated"] = None
@@ -222,7 +231,8 @@ def transform_okta_application(okta_application: Dict) -> Dict:
222
231
 
223
232
  if "activated" in okta_application and okta_application["activated"]:
224
233
  app_props["activated"] = datetime.strptime(
225
- okta_application["activated"], "%Y-%m-%dT%H:%M:%S.%fZ",
234
+ okta_application["activated"],
235
+ "%Y-%m-%dT%H:%M:%S.%fZ",
226
236
  ).strftime("%m/%d/%Y, %H:%M:%S")
227
237
  else:
228
238
  app_props["activated"] = None
@@ -234,7 +244,9 @@ def transform_okta_application(okta_application: Dict) -> Dict:
234
244
 
235
245
 
236
246
  @timeit
237
- def transform_okta_application_extract_replyurls(okta_application: Dict) -> Optional[str]:
247
+ def transform_okta_application_extract_replyurls(
248
+ okta_application: Dict,
249
+ ) -> Optional[str]:
238
250
  """
239
251
  Extracts the reply uri information from an okta app
240
252
  """
@@ -247,7 +259,9 @@ def transform_okta_application_extract_replyurls(okta_application: Dict) -> Opti
247
259
 
248
260
  @timeit
249
261
  def _load_okta_applications(
250
- neo4j_session: neo4j.Session, okta_org_id: str, app_list: List[Dict],
262
+ neo4j_session: neo4j.Session,
263
+ okta_org_id: str,
264
+ app_list: List[Dict],
251
265
  okta_update_tag: int,
252
266
  ) -> None:
253
267
  """
@@ -289,7 +303,9 @@ def _load_okta_applications(
289
303
 
290
304
  @timeit
291
305
  def _load_application_user(
292
- neo4j_session: neo4j.Session, app_id: str, user_list: List[str],
306
+ neo4j_session: neo4j.Session,
307
+ app_id: str,
308
+ user_list: List[str],
293
309
  okta_update_tag: int,
294
310
  ) -> None:
295
311
  """
@@ -321,7 +337,9 @@ def _load_application_user(
321
337
 
322
338
  @timeit
323
339
  def _load_application_group(
324
- neo4j_session: neo4j.Session, app_id: str, group_list: List[str],
340
+ neo4j_session: neo4j.Session,
341
+ app_id: str,
342
+ group_list: List[str],
325
343
  okta_update_tag: int,
326
344
  ) -> None:
327
345
  """
@@ -353,7 +371,9 @@ def _load_application_group(
353
371
 
354
372
  @timeit
355
373
  def _load_application_reply_urls(
356
- neo4j_session: neo4j.Session, app_id: str, reply_urls: List[str],
374
+ neo4j_session: neo4j.Session,
375
+ app_id: str,
376
+ reply_urls: List[str],
357
377
  okta_update_tag: int,
358
378
  ) -> None:
359
379
  """
@@ -390,7 +410,9 @@ def _load_application_reply_urls(
390
410
 
391
411
  @timeit
392
412
  def sync_okta_applications(
393
- neo4j_session: neo4j.Session, okta_org_id: str, okta_update_tag: int,
413
+ neo4j_session: neo4j.Session,
414
+ okta_org_id: str,
415
+ okta_update_tag: int,
394
416
  okta_api_key: str,
395
417
  ) -> None:
396
418
  """
@@ -12,19 +12,25 @@ from cartography.client.core.tx import read_list_of_dicts_tx
12
12
  from cartography.client.core.tx import read_single_value_tx
13
13
  from cartography.util import timeit
14
14
 
15
-
16
- AccountRole = namedtuple('AccountRole', ['account_id', 'role_name'])
17
- OktaGroup = namedtuple('OktaGroup', ['group_id', 'group_name'])
18
- GroupRole = namedtuple('GroupRole', ['okta_group_id', 'aws_role_arn'])
15
+ AccountRole = namedtuple("AccountRole", ["account_id", "role_name"])
16
+ OktaGroup = namedtuple("OktaGroup", ["group_id", "group_name"])
17
+ GroupRole = namedtuple("GroupRole", ["okta_group_id", "aws_role_arn"])
19
18
 
20
19
  logger = logging.getLogger(__name__)
21
20
 
22
21
 
23
22
  def _parse_regex(regex_string: str) -> str:
24
- return regex_string.replace("{{accountid}}", "P<accountid>").replace("{{role}}", "P<role>").strip()
23
+ return (
24
+ regex_string.replace("{{accountid}}", "P<accountid>")
25
+ .replace("{{role}}", "P<role>")
26
+ .strip()
27
+ )
25
28
 
26
29
 
27
- def _parse_okta_group_name(okta_group_name: str, mapping_regex: str) -> AccountRole | None:
30
+ def _parse_okta_group_name(
31
+ okta_group_name: str,
32
+ mapping_regex: str,
33
+ ) -> AccountRole | None:
28
34
  """
29
35
  Extract AWS account id and AWS role name from the given Okta group name using the given mapping regex.
30
36
  """
@@ -37,16 +43,25 @@ def _parse_okta_group_name(okta_group_name: str, mapping_regex: str) -> AccountR
37
43
  return None
38
44
 
39
45
 
40
- def transform_okta_group_to_aws_role(group_id: str, group_name: str, mapping_regex: str) -> Optional[Dict]:
46
+ def transform_okta_group_to_aws_role(
47
+ group_id: str,
48
+ group_name: str,
49
+ mapping_regex: str,
50
+ ) -> Optional[Dict]:
41
51
  account_role = _parse_okta_group_name(group_name, mapping_regex)
42
52
  if account_role:
43
- role_arn = f"arn:aws:iam::{account_role.account_id}:role/{account_role.role_name}"
53
+ role_arn = (
54
+ f"arn:aws:iam::{account_role.account_id}:role/{account_role.role_name}"
55
+ )
44
56
  return {"groupid": group_id, "role": role_arn}
45
57
  return None
46
58
 
47
59
 
48
60
  @timeit
49
- def query_for_okta_to_aws_role_mapping(neo4j_session: neo4j.Session, mapping_regex: str) -> List[Dict]:
61
+ def query_for_okta_to_aws_role_mapping(
62
+ neo4j_session: neo4j.Session,
63
+ mapping_regex: str,
64
+ ) -> List[Dict]:
50
65
  """
51
66
  Query the graph for all groups associated with the amazon_aws application and map them to AWSRoles
52
67
  :param neo4j_session: session from the Neo4j server
@@ -61,12 +76,16 @@ def query_for_okta_to_aws_role_mapping(neo4j_session: neo4j.Session, mapping_reg
61
76
  for res in results:
62
77
  has_results = True
63
78
  # input: okta group id, okta group name. output: aws role arn.
64
- mapping = transform_okta_group_to_aws_role(res["group.id"], res["group.name"], mapping_regex)
79
+ mapping = transform_okta_group_to_aws_role(
80
+ res["group.id"],
81
+ res["group.name"],
82
+ mapping_regex,
83
+ )
65
84
  if mapping:
66
85
  group_to_role_mapping.append(mapping)
67
86
 
68
87
  if has_results and not group_to_role_mapping:
69
- logger.warn(
88
+ logger.warning(
70
89
  "AWS Okta Application present, but no mappings were found. "
71
90
  "Please verify the mapping regex is correct",
72
91
  )
@@ -76,7 +95,8 @@ def query_for_okta_to_aws_role_mapping(neo4j_session: neo4j.Session, mapping_reg
76
95
 
77
96
  @timeit
78
97
  def _load_okta_group_to_aws_roles(
79
- neo4j_session: neo4j.Session, group_to_role: List[Dict],
98
+ neo4j_session: neo4j.Session,
99
+ group_to_role: List[Dict],
80
100
  okta_update_tag: int,
81
101
  ) -> None:
82
102
  """
@@ -104,7 +124,10 @@ def _load_okta_group_to_aws_roles(
104
124
 
105
125
 
106
126
  @timeit
107
- def _load_human_can_assume_role(neo4j_session: neo4j.Session, okta_update_tag: int) -> None:
127
+ def _load_human_can_assume_role(
128
+ neo4j_session: neo4j.Session,
129
+ okta_update_tag: int,
130
+ ) -> None:
108
131
  """
109
132
  Add the CAN_ASSUME_ROLE relationship between Humans and the AWSRoles they can assume
110
133
  :param neo4j_session: session with the Neo4j server
@@ -123,7 +146,10 @@ def _load_human_can_assume_role(neo4j_session: neo4j.Session, okta_update_tag: i
123
146
  )
124
147
 
125
148
 
126
- def get_awssso_okta_groups(neo4j_session: neo4j.Session, okta_org_id: str) -> list[OktaGroup]:
149
+ def get_awssso_okta_groups(
150
+ neo4j_session: neo4j.Session,
151
+ okta_org_id: str,
152
+ ) -> list[OktaGroup]:
127
153
  """
128
154
  Return list of all Okta group ids in the current Okta organization tied to Okta Applications with name
129
155
  "amazon_aws_sso".
@@ -133,11 +159,21 @@ def get_awssso_okta_groups(neo4j_session: neo4j.Session, okta_org_id: str) -> li
133
159
  <-[:RESOURCE]-(:OktaOrganization{id: $okta_org_id})
134
160
  RETURN g.id as group_id, g.name as group_name
135
161
  """
136
- result = neo4j_session.read_transaction(read_list_of_dicts_tx, query, okta_org_id=okta_org_id)
137
- return [OktaGroup(group_name=og['group_name'], group_id=og['group_id']) for og in result]
162
+ result = neo4j_session.read_transaction(
163
+ read_list_of_dicts_tx,
164
+ query,
165
+ okta_org_id=okta_org_id,
166
+ )
167
+ return [
168
+ OktaGroup(group_name=og["group_name"], group_id=og["group_id"]) for og in result
169
+ ]
138
170
 
139
171
 
140
- def get_awssso_role_arn(account_id: str, role_hint: str, neo4j_session: neo4j.Session) -> str | None:
172
+ def get_awssso_role_arn(
173
+ account_id: str,
174
+ role_hint: str,
175
+ neo4j_session: neo4j.Session,
176
+ ) -> str | None:
141
177
  """
142
178
  Attempt to return the AWS role ARN for the given AWS account ID and role hint string.
143
179
  This function exists to handle that AWS SSO roles have a 'AWSReservedSSO' prefix and a hashed suffix
@@ -153,13 +189,18 @@ def get_awssso_role_arn(account_id: str, role_hint: str, neo4j_session: neo4j.Se
153
189
  WHERE SPLIT(role.name, '_')[1..-1][0] = $role_hint
154
190
  RETURN role.arn AS role_arn
155
191
  """
156
- return neo4j_session.read_transaction(read_single_value_tx, query, account_id=account_id, role_hint=role_hint)
192
+ return neo4j_session.read_transaction(
193
+ read_single_value_tx,
194
+ query,
195
+ account_id=account_id,
196
+ role_hint=role_hint,
197
+ )
157
198
 
158
199
 
159
200
  def query_for_okta_to_awssso_role_mapping(
160
- neo4j_session: neo4j.Session,
161
- awssso_okta_groups: list[OktaGroup],
162
- mapping_regex: str,
201
+ neo4j_session: neo4j.Session,
202
+ awssso_okta_groups: list[OktaGroup],
203
+ mapping_regex: str,
163
204
  ) -> list[GroupRole]:
164
205
  """
165
206
  Input:
@@ -176,13 +217,21 @@ def query_for_okta_to_awssso_role_mapping(
176
217
  logger.info(f"Okta group {group.group_name} has no associated AWS SSO role")
177
218
  continue
178
219
 
179
- role_arn = get_awssso_role_arn(account_role.account_id, account_role.role_name, neo4j_session)
220
+ role_arn = get_awssso_role_arn(
221
+ account_role.account_id,
222
+ account_role.role_name,
223
+ neo4j_session,
224
+ )
180
225
  if role_arn:
181
226
  result.append(GroupRole(group.group_id, role_arn))
182
227
  return result
183
228
 
184
229
 
185
- def _load_awssso_tx(tx: neo4j.Transaction, group_to_role: list[GroupRole], okta_update_tag: int) -> None:
230
+ def _load_awssso_tx(
231
+ tx: neo4j.Transaction,
232
+ group_to_role: list[GroupRole],
233
+ okta_update_tag: int,
234
+ ) -> None:
186
235
  ingest_statement = """
187
236
  UNWIND $GROUP_TO_ROLE as app_data
188
237
  MATCH (role:AWSRole{arn: app_data.aws_role_arn})
@@ -199,19 +248,19 @@ def _load_awssso_tx(tx: neo4j.Transaction, group_to_role: list[GroupRole], okta_
199
248
 
200
249
 
201
250
  def _load_okta_group_to_awssso_roles(
202
- neo4j_session: neo4j.Session,
203
- group_to_role: list[GroupRole],
204
- okta_update_tag: int,
251
+ neo4j_session: neo4j.Session,
252
+ group_to_role: list[GroupRole],
253
+ okta_update_tag: int,
205
254
  ) -> None:
206
255
  neo4j_session.write_transaction(_load_awssso_tx, group_to_role, okta_update_tag)
207
256
 
208
257
 
209
258
  @timeit
210
259
  def sync_okta_aws_saml(
211
- neo4j_session: neo4j.Session,
212
- mapping_regex: str,
213
- okta_update_tag: int,
214
- okta_org_id: str,
260
+ neo4j_session: neo4j.Session,
261
+ mapping_regex: str,
262
+ okta_update_tag: int,
263
+ okta_org_id: str,
215
264
  ) -> None:
216
265
  """
217
266
  Sync okta integration with saml. This will link OktaGroups to the AWSRoles they enable.
@@ -228,10 +277,21 @@ def sync_okta_aws_saml(
228
277
  logger.info("Syncing Okta SAML Integration")
229
278
 
230
279
  # Query for the aws application and its associated groups
231
- group_to_role_mapping = query_for_okta_to_aws_role_mapping(neo4j_session, mapping_regex)
280
+ group_to_role_mapping = query_for_okta_to_aws_role_mapping(
281
+ neo4j_session,
282
+ mapping_regex,
283
+ )
232
284
  _load_okta_group_to_aws_roles(neo4j_session, group_to_role_mapping, okta_update_tag)
233
285
  _load_human_can_assume_role(neo4j_session, okta_update_tag)
234
286
 
235
287
  sso_okta_groups = get_awssso_okta_groups(neo4j_session, okta_org_id)
236
- group_to_ssorole_mapping = query_for_okta_to_awssso_role_mapping(neo4j_session, sso_okta_groups, mapping_regex)
237
- _load_okta_group_to_awssso_roles(neo4j_session, group_to_ssorole_mapping, okta_update_tag)
288
+ group_to_ssorole_mapping = query_for_okta_to_awssso_role_mapping(
289
+ neo4j_session,
290
+ sso_okta_groups,
291
+ mapping_regex,
292
+ )
293
+ _load_okta_group_to_awssso_roles(
294
+ neo4j_session,
295
+ group_to_ssorole_mapping,
296
+ okta_update_tag,
297
+ )
@@ -79,12 +79,16 @@ def transform_okta_user_factor(okta_factor_info: Factor) -> Dict:
79
79
  factor_props["provider"] = okta_factor_info.provider
80
80
  factor_props["status"] = okta_factor_info.status
81
81
  if okta_factor_info.created:
82
- factor_props["created"] = okta_factor_info.created.strftime("%m/%d/%Y, %H:%M:%S")
82
+ factor_props["created"] = okta_factor_info.created.strftime(
83
+ "%m/%d/%Y, %H:%M:%S",
84
+ )
83
85
  else:
84
86
  factor_props["created"] = None
85
87
 
86
88
  if okta_factor_info.lastUpdated:
87
- factor_props["okta_last_updated"] = okta_factor_info.lastUpdated.strftime("%m/%d/%Y, %H:%M:%S")
89
+ factor_props["okta_last_updated"] = okta_factor_info.lastUpdated.strftime(
90
+ "%m/%d/%Y, %H:%M:%S",
91
+ )
88
92
  else:
89
93
  factor_props["okta_last_updated"] = None
90
94
 
@@ -93,7 +97,12 @@ def transform_okta_user_factor(okta_factor_info: Factor) -> Dict:
93
97
 
94
98
 
95
99
  @timeit
96
- def _load_user_factors(neo4j_session: neo4j.Session, user_id: str, factors: List[Dict], okta_update_tag: int) -> None:
100
+ def _load_user_factors(
101
+ neo4j_session: neo4j.Session,
102
+ user_id: str,
103
+ factors: List[Dict],
104
+ okta_update_tag: int,
105
+ ) -> None:
97
106
  """
98
107
  Add user factors into the graph
99
108
  :param neo4j_session: session with the Neo4j server
@@ -131,7 +140,10 @@ def _load_user_factors(neo4j_session: neo4j.Session, user_id: str, factors: List
131
140
 
132
141
  @timeit
133
142
  def sync_users_factors(
134
- neo4j_session: neo4j.Session, okta_org_id: str, okta_update_tag: int, okta_api_key: str,
143
+ neo4j_session: neo4j.Session,
144
+ okta_org_id: str,
145
+ okta_update_tag: int,
146
+ okta_api_key: str,
135
147
  sync_state: OktaSyncState,
136
148
  ) -> None:
137
149
  """
@@ -39,9 +39,9 @@ def _get_okta_groups(api_client: ApiClient) -> List[str]:
39
39
  paged_response = api_client.get(next_url)
40
40
  else:
41
41
  params = {
42
- 'limit': 10000,
42
+ "limit": 10000,
43
43
  }
44
- paged_response = api_client.get_path('/', params)
44
+ paged_response = api_client.get_path("/", params)
45
45
 
46
46
  paged_results = PagedResults(paged_response, UserGroup)
47
47
 
@@ -75,9 +75,9 @@ def get_okta_group_members(api_client: ApiClient, group_id: str) -> List[Dict]:
75
75
  paged_response = api_client.get(next_url)
76
76
  else:
77
77
  params = {
78
- 'limit': 1000,
78
+ "limit": 1000,
79
79
  }
80
- paged_response = api_client.get_path(f'/{group_id}/users', params)
80
+ paged_response = api_client.get_path(f"/{group_id}/users", params)
81
81
  except OktaError:
82
82
  logger.error(f"OktaError while listing members of group {group_id}")
83
83
  raise
@@ -95,7 +95,9 @@ def get_okta_group_members(api_client: ApiClient, group_id: str) -> List[Dict]:
95
95
 
96
96
 
97
97
  @timeit
98
- def transform_okta_group_list(okta_group_list: List[UserGroup]) -> Tuple[List[Dict], List[str]]:
98
+ def transform_okta_group_list(
99
+ okta_group_list: List[UserGroup],
100
+ ) -> Tuple[List[Dict], List[str]]:
99
101
  groups: List[Dict] = []
100
102
  groups_id: List[str] = []
101
103
 
@@ -128,7 +130,9 @@ def transform_okta_group(okta_group: UserGroup) -> Dict:
128
130
  group_props["dn"] = None
129
131
 
130
132
  if okta_group.profile.windowsDomainQualifiedName:
131
- group_props["windows_domain_qualified_name"] = okta_group.profile.windowsDomainQualifiedName
133
+ group_props["windows_domain_qualified_name"] = (
134
+ okta_group.profile.windowsDomainQualifiedName
135
+ )
132
136
  else:
133
137
  group_props["windows_domain_qualified_name"] = None
134
138
 
@@ -146,27 +150,31 @@ def transform_okta_group_member_list(okta_member_list: List[Dict]) -> List[Dict]
146
150
  """
147
151
  transformed_member_list: List[Dict] = []
148
152
  for user in okta_member_list:
149
- transformed_member_list.append({
150
- 'first_name': user['profile']['firstName'],
151
- 'last_name': user['profile']['lastName'],
152
- 'login': user['profile']['login'],
153
- 'email': user['profile']['email'],
154
- 'second_email': user['profile'].get('secondEmail'),
155
- 'id': user['id'],
156
- 'created': user['created'],
157
- 'activated': user.get('activated'),
158
- 'status_changed': user.get('status_changed'),
159
- 'last_login': user.get('last_login'),
160
- 'okta_last_updated': user.get('okta_last_updated'),
161
- 'password_changed': user.get('password_changed'),
162
- 'transition_to_status': user.get('transitioningToStatus'),
163
- })
153
+ transformed_member_list.append(
154
+ {
155
+ "first_name": user["profile"]["firstName"],
156
+ "last_name": user["profile"]["lastName"],
157
+ "login": user["profile"]["login"],
158
+ "email": user["profile"]["email"],
159
+ "second_email": user["profile"].get("secondEmail"),
160
+ "id": user["id"],
161
+ "created": user["created"],
162
+ "activated": user.get("activated"),
163
+ "status_changed": user.get("status_changed"),
164
+ "last_login": user.get("last_login"),
165
+ "okta_last_updated": user.get("okta_last_updated"),
166
+ "password_changed": user.get("password_changed"),
167
+ "transition_to_status": user.get("transitioningToStatus"),
168
+ },
169
+ )
164
170
  return transformed_member_list
165
171
 
166
172
 
167
173
  @timeit
168
174
  def _load_okta_groups(
169
- neo4j_session: neo4j.Session, okta_org_id: str, group_list: List[Dict],
175
+ neo4j_session: neo4j.Session,
176
+ okta_org_id: str,
177
+ group_list: List[Dict],
170
178
  okta_update_tag: int,
171
179
  ) -> None:
172
180
  """
@@ -206,7 +214,9 @@ def _load_okta_groups(
206
214
 
207
215
  @timeit
208
216
  def load_okta_group_members(
209
- neo4j_session: neo4j.Session, group_id: str, member_list: List[Dict],
217
+ neo4j_session: neo4j.Session,
218
+ group_id: str,
219
+ member_list: List[Dict],
210
220
  okta_update_tag: int,
211
221
  ) -> None:
212
222
  """
@@ -240,7 +250,7 @@ def load_okta_group_members(
240
250
  ON CREATE SET r.firstseen = timestamp()
241
251
  SET r.lastupdated = $okta_update_tag
242
252
  """
243
- logging.info(f'Loading {len(member_list)} members of group {group_id}')
253
+ logging.info(f"Loading {len(member_list)} members of group {group_id}")
244
254
  neo4j_session.run(
245
255
  ingest,
246
256
  GROUP_ID=group_id,
@@ -251,7 +261,9 @@ def load_okta_group_members(
251
261
 
252
262
  @timeit
253
263
  def sync_okta_group_membership(
254
- neo4j_session: neo4j.Session, api_client: ApiClient, group_list_info: List[Dict],
264
+ neo4j_session: neo4j.Session,
265
+ api_client: ApiClient,
266
+ group_list_info: List[Dict],
255
267
  okta_update_tag: int,
256
268
  ) -> None:
257
269
  """
@@ -266,13 +278,23 @@ def sync_okta_group_membership(
266
278
  for group_info in group_list_info:
267
279
  group_id = group_info["id"]
268
280
  members_data: List[Dict] = get_okta_group_members(api_client, group_id)
269
- transformed_member_data: List[Dict] = transform_okta_group_member_list(members_data)
270
- load_okta_group_members(neo4j_session, group_id, transformed_member_data, okta_update_tag)
281
+ transformed_member_data: List[Dict] = transform_okta_group_member_list(
282
+ members_data,
283
+ )
284
+ load_okta_group_members(
285
+ neo4j_session,
286
+ group_id,
287
+ transformed_member_data,
288
+ okta_update_tag,
289
+ )
271
290
 
272
291
 
273
292
  @timeit
274
293
  def sync_okta_groups(
275
- neo4_session: neo4j.Session, okta_org_id: str, okta_update_tag: int, okta_api_key: str,
294
+ neo4_session: neo4j.Session,
295
+ okta_org_id: str,
296
+ okta_update_tag: int,
297
+ okta_api_key: str,
276
298
  sync_state: OktaSyncState,
277
299
  ) -> None:
278
300
  """
@@ -295,4 +317,9 @@ def sync_okta_groups(
295
317
 
296
318
  _load_okta_groups(neo4_session, okta_org_id, group_list_info, okta_update_tag)
297
319
 
298
- sync_okta_group_membership(neo4_session, api_client, group_list_info, okta_update_tag)
320
+ sync_okta_group_membership(
321
+ neo4_session,
322
+ api_client,
323
+ group_list_info,
324
+ okta_update_tag,
325
+ )
@@ -9,7 +9,11 @@ logger = logging.getLogger(__name__)
9
9
 
10
10
 
11
11
  @timeit
12
- def create_okta_organization(neo4j_session: neo4j.Session, organization: str, okta_update_tag: int) -> None:
12
+ def create_okta_organization(
13
+ neo4j_session: neo4j.Session,
14
+ organization: str,
15
+ okta_update_tag: int,
16
+ ) -> None:
13
17
  """
14
18
  Create Okta organization in the graph
15
19
  :param neo4_session: session with the Neo4j server
@@ -61,7 +61,9 @@ def transform_trusted_origins(data: str) -> List[Dict]:
61
61
 
62
62
  @timeit
63
63
  def _load_trusted_origins(
64
- neo4j_session: neo4j.Session, okta_org_id: str, trusted_list: List[Dict],
64
+ neo4j_session: neo4j.Session,
65
+ okta_org_id: str,
66
+ trusted_list: List[Dict],
65
67
  okta_update_tag: int,
66
68
  ) -> None:
67
69
  """
@@ -104,7 +106,9 @@ def _load_trusted_origins(
104
106
 
105
107
  @timeit
106
108
  def sync_trusted_origins(
107
- neo4j_session: neo4j.Session, okta_org_id: str, okta_update_tag: int,
109
+ neo4j_session: neo4j.Session,
110
+ okta_org_id: str,
111
+ okta_update_tag: int,
108
112
  okta_api_key: str,
109
113
  ) -> None:
110
114
  """