truthound 1.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (877) hide show
  1. truthound/__init__.py +162 -0
  2. truthound/adapters.py +100 -0
  3. truthound/api.py +365 -0
  4. truthound/audit/__init__.py +248 -0
  5. truthound/audit/core.py +967 -0
  6. truthound/audit/filters.py +620 -0
  7. truthound/audit/formatters.py +707 -0
  8. truthound/audit/logger.py +902 -0
  9. truthound/audit/middleware.py +571 -0
  10. truthound/audit/storage.py +1083 -0
  11. truthound/benchmark/__init__.py +123 -0
  12. truthound/benchmark/base.py +757 -0
  13. truthound/benchmark/comparison.py +635 -0
  14. truthound/benchmark/generators.py +706 -0
  15. truthound/benchmark/reporters.py +718 -0
  16. truthound/benchmark/runner.py +635 -0
  17. truthound/benchmark/scenarios.py +712 -0
  18. truthound/cache.py +252 -0
  19. truthound/checkpoint/__init__.py +136 -0
  20. truthound/checkpoint/actions/__init__.py +164 -0
  21. truthound/checkpoint/actions/base.py +324 -0
  22. truthound/checkpoint/actions/custom.py +234 -0
  23. truthound/checkpoint/actions/discord_notify.py +290 -0
  24. truthound/checkpoint/actions/email_notify.py +405 -0
  25. truthound/checkpoint/actions/github_action.py +406 -0
  26. truthound/checkpoint/actions/opsgenie.py +1499 -0
  27. truthound/checkpoint/actions/pagerduty.py +226 -0
  28. truthound/checkpoint/actions/slack_notify.py +233 -0
  29. truthound/checkpoint/actions/store_result.py +249 -0
  30. truthound/checkpoint/actions/teams_notify.py +1570 -0
  31. truthound/checkpoint/actions/telegram_notify.py +419 -0
  32. truthound/checkpoint/actions/update_docs.py +552 -0
  33. truthound/checkpoint/actions/webhook.py +293 -0
  34. truthound/checkpoint/analytics/__init__.py +147 -0
  35. truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
  36. truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
  37. truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
  38. truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
  39. truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
  40. truthound/checkpoint/analytics/analyzers/base.py +270 -0
  41. truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
  42. truthound/checkpoint/analytics/analyzers/trend.py +314 -0
  43. truthound/checkpoint/analytics/models.py +292 -0
  44. truthound/checkpoint/analytics/protocols.py +549 -0
  45. truthound/checkpoint/analytics/service.py +718 -0
  46. truthound/checkpoint/analytics/stores/__init__.py +16 -0
  47. truthound/checkpoint/analytics/stores/base.py +306 -0
  48. truthound/checkpoint/analytics/stores/memory_store.py +353 -0
  49. truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
  50. truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
  51. truthound/checkpoint/async_actions.py +794 -0
  52. truthound/checkpoint/async_base.py +708 -0
  53. truthound/checkpoint/async_checkpoint.py +617 -0
  54. truthound/checkpoint/async_runner.py +639 -0
  55. truthound/checkpoint/checkpoint.py +527 -0
  56. truthound/checkpoint/ci/__init__.py +61 -0
  57. truthound/checkpoint/ci/detector.py +355 -0
  58. truthound/checkpoint/ci/reporter.py +436 -0
  59. truthound/checkpoint/ci/templates.py +454 -0
  60. truthound/checkpoint/circuitbreaker/__init__.py +133 -0
  61. truthound/checkpoint/circuitbreaker/breaker.py +542 -0
  62. truthound/checkpoint/circuitbreaker/core.py +252 -0
  63. truthound/checkpoint/circuitbreaker/detection.py +459 -0
  64. truthound/checkpoint/circuitbreaker/middleware.py +389 -0
  65. truthound/checkpoint/circuitbreaker/registry.py +357 -0
  66. truthound/checkpoint/distributed/__init__.py +139 -0
  67. truthound/checkpoint/distributed/backends/__init__.py +35 -0
  68. truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
  69. truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
  70. truthound/checkpoint/distributed/backends/local_backend.py +397 -0
  71. truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
  72. truthound/checkpoint/distributed/base.py +774 -0
  73. truthound/checkpoint/distributed/orchestrator.py +765 -0
  74. truthound/checkpoint/distributed/protocols.py +842 -0
  75. truthound/checkpoint/distributed/registry.py +449 -0
  76. truthound/checkpoint/idempotency/__init__.py +120 -0
  77. truthound/checkpoint/idempotency/core.py +295 -0
  78. truthound/checkpoint/idempotency/fingerprint.py +454 -0
  79. truthound/checkpoint/idempotency/locking.py +604 -0
  80. truthound/checkpoint/idempotency/service.py +592 -0
  81. truthound/checkpoint/idempotency/stores.py +653 -0
  82. truthound/checkpoint/monitoring/__init__.py +134 -0
  83. truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
  84. truthound/checkpoint/monitoring/aggregators/base.py +372 -0
  85. truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
  86. truthound/checkpoint/monitoring/aggregators/window.py +493 -0
  87. truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
  88. truthound/checkpoint/monitoring/collectors/base.py +257 -0
  89. truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
  90. truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
  91. truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
  92. truthound/checkpoint/monitoring/events.py +410 -0
  93. truthound/checkpoint/monitoring/protocols.py +636 -0
  94. truthound/checkpoint/monitoring/service.py +578 -0
  95. truthound/checkpoint/monitoring/views/__init__.py +17 -0
  96. truthound/checkpoint/monitoring/views/base.py +172 -0
  97. truthound/checkpoint/monitoring/views/queue_view.py +220 -0
  98. truthound/checkpoint/monitoring/views/task_view.py +240 -0
  99. truthound/checkpoint/monitoring/views/worker_view.py +263 -0
  100. truthound/checkpoint/registry.py +337 -0
  101. truthound/checkpoint/runner.py +356 -0
  102. truthound/checkpoint/transaction/__init__.py +133 -0
  103. truthound/checkpoint/transaction/base.py +389 -0
  104. truthound/checkpoint/transaction/compensatable.py +537 -0
  105. truthound/checkpoint/transaction/coordinator.py +576 -0
  106. truthound/checkpoint/transaction/executor.py +622 -0
  107. truthound/checkpoint/transaction/idempotency.py +534 -0
  108. truthound/checkpoint/transaction/saga/__init__.py +143 -0
  109. truthound/checkpoint/transaction/saga/builder.py +584 -0
  110. truthound/checkpoint/transaction/saga/definition.py +515 -0
  111. truthound/checkpoint/transaction/saga/event_store.py +542 -0
  112. truthound/checkpoint/transaction/saga/patterns.py +833 -0
  113. truthound/checkpoint/transaction/saga/runner.py +718 -0
  114. truthound/checkpoint/transaction/saga/state_machine.py +793 -0
  115. truthound/checkpoint/transaction/saga/strategies.py +780 -0
  116. truthound/checkpoint/transaction/saga/testing.py +886 -0
  117. truthound/checkpoint/triggers/__init__.py +58 -0
  118. truthound/checkpoint/triggers/base.py +237 -0
  119. truthound/checkpoint/triggers/event.py +385 -0
  120. truthound/checkpoint/triggers/schedule.py +355 -0
  121. truthound/cli.py +2358 -0
  122. truthound/cli_modules/__init__.py +124 -0
  123. truthound/cli_modules/advanced/__init__.py +45 -0
  124. truthound/cli_modules/advanced/benchmark.py +343 -0
  125. truthound/cli_modules/advanced/docs.py +225 -0
  126. truthound/cli_modules/advanced/lineage.py +209 -0
  127. truthound/cli_modules/advanced/ml.py +320 -0
  128. truthound/cli_modules/advanced/realtime.py +196 -0
  129. truthound/cli_modules/checkpoint/__init__.py +46 -0
  130. truthound/cli_modules/checkpoint/init.py +114 -0
  131. truthound/cli_modules/checkpoint/list.py +71 -0
  132. truthound/cli_modules/checkpoint/run.py +159 -0
  133. truthound/cli_modules/checkpoint/validate.py +67 -0
  134. truthound/cli_modules/common/__init__.py +71 -0
  135. truthound/cli_modules/common/errors.py +414 -0
  136. truthound/cli_modules/common/options.py +419 -0
  137. truthound/cli_modules/common/output.py +507 -0
  138. truthound/cli_modules/common/protocol.py +552 -0
  139. truthound/cli_modules/core/__init__.py +48 -0
  140. truthound/cli_modules/core/check.py +123 -0
  141. truthound/cli_modules/core/compare.py +104 -0
  142. truthound/cli_modules/core/learn.py +57 -0
  143. truthound/cli_modules/core/mask.py +77 -0
  144. truthound/cli_modules/core/profile.py +65 -0
  145. truthound/cli_modules/core/scan.py +61 -0
  146. truthound/cli_modules/profiler/__init__.py +51 -0
  147. truthound/cli_modules/profiler/auto_profile.py +175 -0
  148. truthound/cli_modules/profiler/metadata.py +107 -0
  149. truthound/cli_modules/profiler/suite.py +283 -0
  150. truthound/cli_modules/registry.py +431 -0
  151. truthound/cli_modules/scaffolding/__init__.py +89 -0
  152. truthound/cli_modules/scaffolding/base.py +631 -0
  153. truthound/cli_modules/scaffolding/commands.py +545 -0
  154. truthound/cli_modules/scaffolding/plugins.py +1072 -0
  155. truthound/cli_modules/scaffolding/reporters.py +594 -0
  156. truthound/cli_modules/scaffolding/validators.py +1127 -0
  157. truthound/common/__init__.py +18 -0
  158. truthound/common/resilience/__init__.py +130 -0
  159. truthound/common/resilience/bulkhead.py +266 -0
  160. truthound/common/resilience/circuit_breaker.py +516 -0
  161. truthound/common/resilience/composite.py +332 -0
  162. truthound/common/resilience/config.py +292 -0
  163. truthound/common/resilience/protocols.py +217 -0
  164. truthound/common/resilience/rate_limiter.py +404 -0
  165. truthound/common/resilience/retry.py +341 -0
  166. truthound/datadocs/__init__.py +260 -0
  167. truthound/datadocs/base.py +571 -0
  168. truthound/datadocs/builder.py +761 -0
  169. truthound/datadocs/charts.py +764 -0
  170. truthound/datadocs/dashboard/__init__.py +63 -0
  171. truthound/datadocs/dashboard/app.py +576 -0
  172. truthound/datadocs/dashboard/components.py +584 -0
  173. truthound/datadocs/dashboard/state.py +240 -0
  174. truthound/datadocs/engine/__init__.py +46 -0
  175. truthound/datadocs/engine/context.py +376 -0
  176. truthound/datadocs/engine/pipeline.py +618 -0
  177. truthound/datadocs/engine/registry.py +469 -0
  178. truthound/datadocs/exporters/__init__.py +49 -0
  179. truthound/datadocs/exporters/base.py +198 -0
  180. truthound/datadocs/exporters/html.py +178 -0
  181. truthound/datadocs/exporters/json_exporter.py +253 -0
  182. truthound/datadocs/exporters/markdown.py +284 -0
  183. truthound/datadocs/exporters/pdf.py +392 -0
  184. truthound/datadocs/i18n/__init__.py +86 -0
  185. truthound/datadocs/i18n/catalog.py +960 -0
  186. truthound/datadocs/i18n/formatting.py +505 -0
  187. truthound/datadocs/i18n/loader.py +256 -0
  188. truthound/datadocs/i18n/plurals.py +378 -0
  189. truthound/datadocs/renderers/__init__.py +42 -0
  190. truthound/datadocs/renderers/base.py +401 -0
  191. truthound/datadocs/renderers/custom.py +342 -0
  192. truthound/datadocs/renderers/jinja.py +697 -0
  193. truthound/datadocs/sections.py +736 -0
  194. truthound/datadocs/styles.py +931 -0
  195. truthound/datadocs/themes/__init__.py +101 -0
  196. truthound/datadocs/themes/base.py +336 -0
  197. truthound/datadocs/themes/default.py +417 -0
  198. truthound/datadocs/themes/enterprise.py +419 -0
  199. truthound/datadocs/themes/loader.py +336 -0
  200. truthound/datadocs/themes.py +301 -0
  201. truthound/datadocs/transformers/__init__.py +57 -0
  202. truthound/datadocs/transformers/base.py +268 -0
  203. truthound/datadocs/transformers/enrichers.py +544 -0
  204. truthound/datadocs/transformers/filters.py +447 -0
  205. truthound/datadocs/transformers/i18n.py +468 -0
  206. truthound/datadocs/versioning/__init__.py +62 -0
  207. truthound/datadocs/versioning/diff.py +639 -0
  208. truthound/datadocs/versioning/storage.py +497 -0
  209. truthound/datadocs/versioning/version.py +358 -0
  210. truthound/datasources/__init__.py +223 -0
  211. truthound/datasources/_async_protocols.py +222 -0
  212. truthound/datasources/_protocols.py +159 -0
  213. truthound/datasources/adapters.py +428 -0
  214. truthound/datasources/async_base.py +599 -0
  215. truthound/datasources/async_factory.py +511 -0
  216. truthound/datasources/base.py +516 -0
  217. truthound/datasources/factory.py +433 -0
  218. truthound/datasources/nosql/__init__.py +47 -0
  219. truthound/datasources/nosql/base.py +487 -0
  220. truthound/datasources/nosql/elasticsearch.py +801 -0
  221. truthound/datasources/nosql/mongodb.py +636 -0
  222. truthound/datasources/pandas_optimized.py +582 -0
  223. truthound/datasources/pandas_source.py +216 -0
  224. truthound/datasources/polars_source.py +395 -0
  225. truthound/datasources/spark_source.py +479 -0
  226. truthound/datasources/sql/__init__.py +154 -0
  227. truthound/datasources/sql/base.py +710 -0
  228. truthound/datasources/sql/bigquery.py +410 -0
  229. truthound/datasources/sql/cloud_base.py +199 -0
  230. truthound/datasources/sql/databricks.py +471 -0
  231. truthound/datasources/sql/mysql.py +316 -0
  232. truthound/datasources/sql/oracle.py +427 -0
  233. truthound/datasources/sql/postgresql.py +321 -0
  234. truthound/datasources/sql/redshift.py +479 -0
  235. truthound/datasources/sql/snowflake.py +439 -0
  236. truthound/datasources/sql/sqlite.py +286 -0
  237. truthound/datasources/sql/sqlserver.py +437 -0
  238. truthound/datasources/streaming/__init__.py +47 -0
  239. truthound/datasources/streaming/base.py +350 -0
  240. truthound/datasources/streaming/kafka.py +670 -0
  241. truthound/decorators.py +98 -0
  242. truthound/docs/__init__.py +69 -0
  243. truthound/docs/extractor.py +971 -0
  244. truthound/docs/generator.py +601 -0
  245. truthound/docs/parser.py +1037 -0
  246. truthound/docs/renderer.py +999 -0
  247. truthound/drift/__init__.py +22 -0
  248. truthound/drift/compare.py +189 -0
  249. truthound/drift/detectors.py +464 -0
  250. truthound/drift/report.py +160 -0
  251. truthound/execution/__init__.py +65 -0
  252. truthound/execution/_protocols.py +324 -0
  253. truthound/execution/base.py +576 -0
  254. truthound/execution/distributed/__init__.py +179 -0
  255. truthound/execution/distributed/aggregations.py +731 -0
  256. truthound/execution/distributed/arrow_bridge.py +817 -0
  257. truthound/execution/distributed/base.py +550 -0
  258. truthound/execution/distributed/dask_engine.py +976 -0
  259. truthound/execution/distributed/mixins.py +766 -0
  260. truthound/execution/distributed/protocols.py +756 -0
  261. truthound/execution/distributed/ray_engine.py +1127 -0
  262. truthound/execution/distributed/registry.py +446 -0
  263. truthound/execution/distributed/spark_engine.py +1011 -0
  264. truthound/execution/distributed/validator_adapter.py +682 -0
  265. truthound/execution/pandas_engine.py +401 -0
  266. truthound/execution/polars_engine.py +497 -0
  267. truthound/execution/pushdown/__init__.py +230 -0
  268. truthound/execution/pushdown/ast.py +1550 -0
  269. truthound/execution/pushdown/builder.py +1550 -0
  270. truthound/execution/pushdown/dialects.py +1072 -0
  271. truthound/execution/pushdown/executor.py +829 -0
  272. truthound/execution/pushdown/optimizer.py +1041 -0
  273. truthound/execution/sql_engine.py +518 -0
  274. truthound/infrastructure/__init__.py +189 -0
  275. truthound/infrastructure/audit.py +1515 -0
  276. truthound/infrastructure/config.py +1133 -0
  277. truthound/infrastructure/encryption.py +1132 -0
  278. truthound/infrastructure/logging.py +1503 -0
  279. truthound/infrastructure/metrics.py +1220 -0
  280. truthound/lineage/__init__.py +89 -0
  281. truthound/lineage/base.py +746 -0
  282. truthound/lineage/impact_analysis.py +474 -0
  283. truthound/lineage/integrations/__init__.py +22 -0
  284. truthound/lineage/integrations/openlineage.py +548 -0
  285. truthound/lineage/tracker.py +512 -0
  286. truthound/lineage/visualization/__init__.py +33 -0
  287. truthound/lineage/visualization/protocols.py +145 -0
  288. truthound/lineage/visualization/renderers/__init__.py +20 -0
  289. truthound/lineage/visualization/renderers/cytoscape.py +329 -0
  290. truthound/lineage/visualization/renderers/d3.py +331 -0
  291. truthound/lineage/visualization/renderers/graphviz.py +276 -0
  292. truthound/lineage/visualization/renderers/mermaid.py +308 -0
  293. truthound/maskers.py +113 -0
  294. truthound/ml/__init__.py +124 -0
  295. truthound/ml/anomaly_models/__init__.py +31 -0
  296. truthound/ml/anomaly_models/ensemble.py +362 -0
  297. truthound/ml/anomaly_models/isolation_forest.py +444 -0
  298. truthound/ml/anomaly_models/statistical.py +392 -0
  299. truthound/ml/base.py +1178 -0
  300. truthound/ml/drift_detection/__init__.py +26 -0
  301. truthound/ml/drift_detection/concept.py +381 -0
  302. truthound/ml/drift_detection/distribution.py +361 -0
  303. truthound/ml/drift_detection/feature.py +442 -0
  304. truthound/ml/drift_detection/multivariate.py +495 -0
  305. truthound/ml/monitoring/__init__.py +88 -0
  306. truthound/ml/monitoring/alerting/__init__.py +33 -0
  307. truthound/ml/monitoring/alerting/handlers.py +427 -0
  308. truthound/ml/monitoring/alerting/rules.py +508 -0
  309. truthound/ml/monitoring/collectors/__init__.py +19 -0
  310. truthound/ml/monitoring/collectors/composite.py +105 -0
  311. truthound/ml/monitoring/collectors/drift.py +324 -0
  312. truthound/ml/monitoring/collectors/performance.py +179 -0
  313. truthound/ml/monitoring/collectors/quality.py +369 -0
  314. truthound/ml/monitoring/monitor.py +536 -0
  315. truthound/ml/monitoring/protocols.py +451 -0
  316. truthound/ml/monitoring/stores/__init__.py +15 -0
  317. truthound/ml/monitoring/stores/memory.py +201 -0
  318. truthound/ml/monitoring/stores/prometheus.py +296 -0
  319. truthound/ml/rule_learning/__init__.py +25 -0
  320. truthound/ml/rule_learning/constraint_miner.py +443 -0
  321. truthound/ml/rule_learning/pattern_learner.py +499 -0
  322. truthound/ml/rule_learning/profile_learner.py +462 -0
  323. truthound/multitenancy/__init__.py +326 -0
  324. truthound/multitenancy/core.py +852 -0
  325. truthound/multitenancy/integration.py +597 -0
  326. truthound/multitenancy/isolation.py +630 -0
  327. truthound/multitenancy/manager.py +770 -0
  328. truthound/multitenancy/middleware.py +765 -0
  329. truthound/multitenancy/quota.py +537 -0
  330. truthound/multitenancy/resolvers.py +603 -0
  331. truthound/multitenancy/storage.py +703 -0
  332. truthound/observability/__init__.py +307 -0
  333. truthound/observability/context.py +531 -0
  334. truthound/observability/instrumentation.py +611 -0
  335. truthound/observability/logging.py +887 -0
  336. truthound/observability/metrics.py +1157 -0
  337. truthound/observability/tracing/__init__.py +178 -0
  338. truthound/observability/tracing/baggage.py +310 -0
  339. truthound/observability/tracing/config.py +426 -0
  340. truthound/observability/tracing/exporter.py +787 -0
  341. truthound/observability/tracing/integration.py +1018 -0
  342. truthound/observability/tracing/otel/__init__.py +146 -0
  343. truthound/observability/tracing/otel/adapter.py +982 -0
  344. truthound/observability/tracing/otel/bridge.py +1177 -0
  345. truthound/observability/tracing/otel/compat.py +681 -0
  346. truthound/observability/tracing/otel/config.py +691 -0
  347. truthound/observability/tracing/otel/detection.py +327 -0
  348. truthound/observability/tracing/otel/protocols.py +426 -0
  349. truthound/observability/tracing/processor.py +561 -0
  350. truthound/observability/tracing/propagator.py +757 -0
  351. truthound/observability/tracing/provider.py +569 -0
  352. truthound/observability/tracing/resource.py +515 -0
  353. truthound/observability/tracing/sampler.py +487 -0
  354. truthound/observability/tracing/span.py +676 -0
  355. truthound/plugins/__init__.py +198 -0
  356. truthound/plugins/base.py +599 -0
  357. truthound/plugins/cli.py +680 -0
  358. truthound/plugins/dependencies/__init__.py +42 -0
  359. truthound/plugins/dependencies/graph.py +422 -0
  360. truthound/plugins/dependencies/resolver.py +417 -0
  361. truthound/plugins/discovery.py +379 -0
  362. truthound/plugins/docs/__init__.py +46 -0
  363. truthound/plugins/docs/extractor.py +444 -0
  364. truthound/plugins/docs/renderer.py +499 -0
  365. truthound/plugins/enterprise_manager.py +877 -0
  366. truthound/plugins/examples/__init__.py +19 -0
  367. truthound/plugins/examples/custom_validators.py +317 -0
  368. truthound/plugins/examples/slack_notifier.py +312 -0
  369. truthound/plugins/examples/xml_reporter.py +254 -0
  370. truthound/plugins/hooks.py +558 -0
  371. truthound/plugins/lifecycle/__init__.py +43 -0
  372. truthound/plugins/lifecycle/hot_reload.py +402 -0
  373. truthound/plugins/lifecycle/manager.py +371 -0
  374. truthound/plugins/manager.py +736 -0
  375. truthound/plugins/registry.py +338 -0
  376. truthound/plugins/security/__init__.py +93 -0
  377. truthound/plugins/security/exceptions.py +332 -0
  378. truthound/plugins/security/policies.py +348 -0
  379. truthound/plugins/security/protocols.py +643 -0
  380. truthound/plugins/security/sandbox/__init__.py +45 -0
  381. truthound/plugins/security/sandbox/context.py +158 -0
  382. truthound/plugins/security/sandbox/engines/__init__.py +19 -0
  383. truthound/plugins/security/sandbox/engines/container.py +379 -0
  384. truthound/plugins/security/sandbox/engines/noop.py +144 -0
  385. truthound/plugins/security/sandbox/engines/process.py +336 -0
  386. truthound/plugins/security/sandbox/factory.py +211 -0
  387. truthound/plugins/security/signing/__init__.py +57 -0
  388. truthound/plugins/security/signing/service.py +330 -0
  389. truthound/plugins/security/signing/trust_store.py +368 -0
  390. truthound/plugins/security/signing/verifier.py +459 -0
  391. truthound/plugins/versioning/__init__.py +41 -0
  392. truthound/plugins/versioning/constraints.py +297 -0
  393. truthound/plugins/versioning/resolver.py +329 -0
  394. truthound/profiler/__init__.py +1729 -0
  395. truthound/profiler/_lazy.py +452 -0
  396. truthound/profiler/ab_testing/__init__.py +80 -0
  397. truthound/profiler/ab_testing/analysis.py +449 -0
  398. truthound/profiler/ab_testing/base.py +257 -0
  399. truthound/profiler/ab_testing/experiment.py +395 -0
  400. truthound/profiler/ab_testing/tracking.py +368 -0
  401. truthound/profiler/auto_threshold.py +1170 -0
  402. truthound/profiler/base.py +579 -0
  403. truthound/profiler/cache_patterns.py +911 -0
  404. truthound/profiler/caching.py +1303 -0
  405. truthound/profiler/column_profiler.py +712 -0
  406. truthound/profiler/comparison.py +1007 -0
  407. truthound/profiler/custom_patterns.py +1170 -0
  408. truthound/profiler/dashboard/__init__.py +50 -0
  409. truthound/profiler/dashboard/app.py +476 -0
  410. truthound/profiler/dashboard/components.py +457 -0
  411. truthound/profiler/dashboard/config.py +72 -0
  412. truthound/profiler/distributed/__init__.py +83 -0
  413. truthound/profiler/distributed/base.py +281 -0
  414. truthound/profiler/distributed/dask_backend.py +498 -0
  415. truthound/profiler/distributed/local_backend.py +293 -0
  416. truthound/profiler/distributed/profiler.py +304 -0
  417. truthound/profiler/distributed/ray_backend.py +374 -0
  418. truthound/profiler/distributed/spark_backend.py +375 -0
  419. truthound/profiler/distributed.py +1366 -0
  420. truthound/profiler/enterprise_sampling.py +1065 -0
  421. truthound/profiler/errors.py +488 -0
  422. truthound/profiler/evolution/__init__.py +91 -0
  423. truthound/profiler/evolution/alerts.py +426 -0
  424. truthound/profiler/evolution/changes.py +206 -0
  425. truthound/profiler/evolution/compatibility.py +365 -0
  426. truthound/profiler/evolution/detector.py +372 -0
  427. truthound/profiler/evolution/protocols.py +121 -0
  428. truthound/profiler/generators/__init__.py +48 -0
  429. truthound/profiler/generators/base.py +384 -0
  430. truthound/profiler/generators/ml_rules.py +375 -0
  431. truthound/profiler/generators/pattern_rules.py +384 -0
  432. truthound/profiler/generators/schema_rules.py +267 -0
  433. truthound/profiler/generators/stats_rules.py +324 -0
  434. truthound/profiler/generators/suite_generator.py +857 -0
  435. truthound/profiler/i18n.py +1542 -0
  436. truthound/profiler/incremental.py +554 -0
  437. truthound/profiler/incremental_validation.py +1710 -0
  438. truthound/profiler/integration/__init__.py +73 -0
  439. truthound/profiler/integration/adapters.py +345 -0
  440. truthound/profiler/integration/context.py +371 -0
  441. truthound/profiler/integration/executor.py +527 -0
  442. truthound/profiler/integration/naming.py +75 -0
  443. truthound/profiler/integration/protocols.py +243 -0
  444. truthound/profiler/memory.py +1185 -0
  445. truthound/profiler/migration/__init__.py +60 -0
  446. truthound/profiler/migration/base.py +345 -0
  447. truthound/profiler/migration/manager.py +444 -0
  448. truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
  449. truthound/profiler/ml/__init__.py +73 -0
  450. truthound/profiler/ml/base.py +244 -0
  451. truthound/profiler/ml/classifier.py +507 -0
  452. truthound/profiler/ml/feature_extraction.py +604 -0
  453. truthound/profiler/ml/pretrained.py +448 -0
  454. truthound/profiler/ml_inference.py +1276 -0
  455. truthound/profiler/native_patterns.py +815 -0
  456. truthound/profiler/observability.py +1184 -0
  457. truthound/profiler/process_timeout.py +1566 -0
  458. truthound/profiler/progress.py +568 -0
  459. truthound/profiler/progress_callbacks.py +1734 -0
  460. truthound/profiler/quality.py +1345 -0
  461. truthound/profiler/resilience.py +1180 -0
  462. truthound/profiler/sampled_matcher.py +794 -0
  463. truthound/profiler/sampling.py +1288 -0
  464. truthound/profiler/scheduling/__init__.py +82 -0
  465. truthound/profiler/scheduling/protocols.py +214 -0
  466. truthound/profiler/scheduling/scheduler.py +474 -0
  467. truthound/profiler/scheduling/storage.py +457 -0
  468. truthound/profiler/scheduling/triggers.py +449 -0
  469. truthound/profiler/schema.py +603 -0
  470. truthound/profiler/streaming.py +685 -0
  471. truthound/profiler/streaming_patterns.py +1354 -0
  472. truthound/profiler/suite_cli.py +625 -0
  473. truthound/profiler/suite_config.py +789 -0
  474. truthound/profiler/suite_export.py +1268 -0
  475. truthound/profiler/table_profiler.py +547 -0
  476. truthound/profiler/timeout.py +565 -0
  477. truthound/profiler/validation.py +1532 -0
  478. truthound/profiler/visualization/__init__.py +118 -0
  479. truthound/profiler/visualization/base.py +346 -0
  480. truthound/profiler/visualization/generator.py +1259 -0
  481. truthound/profiler/visualization/plotly_renderer.py +811 -0
  482. truthound/profiler/visualization/renderers.py +669 -0
  483. truthound/profiler/visualization/sections.py +540 -0
  484. truthound/profiler/visualization.py +2122 -0
  485. truthound/profiler/yaml_validation.py +1151 -0
  486. truthound/py.typed +0 -0
  487. truthound/ratelimit/__init__.py +248 -0
  488. truthound/ratelimit/algorithms.py +1108 -0
  489. truthound/ratelimit/core.py +573 -0
  490. truthound/ratelimit/integration.py +532 -0
  491. truthound/ratelimit/limiter.py +663 -0
  492. truthound/ratelimit/middleware.py +700 -0
  493. truthound/ratelimit/policy.py +792 -0
  494. truthound/ratelimit/storage.py +763 -0
  495. truthound/rbac/__init__.py +340 -0
  496. truthound/rbac/core.py +976 -0
  497. truthound/rbac/integration.py +760 -0
  498. truthound/rbac/manager.py +1052 -0
  499. truthound/rbac/middleware.py +842 -0
  500. truthound/rbac/policy.py +954 -0
  501. truthound/rbac/storage.py +878 -0
  502. truthound/realtime/__init__.py +141 -0
  503. truthound/realtime/adapters/__init__.py +43 -0
  504. truthound/realtime/adapters/base.py +533 -0
  505. truthound/realtime/adapters/kafka.py +487 -0
  506. truthound/realtime/adapters/kinesis.py +479 -0
  507. truthound/realtime/adapters/mock.py +243 -0
  508. truthound/realtime/base.py +553 -0
  509. truthound/realtime/factory.py +382 -0
  510. truthound/realtime/incremental.py +660 -0
  511. truthound/realtime/processing/__init__.py +67 -0
  512. truthound/realtime/processing/exactly_once.py +575 -0
  513. truthound/realtime/processing/state.py +547 -0
  514. truthound/realtime/processing/windows.py +647 -0
  515. truthound/realtime/protocols.py +569 -0
  516. truthound/realtime/streaming.py +605 -0
  517. truthound/realtime/testing/__init__.py +32 -0
  518. truthound/realtime/testing/containers.py +615 -0
  519. truthound/realtime/testing/fixtures.py +484 -0
  520. truthound/report.py +280 -0
  521. truthound/reporters/__init__.py +46 -0
  522. truthound/reporters/_protocols.py +30 -0
  523. truthound/reporters/base.py +324 -0
  524. truthound/reporters/ci/__init__.py +66 -0
  525. truthound/reporters/ci/azure.py +436 -0
  526. truthound/reporters/ci/base.py +509 -0
  527. truthound/reporters/ci/bitbucket.py +567 -0
  528. truthound/reporters/ci/circleci.py +547 -0
  529. truthound/reporters/ci/detection.py +364 -0
  530. truthound/reporters/ci/factory.py +182 -0
  531. truthound/reporters/ci/github.py +388 -0
  532. truthound/reporters/ci/gitlab.py +471 -0
  533. truthound/reporters/ci/jenkins.py +525 -0
  534. truthound/reporters/console_reporter.py +299 -0
  535. truthound/reporters/factory.py +211 -0
  536. truthound/reporters/html_reporter.py +524 -0
  537. truthound/reporters/json_reporter.py +256 -0
  538. truthound/reporters/markdown_reporter.py +280 -0
  539. truthound/reporters/sdk/__init__.py +174 -0
  540. truthound/reporters/sdk/builder.py +558 -0
  541. truthound/reporters/sdk/mixins.py +1150 -0
  542. truthound/reporters/sdk/schema.py +1493 -0
  543. truthound/reporters/sdk/templates.py +666 -0
  544. truthound/reporters/sdk/testing.py +968 -0
  545. truthound/scanners.py +170 -0
  546. truthound/scheduling/__init__.py +122 -0
  547. truthound/scheduling/cron.py +1136 -0
  548. truthound/scheduling/presets.py +212 -0
  549. truthound/schema.py +275 -0
  550. truthound/secrets/__init__.py +173 -0
  551. truthound/secrets/base.py +618 -0
  552. truthound/secrets/cloud.py +682 -0
  553. truthound/secrets/integration.py +507 -0
  554. truthound/secrets/manager.py +633 -0
  555. truthound/secrets/oidc/__init__.py +172 -0
  556. truthound/secrets/oidc/base.py +902 -0
  557. truthound/secrets/oidc/credential_provider.py +623 -0
  558. truthound/secrets/oidc/exchangers.py +1001 -0
  559. truthound/secrets/oidc/github/__init__.py +110 -0
  560. truthound/secrets/oidc/github/claims.py +718 -0
  561. truthound/secrets/oidc/github/enhanced_provider.py +693 -0
  562. truthound/secrets/oidc/github/trust_policy.py +742 -0
  563. truthound/secrets/oidc/github/verification.py +723 -0
  564. truthound/secrets/oidc/github/workflow.py +691 -0
  565. truthound/secrets/oidc/providers.py +825 -0
  566. truthound/secrets/providers.py +506 -0
  567. truthound/secrets/resolver.py +495 -0
  568. truthound/stores/__init__.py +177 -0
  569. truthound/stores/backends/__init__.py +18 -0
  570. truthound/stores/backends/_protocols.py +340 -0
  571. truthound/stores/backends/azure_blob.py +530 -0
  572. truthound/stores/backends/concurrent_filesystem.py +915 -0
  573. truthound/stores/backends/connection_pool.py +1365 -0
  574. truthound/stores/backends/database.py +743 -0
  575. truthound/stores/backends/filesystem.py +538 -0
  576. truthound/stores/backends/gcs.py +399 -0
  577. truthound/stores/backends/memory.py +354 -0
  578. truthound/stores/backends/s3.py +434 -0
  579. truthound/stores/backpressure/__init__.py +84 -0
  580. truthound/stores/backpressure/base.py +375 -0
  581. truthound/stores/backpressure/circuit_breaker.py +434 -0
  582. truthound/stores/backpressure/monitor.py +376 -0
  583. truthound/stores/backpressure/strategies.py +677 -0
  584. truthound/stores/base.py +551 -0
  585. truthound/stores/batching/__init__.py +65 -0
  586. truthound/stores/batching/base.py +305 -0
  587. truthound/stores/batching/buffer.py +370 -0
  588. truthound/stores/batching/store.py +248 -0
  589. truthound/stores/batching/writer.py +521 -0
  590. truthound/stores/caching/__init__.py +60 -0
  591. truthound/stores/caching/backends.py +684 -0
  592. truthound/stores/caching/base.py +356 -0
  593. truthound/stores/caching/store.py +305 -0
  594. truthound/stores/compression/__init__.py +193 -0
  595. truthound/stores/compression/adaptive.py +694 -0
  596. truthound/stores/compression/base.py +514 -0
  597. truthound/stores/compression/pipeline.py +868 -0
  598. truthound/stores/compression/providers.py +672 -0
  599. truthound/stores/compression/streaming.py +832 -0
  600. truthound/stores/concurrency/__init__.py +81 -0
  601. truthound/stores/concurrency/atomic.py +556 -0
  602. truthound/stores/concurrency/index.py +775 -0
  603. truthound/stores/concurrency/locks.py +576 -0
  604. truthound/stores/concurrency/manager.py +482 -0
  605. truthound/stores/encryption/__init__.py +297 -0
  606. truthound/stores/encryption/base.py +952 -0
  607. truthound/stores/encryption/keys.py +1191 -0
  608. truthound/stores/encryption/pipeline.py +903 -0
  609. truthound/stores/encryption/providers.py +953 -0
  610. truthound/stores/encryption/streaming.py +950 -0
  611. truthound/stores/expectations.py +227 -0
  612. truthound/stores/factory.py +246 -0
  613. truthound/stores/migration/__init__.py +75 -0
  614. truthound/stores/migration/base.py +480 -0
  615. truthound/stores/migration/manager.py +347 -0
  616. truthound/stores/migration/registry.py +382 -0
  617. truthound/stores/migration/store.py +559 -0
  618. truthound/stores/observability/__init__.py +106 -0
  619. truthound/stores/observability/audit.py +718 -0
  620. truthound/stores/observability/config.py +270 -0
  621. truthound/stores/observability/factory.py +208 -0
  622. truthound/stores/observability/metrics.py +636 -0
  623. truthound/stores/observability/protocols.py +410 -0
  624. truthound/stores/observability/store.py +570 -0
  625. truthound/stores/observability/tracing.py +784 -0
  626. truthound/stores/replication/__init__.py +76 -0
  627. truthound/stores/replication/base.py +260 -0
  628. truthound/stores/replication/monitor.py +269 -0
  629. truthound/stores/replication/store.py +439 -0
  630. truthound/stores/replication/syncer.py +391 -0
  631. truthound/stores/results.py +359 -0
  632. truthound/stores/retention/__init__.py +77 -0
  633. truthound/stores/retention/base.py +378 -0
  634. truthound/stores/retention/policies.py +621 -0
  635. truthound/stores/retention/scheduler.py +279 -0
  636. truthound/stores/retention/store.py +526 -0
  637. truthound/stores/streaming/__init__.py +138 -0
  638. truthound/stores/streaming/base.py +801 -0
  639. truthound/stores/streaming/database.py +984 -0
  640. truthound/stores/streaming/filesystem.py +719 -0
  641. truthound/stores/streaming/reader.py +629 -0
  642. truthound/stores/streaming/s3.py +843 -0
  643. truthound/stores/streaming/writer.py +790 -0
  644. truthound/stores/tiering/__init__.py +108 -0
  645. truthound/stores/tiering/base.py +462 -0
  646. truthound/stores/tiering/manager.py +249 -0
  647. truthound/stores/tiering/policies.py +692 -0
  648. truthound/stores/tiering/store.py +526 -0
  649. truthound/stores/versioning/__init__.py +56 -0
  650. truthound/stores/versioning/base.py +376 -0
  651. truthound/stores/versioning/store.py +660 -0
  652. truthound/stores/versioning/strategies.py +353 -0
  653. truthound/types.py +56 -0
  654. truthound/validators/__init__.py +774 -0
  655. truthound/validators/aggregate/__init__.py +27 -0
  656. truthound/validators/aggregate/central.py +116 -0
  657. truthound/validators/aggregate/extremes.py +116 -0
  658. truthound/validators/aggregate/spread.py +118 -0
  659. truthound/validators/aggregate/sum.py +64 -0
  660. truthound/validators/aggregate/type.py +78 -0
  661. truthound/validators/anomaly/__init__.py +93 -0
  662. truthound/validators/anomaly/base.py +431 -0
  663. truthound/validators/anomaly/ml_based.py +1190 -0
  664. truthound/validators/anomaly/multivariate.py +647 -0
  665. truthound/validators/anomaly/statistical.py +599 -0
  666. truthound/validators/base.py +1089 -0
  667. truthound/validators/business_rule/__init__.py +46 -0
  668. truthound/validators/business_rule/base.py +147 -0
  669. truthound/validators/business_rule/checksum.py +509 -0
  670. truthound/validators/business_rule/financial.py +526 -0
  671. truthound/validators/cache.py +733 -0
  672. truthound/validators/completeness/__init__.py +39 -0
  673. truthound/validators/completeness/conditional.py +73 -0
  674. truthound/validators/completeness/default.py +98 -0
  675. truthound/validators/completeness/empty.py +103 -0
  676. truthound/validators/completeness/nan.py +337 -0
  677. truthound/validators/completeness/null.py +152 -0
  678. truthound/validators/cross_table/__init__.py +17 -0
  679. truthound/validators/cross_table/aggregate.py +333 -0
  680. truthound/validators/cross_table/row_count.py +122 -0
  681. truthound/validators/datetime/__init__.py +29 -0
  682. truthound/validators/datetime/format.py +78 -0
  683. truthound/validators/datetime/freshness.py +269 -0
  684. truthound/validators/datetime/order.py +73 -0
  685. truthound/validators/datetime/parseable.py +185 -0
  686. truthound/validators/datetime/range.py +202 -0
  687. truthound/validators/datetime/timezone.py +69 -0
  688. truthound/validators/distribution/__init__.py +49 -0
  689. truthound/validators/distribution/distribution.py +128 -0
  690. truthound/validators/distribution/monotonic.py +119 -0
  691. truthound/validators/distribution/outlier.py +178 -0
  692. truthound/validators/distribution/quantile.py +80 -0
  693. truthound/validators/distribution/range.py +254 -0
  694. truthound/validators/distribution/set.py +125 -0
  695. truthound/validators/distribution/statistical.py +459 -0
  696. truthound/validators/drift/__init__.py +79 -0
  697. truthound/validators/drift/base.py +427 -0
  698. truthound/validators/drift/multi_feature.py +401 -0
  699. truthound/validators/drift/numeric.py +395 -0
  700. truthound/validators/drift/psi.py +446 -0
  701. truthound/validators/drift/statistical.py +510 -0
  702. truthound/validators/enterprise.py +1658 -0
  703. truthound/validators/geospatial/__init__.py +80 -0
  704. truthound/validators/geospatial/base.py +97 -0
  705. truthound/validators/geospatial/boundary.py +238 -0
  706. truthound/validators/geospatial/coordinate.py +351 -0
  707. truthound/validators/geospatial/distance.py +399 -0
  708. truthound/validators/geospatial/polygon.py +665 -0
  709. truthound/validators/i18n/__init__.py +308 -0
  710. truthound/validators/i18n/bidi.py +571 -0
  711. truthound/validators/i18n/catalogs.py +570 -0
  712. truthound/validators/i18n/dialects.py +763 -0
  713. truthound/validators/i18n/extended_catalogs.py +549 -0
  714. truthound/validators/i18n/formatting.py +1434 -0
  715. truthound/validators/i18n/loader.py +1020 -0
  716. truthound/validators/i18n/messages.py +521 -0
  717. truthound/validators/i18n/plural.py +683 -0
  718. truthound/validators/i18n/protocols.py +855 -0
  719. truthound/validators/i18n/tms.py +1162 -0
  720. truthound/validators/localization/__init__.py +53 -0
  721. truthound/validators/localization/base.py +122 -0
  722. truthound/validators/localization/chinese.py +362 -0
  723. truthound/validators/localization/japanese.py +275 -0
  724. truthound/validators/localization/korean.py +524 -0
  725. truthound/validators/memory/__init__.py +94 -0
  726. truthound/validators/memory/approximate_knn.py +506 -0
  727. truthound/validators/memory/base.py +547 -0
  728. truthound/validators/memory/sgd_online.py +719 -0
  729. truthound/validators/memory/streaming_ecdf.py +753 -0
  730. truthound/validators/ml_feature/__init__.py +54 -0
  731. truthound/validators/ml_feature/base.py +249 -0
  732. truthound/validators/ml_feature/correlation.py +299 -0
  733. truthound/validators/ml_feature/leakage.py +344 -0
  734. truthound/validators/ml_feature/null_impact.py +270 -0
  735. truthound/validators/ml_feature/scale.py +264 -0
  736. truthound/validators/multi_column/__init__.py +89 -0
  737. truthound/validators/multi_column/arithmetic.py +284 -0
  738. truthound/validators/multi_column/base.py +231 -0
  739. truthound/validators/multi_column/comparison.py +273 -0
  740. truthound/validators/multi_column/consistency.py +312 -0
  741. truthound/validators/multi_column/statistical.py +299 -0
  742. truthound/validators/optimization/__init__.py +164 -0
  743. truthound/validators/optimization/aggregation.py +563 -0
  744. truthound/validators/optimization/covariance.py +556 -0
  745. truthound/validators/optimization/geo.py +626 -0
  746. truthound/validators/optimization/graph.py +587 -0
  747. truthound/validators/optimization/orchestrator.py +970 -0
  748. truthound/validators/optimization/profiling.py +1312 -0
  749. truthound/validators/privacy/__init__.py +223 -0
  750. truthound/validators/privacy/base.py +635 -0
  751. truthound/validators/privacy/ccpa.py +670 -0
  752. truthound/validators/privacy/gdpr.py +728 -0
  753. truthound/validators/privacy/global_patterns.py +604 -0
  754. truthound/validators/privacy/plugins.py +867 -0
  755. truthound/validators/profiling/__init__.py +52 -0
  756. truthound/validators/profiling/base.py +175 -0
  757. truthound/validators/profiling/cardinality.py +312 -0
  758. truthound/validators/profiling/entropy.py +391 -0
  759. truthound/validators/profiling/frequency.py +455 -0
  760. truthound/validators/pushdown_support.py +660 -0
  761. truthound/validators/query/__init__.py +91 -0
  762. truthound/validators/query/aggregate.py +346 -0
  763. truthound/validators/query/base.py +246 -0
  764. truthound/validators/query/column.py +249 -0
  765. truthound/validators/query/expression.py +274 -0
  766. truthound/validators/query/result.py +323 -0
  767. truthound/validators/query/row_count.py +264 -0
  768. truthound/validators/referential/__init__.py +80 -0
  769. truthound/validators/referential/base.py +395 -0
  770. truthound/validators/referential/cascade.py +391 -0
  771. truthound/validators/referential/circular.py +563 -0
  772. truthound/validators/referential/foreign_key.py +624 -0
  773. truthound/validators/referential/orphan.py +485 -0
  774. truthound/validators/registry.py +112 -0
  775. truthound/validators/schema/__init__.py +41 -0
  776. truthound/validators/schema/column_count.py +142 -0
  777. truthound/validators/schema/column_exists.py +80 -0
  778. truthound/validators/schema/column_order.py +82 -0
  779. truthound/validators/schema/column_pair.py +85 -0
  780. truthound/validators/schema/column_pair_set.py +195 -0
  781. truthound/validators/schema/column_type.py +94 -0
  782. truthound/validators/schema/multi_column.py +53 -0
  783. truthound/validators/schema/multi_column_aggregate.py +175 -0
  784. truthound/validators/schema/referential.py +274 -0
  785. truthound/validators/schema/table_schema.py +91 -0
  786. truthound/validators/schema_validator.py +219 -0
  787. truthound/validators/sdk/__init__.py +250 -0
  788. truthound/validators/sdk/builder.py +680 -0
  789. truthound/validators/sdk/decorators.py +474 -0
  790. truthound/validators/sdk/enterprise/__init__.py +211 -0
  791. truthound/validators/sdk/enterprise/docs.py +725 -0
  792. truthound/validators/sdk/enterprise/fuzzing.py +659 -0
  793. truthound/validators/sdk/enterprise/licensing.py +709 -0
  794. truthound/validators/sdk/enterprise/manager.py +543 -0
  795. truthound/validators/sdk/enterprise/resources.py +628 -0
  796. truthound/validators/sdk/enterprise/sandbox.py +766 -0
  797. truthound/validators/sdk/enterprise/signing.py +603 -0
  798. truthound/validators/sdk/enterprise/templates.py +865 -0
  799. truthound/validators/sdk/enterprise/versioning.py +659 -0
  800. truthound/validators/sdk/templates.py +757 -0
  801. truthound/validators/sdk/testing.py +807 -0
  802. truthound/validators/security/__init__.py +181 -0
  803. truthound/validators/security/redos/__init__.py +182 -0
  804. truthound/validators/security/redos/core.py +861 -0
  805. truthound/validators/security/redos/cpu_monitor.py +593 -0
  806. truthound/validators/security/redos/cve_database.py +791 -0
  807. truthound/validators/security/redos/ml/__init__.py +155 -0
  808. truthound/validators/security/redos/ml/base.py +785 -0
  809. truthound/validators/security/redos/ml/datasets.py +618 -0
  810. truthound/validators/security/redos/ml/features.py +359 -0
  811. truthound/validators/security/redos/ml/models.py +1000 -0
  812. truthound/validators/security/redos/ml/predictor.py +507 -0
  813. truthound/validators/security/redos/ml/storage.py +632 -0
  814. truthound/validators/security/redos/ml/training.py +571 -0
  815. truthound/validators/security/redos/ml_analyzer.py +937 -0
  816. truthound/validators/security/redos/optimizer.py +674 -0
  817. truthound/validators/security/redos/profiler.py +682 -0
  818. truthound/validators/security/redos/re2_engine.py +709 -0
  819. truthound/validators/security/redos.py +886 -0
  820. truthound/validators/security/sql_security.py +1247 -0
  821. truthound/validators/streaming/__init__.py +126 -0
  822. truthound/validators/streaming/base.py +292 -0
  823. truthound/validators/streaming/completeness.py +210 -0
  824. truthound/validators/streaming/mixin.py +575 -0
  825. truthound/validators/streaming/range.py +308 -0
  826. truthound/validators/streaming/sources.py +846 -0
  827. truthound/validators/string/__init__.py +57 -0
  828. truthound/validators/string/casing.py +158 -0
  829. truthound/validators/string/charset.py +96 -0
  830. truthound/validators/string/format.py +501 -0
  831. truthound/validators/string/json.py +77 -0
  832. truthound/validators/string/json_schema.py +184 -0
  833. truthound/validators/string/length.py +104 -0
  834. truthound/validators/string/like_pattern.py +237 -0
  835. truthound/validators/string/regex.py +202 -0
  836. truthound/validators/string/regex_extended.py +435 -0
  837. truthound/validators/table/__init__.py +88 -0
  838. truthound/validators/table/base.py +78 -0
  839. truthound/validators/table/column_count.py +198 -0
  840. truthound/validators/table/freshness.py +362 -0
  841. truthound/validators/table/row_count.py +251 -0
  842. truthound/validators/table/schema.py +333 -0
  843. truthound/validators/table/size.py +285 -0
  844. truthound/validators/timeout/__init__.py +102 -0
  845. truthound/validators/timeout/advanced/__init__.py +247 -0
  846. truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
  847. truthound/validators/timeout/advanced/prediction.py +773 -0
  848. truthound/validators/timeout/advanced/priority.py +618 -0
  849. truthound/validators/timeout/advanced/redis_backend.py +770 -0
  850. truthound/validators/timeout/advanced/retry.py +721 -0
  851. truthound/validators/timeout/advanced/sampling.py +788 -0
  852. truthound/validators/timeout/advanced/sla.py +661 -0
  853. truthound/validators/timeout/advanced/telemetry.py +804 -0
  854. truthound/validators/timeout/cascade.py +477 -0
  855. truthound/validators/timeout/deadline.py +657 -0
  856. truthound/validators/timeout/degradation.py +525 -0
  857. truthound/validators/timeout/distributed.py +597 -0
  858. truthound/validators/timeseries/__init__.py +89 -0
  859. truthound/validators/timeseries/base.py +326 -0
  860. truthound/validators/timeseries/completeness.py +617 -0
  861. truthound/validators/timeseries/gap.py +485 -0
  862. truthound/validators/timeseries/monotonic.py +310 -0
  863. truthound/validators/timeseries/seasonality.py +422 -0
  864. truthound/validators/timeseries/trend.py +510 -0
  865. truthound/validators/uniqueness/__init__.py +59 -0
  866. truthound/validators/uniqueness/approximate.py +475 -0
  867. truthound/validators/uniqueness/distinct_values.py +253 -0
  868. truthound/validators/uniqueness/duplicate.py +118 -0
  869. truthound/validators/uniqueness/primary_key.py +140 -0
  870. truthound/validators/uniqueness/unique.py +191 -0
  871. truthound/validators/uniqueness/within_record.py +599 -0
  872. truthound/validators/utils.py +756 -0
  873. truthound-1.0.8.dist-info/METADATA +474 -0
  874. truthound-1.0.8.dist-info/RECORD +877 -0
  875. truthound-1.0.8.dist-info/WHEEL +4 -0
  876. truthound-1.0.8.dist-info/entry_points.txt +2 -0
  877. truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,1052 @@
