regscale-cli 6.16.0.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 regscale-cli might be problematic. Click here for more details.

Files changed (481) hide show
  1. regscale/__init__.py +1 -0
  2. regscale/airflow/__init__.py +9 -0
  3. regscale/airflow/azure/__init__.py +9 -0
  4. regscale/airflow/azure/cli.py +89 -0
  5. regscale/airflow/azure/upload_dags.py +116 -0
  6. regscale/airflow/click_dags.py +127 -0
  7. regscale/airflow/click_mixins.py +82 -0
  8. regscale/airflow/config.py +25 -0
  9. regscale/airflow/factories/__init__.py +0 -0
  10. regscale/airflow/factories/connections.py +58 -0
  11. regscale/airflow/factories/workflows.py +78 -0
  12. regscale/airflow/hierarchy.py +88 -0
  13. regscale/airflow/operators/__init__.py +0 -0
  14. regscale/airflow/operators/click.py +36 -0
  15. regscale/airflow/sensors/__init__.py +0 -0
  16. regscale/airflow/sensors/sql.py +107 -0
  17. regscale/airflow/sessions/__init__.py +0 -0
  18. regscale/airflow/sessions/sql/__init__.py +3 -0
  19. regscale/airflow/sessions/sql/queries.py +64 -0
  20. regscale/airflow/sessions/sql/sql_server_queries.py +248 -0
  21. regscale/airflow/tasks/__init__.py +0 -0
  22. regscale/airflow/tasks/branches.py +22 -0
  23. regscale/airflow/tasks/cli.py +116 -0
  24. regscale/airflow/tasks/click.py +73 -0
  25. regscale/airflow/tasks/debugging.py +9 -0
  26. regscale/airflow/tasks/groups.py +116 -0
  27. regscale/airflow/tasks/init.py +60 -0
  28. regscale/airflow/tasks/states.py +47 -0
  29. regscale/airflow/tasks/workflows.py +36 -0
  30. regscale/ansible/__init__.py +9 -0
  31. regscale/core/__init__.py +0 -0
  32. regscale/core/app/__init__.py +3 -0
  33. regscale/core/app/api.py +571 -0
  34. regscale/core/app/application.py +665 -0
  35. regscale/core/app/internal/__init__.py +136 -0
  36. regscale/core/app/internal/admin_actions.py +230 -0
  37. regscale/core/app/internal/assessments_editor.py +873 -0
  38. regscale/core/app/internal/catalog.py +316 -0
  39. regscale/core/app/internal/comparison.py +459 -0
  40. regscale/core/app/internal/control_editor.py +571 -0
  41. regscale/core/app/internal/encrypt.py +79 -0
  42. regscale/core/app/internal/evidence.py +1240 -0
  43. regscale/core/app/internal/file_uploads.py +151 -0
  44. regscale/core/app/internal/healthcheck.py +66 -0
  45. regscale/core/app/internal/login.py +305 -0
  46. regscale/core/app/internal/migrations.py +240 -0
  47. regscale/core/app/internal/model_editor.py +1701 -0
  48. regscale/core/app/internal/poam_editor.py +632 -0
  49. regscale/core/app/internal/workflow.py +105 -0
  50. regscale/core/app/logz.py +74 -0
  51. regscale/core/app/utils/XMLIR.py +258 -0
  52. regscale/core/app/utils/__init__.py +0 -0
  53. regscale/core/app/utils/api_handler.py +358 -0
  54. regscale/core/app/utils/app_utils.py +1110 -0
  55. regscale/core/app/utils/catalog_utils/__init__.py +0 -0
  56. regscale/core/app/utils/catalog_utils/common.py +91 -0
  57. regscale/core/app/utils/catalog_utils/compare_catalog.py +193 -0
  58. regscale/core/app/utils/catalog_utils/diagnostic_catalog.py +97 -0
  59. regscale/core/app/utils/catalog_utils/download_catalog.py +103 -0
  60. regscale/core/app/utils/catalog_utils/update_catalog.py +718 -0
  61. regscale/core/app/utils/catalog_utils/update_catalog_v2.py +1378 -0
  62. regscale/core/app/utils/catalog_utils/update_catalog_v3.py +1272 -0
  63. regscale/core/app/utils/catalog_utils/update_plans.py +334 -0
  64. regscale/core/app/utils/file_utils.py +238 -0
  65. regscale/core/app/utils/parser_utils.py +81 -0
  66. regscale/core/app/utils/pickle_file_handler.py +57 -0
  67. regscale/core/app/utils/regscale_utils.py +319 -0
  68. regscale/core/app/utils/report_utils.py +119 -0
  69. regscale/core/app/utils/variables.py +226 -0
  70. regscale/core/decorators.py +31 -0
  71. regscale/core/lazy_group.py +65 -0
  72. regscale/core/login.py +63 -0
  73. regscale/core/server/__init__.py +0 -0
  74. regscale/core/server/flask_api.py +473 -0
  75. regscale/core/server/helpers.py +373 -0
  76. regscale/core/server/rest.py +64 -0
  77. regscale/core/server/static/css/bootstrap.css +6030 -0
  78. regscale/core/server/static/css/bootstrap.min.css +6 -0
  79. regscale/core/server/static/css/main.css +176 -0
  80. regscale/core/server/static/images/regscale-cli.svg +49 -0
  81. regscale/core/server/static/images/regscale.svg +38 -0
  82. regscale/core/server/templates/base.html +74 -0
  83. regscale/core/server/templates/index.html +43 -0
  84. regscale/core/server/templates/login.html +28 -0
  85. regscale/core/server/templates/make_base64.html +22 -0
  86. regscale/core/server/templates/upload_STIG.html +109 -0
  87. regscale/core/server/templates/upload_STIG_result.html +26 -0
  88. regscale/core/server/templates/upload_ssp.html +144 -0
  89. regscale/core/server/templates/upload_ssp_result.html +128 -0
  90. regscale/core/static/__init__.py +0 -0
  91. regscale/core/static/regex.py +14 -0
  92. regscale/core/utils/__init__.py +117 -0
  93. regscale/core/utils/click_utils.py +13 -0
  94. regscale/core/utils/date.py +238 -0
  95. regscale/core/utils/graphql.py +254 -0
  96. regscale/core/utils/urls.py +23 -0
  97. regscale/dev/__init__.py +6 -0
  98. regscale/dev/analysis.py +454 -0
  99. regscale/dev/cli.py +235 -0
  100. regscale/dev/code_gen.py +492 -0
  101. regscale/dev/dirs.py +69 -0
  102. regscale/dev/docs.py +384 -0
  103. regscale/dev/monitoring.py +26 -0
  104. regscale/dev/profiling.py +216 -0
  105. regscale/exceptions/__init__.py +4 -0
  106. regscale/exceptions/license_exception.py +7 -0
  107. regscale/exceptions/validation_exception.py +9 -0
  108. regscale/integrations/__init__.py +1 -0
  109. regscale/integrations/commercial/__init__.py +486 -0
  110. regscale/integrations/commercial/ad.py +433 -0
  111. regscale/integrations/commercial/amazon/__init__.py +0 -0
  112. regscale/integrations/commercial/amazon/common.py +106 -0
  113. regscale/integrations/commercial/aqua/__init__.py +0 -0
  114. regscale/integrations/commercial/aqua/aqua.py +91 -0
  115. regscale/integrations/commercial/aws/__init__.py +6 -0
  116. regscale/integrations/commercial/aws/cli.py +322 -0
  117. regscale/integrations/commercial/aws/inventory/__init__.py +110 -0
  118. regscale/integrations/commercial/aws/inventory/base.py +64 -0
  119. regscale/integrations/commercial/aws/inventory/resources/__init__.py +19 -0
  120. regscale/integrations/commercial/aws/inventory/resources/compute.py +234 -0
  121. regscale/integrations/commercial/aws/inventory/resources/containers.py +113 -0
  122. regscale/integrations/commercial/aws/inventory/resources/database.py +101 -0
  123. regscale/integrations/commercial/aws/inventory/resources/integration.py +237 -0
  124. regscale/integrations/commercial/aws/inventory/resources/networking.py +253 -0
  125. regscale/integrations/commercial/aws/inventory/resources/security.py +240 -0
  126. regscale/integrations/commercial/aws/inventory/resources/storage.py +91 -0
  127. regscale/integrations/commercial/aws/scanner.py +823 -0
  128. regscale/integrations/commercial/azure/__init__.py +0 -0
  129. regscale/integrations/commercial/azure/common.py +32 -0
  130. regscale/integrations/commercial/azure/intune.py +488 -0
  131. regscale/integrations/commercial/azure/scanner.py +49 -0
  132. regscale/integrations/commercial/burp.py +78 -0
  133. regscale/integrations/commercial/cpe.py +144 -0
  134. regscale/integrations/commercial/crowdstrike.py +1117 -0
  135. regscale/integrations/commercial/defender.py +1511 -0
  136. regscale/integrations/commercial/dependabot.py +210 -0
  137. regscale/integrations/commercial/durosuite/__init__.py +0 -0
  138. regscale/integrations/commercial/durosuite/api.py +1546 -0
  139. regscale/integrations/commercial/durosuite/process_devices.py +101 -0
  140. regscale/integrations/commercial/durosuite/scanner.py +637 -0
  141. regscale/integrations/commercial/durosuite/variables.py +21 -0
  142. regscale/integrations/commercial/ecr.py +90 -0
  143. regscale/integrations/commercial/gcp/__init__.py +237 -0
  144. regscale/integrations/commercial/gcp/auth.py +96 -0
  145. regscale/integrations/commercial/gcp/control_tests.py +238 -0
  146. regscale/integrations/commercial/gcp/variables.py +18 -0
  147. regscale/integrations/commercial/gitlab.py +332 -0
  148. regscale/integrations/commercial/grype.py +165 -0
  149. regscale/integrations/commercial/ibm.py +90 -0
  150. regscale/integrations/commercial/import_all/__init__.py +0 -0
  151. regscale/integrations/commercial/import_all/import_all_cmd.py +467 -0
  152. regscale/integrations/commercial/import_all/scan_file_fingerprints.json +27 -0
  153. regscale/integrations/commercial/jira.py +1046 -0
  154. regscale/integrations/commercial/mappings/__init__.py +0 -0
  155. regscale/integrations/commercial/mappings/csf_controls.json +713 -0
  156. regscale/integrations/commercial/mappings/nist_800_53_r5_controls.json +1516 -0
  157. regscale/integrations/commercial/nessus/__init__.py +0 -0
  158. regscale/integrations/commercial/nessus/nessus_utils.py +429 -0
  159. regscale/integrations/commercial/nessus/scanner.py +416 -0
  160. regscale/integrations/commercial/nexpose.py +90 -0
  161. regscale/integrations/commercial/okta.py +798 -0
  162. regscale/integrations/commercial/opentext/__init__.py +0 -0
  163. regscale/integrations/commercial/opentext/click.py +99 -0
  164. regscale/integrations/commercial/opentext/scanner.py +143 -0
  165. regscale/integrations/commercial/prisma.py +91 -0
  166. regscale/integrations/commercial/qualys.py +1462 -0
  167. regscale/integrations/commercial/salesforce.py +980 -0
  168. regscale/integrations/commercial/sap/__init__.py +0 -0
  169. regscale/integrations/commercial/sap/click.py +31 -0
  170. regscale/integrations/commercial/sap/sysdig/__init__.py +0 -0
  171. regscale/integrations/commercial/sap/sysdig/click.py +57 -0
  172. regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +190 -0
  173. regscale/integrations/commercial/sap/tenable/__init__.py +0 -0
  174. regscale/integrations/commercial/sap/tenable/click.py +49 -0
  175. regscale/integrations/commercial/sap/tenable/scanner.py +196 -0
  176. regscale/integrations/commercial/servicenow.py +1756 -0
  177. regscale/integrations/commercial/sicura/__init__.py +0 -0
  178. regscale/integrations/commercial/sicura/api.py +855 -0
  179. regscale/integrations/commercial/sicura/commands.py +73 -0
  180. regscale/integrations/commercial/sicura/scanner.py +481 -0
  181. regscale/integrations/commercial/sicura/variables.py +16 -0
  182. regscale/integrations/commercial/snyk.py +90 -0
  183. regscale/integrations/commercial/sonarcloud.py +260 -0
  184. regscale/integrations/commercial/sqlserver.py +369 -0
  185. regscale/integrations/commercial/stig_mapper_integration/__init__.py +0 -0
  186. regscale/integrations/commercial/stig_mapper_integration/click_commands.py +38 -0
  187. regscale/integrations/commercial/stig_mapper_integration/mapping_engine.py +353 -0
  188. regscale/integrations/commercial/stigv2/__init__.py +0 -0
  189. regscale/integrations/commercial/stigv2/ckl_parser.py +349 -0
  190. regscale/integrations/commercial/stigv2/click_commands.py +95 -0
  191. regscale/integrations/commercial/stigv2/stig_integration.py +202 -0
  192. regscale/integrations/commercial/synqly/__init__.py +0 -0
  193. regscale/integrations/commercial/synqly/assets.py +46 -0
  194. regscale/integrations/commercial/synqly/ticketing.py +132 -0
  195. regscale/integrations/commercial/synqly/vulnerabilities.py +223 -0
  196. regscale/integrations/commercial/synqly_jira.py +840 -0
  197. regscale/integrations/commercial/tenablev2/__init__.py +0 -0
  198. regscale/integrations/commercial/tenablev2/authenticate.py +31 -0
  199. regscale/integrations/commercial/tenablev2/click.py +1584 -0
  200. regscale/integrations/commercial/tenablev2/scanner.py +504 -0
  201. regscale/integrations/commercial/tenablev2/stig_parsers.py +140 -0
  202. regscale/integrations/commercial/tenablev2/utils.py +78 -0
  203. regscale/integrations/commercial/tenablev2/variables.py +17 -0
  204. regscale/integrations/commercial/trivy.py +162 -0
  205. regscale/integrations/commercial/veracode.py +96 -0
  206. regscale/integrations/commercial/wizv2/WizDataMixin.py +97 -0
  207. regscale/integrations/commercial/wizv2/__init__.py +0 -0
  208. regscale/integrations/commercial/wizv2/click.py +429 -0
  209. regscale/integrations/commercial/wizv2/constants.py +1001 -0
  210. regscale/integrations/commercial/wizv2/issue.py +361 -0
  211. regscale/integrations/commercial/wizv2/models.py +112 -0
  212. regscale/integrations/commercial/wizv2/parsers.py +339 -0
  213. regscale/integrations/commercial/wizv2/sbom.py +115 -0
  214. regscale/integrations/commercial/wizv2/scanner.py +416 -0
  215. regscale/integrations/commercial/wizv2/utils.py +796 -0
  216. regscale/integrations/commercial/wizv2/variables.py +39 -0
  217. regscale/integrations/commercial/wizv2/wiz_auth.py +159 -0
  218. regscale/integrations/commercial/xray.py +91 -0
  219. regscale/integrations/integration/__init__.py +2 -0
  220. regscale/integrations/integration/integration.py +26 -0
  221. regscale/integrations/integration/inventory.py +17 -0
  222. regscale/integrations/integration/issue.py +100 -0
  223. regscale/integrations/integration_override.py +149 -0
  224. regscale/integrations/public/__init__.py +103 -0
  225. regscale/integrations/public/cisa.py +641 -0
  226. regscale/integrations/public/criticality_updater.py +70 -0
  227. regscale/integrations/public/emass.py +411 -0
  228. regscale/integrations/public/emass_slcm_import.py +697 -0
  229. regscale/integrations/public/fedramp/__init__.py +0 -0
  230. regscale/integrations/public/fedramp/appendix_parser.py +548 -0
  231. regscale/integrations/public/fedramp/click.py +479 -0
  232. regscale/integrations/public/fedramp/components.py +714 -0
  233. regscale/integrations/public/fedramp/docx_parser.py +259 -0
  234. regscale/integrations/public/fedramp/fedramp_cis_crm.py +1124 -0
  235. regscale/integrations/public/fedramp/fedramp_common.py +3181 -0
  236. regscale/integrations/public/fedramp/fedramp_docx.py +388 -0
  237. regscale/integrations/public/fedramp/fedramp_five.py +2343 -0
  238. regscale/integrations/public/fedramp/fedramp_traversal.py +138 -0
  239. regscale/integrations/public/fedramp/import_fedramp_r4_ssp.py +279 -0
  240. regscale/integrations/public/fedramp/import_workbook.py +495 -0
  241. regscale/integrations/public/fedramp/inventory_items.py +244 -0
  242. regscale/integrations/public/fedramp/mappings/__init__.py +0 -0
  243. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +7388 -0
  244. regscale/integrations/public/fedramp/mappings/fedramp_r5_params.json +8636 -0
  245. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +9605 -0
  246. regscale/integrations/public/fedramp/mappings/system_roles.py +34 -0
  247. regscale/integrations/public/fedramp/mappings/user.py +175 -0
  248. regscale/integrations/public/fedramp/mappings/values.py +141 -0
  249. regscale/integrations/public/fedramp/markdown_parser.py +150 -0
  250. regscale/integrations/public/fedramp/metadata.py +689 -0
  251. regscale/integrations/public/fedramp/models/__init__.py +59 -0
  252. regscale/integrations/public/fedramp/models/leveraged_auth_new.py +168 -0
  253. regscale/integrations/public/fedramp/models/poam_importer.py +522 -0
  254. regscale/integrations/public/fedramp/parts_mapper.py +107 -0
  255. regscale/integrations/public/fedramp/poam/__init__.py +0 -0
  256. regscale/integrations/public/fedramp/poam/scanner.py +851 -0
  257. regscale/integrations/public/fedramp/properties.py +201 -0
  258. regscale/integrations/public/fedramp/reporting.py +84 -0
  259. regscale/integrations/public/fedramp/resources.py +496 -0
  260. regscale/integrations/public/fedramp/rosetta.py +110 -0
  261. regscale/integrations/public/fedramp/ssp_logger.py +87 -0
  262. regscale/integrations/public/fedramp/system_characteristics.py +922 -0
  263. regscale/integrations/public/fedramp/system_control_implementations.py +582 -0
  264. regscale/integrations/public/fedramp/system_implementation.py +190 -0
  265. regscale/integrations/public/fedramp/xml_utils.py +87 -0
  266. regscale/integrations/public/nist_catalog.py +275 -0
  267. regscale/integrations/public/oscal.py +1946 -0
  268. regscale/integrations/public/otx.py +169 -0
  269. regscale/integrations/scanner_integration.py +2692 -0
  270. regscale/integrations/variables.py +25 -0
  271. regscale/models/__init__.py +7 -0
  272. regscale/models/app_models/__init__.py +5 -0
  273. regscale/models/app_models/catalog_compare.py +213 -0
  274. regscale/models/app_models/click.py +252 -0
  275. regscale/models/app_models/datetime_encoder.py +21 -0
  276. regscale/models/app_models/import_validater.py +321 -0
  277. regscale/models/app_models/mapping.py +260 -0
  278. regscale/models/app_models/pipeline.py +37 -0
  279. regscale/models/click_models.py +413 -0
  280. regscale/models/config.py +154 -0
  281. regscale/models/email_style.css +67 -0
  282. regscale/models/hierarchy.py +8 -0
  283. regscale/models/inspect_models.py +79 -0
  284. regscale/models/integration_models/__init__.py +0 -0
  285. regscale/models/integration_models/amazon_models/__init__.py +0 -0
  286. regscale/models/integration_models/amazon_models/inspector.py +262 -0
  287. regscale/models/integration_models/amazon_models/inspector_scan.py +206 -0
  288. regscale/models/integration_models/aqua.py +247 -0
  289. regscale/models/integration_models/azure_alerts.py +255 -0
  290. regscale/models/integration_models/base64.py +23 -0
  291. regscale/models/integration_models/burp.py +433 -0
  292. regscale/models/integration_models/burp_models.py +128 -0
  293. regscale/models/integration_models/cisa_kev_data.json +19333 -0
  294. regscale/models/integration_models/defender_data.py +93 -0
  295. regscale/models/integration_models/defenderimport.py +143 -0
  296. regscale/models/integration_models/drf.py +443 -0
  297. regscale/models/integration_models/ecr_models/__init__.py +0 -0
  298. regscale/models/integration_models/ecr_models/data.py +69 -0
  299. regscale/models/integration_models/ecr_models/ecr.py +239 -0
  300. regscale/models/integration_models/flat_file_importer.py +1079 -0
  301. regscale/models/integration_models/grype_import.py +247 -0
  302. regscale/models/integration_models/ibm.py +126 -0
  303. regscale/models/integration_models/implementation_results.py +85 -0
  304. regscale/models/integration_models/nexpose.py +140 -0
  305. regscale/models/integration_models/prisma.py +202 -0
  306. regscale/models/integration_models/qualys.py +720 -0
  307. regscale/models/integration_models/qualys_scanner.py +160 -0
  308. regscale/models/integration_models/sbom/__init__.py +0 -0
  309. regscale/models/integration_models/sbom/cyclone_dx.py +139 -0
  310. regscale/models/integration_models/send_reminders.py +620 -0
  311. regscale/models/integration_models/snyk.py +155 -0
  312. regscale/models/integration_models/synqly_models/__init__.py +0 -0
  313. regscale/models/integration_models/synqly_models/capabilities.json +1 -0
  314. regscale/models/integration_models/synqly_models/connector_types.py +22 -0
  315. regscale/models/integration_models/synqly_models/connectors/__init__.py +7 -0
  316. regscale/models/integration_models/synqly_models/connectors/assets.py +97 -0
  317. regscale/models/integration_models/synqly_models/connectors/ticketing.py +583 -0
  318. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +169 -0
  319. regscale/models/integration_models/synqly_models/ocsf_mapper.py +331 -0
  320. regscale/models/integration_models/synqly_models/param.py +72 -0
  321. regscale/models/integration_models/synqly_models/synqly_model.py +733 -0
  322. regscale/models/integration_models/synqly_models/tenants.py +39 -0
  323. regscale/models/integration_models/tenable_models/__init__.py +0 -0
  324. regscale/models/integration_models/tenable_models/integration.py +187 -0
  325. regscale/models/integration_models/tenable_models/models.py +513 -0
  326. regscale/models/integration_models/trivy_import.py +231 -0
  327. regscale/models/integration_models/veracode.py +217 -0
  328. regscale/models/integration_models/xray.py +135 -0
  329. regscale/models/locking.py +100 -0
  330. regscale/models/platform.py +110 -0
  331. regscale/models/regscale_models/__init__.py +67 -0
  332. regscale/models/regscale_models/assessment.py +570 -0
  333. regscale/models/regscale_models/assessment_plan.py +52 -0
  334. regscale/models/regscale_models/asset.py +567 -0
  335. regscale/models/regscale_models/asset_mapping.py +190 -0
  336. regscale/models/regscale_models/case.py +42 -0
  337. regscale/models/regscale_models/catalog.py +261 -0
  338. regscale/models/regscale_models/cci.py +46 -0
  339. regscale/models/regscale_models/change.py +167 -0
  340. regscale/models/regscale_models/checklist.py +372 -0
  341. regscale/models/regscale_models/comment.py +49 -0
  342. regscale/models/regscale_models/compliance_settings.py +112 -0
  343. regscale/models/regscale_models/component.py +412 -0
  344. regscale/models/regscale_models/component_mapping.py +65 -0
  345. regscale/models/regscale_models/control.py +38 -0
  346. regscale/models/regscale_models/control_implementation.py +1128 -0
  347. regscale/models/regscale_models/control_objective.py +261 -0
  348. regscale/models/regscale_models/control_parameter.py +100 -0
  349. regscale/models/regscale_models/control_test.py +34 -0
  350. regscale/models/regscale_models/control_test_plan.py +75 -0
  351. regscale/models/regscale_models/control_test_result.py +52 -0
  352. regscale/models/regscale_models/custom_field.py +245 -0
  353. regscale/models/regscale_models/data.py +109 -0
  354. regscale/models/regscale_models/data_center.py +40 -0
  355. regscale/models/regscale_models/deviation.py +203 -0
  356. regscale/models/regscale_models/email.py +97 -0
  357. regscale/models/regscale_models/evidence.py +47 -0
  358. regscale/models/regscale_models/evidence_mapping.py +40 -0
  359. regscale/models/regscale_models/facility.py +59 -0
  360. regscale/models/regscale_models/file.py +382 -0
  361. regscale/models/regscale_models/filetag.py +37 -0
  362. regscale/models/regscale_models/form_field_value.py +94 -0
  363. regscale/models/regscale_models/group.py +169 -0
  364. regscale/models/regscale_models/implementation_objective.py +335 -0
  365. regscale/models/regscale_models/implementation_option.py +275 -0
  366. regscale/models/regscale_models/implementation_role.py +33 -0
  367. regscale/models/regscale_models/incident.py +177 -0
  368. regscale/models/regscale_models/interconnection.py +43 -0
  369. regscale/models/regscale_models/issue.py +1176 -0
  370. regscale/models/regscale_models/leveraged_authorization.py +125 -0
  371. regscale/models/regscale_models/line_of_inquiry.py +52 -0
  372. regscale/models/regscale_models/link.py +205 -0
  373. regscale/models/regscale_models/meta_data.py +64 -0
  374. regscale/models/regscale_models/mixins/__init__.py +0 -0
  375. regscale/models/regscale_models/mixins/parent_cache.py +124 -0
  376. regscale/models/regscale_models/module.py +224 -0
  377. regscale/models/regscale_models/modules.py +191 -0
  378. regscale/models/regscale_models/objective.py +14 -0
  379. regscale/models/regscale_models/parameter.py +87 -0
  380. regscale/models/regscale_models/ports_protocol.py +81 -0
  381. regscale/models/regscale_models/privacy.py +89 -0
  382. regscale/models/regscale_models/profile.py +50 -0
  383. regscale/models/regscale_models/profile_link.py +68 -0
  384. regscale/models/regscale_models/profile_mapping.py +124 -0
  385. regscale/models/regscale_models/project.py +63 -0
  386. regscale/models/regscale_models/property.py +278 -0
  387. regscale/models/regscale_models/question.py +85 -0
  388. regscale/models/regscale_models/questionnaire.py +87 -0
  389. regscale/models/regscale_models/questionnaire_instance.py +177 -0
  390. regscale/models/regscale_models/rbac.py +132 -0
  391. regscale/models/regscale_models/reference.py +86 -0
  392. regscale/models/regscale_models/regscale_model.py +1643 -0
  393. regscale/models/regscale_models/requirement.py +29 -0
  394. regscale/models/regscale_models/risk.py +274 -0
  395. regscale/models/regscale_models/sbom.py +54 -0
  396. regscale/models/regscale_models/scan_history.py +436 -0
  397. regscale/models/regscale_models/search.py +53 -0
  398. regscale/models/regscale_models/security_control.py +132 -0
  399. regscale/models/regscale_models/security_plan.py +204 -0
  400. regscale/models/regscale_models/software_inventory.py +159 -0
  401. regscale/models/regscale_models/stake_holder.py +64 -0
  402. regscale/models/regscale_models/stig.py +647 -0
  403. regscale/models/regscale_models/supply_chain.py +152 -0
  404. regscale/models/regscale_models/system_role.py +188 -0
  405. regscale/models/regscale_models/system_role_external_assignment.py +40 -0
  406. regscale/models/regscale_models/tag.py +37 -0
  407. regscale/models/regscale_models/tag_mapping.py +19 -0
  408. regscale/models/regscale_models/task.py +133 -0
  409. regscale/models/regscale_models/threat.py +196 -0
  410. regscale/models/regscale_models/user.py +175 -0
  411. regscale/models/regscale_models/user_group.py +55 -0
  412. regscale/models/regscale_models/vulnerability.py +242 -0
  413. regscale/models/regscale_models/vulnerability_mapping.py +162 -0
  414. regscale/models/regscale_models/workflow.py +55 -0
  415. regscale/models/regscale_models/workflow_action.py +34 -0
  416. regscale/models/regscale_models/workflow_instance.py +269 -0
  417. regscale/models/regscale_models/workflow_instance_step.py +114 -0
  418. regscale/models/regscale_models/workflow_template.py +58 -0
  419. regscale/models/regscale_models/workflow_template_step.py +45 -0
  420. regscale/regscale.py +815 -0
  421. regscale/utils/__init__.py +7 -0
  422. regscale/utils/b64conversion.py +14 -0
  423. regscale/utils/click_utils.py +118 -0
  424. regscale/utils/decorators.py +48 -0
  425. regscale/utils/dict_utils.py +59 -0
  426. regscale/utils/files.py +79 -0
  427. regscale/utils/fxns.py +30 -0
  428. regscale/utils/graphql_client.py +113 -0
  429. regscale/utils/lists.py +16 -0
  430. regscale/utils/numbers.py +12 -0
  431. regscale/utils/shell.py +148 -0
  432. regscale/utils/string.py +121 -0
  433. regscale/utils/synqly_utils.py +165 -0
  434. regscale/utils/threading/__init__.py +8 -0
  435. regscale/utils/threading/threadhandler.py +131 -0
  436. regscale/utils/threading/threadsafe_counter.py +47 -0
  437. regscale/utils/threading/threadsafe_dict.py +242 -0
  438. regscale/utils/threading/threadsafe_list.py +83 -0
  439. regscale/utils/version.py +104 -0
  440. regscale/validation/__init__.py +0 -0
  441. regscale/validation/address.py +37 -0
  442. regscale/validation/record.py +48 -0
  443. regscale/visualization/__init__.py +5 -0
  444. regscale/visualization/click.py +34 -0
  445. regscale_cli-6.16.0.0.dist-info/LICENSE +21 -0
  446. regscale_cli-6.16.0.0.dist-info/METADATA +659 -0
  447. regscale_cli-6.16.0.0.dist-info/RECORD +481 -0
  448. regscale_cli-6.16.0.0.dist-info/WHEEL +5 -0
  449. regscale_cli-6.16.0.0.dist-info/entry_points.txt +6 -0
  450. regscale_cli-6.16.0.0.dist-info/top_level.txt +2 -0
  451. tests/fixtures/__init__.py +2 -0
  452. tests/fixtures/api.py +87 -0
  453. tests/fixtures/models.py +91 -0
  454. tests/fixtures/test_fixture.py +144 -0
  455. tests/mocks/__init__.py +0 -0
  456. tests/mocks/objects.py +3 -0
  457. tests/mocks/response.py +32 -0
  458. tests/mocks/xml.py +13 -0
  459. tests/regscale/__init__.py +0 -0
  460. tests/regscale/core/__init__.py +0 -0
  461. tests/regscale/core/test_api.py +232 -0
  462. tests/regscale/core/test_app.py +406 -0
  463. tests/regscale/core/test_login.py +37 -0
  464. tests/regscale/core/test_logz.py +66 -0
  465. tests/regscale/core/test_sbom_generator.py +87 -0
  466. tests/regscale/core/test_validation_utils.py +163 -0
  467. tests/regscale/core/test_version.py +78 -0
  468. tests/regscale/models/__init__.py +0 -0
  469. tests/regscale/models/test_asset.py +71 -0
  470. tests/regscale/models/test_config.py +26 -0
  471. tests/regscale/models/test_control_implementation.py +27 -0
  472. tests/regscale/models/test_import.py +97 -0
  473. tests/regscale/models/test_issue.py +36 -0
  474. tests/regscale/models/test_mapping.py +52 -0
  475. tests/regscale/models/test_platform.py +31 -0
  476. tests/regscale/models/test_regscale_model.py +346 -0
  477. tests/regscale/models/test_report.py +32 -0
  478. tests/regscale/models/test_tenable_integrations.py +118 -0
  479. tests/regscale/models/test_user_model.py +121 -0
  480. tests/regscale/test_about.py +19 -0
  481. tests/regscale/test_authorization.py +65 -0
