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.
- truthound/__init__.py +162 -0
- truthound/adapters.py +100 -0
- truthound/api.py +365 -0
- truthound/audit/__init__.py +248 -0
- truthound/audit/core.py +967 -0
- truthound/audit/filters.py +620 -0
- truthound/audit/formatters.py +707 -0
- truthound/audit/logger.py +902 -0
- truthound/audit/middleware.py +571 -0
- truthound/audit/storage.py +1083 -0
- truthound/benchmark/__init__.py +123 -0
- truthound/benchmark/base.py +757 -0
- truthound/benchmark/comparison.py +635 -0
- truthound/benchmark/generators.py +706 -0
- truthound/benchmark/reporters.py +718 -0
- truthound/benchmark/runner.py +635 -0
- truthound/benchmark/scenarios.py +712 -0
- truthound/cache.py +252 -0
- truthound/checkpoint/__init__.py +136 -0
- truthound/checkpoint/actions/__init__.py +164 -0
- truthound/checkpoint/actions/base.py +324 -0
- truthound/checkpoint/actions/custom.py +234 -0
- truthound/checkpoint/actions/discord_notify.py +290 -0
- truthound/checkpoint/actions/email_notify.py +405 -0
- truthound/checkpoint/actions/github_action.py +406 -0
- truthound/checkpoint/actions/opsgenie.py +1499 -0
- truthound/checkpoint/actions/pagerduty.py +226 -0
- truthound/checkpoint/actions/slack_notify.py +233 -0
- truthound/checkpoint/actions/store_result.py +249 -0
- truthound/checkpoint/actions/teams_notify.py +1570 -0
- truthound/checkpoint/actions/telegram_notify.py +419 -0
- truthound/checkpoint/actions/update_docs.py +552 -0
- truthound/checkpoint/actions/webhook.py +293 -0
- truthound/checkpoint/analytics/__init__.py +147 -0
- truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
- truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
- truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
- truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
- truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
- truthound/checkpoint/analytics/analyzers/base.py +270 -0
- truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
- truthound/checkpoint/analytics/analyzers/trend.py +314 -0
- truthound/checkpoint/analytics/models.py +292 -0
- truthound/checkpoint/analytics/protocols.py +549 -0
- truthound/checkpoint/analytics/service.py +718 -0
- truthound/checkpoint/analytics/stores/__init__.py +16 -0
- truthound/checkpoint/analytics/stores/base.py +306 -0
- truthound/checkpoint/analytics/stores/memory_store.py +353 -0
- truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
- truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
- truthound/checkpoint/async_actions.py +794 -0
- truthound/checkpoint/async_base.py +708 -0
- truthound/checkpoint/async_checkpoint.py +617 -0
- truthound/checkpoint/async_runner.py +639 -0
- truthound/checkpoint/checkpoint.py +527 -0
- truthound/checkpoint/ci/__init__.py +61 -0
- truthound/checkpoint/ci/detector.py +355 -0
- truthound/checkpoint/ci/reporter.py +436 -0
- truthound/checkpoint/ci/templates.py +454 -0
- truthound/checkpoint/circuitbreaker/__init__.py +133 -0
- truthound/checkpoint/circuitbreaker/breaker.py +542 -0
- truthound/checkpoint/circuitbreaker/core.py +252 -0
- truthound/checkpoint/circuitbreaker/detection.py +459 -0
- truthound/checkpoint/circuitbreaker/middleware.py +389 -0
- truthound/checkpoint/circuitbreaker/registry.py +357 -0
- truthound/checkpoint/distributed/__init__.py +139 -0
- truthound/checkpoint/distributed/backends/__init__.py +35 -0
- truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
- truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
- truthound/checkpoint/distributed/backends/local_backend.py +397 -0
- truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
- truthound/checkpoint/distributed/base.py +774 -0
- truthound/checkpoint/distributed/orchestrator.py +765 -0
- truthound/checkpoint/distributed/protocols.py +842 -0
- truthound/checkpoint/distributed/registry.py +449 -0
- truthound/checkpoint/idempotency/__init__.py +120 -0
- truthound/checkpoint/idempotency/core.py +295 -0
- truthound/checkpoint/idempotency/fingerprint.py +454 -0
- truthound/checkpoint/idempotency/locking.py +604 -0
- truthound/checkpoint/idempotency/service.py +592 -0
- truthound/checkpoint/idempotency/stores.py +653 -0
- truthound/checkpoint/monitoring/__init__.py +134 -0
- truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
- truthound/checkpoint/monitoring/aggregators/base.py +372 -0
- truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
- truthound/checkpoint/monitoring/aggregators/window.py +493 -0
- truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
- truthound/checkpoint/monitoring/collectors/base.py +257 -0
- truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
- truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
- truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
- truthound/checkpoint/monitoring/events.py +410 -0
- truthound/checkpoint/monitoring/protocols.py +636 -0
- truthound/checkpoint/monitoring/service.py +578 -0
- truthound/checkpoint/monitoring/views/__init__.py +17 -0
- truthound/checkpoint/monitoring/views/base.py +172 -0
- truthound/checkpoint/monitoring/views/queue_view.py +220 -0
- truthound/checkpoint/monitoring/views/task_view.py +240 -0
- truthound/checkpoint/monitoring/views/worker_view.py +263 -0
- truthound/checkpoint/registry.py +337 -0
- truthound/checkpoint/runner.py +356 -0
- truthound/checkpoint/transaction/__init__.py +133 -0
- truthound/checkpoint/transaction/base.py +389 -0
- truthound/checkpoint/transaction/compensatable.py +537 -0
- truthound/checkpoint/transaction/coordinator.py +576 -0
- truthound/checkpoint/transaction/executor.py +622 -0
- truthound/checkpoint/transaction/idempotency.py +534 -0
- truthound/checkpoint/transaction/saga/__init__.py +143 -0
- truthound/checkpoint/transaction/saga/builder.py +584 -0
- truthound/checkpoint/transaction/saga/definition.py +515 -0
- truthound/checkpoint/transaction/saga/event_store.py +542 -0
- truthound/checkpoint/transaction/saga/patterns.py +833 -0
- truthound/checkpoint/transaction/saga/runner.py +718 -0
- truthound/checkpoint/transaction/saga/state_machine.py +793 -0
- truthound/checkpoint/transaction/saga/strategies.py +780 -0
- truthound/checkpoint/transaction/saga/testing.py +886 -0
- truthound/checkpoint/triggers/__init__.py +58 -0
- truthound/checkpoint/triggers/base.py +237 -0
- truthound/checkpoint/triggers/event.py +385 -0
- truthound/checkpoint/triggers/schedule.py +355 -0
- truthound/cli.py +2358 -0
- truthound/cli_modules/__init__.py +124 -0
- truthound/cli_modules/advanced/__init__.py +45 -0
- truthound/cli_modules/advanced/benchmark.py +343 -0
- truthound/cli_modules/advanced/docs.py +225 -0
- truthound/cli_modules/advanced/lineage.py +209 -0
- truthound/cli_modules/advanced/ml.py +320 -0
- truthound/cli_modules/advanced/realtime.py +196 -0
- truthound/cli_modules/checkpoint/__init__.py +46 -0
- truthound/cli_modules/checkpoint/init.py +114 -0
- truthound/cli_modules/checkpoint/list.py +71 -0
- truthound/cli_modules/checkpoint/run.py +159 -0
- truthound/cli_modules/checkpoint/validate.py +67 -0
- truthound/cli_modules/common/__init__.py +71 -0
- truthound/cli_modules/common/errors.py +414 -0
- truthound/cli_modules/common/options.py +419 -0
- truthound/cli_modules/common/output.py +507 -0
- truthound/cli_modules/common/protocol.py +552 -0
- truthound/cli_modules/core/__init__.py +48 -0
- truthound/cli_modules/core/check.py +123 -0
- truthound/cli_modules/core/compare.py +104 -0
- truthound/cli_modules/core/learn.py +57 -0
- truthound/cli_modules/core/mask.py +77 -0
- truthound/cli_modules/core/profile.py +65 -0
- truthound/cli_modules/core/scan.py +61 -0
- truthound/cli_modules/profiler/__init__.py +51 -0
- truthound/cli_modules/profiler/auto_profile.py +175 -0
- truthound/cli_modules/profiler/metadata.py +107 -0
- truthound/cli_modules/profiler/suite.py +283 -0
- truthound/cli_modules/registry.py +431 -0
- truthound/cli_modules/scaffolding/__init__.py +89 -0
- truthound/cli_modules/scaffolding/base.py +631 -0
- truthound/cli_modules/scaffolding/commands.py +545 -0
- truthound/cli_modules/scaffolding/plugins.py +1072 -0
- truthound/cli_modules/scaffolding/reporters.py +594 -0
- truthound/cli_modules/scaffolding/validators.py +1127 -0
- truthound/common/__init__.py +18 -0
- truthound/common/resilience/__init__.py +130 -0
- truthound/common/resilience/bulkhead.py +266 -0
- truthound/common/resilience/circuit_breaker.py +516 -0
- truthound/common/resilience/composite.py +332 -0
- truthound/common/resilience/config.py +292 -0
- truthound/common/resilience/protocols.py +217 -0
- truthound/common/resilience/rate_limiter.py +404 -0
- truthound/common/resilience/retry.py +341 -0
- truthound/datadocs/__init__.py +260 -0
- truthound/datadocs/base.py +571 -0
- truthound/datadocs/builder.py +761 -0
- truthound/datadocs/charts.py +764 -0
- truthound/datadocs/dashboard/__init__.py +63 -0
- truthound/datadocs/dashboard/app.py +576 -0
- truthound/datadocs/dashboard/components.py +584 -0
- truthound/datadocs/dashboard/state.py +240 -0
- truthound/datadocs/engine/__init__.py +46 -0
- truthound/datadocs/engine/context.py +376 -0
- truthound/datadocs/engine/pipeline.py +618 -0
- truthound/datadocs/engine/registry.py +469 -0
- truthound/datadocs/exporters/__init__.py +49 -0
- truthound/datadocs/exporters/base.py +198 -0
- truthound/datadocs/exporters/html.py +178 -0
- truthound/datadocs/exporters/json_exporter.py +253 -0
- truthound/datadocs/exporters/markdown.py +284 -0
- truthound/datadocs/exporters/pdf.py +392 -0
- truthound/datadocs/i18n/__init__.py +86 -0
- truthound/datadocs/i18n/catalog.py +960 -0
- truthound/datadocs/i18n/formatting.py +505 -0
- truthound/datadocs/i18n/loader.py +256 -0
- truthound/datadocs/i18n/plurals.py +378 -0
- truthound/datadocs/renderers/__init__.py +42 -0
- truthound/datadocs/renderers/base.py +401 -0
- truthound/datadocs/renderers/custom.py +342 -0
- truthound/datadocs/renderers/jinja.py +697 -0
- truthound/datadocs/sections.py +736 -0
- truthound/datadocs/styles.py +931 -0
- truthound/datadocs/themes/__init__.py +101 -0
- truthound/datadocs/themes/base.py +336 -0
- truthound/datadocs/themes/default.py +417 -0
- truthound/datadocs/themes/enterprise.py +419 -0
- truthound/datadocs/themes/loader.py +336 -0
- truthound/datadocs/themes.py +301 -0
- truthound/datadocs/transformers/__init__.py +57 -0
- truthound/datadocs/transformers/base.py +268 -0
- truthound/datadocs/transformers/enrichers.py +544 -0
- truthound/datadocs/transformers/filters.py +447 -0
- truthound/datadocs/transformers/i18n.py +468 -0
- truthound/datadocs/versioning/__init__.py +62 -0
- truthound/datadocs/versioning/diff.py +639 -0
- truthound/datadocs/versioning/storage.py +497 -0
- truthound/datadocs/versioning/version.py +358 -0
- truthound/datasources/__init__.py +223 -0
- truthound/datasources/_async_protocols.py +222 -0
- truthound/datasources/_protocols.py +159 -0
- truthound/datasources/adapters.py +428 -0
- truthound/datasources/async_base.py +599 -0
- truthound/datasources/async_factory.py +511 -0
- truthound/datasources/base.py +516 -0
- truthound/datasources/factory.py +433 -0
- truthound/datasources/nosql/__init__.py +47 -0
- truthound/datasources/nosql/base.py +487 -0
- truthound/datasources/nosql/elasticsearch.py +801 -0
- truthound/datasources/nosql/mongodb.py +636 -0
- truthound/datasources/pandas_optimized.py +582 -0
- truthound/datasources/pandas_source.py +216 -0
- truthound/datasources/polars_source.py +395 -0
- truthound/datasources/spark_source.py +479 -0
- truthound/datasources/sql/__init__.py +154 -0
- truthound/datasources/sql/base.py +710 -0
- truthound/datasources/sql/bigquery.py +410 -0
- truthound/datasources/sql/cloud_base.py +199 -0
- truthound/datasources/sql/databricks.py +471 -0
- truthound/datasources/sql/mysql.py +316 -0
- truthound/datasources/sql/oracle.py +427 -0
- truthound/datasources/sql/postgresql.py +321 -0
- truthound/datasources/sql/redshift.py +479 -0
- truthound/datasources/sql/snowflake.py +439 -0
- truthound/datasources/sql/sqlite.py +286 -0
- truthound/datasources/sql/sqlserver.py +437 -0
- truthound/datasources/streaming/__init__.py +47 -0
- truthound/datasources/streaming/base.py +350 -0
- truthound/datasources/streaming/kafka.py +670 -0
- truthound/decorators.py +98 -0
- truthound/docs/__init__.py +69 -0
- truthound/docs/extractor.py +971 -0
- truthound/docs/generator.py +601 -0
- truthound/docs/parser.py +1037 -0
- truthound/docs/renderer.py +999 -0
- truthound/drift/__init__.py +22 -0
- truthound/drift/compare.py +189 -0
- truthound/drift/detectors.py +464 -0
- truthound/drift/report.py +160 -0
- truthound/execution/__init__.py +65 -0
- truthound/execution/_protocols.py +324 -0
- truthound/execution/base.py +576 -0
- truthound/execution/distributed/__init__.py +179 -0
- truthound/execution/distributed/aggregations.py +731 -0
- truthound/execution/distributed/arrow_bridge.py +817 -0
- truthound/execution/distributed/base.py +550 -0
- truthound/execution/distributed/dask_engine.py +976 -0
- truthound/execution/distributed/mixins.py +766 -0
- truthound/execution/distributed/protocols.py +756 -0
- truthound/execution/distributed/ray_engine.py +1127 -0
- truthound/execution/distributed/registry.py +446 -0
- truthound/execution/distributed/spark_engine.py +1011 -0
- truthound/execution/distributed/validator_adapter.py +682 -0
- truthound/execution/pandas_engine.py +401 -0
- truthound/execution/polars_engine.py +497 -0
- truthound/execution/pushdown/__init__.py +230 -0
- truthound/execution/pushdown/ast.py +1550 -0
- truthound/execution/pushdown/builder.py +1550 -0
- truthound/execution/pushdown/dialects.py +1072 -0
- truthound/execution/pushdown/executor.py +829 -0
- truthound/execution/pushdown/optimizer.py +1041 -0
- truthound/execution/sql_engine.py +518 -0
- truthound/infrastructure/__init__.py +189 -0
- truthound/infrastructure/audit.py +1515 -0
- truthound/infrastructure/config.py +1133 -0
- truthound/infrastructure/encryption.py +1132 -0
- truthound/infrastructure/logging.py +1503 -0
- truthound/infrastructure/metrics.py +1220 -0
- truthound/lineage/__init__.py +89 -0
- truthound/lineage/base.py +746 -0
- truthound/lineage/impact_analysis.py +474 -0
- truthound/lineage/integrations/__init__.py +22 -0
- truthound/lineage/integrations/openlineage.py +548 -0
- truthound/lineage/tracker.py +512 -0
- truthound/lineage/visualization/__init__.py +33 -0
- truthound/lineage/visualization/protocols.py +145 -0
- truthound/lineage/visualization/renderers/__init__.py +20 -0
- truthound/lineage/visualization/renderers/cytoscape.py +329 -0
- truthound/lineage/visualization/renderers/d3.py +331 -0
- truthound/lineage/visualization/renderers/graphviz.py +276 -0
- truthound/lineage/visualization/renderers/mermaid.py +308 -0
- truthound/maskers.py +113 -0
- truthound/ml/__init__.py +124 -0
- truthound/ml/anomaly_models/__init__.py +31 -0
- truthound/ml/anomaly_models/ensemble.py +362 -0
- truthound/ml/anomaly_models/isolation_forest.py +444 -0
- truthound/ml/anomaly_models/statistical.py +392 -0
- truthound/ml/base.py +1178 -0
- truthound/ml/drift_detection/__init__.py +26 -0
- truthound/ml/drift_detection/concept.py +381 -0
- truthound/ml/drift_detection/distribution.py +361 -0
- truthound/ml/drift_detection/feature.py +442 -0
- truthound/ml/drift_detection/multivariate.py +495 -0
- truthound/ml/monitoring/__init__.py +88 -0
- truthound/ml/monitoring/alerting/__init__.py +33 -0
- truthound/ml/monitoring/alerting/handlers.py +427 -0
- truthound/ml/monitoring/alerting/rules.py +508 -0
- truthound/ml/monitoring/collectors/__init__.py +19 -0
- truthound/ml/monitoring/collectors/composite.py +105 -0
- truthound/ml/monitoring/collectors/drift.py +324 -0
- truthound/ml/monitoring/collectors/performance.py +179 -0
- truthound/ml/monitoring/collectors/quality.py +369 -0
- truthound/ml/monitoring/monitor.py +536 -0
- truthound/ml/monitoring/protocols.py +451 -0
- truthound/ml/monitoring/stores/__init__.py +15 -0
- truthound/ml/monitoring/stores/memory.py +201 -0
- truthound/ml/monitoring/stores/prometheus.py +296 -0
- truthound/ml/rule_learning/__init__.py +25 -0
- truthound/ml/rule_learning/constraint_miner.py +443 -0
- truthound/ml/rule_learning/pattern_learner.py +499 -0
- truthound/ml/rule_learning/profile_learner.py +462 -0
- truthound/multitenancy/__init__.py +326 -0
- truthound/multitenancy/core.py +852 -0
- truthound/multitenancy/integration.py +597 -0
- truthound/multitenancy/isolation.py +630 -0
- truthound/multitenancy/manager.py +770 -0
- truthound/multitenancy/middleware.py +765 -0
- truthound/multitenancy/quota.py +537 -0
- truthound/multitenancy/resolvers.py +603 -0
- truthound/multitenancy/storage.py +703 -0
- truthound/observability/__init__.py +307 -0
- truthound/observability/context.py +531 -0
- truthound/observability/instrumentation.py +611 -0
- truthound/observability/logging.py +887 -0
- truthound/observability/metrics.py +1157 -0
- truthound/observability/tracing/__init__.py +178 -0
- truthound/observability/tracing/baggage.py +310 -0
- truthound/observability/tracing/config.py +426 -0
- truthound/observability/tracing/exporter.py +787 -0
- truthound/observability/tracing/integration.py +1018 -0
- truthound/observability/tracing/otel/__init__.py +146 -0
- truthound/observability/tracing/otel/adapter.py +982 -0
- truthound/observability/tracing/otel/bridge.py +1177 -0
- truthound/observability/tracing/otel/compat.py +681 -0
- truthound/observability/tracing/otel/config.py +691 -0
- truthound/observability/tracing/otel/detection.py +327 -0
- truthound/observability/tracing/otel/protocols.py +426 -0
- truthound/observability/tracing/processor.py +561 -0
- truthound/observability/tracing/propagator.py +757 -0
- truthound/observability/tracing/provider.py +569 -0
- truthound/observability/tracing/resource.py +515 -0
- truthound/observability/tracing/sampler.py +487 -0
- truthound/observability/tracing/span.py +676 -0
- truthound/plugins/__init__.py +198 -0
- truthound/plugins/base.py +599 -0
- truthound/plugins/cli.py +680 -0
- truthound/plugins/dependencies/__init__.py +42 -0
- truthound/plugins/dependencies/graph.py +422 -0
- truthound/plugins/dependencies/resolver.py +417 -0
- truthound/plugins/discovery.py +379 -0
- truthound/plugins/docs/__init__.py +46 -0
- truthound/plugins/docs/extractor.py +444 -0
- truthound/plugins/docs/renderer.py +499 -0
- truthound/plugins/enterprise_manager.py +877 -0
- truthound/plugins/examples/__init__.py +19 -0
- truthound/plugins/examples/custom_validators.py +317 -0
- truthound/plugins/examples/slack_notifier.py +312 -0
- truthound/plugins/examples/xml_reporter.py +254 -0
- truthound/plugins/hooks.py +558 -0
- truthound/plugins/lifecycle/__init__.py +43 -0
- truthound/plugins/lifecycle/hot_reload.py +402 -0
- truthound/plugins/lifecycle/manager.py +371 -0
- truthound/plugins/manager.py +736 -0
- truthound/plugins/registry.py +338 -0
- truthound/plugins/security/__init__.py +93 -0
- truthound/plugins/security/exceptions.py +332 -0
- truthound/plugins/security/policies.py +348 -0
- truthound/plugins/security/protocols.py +643 -0
- truthound/plugins/security/sandbox/__init__.py +45 -0
- truthound/plugins/security/sandbox/context.py +158 -0
- truthound/plugins/security/sandbox/engines/__init__.py +19 -0
- truthound/plugins/security/sandbox/engines/container.py +379 -0
- truthound/plugins/security/sandbox/engines/noop.py +144 -0
- truthound/plugins/security/sandbox/engines/process.py +336 -0
- truthound/plugins/security/sandbox/factory.py +211 -0
- truthound/plugins/security/signing/__init__.py +57 -0
- truthound/plugins/security/signing/service.py +330 -0
- truthound/plugins/security/signing/trust_store.py +368 -0
- truthound/plugins/security/signing/verifier.py +459 -0
- truthound/plugins/versioning/__init__.py +41 -0
- truthound/plugins/versioning/constraints.py +297 -0
- truthound/plugins/versioning/resolver.py +329 -0
- truthound/profiler/__init__.py +1729 -0
- truthound/profiler/_lazy.py +452 -0
- truthound/profiler/ab_testing/__init__.py +80 -0
- truthound/profiler/ab_testing/analysis.py +449 -0
- truthound/profiler/ab_testing/base.py +257 -0
- truthound/profiler/ab_testing/experiment.py +395 -0
- truthound/profiler/ab_testing/tracking.py +368 -0
- truthound/profiler/auto_threshold.py +1170 -0
- truthound/profiler/base.py +579 -0
- truthound/profiler/cache_patterns.py +911 -0
- truthound/profiler/caching.py +1303 -0
- truthound/profiler/column_profiler.py +712 -0
- truthound/profiler/comparison.py +1007 -0
- truthound/profiler/custom_patterns.py +1170 -0
- truthound/profiler/dashboard/__init__.py +50 -0
- truthound/profiler/dashboard/app.py +476 -0
- truthound/profiler/dashboard/components.py +457 -0
- truthound/profiler/dashboard/config.py +72 -0
- truthound/profiler/distributed/__init__.py +83 -0
- truthound/profiler/distributed/base.py +281 -0
- truthound/profiler/distributed/dask_backend.py +498 -0
- truthound/profiler/distributed/local_backend.py +293 -0
- truthound/profiler/distributed/profiler.py +304 -0
- truthound/profiler/distributed/ray_backend.py +374 -0
- truthound/profiler/distributed/spark_backend.py +375 -0
- truthound/profiler/distributed.py +1366 -0
- truthound/profiler/enterprise_sampling.py +1065 -0
- truthound/profiler/errors.py +488 -0
- truthound/profiler/evolution/__init__.py +91 -0
- truthound/profiler/evolution/alerts.py +426 -0
- truthound/profiler/evolution/changes.py +206 -0
- truthound/profiler/evolution/compatibility.py +365 -0
- truthound/profiler/evolution/detector.py +372 -0
- truthound/profiler/evolution/protocols.py +121 -0
- truthound/profiler/generators/__init__.py +48 -0
- truthound/profiler/generators/base.py +384 -0
- truthound/profiler/generators/ml_rules.py +375 -0
- truthound/profiler/generators/pattern_rules.py +384 -0
- truthound/profiler/generators/schema_rules.py +267 -0
- truthound/profiler/generators/stats_rules.py +324 -0
- truthound/profiler/generators/suite_generator.py +857 -0
- truthound/profiler/i18n.py +1542 -0
- truthound/profiler/incremental.py +554 -0
- truthound/profiler/incremental_validation.py +1710 -0
- truthound/profiler/integration/__init__.py +73 -0
- truthound/profiler/integration/adapters.py +345 -0
- truthound/profiler/integration/context.py +371 -0
- truthound/profiler/integration/executor.py +527 -0
- truthound/profiler/integration/naming.py +75 -0
- truthound/profiler/integration/protocols.py +243 -0
- truthound/profiler/memory.py +1185 -0
- truthound/profiler/migration/__init__.py +60 -0
- truthound/profiler/migration/base.py +345 -0
- truthound/profiler/migration/manager.py +444 -0
- truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
- truthound/profiler/ml/__init__.py +73 -0
- truthound/profiler/ml/base.py +244 -0
- truthound/profiler/ml/classifier.py +507 -0
- truthound/profiler/ml/feature_extraction.py +604 -0
- truthound/profiler/ml/pretrained.py +448 -0
- truthound/profiler/ml_inference.py +1276 -0
- truthound/profiler/native_patterns.py +815 -0
- truthound/profiler/observability.py +1184 -0
- truthound/profiler/process_timeout.py +1566 -0
- truthound/profiler/progress.py +568 -0
- truthound/profiler/progress_callbacks.py +1734 -0
- truthound/profiler/quality.py +1345 -0
- truthound/profiler/resilience.py +1180 -0
- truthound/profiler/sampled_matcher.py +794 -0
- truthound/profiler/sampling.py +1288 -0
- truthound/profiler/scheduling/__init__.py +82 -0
- truthound/profiler/scheduling/protocols.py +214 -0
- truthound/profiler/scheduling/scheduler.py +474 -0
- truthound/profiler/scheduling/storage.py +457 -0
- truthound/profiler/scheduling/triggers.py +449 -0
- truthound/profiler/schema.py +603 -0
- truthound/profiler/streaming.py +685 -0
- truthound/profiler/streaming_patterns.py +1354 -0
- truthound/profiler/suite_cli.py +625 -0
- truthound/profiler/suite_config.py +789 -0
- truthound/profiler/suite_export.py +1268 -0
- truthound/profiler/table_profiler.py +547 -0
- truthound/profiler/timeout.py +565 -0
- truthound/profiler/validation.py +1532 -0
- truthound/profiler/visualization/__init__.py +118 -0
- truthound/profiler/visualization/base.py +346 -0
- truthound/profiler/visualization/generator.py +1259 -0
- truthound/profiler/visualization/plotly_renderer.py +811 -0
- truthound/profiler/visualization/renderers.py +669 -0
- truthound/profiler/visualization/sections.py +540 -0
- truthound/profiler/visualization.py +2122 -0
- truthound/profiler/yaml_validation.py +1151 -0
- truthound/py.typed +0 -0
- truthound/ratelimit/__init__.py +248 -0
- truthound/ratelimit/algorithms.py +1108 -0
- truthound/ratelimit/core.py +573 -0
- truthound/ratelimit/integration.py +532 -0
- truthound/ratelimit/limiter.py +663 -0
- truthound/ratelimit/middleware.py +700 -0
- truthound/ratelimit/policy.py +792 -0
- truthound/ratelimit/storage.py +763 -0
- truthound/rbac/__init__.py +340 -0
- truthound/rbac/core.py +976 -0
- truthound/rbac/integration.py +760 -0
- truthound/rbac/manager.py +1052 -0
- truthound/rbac/middleware.py +842 -0
- truthound/rbac/policy.py +954 -0
- truthound/rbac/storage.py +878 -0
- truthound/realtime/__init__.py +141 -0
- truthound/realtime/adapters/__init__.py +43 -0
- truthound/realtime/adapters/base.py +533 -0
- truthound/realtime/adapters/kafka.py +487 -0
- truthound/realtime/adapters/kinesis.py +479 -0
- truthound/realtime/adapters/mock.py +243 -0
- truthound/realtime/base.py +553 -0
- truthound/realtime/factory.py +382 -0
- truthound/realtime/incremental.py +660 -0
- truthound/realtime/processing/__init__.py +67 -0
- truthound/realtime/processing/exactly_once.py +575 -0
- truthound/realtime/processing/state.py +547 -0
- truthound/realtime/processing/windows.py +647 -0
- truthound/realtime/protocols.py +569 -0
- truthound/realtime/streaming.py +605 -0
- truthound/realtime/testing/__init__.py +32 -0
- truthound/realtime/testing/containers.py +615 -0
- truthound/realtime/testing/fixtures.py +484 -0
- truthound/report.py +280 -0
- truthound/reporters/__init__.py +46 -0
- truthound/reporters/_protocols.py +30 -0
- truthound/reporters/base.py +324 -0
- truthound/reporters/ci/__init__.py +66 -0
- truthound/reporters/ci/azure.py +436 -0
- truthound/reporters/ci/base.py +509 -0
- truthound/reporters/ci/bitbucket.py +567 -0
- truthound/reporters/ci/circleci.py +547 -0
- truthound/reporters/ci/detection.py +364 -0
- truthound/reporters/ci/factory.py +182 -0
- truthound/reporters/ci/github.py +388 -0
- truthound/reporters/ci/gitlab.py +471 -0
- truthound/reporters/ci/jenkins.py +525 -0
- truthound/reporters/console_reporter.py +299 -0
- truthound/reporters/factory.py +211 -0
- truthound/reporters/html_reporter.py +524 -0
- truthound/reporters/json_reporter.py +256 -0
- truthound/reporters/markdown_reporter.py +280 -0
- truthound/reporters/sdk/__init__.py +174 -0
- truthound/reporters/sdk/builder.py +558 -0
- truthound/reporters/sdk/mixins.py +1150 -0
- truthound/reporters/sdk/schema.py +1493 -0
- truthound/reporters/sdk/templates.py +666 -0
- truthound/reporters/sdk/testing.py +968 -0
- truthound/scanners.py +170 -0
- truthound/scheduling/__init__.py +122 -0
- truthound/scheduling/cron.py +1136 -0
- truthound/scheduling/presets.py +212 -0
- truthound/schema.py +275 -0
- truthound/secrets/__init__.py +173 -0
- truthound/secrets/base.py +618 -0
- truthound/secrets/cloud.py +682 -0
- truthound/secrets/integration.py +507 -0
- truthound/secrets/manager.py +633 -0
- truthound/secrets/oidc/__init__.py +172 -0
- truthound/secrets/oidc/base.py +902 -0
- truthound/secrets/oidc/credential_provider.py +623 -0
- truthound/secrets/oidc/exchangers.py +1001 -0
- truthound/secrets/oidc/github/__init__.py +110 -0
- truthound/secrets/oidc/github/claims.py +718 -0
- truthound/secrets/oidc/github/enhanced_provider.py +693 -0
- truthound/secrets/oidc/github/trust_policy.py +742 -0
- truthound/secrets/oidc/github/verification.py +723 -0
- truthound/secrets/oidc/github/workflow.py +691 -0
- truthound/secrets/oidc/providers.py +825 -0
- truthound/secrets/providers.py +506 -0
- truthound/secrets/resolver.py +495 -0
- truthound/stores/__init__.py +177 -0
- truthound/stores/backends/__init__.py +18 -0
- truthound/stores/backends/_protocols.py +340 -0
- truthound/stores/backends/azure_blob.py +530 -0
- truthound/stores/backends/concurrent_filesystem.py +915 -0
- truthound/stores/backends/connection_pool.py +1365 -0
- truthound/stores/backends/database.py +743 -0
- truthound/stores/backends/filesystem.py +538 -0
- truthound/stores/backends/gcs.py +399 -0
- truthound/stores/backends/memory.py +354 -0
- truthound/stores/backends/s3.py +434 -0
- truthound/stores/backpressure/__init__.py +84 -0
- truthound/stores/backpressure/base.py +375 -0
- truthound/stores/backpressure/circuit_breaker.py +434 -0
- truthound/stores/backpressure/monitor.py +376 -0
- truthound/stores/backpressure/strategies.py +677 -0
- truthound/stores/base.py +551 -0
- truthound/stores/batching/__init__.py +65 -0
- truthound/stores/batching/base.py +305 -0
- truthound/stores/batching/buffer.py +370 -0
- truthound/stores/batching/store.py +248 -0
- truthound/stores/batching/writer.py +521 -0
- truthound/stores/caching/__init__.py +60 -0
- truthound/stores/caching/backends.py +684 -0
- truthound/stores/caching/base.py +356 -0
- truthound/stores/caching/store.py +305 -0
- truthound/stores/compression/__init__.py +193 -0
- truthound/stores/compression/adaptive.py +694 -0
- truthound/stores/compression/base.py +514 -0
- truthound/stores/compression/pipeline.py +868 -0
- truthound/stores/compression/providers.py +672 -0
- truthound/stores/compression/streaming.py +832 -0
- truthound/stores/concurrency/__init__.py +81 -0
- truthound/stores/concurrency/atomic.py +556 -0
- truthound/stores/concurrency/index.py +775 -0
- truthound/stores/concurrency/locks.py +576 -0
- truthound/stores/concurrency/manager.py +482 -0
- truthound/stores/encryption/__init__.py +297 -0
- truthound/stores/encryption/base.py +952 -0
- truthound/stores/encryption/keys.py +1191 -0
- truthound/stores/encryption/pipeline.py +903 -0
- truthound/stores/encryption/providers.py +953 -0
- truthound/stores/encryption/streaming.py +950 -0
- truthound/stores/expectations.py +227 -0
- truthound/stores/factory.py +246 -0
- truthound/stores/migration/__init__.py +75 -0
- truthound/stores/migration/base.py +480 -0
- truthound/stores/migration/manager.py +347 -0
- truthound/stores/migration/registry.py +382 -0
- truthound/stores/migration/store.py +559 -0
- truthound/stores/observability/__init__.py +106 -0
- truthound/stores/observability/audit.py +718 -0
- truthound/stores/observability/config.py +270 -0
- truthound/stores/observability/factory.py +208 -0
- truthound/stores/observability/metrics.py +636 -0
- truthound/stores/observability/protocols.py +410 -0
- truthound/stores/observability/store.py +570 -0
- truthound/stores/observability/tracing.py +784 -0
- truthound/stores/replication/__init__.py +76 -0
- truthound/stores/replication/base.py +260 -0
- truthound/stores/replication/monitor.py +269 -0
- truthound/stores/replication/store.py +439 -0
- truthound/stores/replication/syncer.py +391 -0
- truthound/stores/results.py +359 -0
- truthound/stores/retention/__init__.py +77 -0
- truthound/stores/retention/base.py +378 -0
- truthound/stores/retention/policies.py +621 -0
- truthound/stores/retention/scheduler.py +279 -0
- truthound/stores/retention/store.py +526 -0
- truthound/stores/streaming/__init__.py +138 -0
- truthound/stores/streaming/base.py +801 -0
- truthound/stores/streaming/database.py +984 -0
- truthound/stores/streaming/filesystem.py +719 -0
- truthound/stores/streaming/reader.py +629 -0
- truthound/stores/streaming/s3.py +843 -0
- truthound/stores/streaming/writer.py +790 -0
- truthound/stores/tiering/__init__.py +108 -0
- truthound/stores/tiering/base.py +462 -0
- truthound/stores/tiering/manager.py +249 -0
- truthound/stores/tiering/policies.py +692 -0
- truthound/stores/tiering/store.py +526 -0
- truthound/stores/versioning/__init__.py +56 -0
- truthound/stores/versioning/base.py +376 -0
- truthound/stores/versioning/store.py +660 -0
- truthound/stores/versioning/strategies.py +353 -0
- truthound/types.py +56 -0
- truthound/validators/__init__.py +774 -0
- truthound/validators/aggregate/__init__.py +27 -0
- truthound/validators/aggregate/central.py +116 -0
- truthound/validators/aggregate/extremes.py +116 -0
- truthound/validators/aggregate/spread.py +118 -0
- truthound/validators/aggregate/sum.py +64 -0
- truthound/validators/aggregate/type.py +78 -0
- truthound/validators/anomaly/__init__.py +93 -0
- truthound/validators/anomaly/base.py +431 -0
- truthound/validators/anomaly/ml_based.py +1190 -0
- truthound/validators/anomaly/multivariate.py +647 -0
- truthound/validators/anomaly/statistical.py +599 -0
- truthound/validators/base.py +1089 -0
- truthound/validators/business_rule/__init__.py +46 -0
- truthound/validators/business_rule/base.py +147 -0
- truthound/validators/business_rule/checksum.py +509 -0
- truthound/validators/business_rule/financial.py +526 -0
- truthound/validators/cache.py +733 -0
- truthound/validators/completeness/__init__.py +39 -0
- truthound/validators/completeness/conditional.py +73 -0
- truthound/validators/completeness/default.py +98 -0
- truthound/validators/completeness/empty.py +103 -0
- truthound/validators/completeness/nan.py +337 -0
- truthound/validators/completeness/null.py +152 -0
- truthound/validators/cross_table/__init__.py +17 -0
- truthound/validators/cross_table/aggregate.py +333 -0
- truthound/validators/cross_table/row_count.py +122 -0
- truthound/validators/datetime/__init__.py +29 -0
- truthound/validators/datetime/format.py +78 -0
- truthound/validators/datetime/freshness.py +269 -0
- truthound/validators/datetime/order.py +73 -0
- truthound/validators/datetime/parseable.py +185 -0
- truthound/validators/datetime/range.py +202 -0
- truthound/validators/datetime/timezone.py +69 -0
- truthound/validators/distribution/__init__.py +49 -0
- truthound/validators/distribution/distribution.py +128 -0
- truthound/validators/distribution/monotonic.py +119 -0
- truthound/validators/distribution/outlier.py +178 -0
- truthound/validators/distribution/quantile.py +80 -0
- truthound/validators/distribution/range.py +254 -0
- truthound/validators/distribution/set.py +125 -0
- truthound/validators/distribution/statistical.py +459 -0
- truthound/validators/drift/__init__.py +79 -0
- truthound/validators/drift/base.py +427 -0
- truthound/validators/drift/multi_feature.py +401 -0
- truthound/validators/drift/numeric.py +395 -0
- truthound/validators/drift/psi.py +446 -0
- truthound/validators/drift/statistical.py +510 -0
- truthound/validators/enterprise.py +1658 -0
- truthound/validators/geospatial/__init__.py +80 -0
- truthound/validators/geospatial/base.py +97 -0
- truthound/validators/geospatial/boundary.py +238 -0
- truthound/validators/geospatial/coordinate.py +351 -0
- truthound/validators/geospatial/distance.py +399 -0
- truthound/validators/geospatial/polygon.py +665 -0
- truthound/validators/i18n/__init__.py +308 -0
- truthound/validators/i18n/bidi.py +571 -0
- truthound/validators/i18n/catalogs.py +570 -0
- truthound/validators/i18n/dialects.py +763 -0
- truthound/validators/i18n/extended_catalogs.py +549 -0
- truthound/validators/i18n/formatting.py +1434 -0
- truthound/validators/i18n/loader.py +1020 -0
- truthound/validators/i18n/messages.py +521 -0
- truthound/validators/i18n/plural.py +683 -0
- truthound/validators/i18n/protocols.py +855 -0
- truthound/validators/i18n/tms.py +1162 -0
- truthound/validators/localization/__init__.py +53 -0
- truthound/validators/localization/base.py +122 -0
- truthound/validators/localization/chinese.py +362 -0
- truthound/validators/localization/japanese.py +275 -0
- truthound/validators/localization/korean.py +524 -0
- truthound/validators/memory/__init__.py +94 -0
- truthound/validators/memory/approximate_knn.py +506 -0
- truthound/validators/memory/base.py +547 -0
- truthound/validators/memory/sgd_online.py +719 -0
- truthound/validators/memory/streaming_ecdf.py +753 -0
- truthound/validators/ml_feature/__init__.py +54 -0
- truthound/validators/ml_feature/base.py +249 -0
- truthound/validators/ml_feature/correlation.py +299 -0
- truthound/validators/ml_feature/leakage.py +344 -0
- truthound/validators/ml_feature/null_impact.py +270 -0
- truthound/validators/ml_feature/scale.py +264 -0
- truthound/validators/multi_column/__init__.py +89 -0
- truthound/validators/multi_column/arithmetic.py +284 -0
- truthound/validators/multi_column/base.py +231 -0
- truthound/validators/multi_column/comparison.py +273 -0
- truthound/validators/multi_column/consistency.py +312 -0
- truthound/validators/multi_column/statistical.py +299 -0
- truthound/validators/optimization/__init__.py +164 -0
- truthound/validators/optimization/aggregation.py +563 -0
- truthound/validators/optimization/covariance.py +556 -0
- truthound/validators/optimization/geo.py +626 -0
- truthound/validators/optimization/graph.py +587 -0
- truthound/validators/optimization/orchestrator.py +970 -0
- truthound/validators/optimization/profiling.py +1312 -0
- truthound/validators/privacy/__init__.py +223 -0
- truthound/validators/privacy/base.py +635 -0
- truthound/validators/privacy/ccpa.py +670 -0
- truthound/validators/privacy/gdpr.py +728 -0
- truthound/validators/privacy/global_patterns.py +604 -0
- truthound/validators/privacy/plugins.py +867 -0
- truthound/validators/profiling/__init__.py +52 -0
- truthound/validators/profiling/base.py +175 -0
- truthound/validators/profiling/cardinality.py +312 -0
- truthound/validators/profiling/entropy.py +391 -0
- truthound/validators/profiling/frequency.py +455 -0
- truthound/validators/pushdown_support.py +660 -0
- truthound/validators/query/__init__.py +91 -0
- truthound/validators/query/aggregate.py +346 -0
- truthound/validators/query/base.py +246 -0
- truthound/validators/query/column.py +249 -0
- truthound/validators/query/expression.py +274 -0
- truthound/validators/query/result.py +323 -0
- truthound/validators/query/row_count.py +264 -0
- truthound/validators/referential/__init__.py +80 -0
- truthound/validators/referential/base.py +395 -0
- truthound/validators/referential/cascade.py +391 -0
- truthound/validators/referential/circular.py +563 -0
- truthound/validators/referential/foreign_key.py +624 -0
- truthound/validators/referential/orphan.py +485 -0
- truthound/validators/registry.py +112 -0
- truthound/validators/schema/__init__.py +41 -0
- truthound/validators/schema/column_count.py +142 -0
- truthound/validators/schema/column_exists.py +80 -0
- truthound/validators/schema/column_order.py +82 -0
- truthound/validators/schema/column_pair.py +85 -0
- truthound/validators/schema/column_pair_set.py +195 -0
- truthound/validators/schema/column_type.py +94 -0
- truthound/validators/schema/multi_column.py +53 -0
- truthound/validators/schema/multi_column_aggregate.py +175 -0
- truthound/validators/schema/referential.py +274 -0
- truthound/validators/schema/table_schema.py +91 -0
- truthound/validators/schema_validator.py +219 -0
- truthound/validators/sdk/__init__.py +250 -0
- truthound/validators/sdk/builder.py +680 -0
- truthound/validators/sdk/decorators.py +474 -0
- truthound/validators/sdk/enterprise/__init__.py +211 -0
- truthound/validators/sdk/enterprise/docs.py +725 -0
- truthound/validators/sdk/enterprise/fuzzing.py +659 -0
- truthound/validators/sdk/enterprise/licensing.py +709 -0
- truthound/validators/sdk/enterprise/manager.py +543 -0
- truthound/validators/sdk/enterprise/resources.py +628 -0
- truthound/validators/sdk/enterprise/sandbox.py +766 -0
- truthound/validators/sdk/enterprise/signing.py +603 -0
- truthound/validators/sdk/enterprise/templates.py +865 -0
- truthound/validators/sdk/enterprise/versioning.py +659 -0
- truthound/validators/sdk/templates.py +757 -0
- truthound/validators/sdk/testing.py +807 -0
- truthound/validators/security/__init__.py +181 -0
- truthound/validators/security/redos/__init__.py +182 -0
- truthound/validators/security/redos/core.py +861 -0
- truthound/validators/security/redos/cpu_monitor.py +593 -0
- truthound/validators/security/redos/cve_database.py +791 -0
- truthound/validators/security/redos/ml/__init__.py +155 -0
- truthound/validators/security/redos/ml/base.py +785 -0
- truthound/validators/security/redos/ml/datasets.py +618 -0
- truthound/validators/security/redos/ml/features.py +359 -0
- truthound/validators/security/redos/ml/models.py +1000 -0
- truthound/validators/security/redos/ml/predictor.py +507 -0
- truthound/validators/security/redos/ml/storage.py +632 -0
- truthound/validators/security/redos/ml/training.py +571 -0
- truthound/validators/security/redos/ml_analyzer.py +937 -0
- truthound/validators/security/redos/optimizer.py +674 -0
- truthound/validators/security/redos/profiler.py +682 -0
- truthound/validators/security/redos/re2_engine.py +709 -0
- truthound/validators/security/redos.py +886 -0
- truthound/validators/security/sql_security.py +1247 -0
- truthound/validators/streaming/__init__.py +126 -0
- truthound/validators/streaming/base.py +292 -0
- truthound/validators/streaming/completeness.py +210 -0
- truthound/validators/streaming/mixin.py +575 -0
- truthound/validators/streaming/range.py +308 -0
- truthound/validators/streaming/sources.py +846 -0
- truthound/validators/string/__init__.py +57 -0
- truthound/validators/string/casing.py +158 -0
- truthound/validators/string/charset.py +96 -0
- truthound/validators/string/format.py +501 -0
- truthound/validators/string/json.py +77 -0
- truthound/validators/string/json_schema.py +184 -0
- truthound/validators/string/length.py +104 -0
- truthound/validators/string/like_pattern.py +237 -0
- truthound/validators/string/regex.py +202 -0
- truthound/validators/string/regex_extended.py +435 -0
- truthound/validators/table/__init__.py +88 -0
- truthound/validators/table/base.py +78 -0
- truthound/validators/table/column_count.py +198 -0
- truthound/validators/table/freshness.py +362 -0
- truthound/validators/table/row_count.py +251 -0
- truthound/validators/table/schema.py +333 -0
- truthound/validators/table/size.py +285 -0
- truthound/validators/timeout/__init__.py +102 -0
- truthound/validators/timeout/advanced/__init__.py +247 -0
- truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
- truthound/validators/timeout/advanced/prediction.py +773 -0
- truthound/validators/timeout/advanced/priority.py +618 -0
- truthound/validators/timeout/advanced/redis_backend.py +770 -0
- truthound/validators/timeout/advanced/retry.py +721 -0
- truthound/validators/timeout/advanced/sampling.py +788 -0
- truthound/validators/timeout/advanced/sla.py +661 -0
- truthound/validators/timeout/advanced/telemetry.py +804 -0
- truthound/validators/timeout/cascade.py +477 -0
- truthound/validators/timeout/deadline.py +657 -0
- truthound/validators/timeout/degradation.py +525 -0
- truthound/validators/timeout/distributed.py +597 -0
- truthound/validators/timeseries/__init__.py +89 -0
- truthound/validators/timeseries/base.py +326 -0
- truthound/validators/timeseries/completeness.py +617 -0
- truthound/validators/timeseries/gap.py +485 -0
- truthound/validators/timeseries/monotonic.py +310 -0
- truthound/validators/timeseries/seasonality.py +422 -0
- truthound/validators/timeseries/trend.py +510 -0
- truthound/validators/uniqueness/__init__.py +59 -0
- truthound/validators/uniqueness/approximate.py +475 -0
- truthound/validators/uniqueness/distinct_values.py +253 -0
- truthound/validators/uniqueness/duplicate.py +118 -0
- truthound/validators/uniqueness/primary_key.py +140 -0
- truthound/validators/uniqueness/unique.py +191 -0
- truthound/validators/uniqueness/within_record.py +599 -0
- truthound/validators/utils.py +756 -0
- truthound-1.0.8.dist-info/METADATA +474 -0
- truthound-1.0.8.dist-info/RECORD +877 -0
- truthound-1.0.8.dist-info/WHEEL +4 -0
- truthound-1.0.8.dist-info/entry_points.txt +2 -0
- truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
"""Enhanced GitHub Actions OIDC Provider.
|
|
2
|
+
|
|
3
|
+
This module provides an enhanced GitHub Actions OIDC provider with:
|
|
4
|
+
- Policy-based access control
|
|
5
|
+
- Environment and workflow validation
|
|
6
|
+
- Automatic claims parsing
|
|
7
|
+
- Integration with cloud providers
|
|
8
|
+
- Caching with intelligent refresh
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from datetime import timedelta
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from truthound.secrets.oidc.base import (
|
|
20
|
+
AWSCredentials,
|
|
21
|
+
AzureCredentials,
|
|
22
|
+
CloudCredentials,
|
|
23
|
+
CloudProvider,
|
|
24
|
+
GCPCredentials,
|
|
25
|
+
OIDCConfigurationError,
|
|
26
|
+
OIDCError,
|
|
27
|
+
OIDCProviderNotAvailableError,
|
|
28
|
+
OIDCToken,
|
|
29
|
+
)
|
|
30
|
+
from truthound.secrets.oidc.providers import GitHubActionsOIDCProvider
|
|
31
|
+
from truthound.secrets.oidc.exchangers import (
|
|
32
|
+
AWSTokenExchanger,
|
|
33
|
+
AzureTokenExchanger,
|
|
34
|
+
GCPTokenExchanger,
|
|
35
|
+
VaultTokenExchanger,
|
|
36
|
+
)
|
|
37
|
+
from truthound.secrets.oidc.github.claims import (
|
|
38
|
+
ClaimsValidationPolicy,
|
|
39
|
+
EventType,
|
|
40
|
+
GitHubActionsClaims,
|
|
41
|
+
GitHubActionsContext,
|
|
42
|
+
parse_github_claims,
|
|
43
|
+
validate_claims,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from truthound.secrets.oidc.exchangers import VaultCredentials
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# Policy Classes
|
|
55
|
+
# =============================================================================
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class EnvironmentPolicy:
|
|
60
|
+
"""Policy for deployment environment requirements.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
require: Require a deployment environment.
|
|
64
|
+
allowed: List of allowed environment names.
|
|
65
|
+
deny: List of denied environment names.
|
|
66
|
+
production_only: Only allow production environment.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
require: bool = False
|
|
70
|
+
allowed: list[str] | None = None
|
|
71
|
+
deny: list[str] | None = None
|
|
72
|
+
production_only: bool = False
|
|
73
|
+
|
|
74
|
+
def validate(self, environment: str | None) -> tuple[bool, str | None]:
|
|
75
|
+
"""Validate environment against policy.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Tuple of (is_valid, error_message).
|
|
79
|
+
"""
|
|
80
|
+
if self.require and not environment:
|
|
81
|
+
return False, "Deployment environment is required"
|
|
82
|
+
|
|
83
|
+
if environment:
|
|
84
|
+
if self.production_only and environment.lower() != "production":
|
|
85
|
+
return False, f"Only 'production' environment allowed, got: {environment}"
|
|
86
|
+
|
|
87
|
+
if self.allowed and environment not in self.allowed:
|
|
88
|
+
return False, f"Environment '{environment}' not in allowed list"
|
|
89
|
+
|
|
90
|
+
if self.deny and environment in self.deny:
|
|
91
|
+
return False, f"Environment '{environment}' is explicitly denied"
|
|
92
|
+
|
|
93
|
+
return True, None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class WorkflowPolicy:
|
|
98
|
+
"""Policy for workflow requirements.
|
|
99
|
+
|
|
100
|
+
Attributes:
|
|
101
|
+
allowed_repositories: List of allowed repository patterns.
|
|
102
|
+
allowed_branches: List of allowed branch names.
|
|
103
|
+
allowed_tags: List of allowed tag patterns.
|
|
104
|
+
allowed_actors: List of allowed actor usernames.
|
|
105
|
+
allowed_events: List of allowed event types.
|
|
106
|
+
deny_pull_requests: Deny pull request events.
|
|
107
|
+
deny_forks: Deny forked repository access.
|
|
108
|
+
require_github_hosted: Require GitHub-hosted runners.
|
|
109
|
+
allowed_workflows: List of allowed workflow file patterns.
|
|
110
|
+
max_run_attempts: Maximum allowed run attempts.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
allowed_repositories: list[str] | None = None
|
|
114
|
+
allowed_branches: list[str] | None = None
|
|
115
|
+
allowed_tags: list[str] | None = None
|
|
116
|
+
allowed_actors: list[str] | None = None
|
|
117
|
+
allowed_events: list[EventType] | None = None
|
|
118
|
+
deny_pull_requests: bool = False
|
|
119
|
+
deny_forks: bool = False
|
|
120
|
+
require_github_hosted: bool = False
|
|
121
|
+
allowed_workflows: list[str] | None = None
|
|
122
|
+
max_run_attempts: int | None = None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# =============================================================================
|
|
126
|
+
# Configuration
|
|
127
|
+
# =============================================================================
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class GitHubActionsOIDCConfig:
|
|
132
|
+
"""Configuration for enhanced GitHub Actions OIDC provider.
|
|
133
|
+
|
|
134
|
+
Attributes:
|
|
135
|
+
audience: Default token audience.
|
|
136
|
+
enable_cache: Enable token caching.
|
|
137
|
+
cache_ttl_seconds: Token cache TTL.
|
|
138
|
+
cache_refresh_margin_seconds: Refresh margin before expiry.
|
|
139
|
+
retry_attempts: Number of retry attempts.
|
|
140
|
+
retry_delay_seconds: Base delay between retries.
|
|
141
|
+
request_timeout: HTTP request timeout.
|
|
142
|
+
validate_claims: Enable claims validation.
|
|
143
|
+
environment_policy: Deployment environment policy.
|
|
144
|
+
workflow_policy: Workflow access policy.
|
|
145
|
+
verify_token: Enable token signature verification.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
audience: str | None = None
|
|
149
|
+
enable_cache: bool = True
|
|
150
|
+
cache_ttl_seconds: int = 300
|
|
151
|
+
cache_refresh_margin_seconds: int = 30
|
|
152
|
+
retry_attempts: int = 3
|
|
153
|
+
retry_delay_seconds: float = 1.0
|
|
154
|
+
request_timeout: float = 30.0
|
|
155
|
+
validate_claims: bool = True
|
|
156
|
+
environment_policy: EnvironmentPolicy = field(default_factory=EnvironmentPolicy)
|
|
157
|
+
workflow_policy: WorkflowPolicy = field(default_factory=WorkflowPolicy)
|
|
158
|
+
verify_token: bool = False # Requires cryptography package
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# =============================================================================
|
|
162
|
+
# Enhanced Provider
|
|
163
|
+
# =============================================================================
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class GitHubActionsOIDC:
|
|
167
|
+
"""Enhanced GitHub Actions OIDC integration.
|
|
168
|
+
|
|
169
|
+
Provides a high-level interface for GitHub Actions OIDC with:
|
|
170
|
+
- Policy-based access control
|
|
171
|
+
- Automatic claims validation
|
|
172
|
+
- Easy cloud credential retrieval
|
|
173
|
+
- Intelligent caching
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
>>> # Basic usage
|
|
177
|
+
>>> oidc = GitHubActionsOIDC(audience="sts.amazonaws.com")
|
|
178
|
+
>>> creds = oidc.get_aws_credentials(
|
|
179
|
+
... role_arn="arn:aws:iam::123456789012:role/my-role",
|
|
180
|
+
... )
|
|
181
|
+
>>>
|
|
182
|
+
>>> # With policies
|
|
183
|
+
>>> oidc = GitHubActionsOIDC(
|
|
184
|
+
... audience="sts.amazonaws.com",
|
|
185
|
+
... config=GitHubActionsOIDCConfig(
|
|
186
|
+
... environment_policy=EnvironmentPolicy(
|
|
187
|
+
... require=True,
|
|
188
|
+
... allowed=["production", "staging"],
|
|
189
|
+
... ),
|
|
190
|
+
... workflow_policy=WorkflowPolicy(
|
|
191
|
+
... allowed_repositories=["owner/repo"],
|
|
192
|
+
... allowed_branches=["main"],
|
|
193
|
+
... deny_pull_requests=True,
|
|
194
|
+
... ),
|
|
195
|
+
... ),
|
|
196
|
+
... )
|
|
197
|
+
|
|
198
|
+
Example with decorator:
|
|
199
|
+
>>> @oidc.with_credentials(
|
|
200
|
+
... cloud_provider="aws",
|
|
201
|
+
... role_arn="arn:aws:iam::123456789012:role/my-role",
|
|
202
|
+
... )
|
|
203
|
+
... def my_function(credentials: AWSCredentials):
|
|
204
|
+
... # Use credentials
|
|
205
|
+
... pass
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
# GitHub Actions OIDC issuer
|
|
209
|
+
ISSUER = "https://token.actions.githubusercontent.com"
|
|
210
|
+
|
|
211
|
+
def __init__(
|
|
212
|
+
self,
|
|
213
|
+
audience: str | None = None,
|
|
214
|
+
*,
|
|
215
|
+
config: GitHubActionsOIDCConfig | None = None,
|
|
216
|
+
require_environment: str | None = None,
|
|
217
|
+
**kwargs: Any,
|
|
218
|
+
) -> None:
|
|
219
|
+
"""Initialize GitHub Actions OIDC provider.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
audience: Token audience.
|
|
223
|
+
config: Full configuration.
|
|
224
|
+
require_environment: Shortcut to require specific environment.
|
|
225
|
+
**kwargs: Additional config options.
|
|
226
|
+
"""
|
|
227
|
+
# Build config
|
|
228
|
+
if config is None:
|
|
229
|
+
config = GitHubActionsOIDCConfig(audience=audience)
|
|
230
|
+
elif audience:
|
|
231
|
+
config.audience = audience
|
|
232
|
+
|
|
233
|
+
# Apply kwargs to config
|
|
234
|
+
for key, value in kwargs.items():
|
|
235
|
+
if hasattr(config, key):
|
|
236
|
+
setattr(config, key, value)
|
|
237
|
+
|
|
238
|
+
# Handle require_environment shortcut
|
|
239
|
+
if require_environment:
|
|
240
|
+
config.environment_policy.require = True
|
|
241
|
+
config.environment_policy.allowed = [require_environment]
|
|
242
|
+
|
|
243
|
+
self._config = config
|
|
244
|
+
|
|
245
|
+
# Create underlying provider with config
|
|
246
|
+
from truthound.secrets.oidc.providers import GitHubActionsConfig
|
|
247
|
+
|
|
248
|
+
provider_config = GitHubActionsConfig(
|
|
249
|
+
audience=config.audience,
|
|
250
|
+
cache_ttl_seconds=config.cache_ttl_seconds,
|
|
251
|
+
enable_cache=config.enable_cache,
|
|
252
|
+
)
|
|
253
|
+
self._provider = GitHubActionsOIDCProvider(config=provider_config)
|
|
254
|
+
|
|
255
|
+
# Cached claims
|
|
256
|
+
self._cached_claims: GitHubActionsClaims | None = None
|
|
257
|
+
self._context: GitHubActionsContext | None = None
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def is_available(self) -> bool:
|
|
261
|
+
"""Check if GitHub Actions OIDC is available."""
|
|
262
|
+
return self._provider.is_available()
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def claims(self) -> GitHubActionsClaims:
|
|
266
|
+
"""Get parsed claims from current token."""
|
|
267
|
+
if self._cached_claims is None:
|
|
268
|
+
token = self.get_token()
|
|
269
|
+
self._cached_claims = parse_github_claims(token.claims.__dict__)
|
|
270
|
+
return self._cached_claims
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def context(self) -> GitHubActionsContext:
|
|
274
|
+
"""Get GitHub Actions context from environment."""
|
|
275
|
+
if self._context is None:
|
|
276
|
+
self._context = GitHubActionsContext.from_environment()
|
|
277
|
+
return self._context
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def repository(self) -> str:
|
|
281
|
+
"""Get repository name (owner/repo)."""
|
|
282
|
+
return self.claims.repository
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def branch(self) -> str | None:
|
|
286
|
+
"""Get current branch name."""
|
|
287
|
+
return self.claims.branch_name
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def commit_sha(self) -> str:
|
|
291
|
+
"""Get commit SHA."""
|
|
292
|
+
return self.claims.sha
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def environment(self) -> str | None:
|
|
296
|
+
"""Get deployment environment name."""
|
|
297
|
+
return self.claims.environment
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def actor(self) -> str:
|
|
301
|
+
"""Get actor who triggered the workflow."""
|
|
302
|
+
return self.claims.actor
|
|
303
|
+
|
|
304
|
+
def get_token(self, audience: str | None = None) -> OIDCToken:
|
|
305
|
+
"""Get OIDC token with validation.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
audience: Token audience (uses config default if not provided).
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
OIDC token.
|
|
312
|
+
|
|
313
|
+
Raises:
|
|
314
|
+
OIDCError: If token retrieval or validation fails.
|
|
315
|
+
"""
|
|
316
|
+
effective_audience = audience or self._config.audience
|
|
317
|
+
token = self._provider.get_token(effective_audience)
|
|
318
|
+
|
|
319
|
+
# Parse and validate claims
|
|
320
|
+
if self._config.validate_claims:
|
|
321
|
+
claims = parse_github_claims(token.claims.__dict__)
|
|
322
|
+
self._validate_claims(claims)
|
|
323
|
+
self._cached_claims = claims
|
|
324
|
+
|
|
325
|
+
return token
|
|
326
|
+
|
|
327
|
+
def _validate_claims(self, claims: GitHubActionsClaims) -> None:
|
|
328
|
+
"""Validate claims against configured policies."""
|
|
329
|
+
errors: list[str] = []
|
|
330
|
+
|
|
331
|
+
# Validate environment policy
|
|
332
|
+
env_policy = self._config.environment_policy
|
|
333
|
+
is_valid, error = env_policy.validate(claims.environment)
|
|
334
|
+
if not is_valid and error:
|
|
335
|
+
errors.append(error)
|
|
336
|
+
|
|
337
|
+
# Validate workflow policy
|
|
338
|
+
wf_policy = self._config.workflow_policy
|
|
339
|
+
|
|
340
|
+
# Check repositories
|
|
341
|
+
if wf_policy.allowed_repositories:
|
|
342
|
+
if not any(
|
|
343
|
+
claims.matches_repository(pattern)
|
|
344
|
+
for pattern in wf_policy.allowed_repositories
|
|
345
|
+
):
|
|
346
|
+
errors.append(
|
|
347
|
+
f"Repository '{claims.repository}' not in allowed list"
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Check branches
|
|
351
|
+
if wf_policy.allowed_branches and claims.branch_name:
|
|
352
|
+
if claims.branch_name not in wf_policy.allowed_branches:
|
|
353
|
+
errors.append(
|
|
354
|
+
f"Branch '{claims.branch_name}' not in allowed list"
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# Check tags
|
|
358
|
+
if wf_policy.allowed_tags and claims.tag_name:
|
|
359
|
+
tag_match = False
|
|
360
|
+
for pattern in wf_policy.allowed_tags:
|
|
361
|
+
if claims.matches_ref(f"refs/tags/{pattern}"):
|
|
362
|
+
tag_match = True
|
|
363
|
+
break
|
|
364
|
+
if not tag_match:
|
|
365
|
+
errors.append(f"Tag '{claims.tag_name}' not in allowed list")
|
|
366
|
+
|
|
367
|
+
# Check actors
|
|
368
|
+
if wf_policy.allowed_actors:
|
|
369
|
+
if claims.actor not in wf_policy.allowed_actors:
|
|
370
|
+
errors.append(f"Actor '{claims.actor}' not in allowed list")
|
|
371
|
+
|
|
372
|
+
# Check events
|
|
373
|
+
if wf_policy.allowed_events:
|
|
374
|
+
if claims.event_name not in wf_policy.allowed_events:
|
|
375
|
+
errors.append(
|
|
376
|
+
f"Event '{claims.event_name.value}' not in allowed list"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Check pull requests
|
|
380
|
+
if wf_policy.deny_pull_requests and claims.is_pull_request:
|
|
381
|
+
errors.append("Pull request events are denied by policy")
|
|
382
|
+
|
|
383
|
+
# Check GitHub-hosted requirement
|
|
384
|
+
if wf_policy.require_github_hosted and not claims.is_github_hosted:
|
|
385
|
+
errors.append("GitHub-hosted runners are required by policy")
|
|
386
|
+
|
|
387
|
+
# Check workflows
|
|
388
|
+
if wf_policy.allowed_workflows:
|
|
389
|
+
workflow_match = False
|
|
390
|
+
for pattern in wf_policy.allowed_workflows:
|
|
391
|
+
if claims.matches_ref(pattern):
|
|
392
|
+
workflow_match = True
|
|
393
|
+
break
|
|
394
|
+
if not workflow_match:
|
|
395
|
+
errors.append(
|
|
396
|
+
f"Workflow '{claims.workflow_ref}' not in allowed list"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Check run attempts
|
|
400
|
+
if wf_policy.max_run_attempts:
|
|
401
|
+
try:
|
|
402
|
+
run_attempt = int(claims.run_attempt)
|
|
403
|
+
if run_attempt > wf_policy.max_run_attempts:
|
|
404
|
+
errors.append(
|
|
405
|
+
f"Run attempt {run_attempt} exceeds maximum "
|
|
406
|
+
f"{wf_policy.max_run_attempts}"
|
|
407
|
+
)
|
|
408
|
+
except ValueError:
|
|
409
|
+
pass
|
|
410
|
+
|
|
411
|
+
if errors:
|
|
412
|
+
raise OIDCConfigurationError(
|
|
413
|
+
f"Claims validation failed: {'; '.join(errors)}"
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def get_aws_credentials(
|
|
417
|
+
self,
|
|
418
|
+
role_arn: str | None = None,
|
|
419
|
+
*,
|
|
420
|
+
session_name: str = "github-actions-oidc",
|
|
421
|
+
session_duration_seconds: int = 3600,
|
|
422
|
+
region: str | None = None,
|
|
423
|
+
) -> AWSCredentials:
|
|
424
|
+
"""Get AWS credentials using OIDC.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
role_arn: IAM role ARN to assume.
|
|
428
|
+
session_name: Session name for assumed role.
|
|
429
|
+
session_duration_seconds: Credential validity duration.
|
|
430
|
+
region: AWS region.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
AWS credentials.
|
|
434
|
+
"""
|
|
435
|
+
role_arn = role_arn or os.environ.get("AWS_ROLE_ARN")
|
|
436
|
+
if not role_arn:
|
|
437
|
+
raise OIDCConfigurationError("AWS role ARN is required")
|
|
438
|
+
|
|
439
|
+
# Get token with AWS STS audience
|
|
440
|
+
token = self.get_token(audience="sts.amazonaws.com")
|
|
441
|
+
|
|
442
|
+
# Create exchanger and get credentials
|
|
443
|
+
exchanger = AWSTokenExchanger(
|
|
444
|
+
role_arn=role_arn,
|
|
445
|
+
session_name=session_name,
|
|
446
|
+
session_duration_seconds=session_duration_seconds,
|
|
447
|
+
region=region,
|
|
448
|
+
enable_cache=self._config.enable_cache,
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
return exchanger.exchange(token) # type: ignore
|
|
452
|
+
|
|
453
|
+
def get_gcp_credentials(
|
|
454
|
+
self,
|
|
455
|
+
project_number: str | None = None,
|
|
456
|
+
pool_id: str | None = None,
|
|
457
|
+
provider_id: str | None = None,
|
|
458
|
+
service_account_email: str | None = None,
|
|
459
|
+
) -> GCPCredentials:
|
|
460
|
+
"""Get GCP credentials using OIDC.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
project_number: GCP project number.
|
|
464
|
+
pool_id: Workload Identity Pool ID.
|
|
465
|
+
provider_id: Workload Identity Provider ID.
|
|
466
|
+
service_account_email: Service account to impersonate.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
GCP credentials.
|
|
470
|
+
"""
|
|
471
|
+
project_number = project_number or os.environ.get("GCP_PROJECT_NUMBER")
|
|
472
|
+
pool_id = pool_id or os.environ.get("GCP_POOL_ID")
|
|
473
|
+
provider_id = provider_id or os.environ.get("GCP_PROVIDER_ID")
|
|
474
|
+
service_account_email = service_account_email or os.environ.get(
|
|
475
|
+
"GCP_SERVICE_ACCOUNT"
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
if not all([project_number, pool_id, provider_id, service_account_email]):
|
|
479
|
+
raise OIDCConfigurationError(
|
|
480
|
+
"GCP project_number, pool_id, provider_id, and "
|
|
481
|
+
"service_account_email are required"
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
# Build audience for GCP
|
|
485
|
+
audience = (
|
|
486
|
+
f"//iam.googleapis.com/projects/{project_number}/"
|
|
487
|
+
f"locations/global/workloadIdentityPools/{pool_id}/"
|
|
488
|
+
f"providers/{provider_id}"
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
token = self.get_token(audience=audience)
|
|
492
|
+
|
|
493
|
+
exchanger = GCPTokenExchanger(
|
|
494
|
+
project_number=project_number,
|
|
495
|
+
pool_id=pool_id,
|
|
496
|
+
provider_id=provider_id,
|
|
497
|
+
service_account_email=service_account_email,
|
|
498
|
+
enable_cache=self._config.enable_cache,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
return exchanger.exchange(token) # type: ignore
|
|
502
|
+
|
|
503
|
+
def get_azure_credentials(
|
|
504
|
+
self,
|
|
505
|
+
tenant_id: str | None = None,
|
|
506
|
+
client_id: str | None = None,
|
|
507
|
+
*,
|
|
508
|
+
subscription_id: str | None = None,
|
|
509
|
+
) -> AzureCredentials:
|
|
510
|
+
"""Get Azure credentials using OIDC.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
tenant_id: Azure tenant ID.
|
|
514
|
+
client_id: Azure client/app ID.
|
|
515
|
+
subscription_id: Azure subscription ID.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
Azure credentials.
|
|
519
|
+
"""
|
|
520
|
+
tenant_id = tenant_id or os.environ.get("AZURE_TENANT_ID")
|
|
521
|
+
client_id = client_id or os.environ.get("AZURE_CLIENT_ID")
|
|
522
|
+
subscription_id = subscription_id or os.environ.get("AZURE_SUBSCRIPTION_ID")
|
|
523
|
+
|
|
524
|
+
if not tenant_id or not client_id:
|
|
525
|
+
raise OIDCConfigurationError(
|
|
526
|
+
"Azure tenant_id and client_id are required"
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
# Get token with Azure-specific audience
|
|
530
|
+
token = self.get_token(
|
|
531
|
+
audience=f"api://AzureADTokenExchange"
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
exchanger = AzureTokenExchanger(
|
|
535
|
+
tenant_id=tenant_id,
|
|
536
|
+
client_id=client_id,
|
|
537
|
+
subscription_id=subscription_id,
|
|
538
|
+
enable_cache=self._config.enable_cache,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
return exchanger.exchange(token) # type: ignore
|
|
542
|
+
|
|
543
|
+
def get_vault_credentials(
|
|
544
|
+
self,
|
|
545
|
+
vault_url: str | None = None,
|
|
546
|
+
role: str | None = None,
|
|
547
|
+
*,
|
|
548
|
+
jwt_auth_path: str = "jwt",
|
|
549
|
+
) -> "VaultCredentials":
|
|
550
|
+
"""Get HashiCorp Vault credentials using OIDC.
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
vault_url: Vault server URL.
|
|
554
|
+
role: Vault role name.
|
|
555
|
+
jwt_auth_path: JWT auth backend path.
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
Vault credentials.
|
|
559
|
+
"""
|
|
560
|
+
vault_url = vault_url or os.environ.get("VAULT_ADDR")
|
|
561
|
+
role = role or os.environ.get("VAULT_ROLE")
|
|
562
|
+
|
|
563
|
+
if not vault_url or not role:
|
|
564
|
+
raise OIDCConfigurationError(
|
|
565
|
+
"Vault URL and role are required"
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
# Get token with Vault-specific audience
|
|
569
|
+
token = self.get_token(audience=vault_url)
|
|
570
|
+
|
|
571
|
+
exchanger = VaultTokenExchanger(
|
|
572
|
+
vault_url=vault_url,
|
|
573
|
+
role=role,
|
|
574
|
+
jwt_auth_path=jwt_auth_path,
|
|
575
|
+
enable_cache=self._config.enable_cache,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
return exchanger.exchange(token) # type: ignore
|
|
579
|
+
|
|
580
|
+
def get_credentials(
|
|
581
|
+
self,
|
|
582
|
+
cloud_provider: CloudProvider | str,
|
|
583
|
+
**kwargs: Any,
|
|
584
|
+
) -> CloudCredentials:
|
|
585
|
+
"""Get credentials for the specified cloud provider.
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
cloud_provider: Target cloud provider.
|
|
589
|
+
**kwargs: Provider-specific configuration.
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
Cloud credentials.
|
|
593
|
+
"""
|
|
594
|
+
if isinstance(cloud_provider, str):
|
|
595
|
+
cloud_provider = CloudProvider(cloud_provider.lower())
|
|
596
|
+
|
|
597
|
+
if cloud_provider == CloudProvider.AWS:
|
|
598
|
+
return self.get_aws_credentials(**kwargs)
|
|
599
|
+
elif cloud_provider == CloudProvider.GCP:
|
|
600
|
+
return self.get_gcp_credentials(**kwargs)
|
|
601
|
+
elif cloud_provider == CloudProvider.AZURE:
|
|
602
|
+
return self.get_azure_credentials(**kwargs)
|
|
603
|
+
elif cloud_provider == CloudProvider.VAULT:
|
|
604
|
+
return self.get_vault_credentials(**kwargs)
|
|
605
|
+
else:
|
|
606
|
+
raise ValueError(f"Unsupported cloud provider: {cloud_provider}")
|
|
607
|
+
|
|
608
|
+
def validate_environment(self, environment: str) -> bool:
|
|
609
|
+
"""Validate that current environment matches expected.
|
|
610
|
+
|
|
611
|
+
Args:
|
|
612
|
+
environment: Expected environment name.
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
True if environment matches.
|
|
616
|
+
"""
|
|
617
|
+
return self.claims.environment == environment
|
|
618
|
+
|
|
619
|
+
def validate_repository(self, repository: str) -> bool:
|
|
620
|
+
"""Validate that current repository matches pattern.
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
repository: Repository pattern.
|
|
624
|
+
|
|
625
|
+
Returns:
|
|
626
|
+
True if repository matches.
|
|
627
|
+
"""
|
|
628
|
+
return self.claims.matches_repository(repository)
|
|
629
|
+
|
|
630
|
+
def validate_branch(self, branch: str) -> bool:
|
|
631
|
+
"""Validate that current branch matches.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
branch: Branch name.
|
|
635
|
+
|
|
636
|
+
Returns:
|
|
637
|
+
True if branch matches.
|
|
638
|
+
"""
|
|
639
|
+
return self.claims.branch_name == branch
|
|
640
|
+
|
|
641
|
+
def require_environment(self, environment: str) -> None:
|
|
642
|
+
"""Assert that current environment matches.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
environment: Required environment name.
|
|
646
|
+
|
|
647
|
+
Raises:
|
|
648
|
+
OIDCError: If environment doesn't match.
|
|
649
|
+
"""
|
|
650
|
+
if not self.validate_environment(environment):
|
|
651
|
+
raise OIDCError(
|
|
652
|
+
f"Required environment '{environment}', "
|
|
653
|
+
f"but got '{self.claims.environment}'"
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
def require_repository(self, repository: str) -> None:
|
|
657
|
+
"""Assert that current repository matches.
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
repository: Required repository pattern.
|
|
661
|
+
|
|
662
|
+
Raises:
|
|
663
|
+
OIDCError: If repository doesn't match.
|
|
664
|
+
"""
|
|
665
|
+
if not self.validate_repository(repository):
|
|
666
|
+
raise OIDCError(
|
|
667
|
+
f"Required repository '{repository}', "
|
|
668
|
+
f"but got '{self.claims.repository}'"
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
def require_branch(self, branch: str) -> None:
|
|
672
|
+
"""Assert that current branch matches.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
branch: Required branch name.
|
|
676
|
+
|
|
677
|
+
Raises:
|
|
678
|
+
OIDCError: If branch doesn't match.
|
|
679
|
+
"""
|
|
680
|
+
if not self.validate_branch(branch):
|
|
681
|
+
raise OIDCError(
|
|
682
|
+
f"Required branch '{branch}', "
|
|
683
|
+
f"but got '{self.claims.branch_name}'"
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
def clear_cache(self) -> None:
|
|
687
|
+
"""Clear all cached data."""
|
|
688
|
+
self._provider.clear_cache()
|
|
689
|
+
self._cached_claims = None
|
|
690
|
+
|
|
691
|
+
def __repr__(self) -> str:
|
|
692
|
+
available = "available" if self.is_available else "unavailable"
|
|
693
|
+
return f"GitHubActionsOIDC(status={available})"
|