1
+ """RBAC manager for role-based access control.
2
+
3
+ This module provides the central RBACManager class that orchestrates
4
+ all RBAC-related operations including role management, principal management,
5
+ permission checking, and policy evaluation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import threading
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime, timezone
13
+ from typing import Any, Callable, Iterator
14
+
15
+ from truthound.rbac.core import (
16
+ AccessContext,
17
+ AccessDecision,
18
+ Permission,
19
+ PermissionAction,
20
+ PermissionDeniedError,
21
+ Principal,
22
+ PrincipalStore,
23
+ PrincipalType,
24
+ Role,
25
+ RoleNotFoundError,
26
+ RoleStore,
27
+ RoleType,
28
+ SecurityContext,
29
+ generate_principal_id,
30
+ generate_role_id,
31
+ )
32
+ from truthound.rbac.policy import (
33
+ PolicyCombination,
34
+ PolicyEngine,
35
+ PolicyEngineConfig,
36
+ RoleBasedEvaluator,
37
+ )
38
+ from truthound.rbac.storage import (
39
+ MemoryPrincipalStore,
40
+ MemoryRoleStore,
41
+ create_principal_store,
42
+ create_role_store,
43
+ )
44
+
45
+
46
+ # =============================================================================
47
+ # Configuration
48
+ # =============================================================================
49
+
50
+
51
+ @dataclass
52
+ class RBACManagerConfig:
53
+ """Configuration for the RBAC manager."""
54
+
55
+ # Default settings for new principals
56
+ default_principal_type: PrincipalType = PrincipalType.USER
57
+ default_principal_roles: set[str] = field(default_factory=set)
58
+
59
+ # Policy evaluation
60
+ policy_combination: PolicyCombination = PolicyCombination.DENY_OVERRIDES
61
+ cache_decisions: bool = True
62
+ cache_ttl_seconds: int = 300
63
+
64
+ # Security settings
65
+ require_authentication: bool = True
66
+ anonymous_principal: Principal | None = None
67
+
68
+ # Lifecycle hooks
69
+ on_role_create: list[Callable[[Role], None]] = field(default_factory=list)
70
+ on_role_update: list[Callable[[Role, Role], None]] = field(default_factory=list)
71
+ on_role_delete: list[Callable[[str], None]] = field(default_factory=list)
72
+ on_principal_create: list[Callable[[Principal], None]] = field(default_factory=list)
73
+ on_principal_update: list[Callable[[Principal, Principal], None]] = field(default_factory=list)
74
+ on_principal_delete: list[Callable[[str], None]] = field(default_factory=list)
75
+ on_access_decision: list[Callable[[AccessContext, AccessDecision], None]] = field(default_factory=list)
76
+
77
+ # Audit
78
+ audit_access_decisions: bool = True
79
+
80
+
81
+ # =============================================================================
82
+ # RBAC Manager
83
+ # =============================================================================
84
+
85
+
86
+ class RBACManager:
87
+ """Central manager for all RBAC operations.
88
+
89
+ The RBACManager provides a unified interface for:
90
+ - CRUD operations on roles and principals
91
+ - Permission checking and policy evaluation
92
+ - Role inheritance management
93
+ - Security context management
94
+
95
+ Example:
96
+ >>> manager = RBACManager()
97
+ >>>
98
+ >>> # Create a role
99
+ >>> role = manager.create_role(
100
+ ... name="Data Analyst",
101
+ ... permissions={"dataset:read", "validation:execute"},
102
+ ... )
103
+ >>>
104
+ >>> # Create a principal
105
+ >>> principal = manager.create_principal(
106
+ ... name="john.doe@example.com",
107
+ ... roles={role.id},
108
+ ... )
109
+ >>>
110
+ >>> # Check permission
111
+ >>> decision = manager.check(principal, "dataset", "read")
112
+ >>> print(decision.allowed) # True
113
+ """
114
+
115
+ def __init__(
116
+ self,
117
+ role_store: RoleStore | None = None,
118
+ principal_store: PrincipalStore | None = None,
119
+ policy_engine: PolicyEngine | None = None,
120
+ config: RBACManagerConfig | None = None,
121
+ ) -> None:
122
+ self._role_store = role_store or MemoryRoleStore()
123
+ self._principal_store = principal_store or MemoryPrincipalStore()
124
+ self._config = config or RBACManagerConfig()
125
+
126
+ # Create or use provided policy engine
127
+ if policy_engine:
128
+ self._engine = policy_engine
129
+ else:
130
+ engine_config = PolicyEngineConfig(
131
+ combination=self._config.policy_combination,
132
+ cache_decisions=self._config.cache_decisions,
133
+ cache_ttl_seconds=self._config.cache_ttl_seconds,
134
+ )
135
+ self._engine = PolicyEngine(config=engine_config)
136
+ # Add default role-based evaluator
137
+ self._engine.add_evaluator(RoleBasedEvaluator(self._role_store))
138
+
139
+ self._lock = threading.RLock()
140
+
141
+ # Initialize default roles
142
+ self._init_default_roles()
143
+
144
+ # =========================================================================
145
+ # Properties
146
+ # =========================================================================
147
+
148
+ @property
149
+ def role_store(self) -> RoleStore:
150
+ """Get the role store."""
151
+ return self._role_store
152
+
153
+ @property
154
+ def principal_store(self) -> PrincipalStore:
155
+ """Get the principal store."""
156
+ return self._principal_store
157
+
158
+ @property
159
+ def engine(self) -> PolicyEngine:
160
+ """Get the policy engine."""
161
+ return self._engine
162
+
163
+ @property
164
+ def config(self) -> RBACManagerConfig:
165
+ """Get the configuration."""
166
+ return self._config
167
+
168
+ # =========================================================================
169
+ # Role Management
170
+ # =========================================================================
171
+
172
+ def create_role(
173
+ self,
174
+ name: str,
175
+ role_id: str | None = None,
176
+ permissions: set[str | Permission] | None = None,
177
+ parent_roles: set[str] | None = None,
178
+ role_type: RoleType = RoleType.CUSTOM,
179
+ description: str = "",
180
+ tenant_id: str | None = None,
181
+ **kwargs: Any,
182
+ ) -> Role:
183
+ """Create a new role.
184
+
185
+ Args:
186
+ name: Human-readable role name
187
+ role_id: Optional custom ID (auto-generated if not provided)
188
+ permissions: Permission strings or objects
189
+ parent_roles: Parent role IDs for inheritance
190
+ role_type: Type of role
191
+ description: Role description
192
+ tenant_id: Tenant ID for scoped roles
193
+ **kwargs: Additional role attributes
194
+
195
+ Returns:
196
+ Created Role object.
197
+
198
+ Example:
199
+ >>> role = manager.create_role(
200
+ ... name="Editor",
201
+ ... permissions={"dataset:read", "dataset:update"},
202
+ ... )
203
+ """
204
+ with self._lock:
205
+ # Generate ID if not provided
206
+ if not role_id:
207
+ role_id = generate_role_id(name)
208
+ if tenant_id:
209
+ role_id = f"{tenant_id}:{role_id}"
210
+
211
+ # Check for duplicates
212
+ if self._role_store.exists(role_id):
213
+ raise ValueError(f"Role already exists: {role_id}")
214
+
215
+ # Parse permissions
216
+ parsed_permissions = set()
217
+ for perm in (permissions or set()):
218
+ if isinstance(perm, str):
219
+ parsed_permissions.add(Permission.parse(perm))
220
+ else:
221
+ parsed_permissions.add(perm)
222
+
223
+ # Create role
224
+ role = Role(
225
+ id=role_id,
226
+ name=name,
227
+ description=description,
228
+ role_type=role_type,
229
+ permissions=parsed_permissions,
230
+ parent_roles=parent_roles or set(),
231
+ tenant_id=tenant_id,
232
+ **kwargs,
233
+ )
234
+
235
+ self._role_store.save(role)
236
+
237
+ # Call hooks
238
+ for hook in self._config.on_role_create:
239
+ try:
240
+ hook(role)
241
+ except Exception:
242
+ pass
243
+
244
+ return role
245
+
246
+ def get_role(self, role_id: str) -> Role | None:
247
+ """Get a role by ID.
248
+
249
+ Args:
250
+ role_id: Role ID
251
+
252
+ Returns:
253
+ Role object if found, None otherwise.
254
+ """
255
+ return self._role_store.get(role_id)
256
+
257
+ def require_role(self, role_id: str) -> Role:
258
+ """Get a role by ID, raising if not found.
259
+
260
+ Args:
261
+ role_id: Role ID
262
+
263
+ Returns:
264
+ Role object.
265
+
266
+ Raises:
267
+ RoleNotFoundError: If role not found.
268
+ """
269
+ role = self.get_role(role_id)
270
+ if not role:
271
+ raise RoleNotFoundError(f"Role not found: {role_id}")
272
+ return role
273
+
274
+ def list_roles(
275
+ self,
276
+ tenant_id: str | None = None,
277
+ role_type: RoleType | None = None,
278
+ enabled: bool | None = None,
279
+ ) -> list[Role]:
280
+ """List roles with optional filters.
281
+
282
+ Args:
283
+ tenant_id: Filter by tenant
284
+ role_type: Filter by role type
285
+ enabled: Filter by enabled status
286
+
287
+ Returns:
288
+ List of Role objects.
289
+ """
290
+ return self._role_store.list(
291
+ tenant_id=tenant_id,
292
+ role_type=role_type,
293
+ enabled=enabled,
294
+ )
295
+
296
+ def update_role(
297
+ self,
298
+ role_id: str,
299
+ **updates: Any,
300
+ ) -> Role:
301
+ """Update a role.
302
+
303
+ Args:
304
+ role_id: Role ID
305
+ **updates: Fields to update
306
+
307
+ Returns:
308
+ Updated Role object.
309
+
310
+ Example:
311
+ >>> role = manager.update_role(
312
+ ... "editor",
313
+ ... name="Senior Editor",
314
+ ... description="Updated description",
315
+ ... )
316
+ """
317
+ with self._lock:
318
+ role = self.require_role(role_id)
319
+ old_role = Role.from_dict(role.to_dict()) # Copy
320
+
321
+ # Apply updates
322
+ for key, value in updates.items():
323
+ if hasattr(role, key):
324
+ setattr(role, key, value)
325
+
326
+ role.updated_at = datetime.now(timezone.utc)
327
+ self._role_store.save(role)
328
+
329
+ # Call hooks
330
+ for hook in self._config.on_role_update:
331
+ try:
332
+ hook(old_role, role)
333
+ except Exception:
334
+ pass
335
+
336
+ return role
337
+
338
+ def delete_role(self, role_id: str) -> bool:
339
+ """Delete a role.
340
+
341
+ Args:
342
+ role_id: Role ID
343
+
344
+ Returns:
345
+ True if deleted, False if not found.
346
+ """
347
+ with self._lock:
348
+ deleted = self._role_store.delete(role_id)
349
+
350
+ if deleted:
351
+ for hook in self._config.on_role_delete:
352
+ try:
353
+ hook(role_id)
354
+ except Exception:
355
+ pass
356
+
357
+ return deleted
358
+
359
+ def add_permission_to_role(
360
+ self,
361
+ role_id: str,
362
+ permission: str | Permission,
363
+ ) -> Role:
364
+ """Add a permission to a role.
365
+
366
+ Args:
367
+ role_id: Role ID
368
+ permission: Permission string or object
369
+
370
+ Returns:
371
+ Updated Role object.
372
+ """
373
+ with self._lock:
374
+ role = self.require_role(role_id)
375
+
376
+ if isinstance(permission, str):
377
+ permission = Permission.parse(permission)
378
+
379
+ role.add_permission(permission)
380
+ role.updated_at = datetime.now(timezone.utc)
381
+ self._role_store.save(role)
382
+
383
+ return role
384
+
385
+ def remove_permission_from_role(
386
+ self,
387
+ role_id: str,
388
+ permission: str | Permission,
389
+ ) -> Role:
390
+ """Remove a permission from a role.
391
+
392
+ Args:
393
+ role_id: Role ID
394
+ permission: Permission string or object
395
+
396
+ Returns:
397
+ Updated Role object.
398
+ """
399
+ with self._lock:
400
+ role = self.require_role(role_id)
401
+
402
+ if isinstance(permission, str):
403
+ permission = Permission.parse(permission)
404
+
405
+ role.remove_permission(permission)
406
+ role.updated_at = datetime.now(timezone.utc)
407
+ self._role_store.save(role)
408
+
409
+ return role
410
+
411
+ def get_all_role_permissions(self, role_id: str) -> set[Permission]:
412
+ """Get all permissions for a role, including inherited ones.
413
+
414
+ Args:
415
+ role_id: Role ID
416
+
417
+ Returns:
418
+ Set of Permission objects.
419
+ """
420
+ return self._role_store.get_all_permissions(role_id)
421
+
422
+ # =========================================================================
423
+ # Principal Management
424
+ # =========================================================================
425
+
426
+ def create_principal(
427
+ self,
428
+ name: str,
429
+ principal_id: str | None = None,
430
+ principal_type: PrincipalType | None = None,
431
+ email: str = "",
432
+ roles: set[str] | None = None,
433
+ permissions: set[str | Permission] | None = None,
434
+ attributes: dict[str, Any] | None = None,
435
+ tenant_id: str | None = None,
436
+ **kwargs: Any,
437
+ ) -> Principal:
438
+ """Create a new principal.
439
+
440
+ Args:
441
+ name: Principal name
442
+ principal_id: Optional custom ID
443
+ principal_type: Type of principal
444
+ email: Principal email
445
+ roles: Role IDs to assign
446
+ permissions: Direct permissions
447
+ attributes: ABAC attributes
448
+ tenant_id: Tenant ID
449
+ **kwargs: Additional attributes
450
+
451
+ Returns:
452
+ Created Principal object.
453
+
454
+ Example:
455
+ >>> principal = manager.create_principal(
456
+ ... name="john.doe@example.com",
457
+ ... email="john.doe@example.com",
458
+ ... roles={"editor"},
459
+ ... )
460
+ """
461
+ with self._lock:
462
+ # Generate ID if not provided
463
+ if not principal_id:
464
+ prefix = "user"
465
+ if tenant_id:
466
+ prefix = f"{tenant_id}_{prefix}"
467
+ principal_id = generate_principal_id(prefix)
468
+
469
+ # Use default type from config
470
+ if principal_type is None:
471
+ principal_type = self._config.default_principal_type
472
+
473
+ # Parse direct permissions
474
+ parsed_permissions = set()
475
+ for perm in (permissions or set()):
476
+ if isinstance(perm, str):
477
+ parsed_permissions.add(Permission.parse(perm))
478
+ else:
479
+ parsed_permissions.add(perm)
480
+
481
+ # Combine with default roles
482
+ all_roles = set(roles or set())
483
+ all_roles.update(self._config.default_principal_roles)
484
+
485
+ # Create principal
486
+ principal = Principal(
487
+ id=principal_id,
488
+ type=principal_type,
489
+ name=name,
490
+ email=email,
491
+ roles=all_roles,
492
+ direct_permissions=parsed_permissions,
493
+ attributes=attributes or {},
494
+ tenant_id=tenant_id,
495
+ **kwargs,
496
+ )
497
+
498
+ self._principal_store.save(principal)
499
+
500
+ # Call hooks
501
+ for hook in self._config.on_principal_create:
502
+ try:
503
+ hook(principal)
504
+ except Exception:
505
+ pass
506
+
507
+ return principal
508
+
509
+ def get_principal(self, principal_id: str) -> Principal | None:
510
+ """Get a principal by ID.
511
+
512
+ Args:
513
+ principal_id: Principal ID
514
+
515
+ Returns:
516
+ Principal object if found, None otherwise.
517
+ """
518
+ return self._principal_store.get(principal_id)
519
+
520
+ def get_principal_by_email(self, email: str) -> Principal | None:
521
+ """Get a principal by email.
522
+
523
+ Args:
524
+ email: Principal email
525
+
526
+ Returns:
527
+ Principal object if found, None otherwise.
528
+ """
529
+ return self._principal_store.get_by_email(email)
530
+
531
+ def require_principal(self, principal_id: str) -> Principal:
532
+ """Get a principal by ID, raising if not found.
533
+
534
+ Args:
535
+ principal_id: Principal ID
536
+
537
+ Returns:
538
+ Principal object.
539
+
540
+ Raises:
541
+ ValueError: If principal not found.
542
+ """
543
+ principal = self.get_principal(principal_id)
544
+ if not principal:
545
+ raise ValueError(f"Principal not found: {principal_id}")
546
+ return principal
547
+
548
+ def list_principals(
549
+ self,
550
+ tenant_id: str | None = None,
551
+ principal_type: PrincipalType | None = None,
552
+ role_id: str | None = None,
553
+ ) -> list[Principal]:
554
+ """List principals with optional filters.
555
+
556
+ Args:
557
+ tenant_id: Filter by tenant
558
+ principal_type: Filter by type
559
+ role_id: Filter by role
560
+
561
+ Returns:
562
+ List of Principal objects.
563
+ """
564
+ return self._principal_store.list(
565
+ tenant_id=tenant_id,
566
+ principal_type=principal_type,
567
+ role_id=role_id,
568
+ )
569
+
570
+ def update_principal(
571
+ self,
572
+ principal_id: str,
573
+ **updates: Any,
574
+ ) -> Principal:
575
+ """Update a principal.
576
+
577
+ Args:
578
+ principal_id: Principal ID
579
+ **updates: Fields to update
580
+
581
+ Returns:
582
+ Updated Principal object.
583
+ """
584
+ with self._lock:
585
+ principal = self.require_principal(principal_id)
586
+ old_principal = Principal.from_dict(principal.to_dict())
587
+
588
+ for key, value in updates.items():
589
+ if hasattr(principal, key):
590
+ setattr(principal, key, value)
591
+
592
+ self._principal_store.save(principal)
593
+
594
+ for hook in self._config.on_principal_update:
595
+ try:
596
+ hook(old_principal, principal)
597
+ except Exception:
598
+ pass
599
+
600
+ return principal
601
+
602
+ def delete_principal(self, principal_id: str) -> bool:
603
+ """Delete a principal.
604
+
605
+ Args:
606
+ principal_id: Principal ID
607
+
608
+ Returns:
609
+ True if deleted, False if not found.
610
+ """
611
+ with self._lock:
612
+ deleted = self._principal_store.delete(principal_id)
613
+
614
+ if deleted:
615
+ for hook in self._config.on_principal_delete:
616
+ try:
617
+ hook(principal_id)
618
+ except Exception:
619
+ pass
620
+
621
+ return deleted
622
+
623
+ def assign_role(self, principal_id: str, role_id: str) -> Principal:
624
+ """Assign a role to a principal.
625
+
626
+ Args:
627
+ principal_id: Principal ID
628
+ role_id: Role ID
629
+
630
+ Returns:
631
+ Updated Principal object.
632
+ """
633
+ with self._lock:
634
+ principal = self.require_principal(principal_id)
635
+ principal.add_role(role_id)
636
+ self._principal_store.save(principal)
637
+ return principal
638
+
639
+ def revoke_role(self, principal_id: str, role_id: str) -> Principal:
640
+ """Revoke a role from a principal.
641
+
642
+ Args:
643
+ principal_id: Principal ID
644
+ role_id: Role ID
645
+
646
+ Returns:
647
+ Updated Principal object.
648
+ """
649
+ with self._lock:
650
+ principal = self.require_principal(principal_id)
651
+ principal.remove_role(role_id)
652
+ self._principal_store.save(principal)
653
+ return principal
654
+
655
+ def get_principal_permissions(self, principal_id: str) -> set[Permission]:
656
+ """Get all effective permissions for a principal.
657
+
658
+ Includes permissions from all roles and direct permissions.
659
+
660
+ Args:
661
+ principal_id: Principal ID
662
+
663
+ Returns:
664
+ Set of Permission objects.
665
+ """
666
+ principal = self.require_principal(principal_id)
667
+ permissions = set(principal.direct_permissions)
668
+
669
+ for role_id in principal.roles:
670
+ role_perms = self.get_all_role_permissions(role_id)
671
+ permissions.update(role_perms)
672
+
673
+ return permissions
674
+
675
+ # =========================================================================
676
+ # Permission Checking
677
+ # =========================================================================
678
+
679
+ def check(
680
+ self,
681
+ principal: Principal | str,
682
+ resource: str,
683
+ action: str | PermissionAction,
684
+ resource_attributes: dict[str, Any] | None = None,
685
+ ) -> AccessDecision:
686
+ """Check if a principal has permission.
687
+
688
+ Args:
689
+ principal: Principal or principal ID
690
+ resource: Resource being accessed
691
+ action: Action being performed
692
+ resource_attributes: Resource attributes for ABAC
693
+
694
+ Returns:
695
+ Access decision.
696
+
697
+ Example:
698
+ >>> decision = manager.check(principal, "dataset", "read")
699
+ >>> if decision.allowed:
700
+ ... print("Access granted")
701
+ """
702
+ if isinstance(principal, str):
703
+ principal = self.require_principal(principal)
704
+
705
+ decision = self._engine.check(
706
+ principal=principal,
707
+ resource=resource,
708
+ action=action,
709
+ resource_attributes=resource_attributes,
710
+ )
711
+
712
+ # Call hooks
713
+ if self._config.audit_access_decisions:
714
+ context = AccessContext(
715
+ principal=principal,
716
+ resource=resource,
717
+ action=action,
718
+ resource_attributes=resource_attributes or {},
719
+ )
720
+ for hook in self._config.on_access_decision:
721
+ try:
722
+ hook(context, decision)
723
+ except Exception:
724
+ pass
725
+
726
+ return decision
727
+
728
+ def require(
729
+ self,
730
+ principal: Principal | str,
731
+ resource: str,
732
+ action: str | PermissionAction,
733
+ resource_attributes: dict[str, Any] | None = None,
734
+ ) -> None:
735
+ """Require permission, raising if denied.
736
+
737
+ Args:
738
+ principal: Principal or principal ID
739
+ resource: Resource being accessed
740
+ action: Action being performed
741
+ resource_attributes: Resource attributes
742
+
743
+ Raises:
744
+ PermissionDeniedError: If access is denied.
745
+ """
746
+ decision = self.check(principal, resource, action, resource_attributes)
747
+
748
+ if not decision.allowed:
749
+ if isinstance(principal, str):
750
+ principal_id = principal
751
+ else:
752
+ principal_id = principal.id
753
+
754
+ raise PermissionDeniedError(
755
+ decision.reason,
756
+ principal_id=principal_id,
757
+ resource=resource,
758
+ action=action if isinstance(action, str) else action.value,
759
+ )
760
+
761
+ def has_permission(
762
+ self,
763
+ principal: Principal | str,
764
+ permission: str | Permission,
765
+ ) -> bool:
766
+ """Check if principal has a specific permission.
767
+
768
+ Args:
769
+ principal: Principal or principal ID
770
+ permission: Permission string or object
771
+
772
+ Returns:
773
+ True if principal has the permission.
774
+ """
775
+ if isinstance(principal, str):
776
+ principal = self.require_principal(principal)
777
+
778
+ if isinstance(permission, str):
779
+ permission = Permission.parse(permission)
780
+
781
+ # Check direct permissions
782
+ for perm in principal.direct_permissions:
783
+ if perm.matches(permission):
784
+ return True
785
+
786
+ # Check role permissions
787
+ for role_id in principal.roles:
788
+ role_perms = self.get_all_role_permissions(role_id)
789
+ for perm in role_perms:
790
+ if perm.matches(permission):
791
+ return True
792
+
793
+ return False
794
+
795
+ def has_role(
796
+ self,
797
+ principal: Principal | str,
798
+ role: str | set[str],
799
+ require_all: bool = False,
800
+ ) -> bool:
801
+ """Check if principal has specific role(s).
802
+
803
+ Args:
804
+ principal: Principal or principal ID
805
+ role: Role ID or set of role IDs
806
+ require_all: If True, require all roles
807
+
808
+ Returns:
809
+ True if principal has the role(s).
810
+ """
811
+ if isinstance(principal, str):
812
+ principal = self.require_principal(principal)
813
+
814
+ required_roles = {role} if isinstance(role, str) else role
815
+
816
+ if require_all:
817
+ return required_roles.issubset(principal.roles)
818
+ else:
819
+ return bool(required_roles.intersection(principal.roles))
820
+
821
+ # =========================================================================
822
+ # Context Management
823
+ # =========================================================================
824
+
825
+ def context(self, principal: Principal | str) -> SecurityContext:
826
+ """Get a context manager for principal operations.
827
+
828
+ Args:
829
+ principal: Principal or ID
830
+
831
+ Returns:
832
+ Context manager that sets security context.
833
+
834
+ Example:
835
+ >>> with manager.context(principal):
836
+ ... # All operations use this principal
837
+ ... run_operation()
838
+ """
839
+ if isinstance(principal, str):
840
+ principal = self.require_principal(principal)
841
+
842
+ return SecurityContext.set_principal(principal)
843
+
844
+ def current_principal(self) -> Principal | None:
845
+ """Get the current principal from context.
846
+
847
+ Returns:
848
+ Current Principal or None.
849
+ """
850
+ return SecurityContext.get_current_principal()
851
+
852
+ def require_current_principal(self) -> Principal:
853
+ """Get the current principal, raising if not set.
854
+
855
+ Returns:
856
+ Current Principal.
857
+
858
+ Raises:
859
+ PermissionDeniedError: If no principal in context.
860
+ """
861
+ return SecurityContext.require_principal()
862
+
863
+ # =========================================================================
864
+ # Default Roles
865
+ # =========================================================================
866
+
867
+ def _init_default_roles(self) -> None:
868
+ """Initialize default system roles."""
869
+ default_roles = [
870
+ Role(
871
+ id="system_admin",
872
+ name="System Admin",
873
+ description="Full system access",
874
+ role_type=RoleType.SYSTEM,
875
+ permissions={Permission.all()},
876
+ ),
877
+ Role(
878
+ id="tenant_admin",
879
+ name="Tenant Admin",
880
+ description="Full access within a tenant",
881
+ role_type=RoleType.SYSTEM,
882
+ permissions={
883
+ Permission("*", "*", scope="tenant"),
884
+ },
885
+ ),
886
+ Role(
887
+ id="viewer",
888
+ name="Viewer",
889
+ description="Read-only access",
890
+ role_type=RoleType.SYSTEM,
891
+ permissions={
892
+ Permission("*", "read"),
893
+ Permission("*", "list"),
894
+ },
895
+ ),
896
+ Role(
897
+ id="editor",
898
+ name="Editor",
899
+ description="Read and write access",
900
+ role_type=RoleType.SYSTEM,
901
+ permissions={
902
+ Permission("*", "read"),
903
+ Permission("*", "list"),
904
+ Permission("*", "create"),
905
+ Permission("*", "update"),
906
+ },
907
+ parent_roles={"viewer"},
908
+ ),
909
+ Role(
910
+ id="admin",
911
+ name="Admin",
912
+ description="Full access except system settings",
913
+ role_type=RoleType.SYSTEM,
914
+ permissions={
915
+ Permission("*", "*"),
916
+ },
917
+ parent_roles={"editor"},
918
+ ),
919
+ ]
920
+
921
+ for role in default_roles:
922
+ if not self._role_store.exists(role.id):
923
+ self._role_store.save(role)
924
+
925
+
926
+ # =============================================================================
927
+ # Global Manager
928
+ # =============================================================================
929
+
930
+
931
+ _default_manager: RBACManager | None = None
932
+ _manager_lock = threading.Lock()
933
+
934
+
935
+ def get_rbac_manager() -> RBACManager:
936
+ """Get the global RBAC manager.
937
+
938
+ Returns:
939
+ RBACManager instance.
940
+ """
941
+ global _default_manager
942
+ with _manager_lock:
943
+ if _default_manager is None:
944
+ _default_manager = RBACManager()
945
+ return _default_manager
946
+
947
+
948
+ def set_rbac_manager(manager: RBACManager) -> None:
949
+ """Set the global RBAC manager.
950
+
951
+ Args:
952
+ manager: RBACManager instance to use globally.
953
+ """
954
+ global _default_manager
955
+ with _manager_lock:
956
+ _default_manager = manager
957
+
958
+
959
+ def configure_rbac_manager(
960
+ role_store: RoleStore | None = None,
961
+ principal_store: PrincipalStore | None = None,
962
+ policy_engine: PolicyEngine | None = None,
963
+ config: RBACManagerConfig | None = None,
964
+ ) -> RBACManager:
965
+ """Configure and set the global RBAC manager.
966
+
967
+ Args:
968
+ role_store: Role storage backend
969
+ principal_store: Principal storage backend
970
+ policy_engine: Policy engine
971
+ config: Manager configuration
972
+
973
+ Returns:
974
+ Configured RBACManager.
975
+ """
976
+ manager = RBACManager(
977
+ role_store=role_store,
978
+ principal_store=principal_store,
979
+ policy_engine=policy_engine,
980
+ config=config,
981
+ )
982
+ set_rbac_manager(manager)
983
+ return manager
984
+
985
+
986
+ # =============================================================================
987
+ # Convenience Functions
988
+ # =============================================================================
989
+
990
+
991
+ def create_role(name: str, **kwargs: Any) -> Role:
992
+ """Create a role using the global manager.
993
+
994
+ See RBACManager.create_role for full documentation.
995
+ """
996
+ return get_rbac_manager().create_role(name=name, **kwargs)
997
+
998
+
999
+ def get_role(role_id: str) -> Role | None:
1000
+ """Get a role using the global manager.
1001
+
1002
+ See RBACManager.get_role for full documentation.
1003
+ """
1004
+ return get_rbac_manager().get_role(role_id)
1005
+
1006
+
1007
+ def create_principal(name: str, **kwargs: Any) -> Principal:
1008
+ """Create a principal using the global manager.
1009
+
1010
+ See RBACManager.create_principal for full documentation.
1011
+ """
1012
+ return get_rbac_manager().create_principal(name=name, **kwargs)
1013
+
1014
+
1015
+ def get_principal(principal_id: str) -> Principal | None:
1016
+ """Get a principal using the global manager.
1017
+
1018
+ See RBACManager.get_principal for full documentation.
1019
+ """
1020
+ return get_rbac_manager().get_principal(principal_id)
1021
+
1022
+
1023
+ def check_permission(
1024
+ principal: Principal | str,
1025
+ resource: str,
1026
+ action: str,
1027
+ ) -> AccessDecision:
1028
+ """Check permission using the global manager.
1029
+
1030
+ See RBACManager.check for full documentation.
1031
+ """
1032
+ return get_rbac_manager().check(principal, resource, action)
1033
+
1034
+
1035
+ def require_permission(
1036
+ principal: Principal | str,
1037
+ resource: str,
1038
+ action: str,
1039
+ ) -> None:
1040
+ """Require permission using the global manager.
1041
+
1042
+ See RBACManager.require for full documentation.
1043
+ """
1044
+ get_rbac_manager().require(principal, resource, action)
1045
+
1046
+
1047
+ def current_principal() -> Principal | None:
1048
+ """Get the current principal from context.
1049
+
1050
+ See RBACManager.current_principal for full documentation.
1051
+ """
1052
+ return get_rbac_manager().current_principal()