exonware-xwsystem 0.1.0.1__py3-none-any.whl → 0.1.0.3__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.
- exonware/__init__.py +2 -1
- exonware/conf.py +2 -2
- exonware/xwsystem/__init__.py +115 -43
- exonware/xwsystem/base.py +30 -0
- exonware/xwsystem/caching/__init__.py +39 -13
- exonware/xwsystem/caching/base.py +24 -6
- exonware/xwsystem/caching/bloom_cache.py +2 -2
- exonware/xwsystem/caching/cache_manager.py +2 -1
- exonware/xwsystem/caching/conditional.py +2 -2
- exonware/xwsystem/caching/contracts.py +85 -139
- exonware/xwsystem/caching/decorators.py +6 -19
- exonware/xwsystem/caching/defs.py +2 -1
- exonware/xwsystem/caching/disk_cache.py +2 -1
- exonware/xwsystem/caching/distributed.py +2 -1
- exonware/xwsystem/caching/errors.py +2 -1
- exonware/xwsystem/caching/events.py +110 -27
- exonware/xwsystem/caching/eviction_strategies.py +2 -2
- exonware/xwsystem/caching/external_caching_python.py +701 -0
- exonware/xwsystem/caching/facade.py +253 -0
- exonware/xwsystem/caching/factory.py +300 -0
- exonware/xwsystem/caching/fluent.py +14 -12
- exonware/xwsystem/caching/integrity.py +21 -6
- exonware/xwsystem/caching/lfu_cache.py +2 -1
- exonware/xwsystem/caching/lfu_optimized.py +18 -6
- exonware/xwsystem/caching/lru_cache.py +7 -4
- exonware/xwsystem/caching/memory_bounded.py +2 -2
- exonware/xwsystem/caching/metrics_exporter.py +2 -2
- exonware/xwsystem/caching/observable_cache.py +2 -2
- exonware/xwsystem/caching/pluggable_cache.py +2 -2
- exonware/xwsystem/caching/rate_limiter.py +2 -2
- exonware/xwsystem/caching/read_through.py +2 -2
- exonware/xwsystem/caching/secure_cache.py +81 -28
- exonware/xwsystem/caching/serializable.py +9 -7
- exonware/xwsystem/caching/stats.py +2 -2
- exonware/xwsystem/caching/tagging.py +2 -2
- exonware/xwsystem/caching/ttl_cache.py +4 -3
- exonware/xwsystem/caching/two_tier_cache.py +6 -3
- exonware/xwsystem/caching/utils.py +30 -12
- exonware/xwsystem/caching/validation.py +2 -2
- exonware/xwsystem/caching/warming.py +6 -3
- exonware/xwsystem/caching/write_behind.py +15 -6
- exonware/xwsystem/config/__init__.py +11 -17
- exonware/xwsystem/config/base.py +5 -5
- exonware/xwsystem/config/contracts.py +93 -153
- exonware/xwsystem/config/defaults.py +3 -2
- exonware/xwsystem/config/defs.py +3 -2
- exonware/xwsystem/config/errors.py +2 -5
- exonware/xwsystem/config/logging.py +12 -8
- exonware/xwsystem/config/logging_setup.py +3 -2
- exonware/xwsystem/config/performance.py +1 -46
- exonware/xwsystem/config/performance_modes.py +9 -8
- exonware/xwsystem/config/version_manager.py +1 -0
- exonware/xwsystem/config.py +27 -0
- exonware/xwsystem/console/__init__.py +53 -0
- exonware/xwsystem/console/base.py +133 -0
- exonware/xwsystem/console/cli/__init__.py +61 -0
- exonware/xwsystem/{cli → console/cli}/args.py +27 -24
- exonware/xwsystem/{cli → console/cli}/base.py +18 -87
- exonware/xwsystem/{cli → console/cli}/colors.py +15 -13
- exonware/xwsystem/console/cli/console.py +98 -0
- exonware/xwsystem/{cli → console/cli}/contracts.py +51 -69
- exonware/xwsystem/console/cli/defs.py +87 -0
- exonware/xwsystem/console/cli/encoding.py +69 -0
- exonware/xwsystem/{cli → console/cli}/errors.py +8 -3
- exonware/xwsystem/console/cli/event_logger.py +166 -0
- exonware/xwsystem/{cli → console/cli}/progress.py +25 -21
- exonware/xwsystem/{cli → console/cli}/prompts.py +3 -2
- exonware/xwsystem/{cli → console/cli}/tables.py +27 -24
- exonware/xwsystem/console/contracts.py +113 -0
- exonware/xwsystem/console/defs.py +154 -0
- exonware/xwsystem/console/errors.py +34 -0
- exonware/xwsystem/console/event_logger.py +385 -0
- exonware/xwsystem/console/writer.py +132 -0
- exonware/xwsystem/contracts.py +28 -0
- exonware/xwsystem/data_structures/__init__.py +23 -0
- exonware/xwsystem/data_structures/trie.py +34 -0
- exonware/xwsystem/data_structures/union_find.py +144 -0
- exonware/xwsystem/defs.py +17 -0
- exonware/xwsystem/errors.py +23 -0
- exonware/xwsystem/facade.py +62 -0
- exonware/xwsystem/http_client/__init__.py +22 -1
- exonware/xwsystem/http_client/advanced_client.py +8 -5
- exonware/xwsystem/http_client/base.py +3 -2
- exonware/xwsystem/http_client/client.py +7 -4
- exonware/xwsystem/http_client/contracts.py +42 -56
- exonware/xwsystem/http_client/defs.py +2 -1
- exonware/xwsystem/http_client/errors.py +2 -1
- exonware/xwsystem/http_client/facade.py +156 -0
- exonware/xwsystem/io/__init__.py +22 -3
- exonware/xwsystem/io/archive/__init__.py +8 -2
- exonware/xwsystem/io/archive/archive.py +1 -1
- exonware/xwsystem/io/archive/archive_files.py +4 -7
- exonware/xwsystem/io/archive/archivers.py +120 -10
- exonware/xwsystem/io/archive/base.py +4 -5
- exonware/xwsystem/io/archive/codec_integration.py +1 -2
- exonware/xwsystem/io/archive/compression.py +1 -2
- exonware/xwsystem/io/archive/facade.py +263 -0
- exonware/xwsystem/io/archive/formats/__init__.py +2 -3
- exonware/xwsystem/io/archive/formats/brotli_format.py +20 -7
- exonware/xwsystem/io/archive/formats/lz4_format.py +20 -7
- exonware/xwsystem/io/archive/formats/rar.py +11 -5
- exonware/xwsystem/io/archive/formats/sevenzip.py +12 -6
- exonware/xwsystem/io/archive/formats/squashfs_format.py +1 -2
- exonware/xwsystem/io/archive/formats/tar.py +52 -7
- exonware/xwsystem/io/archive/formats/wim_format.py +11 -5
- exonware/xwsystem/io/archive/formats/zip.py +1 -2
- exonware/xwsystem/io/archive/formats/zpaq_format.py +1 -2
- exonware/xwsystem/io/archive/formats/zstandard.py +20 -7
- exonware/xwsystem/io/base.py +119 -115
- exonware/xwsystem/io/codec/__init__.py +4 -2
- exonware/xwsystem/io/codec/base.py +19 -13
- exonware/xwsystem/io/codec/contracts.py +59 -2
- exonware/xwsystem/io/codec/registry.py +67 -21
- exonware/xwsystem/io/common/__init__.py +1 -1
- exonware/xwsystem/io/common/atomic.py +29 -16
- exonware/xwsystem/io/common/base.py +11 -10
- exonware/xwsystem/io/common/lock.py +6 -5
- exonware/xwsystem/io/common/path_manager.py +2 -1
- exonware/xwsystem/io/common/watcher.py +1 -2
- exonware/xwsystem/io/contracts.py +301 -433
- exonware/xwsystem/io/contracts_1.py +1180 -0
- exonware/xwsystem/io/data_operations.py +19 -20
- exonware/xwsystem/io/defs.py +4 -3
- exonware/xwsystem/io/errors.py +3 -2
- exonware/xwsystem/io/facade.py +87 -61
- exonware/xwsystem/io/file/__init__.py +1 -1
- exonware/xwsystem/io/file/base.py +8 -9
- exonware/xwsystem/io/file/conversion.py +2 -3
- exonware/xwsystem/io/file/file.py +61 -18
- exonware/xwsystem/io/file/paged_source.py +8 -8
- exonware/xwsystem/io/file/paging/__init__.py +1 -2
- exonware/xwsystem/io/file/paging/byte_paging.py +4 -5
- exonware/xwsystem/io/file/paging/line_paging.py +2 -3
- exonware/xwsystem/io/file/paging/record_paging.py +2 -3
- exonware/xwsystem/io/file/paging/registry.py +1 -2
- exonware/xwsystem/io/file/source.py +13 -17
- exonware/xwsystem/io/filesystem/__init__.py +1 -1
- exonware/xwsystem/io/filesystem/base.py +1 -2
- exonware/xwsystem/io/filesystem/local.py +3 -4
- exonware/xwsystem/io/folder/__init__.py +1 -1
- exonware/xwsystem/io/folder/base.py +1 -2
- exonware/xwsystem/io/folder/folder.py +16 -7
- exonware/xwsystem/io/indexing/__init__.py +14 -0
- exonware/xwsystem/io/indexing/facade.py +443 -0
- exonware/xwsystem/io/path_parser.py +98 -0
- exonware/xwsystem/io/serialization/__init__.py +21 -3
- exonware/xwsystem/io/serialization/auto_serializer.py +146 -20
- exonware/xwsystem/io/serialization/base.py +84 -34
- exonware/xwsystem/io/serialization/contracts.py +50 -73
- exonware/xwsystem/io/serialization/defs.py +2 -1
- exonware/xwsystem/io/serialization/errors.py +2 -1
- exonware/xwsystem/io/serialization/flyweight.py +154 -7
- exonware/xwsystem/io/serialization/format_detector.py +15 -14
- exonware/xwsystem/io/serialization/formats/__init__.py +8 -5
- exonware/xwsystem/io/serialization/formats/binary/bson.py +15 -6
- exonware/xwsystem/io/serialization/formats/binary/cbor.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/marshal.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/msgpack.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/pickle.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/plistlib.py +5 -5
- exonware/xwsystem/io/serialization/formats/database/dbm.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/shelve.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/sqlite3.py +7 -7
- exonware/xwsystem/io/serialization/formats/tabular/__init__.py +27 -0
- exonware/xwsystem/io/serialization/formats/tabular/base.py +89 -0
- exonware/xwsystem/io/serialization/formats/tabular/csv.py +319 -0
- exonware/xwsystem/io/serialization/formats/tabular/df.py +249 -0
- exonware/xwsystem/io/serialization/formats/tabular/excel.py +291 -0
- exonware/xwsystem/io/serialization/formats/tabular/googlesheets.py +374 -0
- exonware/xwsystem/io/serialization/formats/text/__init__.py +1 -1
- exonware/xwsystem/io/serialization/formats/text/append_only_log.py +5 -7
- exonware/xwsystem/io/serialization/formats/text/configparser.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/csv.py +7 -5
- exonware/xwsystem/io/serialization/formats/text/formdata.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/json.py +27 -18
- exonware/xwsystem/io/serialization/formats/text/json5.py +8 -4
- exonware/xwsystem/io/serialization/formats/text/jsonlines.py +18 -14
- exonware/xwsystem/io/serialization/formats/text/multipart.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/toml.py +8 -6
- exonware/xwsystem/io/serialization/formats/text/xml.py +25 -20
- exonware/xwsystem/io/serialization/formats/text/yaml.py +8 -6
- exonware/xwsystem/io/serialization/parsers/__init__.py +3 -2
- exonware/xwsystem/io/serialization/parsers/base.py +6 -5
- exonware/xwsystem/io/serialization/parsers/hybrid_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/msgspec_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/orjson_direct_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/orjson_parser.py +11 -8
- exonware/xwsystem/io/serialization/parsers/pysimdjson_parser.py +13 -9
- exonware/xwsystem/io/serialization/parsers/rapidjson_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/registry.py +11 -10
- exonware/xwsystem/io/serialization/parsers/standard.py +7 -6
- exonware/xwsystem/io/serialization/parsers/ujson_parser.py +10 -7
- exonware/xwsystem/io/serialization/registry.py +4 -4
- exonware/xwsystem/io/serialization/serializer.py +168 -79
- exonware/xwsystem/io/serialization/universal_options.py +367 -0
- exonware/xwsystem/io/serialization/utils/__init__.py +1 -2
- exonware/xwsystem/io/serialization/utils/path_ops.py +5 -6
- exonware/xwsystem/io/source_reader.py +223 -0
- exonware/xwsystem/io/stream/__init__.py +1 -1
- exonware/xwsystem/io/stream/async_operations.py +61 -14
- exonware/xwsystem/io/stream/base.py +1 -2
- exonware/xwsystem/io/stream/codec_io.py +6 -7
- exonware/xwsystem/ipc/__init__.py +1 -0
- exonware/xwsystem/ipc/async_fabric.py +4 -4
- exonware/xwsystem/ipc/base.py +6 -5
- exonware/xwsystem/ipc/contracts.py +41 -66
- exonware/xwsystem/ipc/defs.py +2 -1
- exonware/xwsystem/ipc/errors.py +2 -1
- exonware/xwsystem/ipc/message_queue.py +5 -2
- exonware/xwsystem/ipc/pipes.py +70 -34
- exonware/xwsystem/ipc/process_manager.py +7 -5
- exonware/xwsystem/ipc/process_pool.py +6 -5
- exonware/xwsystem/ipc/shared_memory.py +64 -11
- exonware/xwsystem/monitoring/__init__.py +7 -0
- exonware/xwsystem/monitoring/base.py +11 -8
- exonware/xwsystem/monitoring/contracts.py +86 -144
- exonware/xwsystem/monitoring/defs.py +2 -1
- exonware/xwsystem/monitoring/error_recovery.py +16 -3
- exonware/xwsystem/monitoring/errors.py +2 -1
- exonware/xwsystem/monitoring/facade.py +183 -0
- exonware/xwsystem/monitoring/memory_monitor.py +1 -0
- exonware/xwsystem/monitoring/metrics.py +1 -0
- exonware/xwsystem/monitoring/performance_manager_generic.py +7 -7
- exonware/xwsystem/monitoring/performance_monitor.py +1 -0
- exonware/xwsystem/monitoring/performance_validator.py +1 -0
- exonware/xwsystem/monitoring/system_monitor.py +6 -5
- exonware/xwsystem/monitoring/tracing.py +18 -16
- exonware/xwsystem/monitoring/tracker.py +2 -1
- exonware/xwsystem/operations/__init__.py +5 -50
- exonware/xwsystem/operations/base.py +3 -44
- exonware/xwsystem/operations/contracts.py +25 -15
- exonware/xwsystem/operations/defs.py +1 -1
- exonware/xwsystem/operations/diff.py +5 -4
- exonware/xwsystem/operations/errors.py +1 -1
- exonware/xwsystem/operations/merge.py +6 -4
- exonware/xwsystem/operations/patch.py +5 -4
- exonware/xwsystem/patterns/__init__.py +1 -0
- exonware/xwsystem/patterns/base.py +2 -1
- exonware/xwsystem/patterns/context_manager.py +2 -1
- exonware/xwsystem/patterns/contracts.py +215 -256
- exonware/xwsystem/patterns/defs.py +2 -1
- exonware/xwsystem/patterns/dynamic_facade.py +1 -0
- exonware/xwsystem/patterns/errors.py +2 -4
- exonware/xwsystem/patterns/handler_factory.py +2 -3
- exonware/xwsystem/patterns/import_registry.py +1 -0
- exonware/xwsystem/patterns/object_pool.py +1 -0
- exonware/xwsystem/patterns/registry.py +4 -43
- exonware/xwsystem/plugins/__init__.py +2 -1
- exonware/xwsystem/plugins/base.py +6 -5
- exonware/xwsystem/plugins/contracts.py +94 -158
- exonware/xwsystem/plugins/defs.py +2 -1
- exonware/xwsystem/plugins/errors.py +2 -1
- exonware/xwsystem/py.typed +3 -0
- exonware/xwsystem/query/__init__.py +36 -0
- exonware/xwsystem/query/contracts.py +56 -0
- exonware/xwsystem/query/errors.py +22 -0
- exonware/xwsystem/query/registry.py +128 -0
- exonware/xwsystem/runtime/__init__.py +2 -1
- exonware/xwsystem/runtime/base.py +4 -3
- exonware/xwsystem/runtime/contracts.py +39 -60
- exonware/xwsystem/runtime/defs.py +2 -1
- exonware/xwsystem/runtime/env.py +11 -9
- exonware/xwsystem/runtime/errors.py +2 -1
- exonware/xwsystem/runtime/reflection.py +3 -2
- exonware/xwsystem/security/__init__.py +68 -11
- exonware/xwsystem/security/audit.py +167 -0
- exonware/xwsystem/security/base.py +121 -24
- exonware/xwsystem/security/contracts.py +91 -146
- exonware/xwsystem/security/crypto.py +17 -16
- exonware/xwsystem/security/defs.py +2 -1
- exonware/xwsystem/security/errors.py +2 -1
- exonware/xwsystem/security/facade.py +321 -0
- exonware/xwsystem/security/file_security.py +330 -0
- exonware/xwsystem/security/hazmat.py +11 -8
- exonware/xwsystem/security/monitor.py +372 -0
- exonware/xwsystem/security/path_validator.py +140 -18
- exonware/xwsystem/security/policy.py +357 -0
- exonware/xwsystem/security/resource_limits.py +1 -0
- exonware/xwsystem/security/validator.py +455 -0
- exonware/xwsystem/shared/__init__.py +14 -1
- exonware/xwsystem/shared/base.py +285 -2
- exonware/xwsystem/shared/contracts.py +415 -126
- exonware/xwsystem/shared/defs.py +2 -1
- exonware/xwsystem/shared/errors.py +2 -2
- exonware/xwsystem/shared/xwobject.py +316 -0
- exonware/xwsystem/structures/__init__.py +1 -0
- exonware/xwsystem/structures/base.py +3 -2
- exonware/xwsystem/structures/circular_detector.py +15 -14
- exonware/xwsystem/structures/contracts.py +53 -76
- exonware/xwsystem/structures/defs.py +2 -1
- exonware/xwsystem/structures/errors.py +2 -1
- exonware/xwsystem/structures/tree_walker.py +2 -1
- exonware/xwsystem/threading/__init__.py +21 -4
- exonware/xwsystem/threading/async_primitives.py +6 -5
- exonware/xwsystem/threading/base.py +3 -2
- exonware/xwsystem/threading/contracts.py +87 -143
- exonware/xwsystem/threading/defs.py +2 -1
- exonware/xwsystem/threading/errors.py +2 -1
- exonware/xwsystem/threading/facade.py +175 -0
- exonware/xwsystem/threading/locks.py +1 -0
- exonware/xwsystem/threading/safe_factory.py +1 -0
- exonware/xwsystem/utils/__init__.py +40 -0
- exonware/xwsystem/utils/base.py +22 -21
- exonware/xwsystem/utils/contracts.py +50 -73
- exonware/xwsystem/utils/dt/__init__.py +19 -3
- exonware/xwsystem/utils/dt/base.py +5 -4
- exonware/xwsystem/utils/dt/contracts.py +22 -29
- exonware/xwsystem/utils/dt/defs.py +2 -1
- exonware/xwsystem/utils/dt/errors.py +2 -5
- exonware/xwsystem/utils/dt/formatting.py +88 -2
- exonware/xwsystem/utils/dt/humanize.py +10 -9
- exonware/xwsystem/utils/dt/parsing.py +56 -5
- exonware/xwsystem/utils/dt/timezone_utils.py +2 -24
- exonware/xwsystem/utils/errors.py +2 -4
- exonware/xwsystem/utils/paths.py +1 -0
- exonware/xwsystem/utils/string.py +49 -0
- exonware/xwsystem/utils/test_runner.py +185 -0
- exonware/xwsystem/utils/utils_contracts.py +2 -1
- exonware/xwsystem/utils/web.py +110 -0
- exonware/xwsystem/validation/__init__.py +25 -1
- exonware/xwsystem/validation/base.py +6 -5
- exonware/xwsystem/validation/contracts.py +29 -41
- exonware/xwsystem/validation/data_validator.py +1 -0
- exonware/xwsystem/validation/declarative.py +11 -8
- exonware/xwsystem/validation/defs.py +2 -1
- exonware/xwsystem/validation/errors.py +2 -1
- exonware/xwsystem/validation/facade.py +198 -0
- exonware/xwsystem/validation/fluent_validator.py +22 -19
- exonware/xwsystem/validation/schema_discovery.py +210 -0
- exonware/xwsystem/validation/type_safety.py +2 -1
- exonware/xwsystem/version.py +2 -2
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/METADATA +71 -4
- exonware_xwsystem-0.1.0.3.dist-info/RECORD +337 -0
- exonware/xwsystem/cli/__init__.py +0 -43
- exonware/xwsystem/cli/console.py +0 -113
- exonware/xwsystem/cli/defs.py +0 -134
- exonware/xwsystem/conf.py +0 -44
- exonware/xwsystem/security/auth.py +0 -484
- exonware_xwsystem-0.1.0.1.dist-info/RECORD +0 -284
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/WHEEL +0 -0
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/licenses/LICENSE +0 -0
exonware/xwsystem/cli/defs.py
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#exonware/xwsystem/cli/types.py
|
|
3
|
-
"""
|
|
4
|
-
Company: eXonware.com
|
|
5
|
-
Author: Eng. Muhammad AlShehri
|
|
6
|
-
Email: connect@exonware.com
|
|
7
|
-
Version: 0.1.0.1
|
|
8
|
-
Generation Date: 07-Sep-2025
|
|
9
|
-
|
|
10
|
-
CLI types and enums for XWSystem.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from enum import Enum
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# ============================================================================
|
|
17
|
-
# CLI ENUMS
|
|
18
|
-
# ============================================================================
|
|
19
|
-
|
|
20
|
-
class ColorType(Enum):
|
|
21
|
-
"""Color types for CLI output."""
|
|
22
|
-
RESET = "reset"
|
|
23
|
-
BOLD = "bold"
|
|
24
|
-
DIM = "dim"
|
|
25
|
-
UNDERLINE = "underline"
|
|
26
|
-
RED = "red"
|
|
27
|
-
GREEN = "green"
|
|
28
|
-
YELLOW = "yellow"
|
|
29
|
-
BLUE = "blue"
|
|
30
|
-
MAGENTA = "magenta"
|
|
31
|
-
CYAN = "cyan"
|
|
32
|
-
WHITE = "white"
|
|
33
|
-
GRAY = "gray"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class ProgressStyle(Enum):
|
|
37
|
-
"""Progress bar styles."""
|
|
38
|
-
BAR = "bar"
|
|
39
|
-
SPINNER = "spinner"
|
|
40
|
-
PERCENTAGE = "percentage"
|
|
41
|
-
COUNTER = "counter"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class TableStyle(Enum):
|
|
45
|
-
"""Table display styles."""
|
|
46
|
-
SIMPLE = "simple"
|
|
47
|
-
GRID = "grid"
|
|
48
|
-
FANCY = "fancy"
|
|
49
|
-
MINIMAL = "minimal"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class PromptType(Enum):
|
|
53
|
-
"""Prompt input types."""
|
|
54
|
-
TEXT = "text"
|
|
55
|
-
PASSWORD = "password"
|
|
56
|
-
CONFIRM = "confirm"
|
|
57
|
-
SELECT = "select"
|
|
58
|
-
MULTISELECT = "multiselect"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class Alignment(Enum):
|
|
62
|
-
"""Text alignment options."""
|
|
63
|
-
LEFT = "left"
|
|
64
|
-
CENTER = "center"
|
|
65
|
-
RIGHT = "right"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class BorderStyle(Enum):
|
|
69
|
-
"""Table border styles."""
|
|
70
|
-
NONE = "none"
|
|
71
|
-
ASCII = "ascii"
|
|
72
|
-
SIMPLE = "simple"
|
|
73
|
-
ROUNDED = "rounded"
|
|
74
|
-
DOUBLE = "double"
|
|
75
|
-
THICK = "thick"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class Colors(Enum):
|
|
79
|
-
"""ANSI color codes."""
|
|
80
|
-
# Standard colors
|
|
81
|
-
BLACK = "\033[30m"
|
|
82
|
-
RED = "\033[31m"
|
|
83
|
-
GREEN = "\033[32m"
|
|
84
|
-
YELLOW = "\033[33m"
|
|
85
|
-
BLUE = "\033[34m"
|
|
86
|
-
MAGENTA = "\033[35m"
|
|
87
|
-
CYAN = "\033[36m"
|
|
88
|
-
WHITE = "\033[37m"
|
|
89
|
-
|
|
90
|
-
# Bright colors
|
|
91
|
-
BRIGHT_BLACK = "\033[90m"
|
|
92
|
-
BRIGHT_RED = "\033[91m"
|
|
93
|
-
BRIGHT_GREEN = "\033[92m"
|
|
94
|
-
BRIGHT_YELLOW = "\033[93m"
|
|
95
|
-
BRIGHT_BLUE = "\033[94m"
|
|
96
|
-
BRIGHT_MAGENTA = "\033[95m"
|
|
97
|
-
BRIGHT_CYAN = "\033[96m"
|
|
98
|
-
BRIGHT_WHITE = "\033[97m"
|
|
99
|
-
|
|
100
|
-
# Background colors
|
|
101
|
-
BG_BLACK = "\033[40m"
|
|
102
|
-
BG_RED = "\033[41m"
|
|
103
|
-
BG_GREEN = "\033[42m"
|
|
104
|
-
BG_YELLOW = "\033[43m"
|
|
105
|
-
BG_BLUE = "\033[44m"
|
|
106
|
-
BG_MAGENTA = "\033[45m"
|
|
107
|
-
BG_CYAN = "\033[46m"
|
|
108
|
-
BG_WHITE = "\033[47m"
|
|
109
|
-
|
|
110
|
-
# Reset
|
|
111
|
-
RESET = "\033[0m"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class Style(Enum):
|
|
115
|
-
"""ANSI style codes."""
|
|
116
|
-
RESET = "\033[0m"
|
|
117
|
-
BOLD = "\033[1m"
|
|
118
|
-
DIM = "\033[2m"
|
|
119
|
-
ITALIC = "\033[3m"
|
|
120
|
-
UNDERLINE = "\033[4m"
|
|
121
|
-
BLINK = "\033[5m"
|
|
122
|
-
REVERSE = "\033[7m"
|
|
123
|
-
STRIKETHROUGH = "\033[9m"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class ArgumentType(Enum):
|
|
127
|
-
"""Types of command-line arguments."""
|
|
128
|
-
STRING = "string"
|
|
129
|
-
INTEGER = "int"
|
|
130
|
-
FLOAT = "float"
|
|
131
|
-
BOOLEAN = "bool"
|
|
132
|
-
FILE = "file"
|
|
133
|
-
DIRECTORY = "dir"
|
|
134
|
-
CHOICE = "choice"
|
exonware/xwsystem/conf.py
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Public-facing configuration shorthands.
|
|
3
|
-
|
|
4
|
-
This module delegates to exonware.conf for backward compatibility.
|
|
5
|
-
For new code, prefer: import exonware.conf as conf
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import sys
|
|
11
|
-
import types
|
|
12
|
-
|
|
13
|
-
# Delegate to top-level conf for backward compatibility
|
|
14
|
-
# This allows both import patterns to work:
|
|
15
|
-
# - import exonware.conf as conf (preferred - no xwsystem initialization)
|
|
16
|
-
# - import exonware.xwsystem.conf as conf (backward compatible)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class _ConfModule(types.ModuleType):
|
|
20
|
-
"""Expose selected config toggles as module-level attributes."""
|
|
21
|
-
|
|
22
|
-
def __getattr__(self, name: str):
|
|
23
|
-
# Delegate to top-level conf module
|
|
24
|
-
try:
|
|
25
|
-
import exonware.conf as top_conf
|
|
26
|
-
return getattr(top_conf.xwsystem, name)
|
|
27
|
-
except (ImportError, AttributeError):
|
|
28
|
-
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
29
|
-
|
|
30
|
-
def __setattr__(self, name: str, value) -> None:
|
|
31
|
-
if name.startswith('_'):
|
|
32
|
-
super().__setattr__(name, value)
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
# Delegate to top-level conf module
|
|
36
|
-
try:
|
|
37
|
-
import exonware.conf as top_conf
|
|
38
|
-
setattr(top_conf.xwsystem, name, value)
|
|
39
|
-
except (ImportError, AttributeError):
|
|
40
|
-
super().__setattr__(name, value)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
sys.modules[__name__].__class__ = _ConfModule
|
|
44
|
-
|
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Company: eXonware.com
|
|
3
|
-
Author: Eng. Muhammad AlShehri
|
|
4
|
-
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.1
|
|
6
|
-
Generation Date: September 04, 2025
|
|
7
|
-
|
|
8
|
-
Advanced Authentication Providers for Enterprise Integration
|
|
9
|
-
|
|
10
|
-
Provides enterprise authentication mechanisms:
|
|
11
|
-
- OAuth2 (Authorization Code, Client Credentials, Resource Owner)
|
|
12
|
-
- JWT (JSON Web Tokens) with RS256/HS256
|
|
13
|
-
- SAML 2.0 integration
|
|
14
|
-
- Multi-factor authentication support
|
|
15
|
-
- Token refresh and validation
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
import json
|
|
19
|
-
import time
|
|
20
|
-
import base64
|
|
21
|
-
import hashlib
|
|
22
|
-
import hmac
|
|
23
|
-
from typing import Any, Optional, Union
|
|
24
|
-
from urllib.parse import urlencode, parse_qs
|
|
25
|
-
from .base import AAuthProvider, ATokenInfo, AUserInfo
|
|
26
|
-
from .errors import AuthenticationError, AuthorizationError, TokenExpiredError
|
|
27
|
-
from .defs import OAuth2GrantType
|
|
28
|
-
|
|
29
|
-
from ..config.logging_setup import get_logger
|
|
30
|
-
|
|
31
|
-
# Lazy imports to avoid import errors during test collection
|
|
32
|
-
# These are only imported when actually needed
|
|
33
|
-
def _get_jwt():
|
|
34
|
-
"""Lazy import jwt module."""
|
|
35
|
-
try:
|
|
36
|
-
import jwt
|
|
37
|
-
return jwt
|
|
38
|
-
except ImportError:
|
|
39
|
-
raise ImportError(
|
|
40
|
-
"PyJWT is required for JWT authentication. "
|
|
41
|
-
"Install it with: pip install PyJWT"
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
def _get_requests():
|
|
45
|
-
"""Lazy import requests module."""
|
|
46
|
-
try:
|
|
47
|
-
import requests
|
|
48
|
-
return requests
|
|
49
|
-
except ImportError:
|
|
50
|
-
raise ImportError(
|
|
51
|
-
"requests is required for OAuth2 authentication. "
|
|
52
|
-
"Install it with: pip install requests"
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
logger = get_logger("xwsystem.security.auth")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class OAuth2Provider(AAuthProvider):
|
|
59
|
-
"""OAuth2 authentication provider."""
|
|
60
|
-
|
|
61
|
-
def __init__(
|
|
62
|
-
self,
|
|
63
|
-
client_id: str,
|
|
64
|
-
client_secret: str,
|
|
65
|
-
authorization_url: str,
|
|
66
|
-
token_url: str,
|
|
67
|
-
userinfo_url: Optional[str] = None,
|
|
68
|
-
scopes: Optional[list[str]] = None
|
|
69
|
-
):
|
|
70
|
-
"""
|
|
71
|
-
Initialize OAuth2 provider.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
client_id: OAuth2 client ID
|
|
75
|
-
client_secret: OAuth2 client secret
|
|
76
|
-
authorization_url: Authorization endpoint URL
|
|
77
|
-
token_url: Token endpoint URL
|
|
78
|
-
userinfo_url: Optional user info endpoint URL
|
|
79
|
-
scopes: Optional list of scopes to request
|
|
80
|
-
"""
|
|
81
|
-
# requests is now required
|
|
82
|
-
|
|
83
|
-
self.client_id = client_id
|
|
84
|
-
self.client_secret = client_secret
|
|
85
|
-
self.authorization_url = authorization_url
|
|
86
|
-
self.token_url = token_url
|
|
87
|
-
self.userinfo_url = userinfo_url
|
|
88
|
-
self.scopes = scopes or []
|
|
89
|
-
|
|
90
|
-
def get_authorization_url(self, redirect_uri: str, state: Optional[str] = None) -> str:
|
|
91
|
-
"""Get authorization URL for OAuth2 flow."""
|
|
92
|
-
params = {
|
|
93
|
-
'response_type': 'code',
|
|
94
|
-
'client_id': self.client_id,
|
|
95
|
-
'redirect_uri': redirect_uri,
|
|
96
|
-
'scope': ' '.join(self.scopes)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if state:
|
|
100
|
-
params['state'] = state
|
|
101
|
-
|
|
102
|
-
return f"{self.authorization_url}?{urlencode(params)}"
|
|
103
|
-
|
|
104
|
-
async def authenticate(self, credentials: dict[str, Any]) -> ATokenInfo:
|
|
105
|
-
"""Authenticate using OAuth2 flow."""
|
|
106
|
-
import asyncio
|
|
107
|
-
|
|
108
|
-
def _authenticate():
|
|
109
|
-
grant_type = credentials.get('grant_type', OAuth2GrantType.AUTHORIZATION_CODE.value)
|
|
110
|
-
|
|
111
|
-
if grant_type == OAuth2GrantType.AUTHORIZATION_CODE.value:
|
|
112
|
-
return self._authenticate_authorization_code(credentials)
|
|
113
|
-
elif grant_type == OAuth2GrantType.CLIENT_CREDENTIALS.value:
|
|
114
|
-
return self._authenticate_client_credentials()
|
|
115
|
-
elif grant_type == OAuth2GrantType.RESOURCE_OWNER.value:
|
|
116
|
-
return self._authenticate_resource_owner(credentials)
|
|
117
|
-
else:
|
|
118
|
-
raise AuthenticationError(f"Unsupported grant type: {grant_type}")
|
|
119
|
-
|
|
120
|
-
return await asyncio.to_thread(_authenticate)
|
|
121
|
-
|
|
122
|
-
def _authenticate_authorization_code(self, credentials: dict[str, Any]) -> ATokenInfo:
|
|
123
|
-
"""Authenticate using authorization code."""
|
|
124
|
-
requests = _get_requests()
|
|
125
|
-
data = {
|
|
126
|
-
'grant_type': OAuth2GrantType.AUTHORIZATION_CODE.value,
|
|
127
|
-
'client_id': self.client_id,
|
|
128
|
-
'client_secret': self.client_secret,
|
|
129
|
-
'code': credentials['code'],
|
|
130
|
-
'redirect_uri': credentials['redirect_uri']
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
response = requests.post(self.token_url, data=data)
|
|
134
|
-
|
|
135
|
-
if response.status_code != 200:
|
|
136
|
-
raise AuthenticationError(f"OAuth2 authentication failed: {response.text}")
|
|
137
|
-
|
|
138
|
-
token_data = response.json()
|
|
139
|
-
return ATokenInfo(
|
|
140
|
-
access_token=token_data['access_token'],
|
|
141
|
-
token_type=token_data.get('token_type', 'Bearer'),
|
|
142
|
-
expires_in=token_data.get('expires_in'),
|
|
143
|
-
refresh_token=token_data.get('refresh_token'),
|
|
144
|
-
scope=token_data.get('scope')
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
def _authenticate_client_credentials(self) -> ATokenInfo:
|
|
148
|
-
"""Authenticate using client credentials."""
|
|
149
|
-
requests = _get_requests()
|
|
150
|
-
data = {
|
|
151
|
-
'grant_type': OAuth2GrantType.CLIENT_CREDENTIALS.value,
|
|
152
|
-
'client_id': self.client_id,
|
|
153
|
-
'client_secret': self.client_secret,
|
|
154
|
-
'scope': ' '.join(self.scopes)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
response = requests.post(self.token_url, data=data)
|
|
158
|
-
|
|
159
|
-
if response.status_code != 200:
|
|
160
|
-
raise AuthenticationError(f"OAuth2 authentication failed: {response.text}")
|
|
161
|
-
|
|
162
|
-
token_data = response.json()
|
|
163
|
-
return ATokenInfo(
|
|
164
|
-
access_token=token_data['access_token'],
|
|
165
|
-
token_type=token_data.get('token_type', 'Bearer'),
|
|
166
|
-
expires_in=token_data.get('expires_in'),
|
|
167
|
-
scope=token_data.get('scope')
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
def _authenticate_resource_owner(self, credentials: dict[str, Any]) -> ATokenInfo:
|
|
171
|
-
"""Authenticate using resource owner credentials."""
|
|
172
|
-
requests = _get_requests()
|
|
173
|
-
data = {
|
|
174
|
-
'grant_type': OAuth2GrantType.RESOURCE_OWNER.value,
|
|
175
|
-
'client_id': self.client_id,
|
|
176
|
-
'client_secret': self.client_secret,
|
|
177
|
-
'username': credentials['username'],
|
|
178
|
-
'password': credentials['password'],
|
|
179
|
-
'scope': ' '.join(self.scopes)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
response = requests.post(self.token_url, data=data)
|
|
183
|
-
|
|
184
|
-
if response.status_code != 200:
|
|
185
|
-
raise AuthenticationError(f"OAuth2 authentication failed: {response.text}")
|
|
186
|
-
|
|
187
|
-
token_data = response.json()
|
|
188
|
-
return ATokenInfo(
|
|
189
|
-
access_token=token_data['access_token'],
|
|
190
|
-
token_type=token_data.get('token_type', 'Bearer'),
|
|
191
|
-
expires_in=token_data.get('expires_in'),
|
|
192
|
-
refresh_token=token_data.get('refresh_token'),
|
|
193
|
-
scope=token_data.get('scope')
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
async def validate_token(self, token: str) -> AUserInfo:
|
|
197
|
-
"""Validate OAuth2 token."""
|
|
198
|
-
if not self.userinfo_url:
|
|
199
|
-
raise AuthenticationError("User info URL not configured")
|
|
200
|
-
|
|
201
|
-
import asyncio
|
|
202
|
-
|
|
203
|
-
def _validate():
|
|
204
|
-
requests = _get_requests()
|
|
205
|
-
headers = {'Authorization': f'Bearer {token}'}
|
|
206
|
-
response = requests.get(self.userinfo_url, headers=headers)
|
|
207
|
-
|
|
208
|
-
if response.status_code == 401:
|
|
209
|
-
raise TokenExpiredError("Token expired or invalid")
|
|
210
|
-
elif response.status_code != 200:
|
|
211
|
-
raise AuthenticationError(f"Token validation failed: {response.text}")
|
|
212
|
-
|
|
213
|
-
user_data = response.json()
|
|
214
|
-
return AUserInfo(
|
|
215
|
-
user_id=user_data.get('sub', user_data.get('id', 'unknown')),
|
|
216
|
-
username=user_data.get('preferred_username', user_data.get('username')),
|
|
217
|
-
email=user_data.get('email'),
|
|
218
|
-
attributes=user_data
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
return await asyncio.to_thread(_validate)
|
|
222
|
-
|
|
223
|
-
async def refresh_token(self, refresh_token: str) -> ATokenInfo:
|
|
224
|
-
"""Refresh OAuth2 token."""
|
|
225
|
-
import asyncio
|
|
226
|
-
|
|
227
|
-
def _refresh():
|
|
228
|
-
requests = _get_requests()
|
|
229
|
-
data = {
|
|
230
|
-
'grant_type': OAuth2GrantType.REFRESH_TOKEN.value,
|
|
231
|
-
'client_id': self.client_id,
|
|
232
|
-
'client_secret': self.client_secret,
|
|
233
|
-
'refresh_token': refresh_token
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
response = requests.post(self.token_url, data=data)
|
|
237
|
-
|
|
238
|
-
if response.status_code != 200:
|
|
239
|
-
raise AuthenticationError(f"Token refresh failed: {response.text}")
|
|
240
|
-
|
|
241
|
-
token_data = response.json()
|
|
242
|
-
return ATokenInfo(
|
|
243
|
-
access_token=token_data['access_token'],
|
|
244
|
-
token_type=token_data.get('token_type', 'Bearer'),
|
|
245
|
-
expires_in=token_data.get('expires_in'),
|
|
246
|
-
refresh_token=token_data.get('refresh_token', refresh_token),
|
|
247
|
-
scope=token_data.get('scope')
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
return await asyncio.to_thread(_refresh)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
class JWTProvider(AAuthProvider):
|
|
254
|
-
"""JWT (JSON Web Token) authentication provider."""
|
|
255
|
-
|
|
256
|
-
def __init__(
|
|
257
|
-
self,
|
|
258
|
-
secret_key: str,
|
|
259
|
-
algorithm: str = "HS256",
|
|
260
|
-
issuer: Optional[str] = None,
|
|
261
|
-
audience: Optional[str] = None,
|
|
262
|
-
expiration_time: int = 3600
|
|
263
|
-
):
|
|
264
|
-
"""
|
|
265
|
-
Initialize JWT provider.
|
|
266
|
-
|
|
267
|
-
Args:
|
|
268
|
-
secret_key: Secret key for signing tokens
|
|
269
|
-
algorithm: JWT algorithm (HS256, RS256, etc.)
|
|
270
|
-
issuer: Token issuer
|
|
271
|
-
audience: Token audience
|
|
272
|
-
expiration_time: Token expiration time in seconds
|
|
273
|
-
"""
|
|
274
|
-
# PyJWT is now required
|
|
275
|
-
|
|
276
|
-
self.secret_key = secret_key
|
|
277
|
-
self.algorithm = algorithm
|
|
278
|
-
self.issuer = issuer
|
|
279
|
-
self.audience = audience
|
|
280
|
-
self.expiration_time = expiration_time
|
|
281
|
-
|
|
282
|
-
async def authenticate(self, credentials: dict[str, Any]) -> ATokenInfo:
|
|
283
|
-
"""Create JWT token from user credentials."""
|
|
284
|
-
import asyncio
|
|
285
|
-
|
|
286
|
-
def _authenticate():
|
|
287
|
-
jwt = _get_jwt()
|
|
288
|
-
# In a real implementation, you'd validate credentials against a database
|
|
289
|
-
user_id = credentials.get('user_id')
|
|
290
|
-
if not user_id:
|
|
291
|
-
raise AuthenticationError("user_id required for JWT authentication")
|
|
292
|
-
|
|
293
|
-
now = time.time()
|
|
294
|
-
payload = {
|
|
295
|
-
'sub': user_id,
|
|
296
|
-
'iat': now,
|
|
297
|
-
'exp': now + self.expiration_time,
|
|
298
|
-
**{k: v for k, v in credentials.items() if k not in ['user_id', 'password']}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if self.issuer:
|
|
302
|
-
payload['iss'] = self.issuer
|
|
303
|
-
if self.audience:
|
|
304
|
-
payload['aud'] = self.audience
|
|
305
|
-
|
|
306
|
-
token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
|
|
307
|
-
|
|
308
|
-
return ATokenInfo(
|
|
309
|
-
access_token=token,
|
|
310
|
-
token_type="Bearer",
|
|
311
|
-
expires_in=self.expiration_time
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
return await asyncio.to_thread(_authenticate)
|
|
315
|
-
|
|
316
|
-
async def validate_token(self, token: str) -> AUserInfo:
|
|
317
|
-
"""Validate JWT token."""
|
|
318
|
-
import asyncio
|
|
319
|
-
|
|
320
|
-
def _validate():
|
|
321
|
-
jwt = _get_jwt()
|
|
322
|
-
try:
|
|
323
|
-
payload = jwt.decode(
|
|
324
|
-
token,
|
|
325
|
-
self.secret_key,
|
|
326
|
-
algorithms=[self.algorithm],
|
|
327
|
-
issuer=self.issuer,
|
|
328
|
-
audience=self.audience
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
return AUserInfo(
|
|
332
|
-
user_id=payload['sub'],
|
|
333
|
-
username=payload.get('username'),
|
|
334
|
-
email=payload.get('email'),
|
|
335
|
-
roles=payload.get('roles', []),
|
|
336
|
-
attributes=payload
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
except jwt.ExpiredSignatureError:
|
|
340
|
-
raise TokenExpiredError("JWT token has expired")
|
|
341
|
-
except jwt.InvalidTokenError as e:
|
|
342
|
-
raise AuthenticationError(f"Invalid JWT token: {e}")
|
|
343
|
-
|
|
344
|
-
return await asyncio.to_thread(_validate)
|
|
345
|
-
|
|
346
|
-
async def refresh_token(self, refresh_token: str) -> ATokenInfo:
|
|
347
|
-
"""Refresh JWT token (create new token from existing)."""
|
|
348
|
-
jwt = _get_jwt()
|
|
349
|
-
try:
|
|
350
|
-
# Validate existing token (ignore expiration for refresh)
|
|
351
|
-
payload = jwt.decode(
|
|
352
|
-
refresh_token,
|
|
353
|
-
self.secret_key,
|
|
354
|
-
algorithms=[self.algorithm],
|
|
355
|
-
options={"verify_exp": False}
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
# Create new token with same payload but new timestamps
|
|
359
|
-
now = time.time()
|
|
360
|
-
payload.update({
|
|
361
|
-
'iat': now,
|
|
362
|
-
'exp': now + self.expiration_time
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
new_token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
|
|
366
|
-
|
|
367
|
-
return ATokenInfo(
|
|
368
|
-
access_token=new_token,
|
|
369
|
-
token_type="Bearer",
|
|
370
|
-
expires_in=self.expiration_time
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
except Exception as e:
|
|
374
|
-
jwt_module = _get_jwt()
|
|
375
|
-
if isinstance(e, jwt_module.InvalidTokenError):
|
|
376
|
-
raise AuthenticationError(f"Invalid refresh token: {e}")
|
|
377
|
-
raise
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
class SAMLProvider(AAuthProvider):
|
|
381
|
-
"""SAML 2.0 authentication provider (simplified implementation)."""
|
|
382
|
-
|
|
383
|
-
def __init__(
|
|
384
|
-
self,
|
|
385
|
-
idp_url: str,
|
|
386
|
-
sp_entity_id: str,
|
|
387
|
-
certificate_path: Optional[str] = None
|
|
388
|
-
):
|
|
389
|
-
"""
|
|
390
|
-
Initialize SAML provider.
|
|
391
|
-
|
|
392
|
-
Args:
|
|
393
|
-
idp_url: Identity Provider URL
|
|
394
|
-
sp_entity_id: Service Provider entity ID
|
|
395
|
-
certificate_path: Path to IdP certificate for validation
|
|
396
|
-
"""
|
|
397
|
-
self.idp_url = idp_url
|
|
398
|
-
self.sp_entity_id = sp_entity_id
|
|
399
|
-
self.certificate_path = certificate_path
|
|
400
|
-
|
|
401
|
-
logger.warning("SAML provider is a simplified implementation. Use a full SAML library for production.")
|
|
402
|
-
|
|
403
|
-
async def authenticate(self, credentials: dict[str, Any]) -> ATokenInfo:
|
|
404
|
-
"""Authenticate using SAML (simplified)."""
|
|
405
|
-
# This is a placeholder implementation
|
|
406
|
-
# In practice, you'd use a library like python3-saml
|
|
407
|
-
raise AuthenticationError("SAML authentication requires a full SAML library implementation")
|
|
408
|
-
|
|
409
|
-
async def validate_token(self, token: str) -> AUserInfo:
|
|
410
|
-
"""Validate SAML token (simplified)."""
|
|
411
|
-
# This is a placeholder implementation
|
|
412
|
-
raise AuthenticationError("SAML token validation requires a full SAML library implementation")
|
|
413
|
-
|
|
414
|
-
async def refresh_token(self, refresh_token: str) -> ATokenInfo:
|
|
415
|
-
"""SAML doesn't typically support token refresh."""
|
|
416
|
-
raise AuthenticationError("SAML does not support token refresh")
|
|
417
|
-
|
|
418
|
-
def get_login_url(self, return_url: str) -> str:
|
|
419
|
-
"""Get SAML login URL."""
|
|
420
|
-
# This would generate a proper SAML AuthnRequest
|
|
421
|
-
params = {
|
|
422
|
-
'SAMLRequest': base64.b64encode(f'<saml:AuthnRequest><saml:Issuer>{self.sp_entity_id}</saml:Issuer></saml:AuthnRequest>'.encode()).decode(),
|
|
423
|
-
'RelayState': return_url
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return f"{self.idp_url}?{urlencode(params)}"
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
class EnterpriseAuth:
|
|
430
|
-
"""Enterprise authentication manager."""
|
|
431
|
-
|
|
432
|
-
def __init__(self):
|
|
433
|
-
self._providers = {}
|
|
434
|
-
self._active_provider = None
|
|
435
|
-
|
|
436
|
-
def add_provider(self, name: str, provider: AAuthProvider):
|
|
437
|
-
"""Add authentication provider."""
|
|
438
|
-
self._providers[name] = provider
|
|
439
|
-
|
|
440
|
-
def set_active_provider(self, name: str):
|
|
441
|
-
"""Set active authentication provider."""
|
|
442
|
-
if name not in self._providers:
|
|
443
|
-
raise AuthenticationError(f"Provider '{name}' not found")
|
|
444
|
-
self._active_provider = name
|
|
445
|
-
|
|
446
|
-
def get_provider(self, name: Optional[str] = None) -> AAuthProvider:
|
|
447
|
-
"""Get authentication provider."""
|
|
448
|
-
if name is None:
|
|
449
|
-
name = self._active_provider
|
|
450
|
-
|
|
451
|
-
if name is None:
|
|
452
|
-
raise AuthenticationError("No active provider set")
|
|
453
|
-
|
|
454
|
-
if name not in self._providers:
|
|
455
|
-
raise AuthenticationError(f"Provider '{name}' not found")
|
|
456
|
-
|
|
457
|
-
return self._providers[name]
|
|
458
|
-
|
|
459
|
-
async def authenticate(self, credentials: dict[str, Any], provider: Optional[str] = None) -> ATokenInfo:
|
|
460
|
-
"""Authenticate using specified or active provider."""
|
|
461
|
-
provider_instance = self.get_provider(provider)
|
|
462
|
-
return await provider_instance.authenticate(credentials)
|
|
463
|
-
|
|
464
|
-
async def validate_token(self, token: str, provider: Optional[str] = None) -> AUserInfo:
|
|
465
|
-
"""Validate token using specified or active provider."""
|
|
466
|
-
provider_instance = self.get_provider(provider)
|
|
467
|
-
return await provider_instance.validate_token(token)
|
|
468
|
-
|
|
469
|
-
async def refresh_token(self, refresh_token: str, provider: Optional[str] = None) -> ATokenInfo:
|
|
470
|
-
"""Refresh token using specified or active provider."""
|
|
471
|
-
provider_instance = self.get_provider(provider)
|
|
472
|
-
return await provider_instance.refresh_token(refresh_token)
|
|
473
|
-
|
|
474
|
-
def list_providers(self) -> list[str]:
|
|
475
|
-
"""List available providers."""
|
|
476
|
-
return list(self._providers.keys())
|
|
477
|
-
|
|
478
|
-
def remove_provider(self, name: str):
|
|
479
|
-
"""Remove authentication provider."""
|
|
480
|
-
if name in self._providers:
|
|
481
|
-
del self._providers[name]
|
|
482
|
-
if self._active_provider == name:
|
|
483
|
-
self._active_provider = None
|
|
484
|
-
|