regscale/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "6.16.0.0"
@@ -0,0 +1,9 @@
1
+ """Provide Airflow integrations with RegScale."""
2
+
3
+ try:
4
+ import airflow
5
+ except ImportError:
6
+ raise ImportError(
7
+ "The 'apache-airflow' package is required for using regscale.airflow. "
8
+ "To install it, run: `pip install regscale-cli[airflow]`"
9
+ )
@@ -0,0 +1,9 @@
1
+ """Prevent usage and imports that will break when used outside of airflow-azure extra."""
2
+
3
+ import sys
4
+
5
+ try:
6
+ from azure.storage.blob import BlobServiceClient
7
+ except ImportError:
8
+ print("To use Azure Blob Storage features, you need to install the [airflow-azure] extra package.")
9
+ sys.exit(1)
@@ -0,0 +1,89 @@
1
+ """Provide a CLI for uploading DAGs to Azure Blob Storage."""
2
+
3
+ import os
4
+ import sys
5
+ import click
6
+
7
+ from regscale.airflow.azure.upload_dags import (
8
+ upload_dag_to_blob_storage,
9
+ upload_dags_to_blob_storage,
10
+ )
11
+
12
+
13
+ @click.group(name="dags")
14
+ def cli():
15
+ """Upload DAGs or files to Azure Blob Storage."""
16
+ pass
17
+
18
+
19
+ @cli.command()
20
+ @click.option(
21
+ "--file",
22
+ "-f",
23
+ "file_path",
24
+ required=True,
25
+ type=click.Path(exists=True),
26
+ help="Path to the DAG file to upload.",
27
+ )
28
+ @click.option(
29
+ "--conn-string",
30
+ "-c",
31
+ "connection_string",
32
+ default=os.getenv("AZURE_STORAGE_CONNECTION_STRING", None),
33
+ help="Azure Blob Storage connection string.",
34
+ )
35
+ @click.option(
36
+ "--container",
37
+ "-n",
38
+ "container_name",
39
+ default=os.getenv("AZURE_STORAGE_CONTAINER_NAME", "dags"),
40
+ help="Azure Blob Storage container name.",
41
+ )
42
+ def upload_dag(file_path: str, connection_string: str, container_name: str) -> None:
43
+ """Upload a single DAG to Azure Blob Storage."""
44
+ if connection_string is None:
45
+ click.echo("You can set the connection string with the AZURE_STORAGE_CONNECTION_STRING environment variable.")
46
+ if container_name is None:
47
+ click.echo("You can set the container name with the AZURE_STORAGE_CONTAINER_NAME environment variable.")
48
+ if connection_string is None or container_name is None:
49
+ click.echo("Please provide the connection string and container name for Azure Blob Storage.")
50
+ sys.exit(1)
51
+ upload_dag_to_blob_storage(
52
+ connection_string=connection_string,
53
+ container_name=container_name,
54
+ blob_name=file_path,
55
+ file_path=file_path,
56
+ )
57
+
58
+
59
+ @cli.command()
60
+ @click.option("--path", "-p", "path", default="airflow/dags/", help="Path to the DAGs folder.")
61
+ @click.option(
62
+ "--conn-string",
63
+ "-c",
64
+ "connection_string",
65
+ default=os.getenv("AZURE_STORAGE_CONNECTION_STRING", None),
66
+ help="Azure Blob Storage connection string.",
67
+ )
68
+ @click.option(
69
+ "--container",
70
+ "-n",
71
+ "container_name",
72
+ default=os.getenv("AZURE_STORAGE_CONTAINER_NAME", "dags"),
73
+ help="Azure Blob Storage container name.",
74
+ )
75
+ def upload_dags(path: str, connection_string: str, container_name: str) -> None:
76
+ """Upload DAGs to Azure Blob Storage."""
77
+ if connection_string is None or container_name is None:
78
+ click.echo("Please provide the connection string and container name for Azure Blob Storage.")
79
+ if connection_string is None:
80
+ click.echo("You can set the connection string with the AZURE_STORAGE_CONNECTION_STRING environment variable.")
81
+ if container_name is None:
82
+ click.echo("You can set the container name with the AZURE_STORAGE_CONTAINER_NAME environment variable.")
83
+ if connection_string is None or container_name is None:
84
+ sys.exit(1)
85
+ upload_dags_to_blob_storage(
86
+ path=path,
87
+ connection_string=connection_string,
88
+ container_name=container_name,
89
+ )
@@ -0,0 +1,116 @@
1
+ """Provide functions and an entrypoint for uploading DAGs to Azure Blob Storage."""
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import Optional, Union, List, Tuple
7
+
8
+ from azure.storage.blob import BlobServiceClient
9
+
10
+ from regscale.core.app.logz import create_logger
11
+
12
+
13
+ def upload_dag_to_blob_storage(
14
+ connection_string: str,
15
+ container_name: str,
16
+ blob_name: str,
17
+ file_path: Union[str, Path],
18
+ exit_on_failure: bool = True,
19
+ ) -> None:
20
+ """Upload a DAG to Azure Blob Storage
21
+
22
+ :param str connection_string: Azure Blob Storage connection string
23
+ :param str container_name: Azure Blob Storage container name
24
+ :param str blob_name: Azure Blob Storage blob name
25
+ :param Union[str, Path] file_path: Path to the DAG file
26
+ :param bool exit_on_failure: Whether to exit on failure, default True
27
+ :rtype: None
28
+ """
29
+ logger = create_logger()
30
+ if isinstance(file_path, str):
31
+ file_path = Path(file_path)
32
+ try:
33
+ blob_service_client = BlobServiceClient.from_connection_string(connection_string)
34
+ blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
35
+ blob_client.upload_blob(file_path.read_bytes(), overwrite=True)
36
+ logger.info(f"{file_path} with {blob_name} uploaded to Azure Blob Storage")
37
+ except Exception as e:
38
+ logger.error(f"Failed to upload DAG to Azure Blob Storage: {e}")
39
+ if exit_on_failure:
40
+ sys.exit(1)
41
+
42
+
43
+ def retrieve_dags_to_upload(path: Union[str, Path], file_extension: str = ".py") -> List[Tuple[Path, str]]:
44
+ """
45
+ Retrieve DAGs to upload to Azure Blob Storage
46
+
47
+ :param Union[str, Path] path: Path to the DAGs folder
48
+ :param str file_extension: File extension of the DAGs, default .py
49
+ :return: List of DAGs to upload
50
+ :rtype: List[Tuple[Path, str]]
51
+ """
52
+ if isinstance(path, str):
53
+ path = Path(path)
54
+ dags = path.glob(f"*{file_extension}")
55
+ return [(dag, dag.name) for dag in dags]
56
+
57
+
58
+ def upload_dags_to_blob_storage(
59
+ path: str = "airflow/dags/",
60
+ connection_string: Optional[str] = None,
61
+ container_name: Optional[str] = None,
62
+ exit_on_failure: bool = True,
63
+ ) -> None:
64
+ """Upload DAGs to Azure Blob Storage
65
+
66
+ :param str path: Path to the DAGs folder, default airflow/dags/
67
+ :param Optional[str] connection_string: Azure Blob Storage connection string
68
+ :param Optional[str] container_name: Azure Blob Storage container name
69
+ :param bool exit_on_failure: Whether to exit on failure, default True
70
+ :rtype: None
71
+ """
72
+ logger = create_logger()
73
+ if connection_string is None:
74
+ connection_string = os.environ.get("AZURE_STORAGE_CONNECTION_STRING")
75
+ if container_name is None:
76
+ container_name = os.environ.get("AZURE_STORAGE_CONTAINER_NAME")
77
+ if connection_string is None or container_name is None:
78
+ logger.error("Please provide the connection string and container name for Azure Blob Storage.")
79
+ if exit_on_failure:
80
+ sys.exit(1)
81
+ dags = retrieve_dags_to_upload(path)
82
+ for dag, blob_name in dags:
83
+ upload_dag_to_blob_storage(
84
+ connection_string=connection_string,
85
+ container_name=container_name,
86
+ blob_name=blob_name,
87
+ file_path=dag,
88
+ exit_on_failure=exit_on_failure,
89
+ )
90
+
91
+
92
+ def main():
93
+ """Entrypoint for uploading DAGs to Azure Blob Storage."""
94
+ logger = create_logger()
95
+ if len(sys.argv) == 2:
96
+ connection_string = sys.argv[1]
97
+ container_name = sys.argv[2]
98
+ else:
99
+ connection_string = os.environ.get("ADS_CONN_STRING")
100
+ container_name = os.environ.get("AZURE_STORAGE_CONTAINER_NAME", "dags")
101
+ if connection_string is None or container_name is None:
102
+ logger.error("Please provide the connection string and container name for Azure Blob Storage.")
103
+ if connection_string is None:
104
+ logger.error("Connection string not provided.")
105
+ if container_name is None:
106
+ logger.error("Container name not provided.")
107
+ sys.exit(1)
108
+ upload_dags_to_blob_storage(
109
+ connection_string=connection_string,
110
+ container_name=container_name,
111
+ exit_on_failure=False,
112
+ )
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()
@@ -0,0 +1,127 @@
1
+ """Create functions to generate a DAG based on click hierarchy."""
2
+
3
+ import os
4
+ from datetime import datetime, timezone
5
+ from typing import Optional
6
+
7
+ import click
8
+ from airflow.operators.python import PythonOperator
9
+
10
+ from airflow import DAG
11
+ from regscale.airflow.tasks.cli import make_login_task, make_set_domain_task
12
+ from regscale.airflow.tasks.click import execute_click_command
13
+ from regscale.models.click_models import ClickCommand
14
+ from regscale.regscale import cli
15
+
16
+
17
+ def generate_operator_for_command(
18
+ command: ClickCommand,
19
+ dag: DAG,
20
+ command_name: str = None,
21
+ ) -> PythonOperator:
22
+ """Generate an Operator for click command
23
+
24
+ :param ClickCommand command: a click.Command instruction
25
+ :param DAG dag: an Airflow DAG
26
+ :param str command_name: an optional string for command_name, will default to click.Command.name
27
+ :return: A PythonOperator configured with the click command.name
28
+ :rtype: PythonOperator
29
+ """
30
+ return PythonOperator(
31
+ task_id=command_name or command.name, # TODO - also name based on group id
32
+ python_callable=execute_click_command,
33
+ op_kwargs={"command": command},
34
+ dag=dag,
35
+ )
36
+
37
+
38
+ def generate_dag_for_group(
39
+ group: click.Group,
40
+ default_args: dict,
41
+ command_name: str = None,
42
+ ) -> DAG:
43
+ """Generate a dag for a click group
44
+
45
+ :param click.Group group: a click Group object to generate dags for
46
+ :param dict default_args: dict to be passed when creating the DAGs
47
+ :param str command_name: an optional string for command_name, will default to click.Command.name
48
+ :return: a dag for each click group
49
+ :rtype: DAG
50
+ """
51
+ if command_name is None:
52
+ command_name = group.name
53
+ dag = DAG(
54
+ command_name,
55
+ default_args=default_args,
56
+ description=f"DAG for Click Group: {group.name}",
57
+ schedule_interval=None,
58
+ )
59
+
60
+ # iterate through the group value commands
61
+ for command in group.commands.values():
62
+ if isinstance(command, click.Group):
63
+ # if it is click.Group, call this recursively, adding to the command_name
64
+ generate_dag_for_group(
65
+ command,
66
+ default_args,
67
+ command_name="__".join([command_name, command.name]),
68
+ )
69
+ else:
70
+ login_task = make_login_task(dag=dag)
71
+ domain_task = make_set_domain_task(dag=dag)
72
+ operator = generate_operator_for_command(
73
+ command=command,
74
+ dag=dag,
75
+ command_name="__".join([command_name, command.name]),
76
+ )
77
+ # assign the relationships for the operators
78
+ domain_task >> login_task >> operator
79
+ return dag
80
+
81
+
82
+ def generate_dags_for_click_app(
83
+ app: Optional[click.Group] = None,
84
+ default_args: Optional[dict] = None,
85
+ command_name: Optional[str] = None,
86
+ ) -> list:
87
+ """Generate DAGs for a click app
88
+
89
+ :param Optional[click.Group] app: a click app to generate dags for, defaults to None
90
+ :param Optional[dict] default_args: dict to be passed when creating the DAGs, defaults to None
91
+ :param Optional[str] command_name: the name of the command to generate the DAG for, defaults to None
92
+ :return: a list of DAGs for the click app
93
+ :rtype: list
94
+ """
95
+ # if app is not passed, use the cli
96
+ if app is None:
97
+ app = cli
98
+ if not default_args:
99
+ default_args = dict(
100
+ owner=os.getenv("REGSCALE_AIRFLOW_USER", "regscale"),
101
+ start_date=datetime.now(timezone.utc),
102
+ )
103
+ if not command_name:
104
+ command_name = "regscale" if app.name == "cli" else app.name
105
+ dags = []
106
+ for group in app.commands.values():
107
+ if isinstance(group, click.Group):
108
+ command_name = "regscale" if group.name == "cli" else group.name
109
+ for sub_group in group.commands.values():
110
+ if isinstance(sub_group, click.Group):
111
+ generate_dag_for_group(app=sub_group, default_args=default_args)
112
+ else:
113
+ dag = generate_dag_for_group(
114
+ group=group,
115
+ default_args=default_args,
116
+ command_name=command_name,
117
+ )
118
+ dags.append(dag)
119
+ elif isinstance(group, click.Command):
120
+ dag = generate_dag_for_group(
121
+ group=group,
122
+ default_args=default_args,
123
+ command_name=command_name,
124
+ )
125
+ dags.append(dag)
126
+
127
+ return dags
@@ -0,0 +1,82 @@
1
+ """Provide mixins for click models."""
2
+
3
+ from typing import Any
4
+ from uuid import uuid4
5
+ from airflow.operators.python import PythonOperator
6
+
7
+ from regscale.models.click_models import ClickGroup, ClickCommand
8
+ from regscale.airflow.tasks.click import execute_click_command
9
+
10
+
11
+ class AirflowOperatorMixin:
12
+ """Mixin to the ClickGroup to flatten for an Airflow Operator"""
13
+
14
+ def flatten_operator(self):
15
+ """Flatten the group to a dictionary of PythonOperator objects"""
16
+ operators_ = {}
17
+
18
+ def _flatten_operator(group_: Any, prefix: str = "") -> dict:
19
+ """Flatten the group to a dictionary of PythonOperator objects
20
+
21
+ :param Any group_: a click.Group object to generate dags for
22
+ :param str prefix: a string to prefix the command name with, defaults to ""
23
+ :return: a dictionary of PythonOperator objects
24
+ :rtype: dict
25
+ """
26
+ for name, cmd in group_.commands.items():
27
+ if isinstance(cmd, ClickCommand):
28
+ cmd_name = cmd.name if prefix == "" else f"{prefix}__{cmd.name}"
29
+
30
+ def _make_operator_wrapper(
31
+ cmd_name_: str, cmd_: ClickCommand, suffix: str = None, **kwargs
32
+ ) -> PythonOperator:
33
+ """Create a wrapper to make a python operator."""
34
+ if suffix is None:
35
+ suffix = str(uuid4())[:8]
36
+ op_kwargs = None
37
+ if "op_kwargs" in kwargs:
38
+ op_kwargs = kwargs.pop("op_kwargs")
39
+ inputs = dict(
40
+ task_id=f"{cmd_name_}-{suffix}",
41
+ python_callable=execute_click_command,
42
+ )
43
+ if kwargs:
44
+ inputs |= kwargs
45
+ if op_kwargs:
46
+ inputs |= {"op_kwargs": {"command": cmd_, **op_kwargs}}
47
+ else:
48
+ inputs |= {"op_kwargs": {"command": cmd_}}
49
+ return PythonOperator(**inputs)
50
+
51
+ def _construct_lambda_wrapper(cmd_name_, cmd_, suffix: str = None, **kwargs):
52
+ return _make_operator_wrapper(cmd_name_=cmd_name_, cmd_=cmd_, suffix=suffix, **kwargs)
53
+
54
+ operators_[cmd_name] = {
55
+ "operator": PythonOperator(
56
+ task_id=cmd_name,
57
+ python_callable=execute_click_command,
58
+ op_kwargs={"command": cmd},
59
+ ),
60
+ "lambda": lambda cmd_name_=cmd_name, cmd_=cmd, suffix=None, **kwargs: _construct_lambda_wrapper(
61
+ cmd_name_=cmd_name_, cmd_=cmd_, suffix=suffix, **kwargs
62
+ ),
63
+ "command": cmd,
64
+ }
65
+ elif isinstance(cmd, ClickGroup):
66
+ new_prefix = f"{prefix}__{cmd.group_name}" if prefix else cmd.group_name
67
+ _flatten_operator(cmd, new_prefix)
68
+
69
+ _flatten_operator(self)
70
+ return operators_
71
+
72
+
73
+ class AirflowClickGroup(AirflowOperatorMixin, ClickGroup):
74
+ """Initialize the AirflowClickGroup object."""
75
+
76
+
77
+ # here's an example of how to generate the OPERATORS AirflowClickGroup class
78
+ if __name__ == "__main__":
79
+ from regscale.regscale import cli
80
+
81
+ group = AirflowClickGroup.from_group(cli, prefix="regscale")
82
+ operators = group.flatten_operator()
@@ -0,0 +1,25 @@
1
+ """Provide configurations for Airflow."""
2
+
3
+ from datetime import datetime, timedelta
4
+
5
+
6
+ def yesterday():
7
+ """Return yesterday from now in datetime"""
8
+ return datetime.now() - timedelta(days=1)
9
+
10
+
11
+ DEFAULT_ARGS = {
12
+ "owner": "airflow",
13
+ "depends_on_past": False,
14
+ # 'start_date': (datetime.now() - timedelta(days=1)).date(), # left here to show we intentionally disable
15
+ "email": ["airflow@regscale.com"],
16
+ "email_on_failure": False,
17
+ "email_on_retry": False,
18
+ "retries": 2,
19
+ "retry_delay": timedelta(minutes=2),
20
+ "execution_timeout": timedelta(hours=3),
21
+ # 'queue': 'whatever queue we want to implement', # left here for an example
22
+ # 'pool': 'backfill', # another example default arg
23
+ # 'priority_weight': 10, # give this a high priority weight
24
+ # 'end_date': datetime(2038, 1, 1), # left to show that end dates can be set
25
+ }
File without changes
@@ -0,0 +1,58 @@
1
+ """Provide connection management functions."""
2
+
3
+ import os
4
+ from typing import Literal
5
+
6
+ from airflow.models import Connection
7
+ from airflow.operators.python import PythonOperator
8
+
9
+ from airflow import settings
10
+
11
+
12
+ def create_connection_operator(
13
+ conn_id: str,
14
+ conn_type: Literal["postgres", "mssql"],
15
+ **kwargs: dict,
16
+ ) -> PythonOperator:
17
+ """Create a connection if it does not exist
18
+
19
+ :param str conn_id: the connection id
20
+ :param Literal["postgres", "mssql"] conn_type: the connection type
21
+ :param dict **kwargs: additional keyword arguments
22
+ :return: a PythonOperator to create the connection
23
+ :rtype: PythonOperator
24
+ """
25
+
26
+ def _create_connection():
27
+ """Create a connection"""
28
+ session = settings.Session()
29
+ # Check if connection exists
30
+ conn = session.query(Connection).filter(Connection.conn_id == conn_id).first()
31
+ # If connection does not exist, create it
32
+ if conn is None:
33
+ new_conn = Connection(
34
+ conn_id=conn_id,
35
+ conn_type=conn_type,
36
+ host=os.getenv("PLATFORM_DB_HOST") or "atlas",
37
+ # FIXME - this envar needs added to the instance container app
38
+ schema=os.getenv("PLATFORM_DB_NAME") or "airflowtest-sqlDatabase",
39
+ login=os.getenv("PLATFORM_DB_USER"),
40
+ password=os.getenv("PLATFORM_DB_PASSWORD"),
41
+ port=int(
42
+ os.getenv(
43
+ "PLATFORM_DB_PORT",
44
+ )
45
+ or (5432 if conn_type == "postgres" else 1433)
46
+ ),
47
+ )
48
+ session.add(new_conn)
49
+ session.commit()
50
+ print("Connection created.")
51
+ else:
52
+ print("Connection already exists.")
53
+
54
+ return PythonOperator(
55
+ task_id=f"create-connection-{conn_id}",
56
+ python_callable=_create_connection,
57
+ **kwargs,
58
+ )
@@ -0,0 +1,78 @@
1
+ """Provide workflow factory functions."""
2
+
3
+ from typing import Union
4
+ from uuid import uuid4
5
+
6
+ from airflow import DAG
7
+ from airflow.operators.python import PythonOperator
8
+ from airflow.utils.task_group import TaskGroup
9
+
10
+ from regscale.airflow.sensors.sql import build_sql_sensor_xcon
11
+ from regscale.airflow.sessions.sql.sql_server_queries import (
12
+ CHECK_IF_COMPLETED_SQL_QUERY,
13
+ )
14
+ from regscale.airflow.tasks.branches import tri_branch_func
15
+ from regscale.airflow.tasks.workflows import (
16
+ build_complete,
17
+ build_rejected,
18
+ )
19
+
20
+
21
+ def workflow_listener_factory(
22
+ workflow_name: str,
23
+ dag: DAG,
24
+ next_step_name: str,
25
+ next_step: Union[TaskGroup, PythonOperator],
26
+ unique: bool = False,
27
+ **kwargs,
28
+ ) -> TaskGroup:
29
+ """Build a workflow listener task group
30
+
31
+ :param str workflow_name: the name of the workflow
32
+ :param DAG dag: the DAG to add the task group to
33
+ :param str next_step_name: the name of the next step
34
+ :param Union[TaskGroup, PythonOperator] next_step: the next step
35
+ :param dict kwargs: keyword arguments to pass to the task group
36
+ :param bool unique: whether to make the task group unique
37
+ :return: a workflow listener task group
38
+ :rtype: TaskGroup
39
+ """
40
+ uid = None
41
+ if unique:
42
+ uid = str(uuid4())[:8]
43
+ name = f"{workflow_name}-{uid}"
44
+ else:
45
+ name = f"{workflow_name}"
46
+ with TaskGroup(
47
+ group_id=name,
48
+ dag=dag,
49
+ **kwargs,
50
+ ) as task_group:
51
+ # You need to have this factory upstream from the is_completed_listener_name
52
+ is_completed_listener_name = f"{name}-listener"
53
+ complete_name = f"{name}-complete"
54
+ # this task needs to be the name of the next listener
55
+ rejected_name = f"{name}-rejected"
56
+ is_completed = build_sql_sensor_xcon(
57
+ sql=CHECK_IF_COMPLETED_SQL_QUERY,
58
+ step_name=next_step_name,
59
+ name=is_completed_listener_name,
60
+ dag=dag,
61
+ )
62
+ complete = build_complete(uid=uid or complete_name, dag=dag)
63
+ rejected = build_rejected(uid=uid or rejected_name, dag=dag)
64
+ decision = PythonOperator(
65
+ task_id=f"decision-{uid or name}",
66
+ python_callable=tri_branch_func,
67
+ op_kwargs={
68
+ "pull_from": is_completed_listener_name,
69
+ "negative_task": rejected_name,
70
+ "neutral_task": next_step, # neutral_step_name in DAG
71
+ "positive_task": complete_name,
72
+ },
73
+ dag=dag,
74
+ )
75
+
76
+ is_completed >> decision >> [complete, next_step, rejected]
77
+
78
+ return task_group