pyauthz 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. pyauthz-0.9.0/.gitignore +50 -0
  2. pyauthz-0.9.0/ARCHITECTURE.md +124 -0
  3. pyauthz-0.9.0/CHANGELOG.md +140 -0
  4. pyauthz-0.9.0/CONTRIBUTING.md +123 -0
  5. pyauthz-0.9.0/LICENSE +190 -0
  6. pyauthz-0.9.0/PKG-INFO +206 -0
  7. pyauthz-0.9.0/README.md +148 -0
  8. pyauthz-0.9.0/SECURITY.md +42 -0
  9. pyauthz-0.9.0/docs/FEDRAMP_GUIDE.md +243 -0
  10. pyauthz-0.9.0/llms.txt +21 -0
  11. pyauthz-0.9.0/pyauthz/__init__.py +108 -0
  12. pyauthz-0.9.0/pyauthz/audit/__init__.py +0 -0
  13. pyauthz-0.9.0/pyauthz/audit/compliance.py +102 -0
  14. pyauthz-0.9.0/pyauthz/audit/integrity.py +92 -0
  15. pyauthz-0.9.0/pyauthz/audit/logger.py +92 -0
  16. pyauthz-0.9.0/pyauthz/audit/models.py +74 -0
  17. pyauthz-0.9.0/pyauthz/cli/__init__.py +1 -0
  18. pyauthz-0.9.0/pyauthz/cli/main.py +332 -0
  19. pyauthz-0.9.0/pyauthz/contrib/__init__.py +0 -0
  20. pyauthz-0.9.0/pyauthz/contrib/x509.py +183 -0
  21. pyauthz-0.9.0/pyauthz/core/__init__.py +0 -0
  22. pyauthz-0.9.0/pyauthz/core/breakglass.py +183 -0
  23. pyauthz-0.9.0/pyauthz/core/cache.py +111 -0
  24. pyauthz-0.9.0/pyauthz/core/conditions.py +151 -0
  25. pyauthz-0.9.0/pyauthz/core/decision.py +44 -0
  26. pyauthz-0.9.0/pyauthz/core/delegation.py +155 -0
  27. pyauthz-0.9.0/pyauthz/core/diff.py +143 -0
  28. pyauthz-0.9.0/pyauthz/core/engine.py +669 -0
  29. pyauthz-0.9.0/pyauthz/core/errors.py +83 -0
  30. pyauthz-0.9.0/pyauthz/core/explain.py +121 -0
  31. pyauthz-0.9.0/pyauthz/core/lint.py +244 -0
  32. pyauthz-0.9.0/pyauthz/core/simulation.py +150 -0
  33. pyauthz-0.9.0/pyauthz/core/temporal.py +160 -0
  34. pyauthz-0.9.0/pyauthz/core/tenancy.py +74 -0
  35. pyauthz-0.9.0/pyauthz/filtering/__init__.py +42 -0
  36. pyauthz-0.9.0/pyauthz/filtering/compiler.py +292 -0
  37. pyauthz-0.9.0/pyauthz/filtering/django.py +110 -0
  38. pyauthz-0.9.0/pyauthz/filtering/expressions.py +78 -0
  39. pyauthz-0.9.0/pyauthz/filtering/raw_sql.py +148 -0
  40. pyauthz-0.9.0/pyauthz/filtering/sql.py +134 -0
  41. pyauthz-0.9.0/pyauthz/middleware/__init__.py +0 -0
  42. pyauthz-0.9.0/pyauthz/models/__init__.py +19 -0
  43. pyauthz-0.9.0/pyauthz/models/action.py +18 -0
  44. pyauthz-0.9.0/pyauthz/models/condition.py +38 -0
  45. pyauthz-0.9.0/pyauthz/models/context.py +29 -0
  46. pyauthz-0.9.0/pyauthz/models/permission.py +79 -0
  47. pyauthz-0.9.0/pyauthz/models/policy.py +46 -0
  48. pyauthz-0.9.0/pyauthz/models/resource.py +27 -0
  49. pyauthz-0.9.0/pyauthz/models/role.py +22 -0
  50. pyauthz-0.9.0/pyauthz/models/subject.py +24 -0
  51. pyauthz-0.9.0/pyauthz/policies/__init__.py +0 -0
  52. pyauthz-0.9.0/pyauthz/policies/combiners.py +140 -0
  53. pyauthz-0.9.0/pyauthz/py.typed +0 -0
  54. pyauthz-0.9.0/pyauthz/storage/__init__.py +0 -0
  55. pyauthz-0.9.0/pyauthz/storage/base.py +49 -0
  56. pyauthz-0.9.0/pyauthz/storage/memory.py +82 -0
  57. pyauthz-0.9.0/pyauthz/storage/watcher.py +133 -0
  58. pyauthz-0.9.0/pyauthz/storage/yaml_file.py +167 -0
  59. pyauthz-0.9.0/pyauthz/testing/__init__.py +22 -0
  60. pyauthz-0.9.0/pyauthz/testing/assertions.py +33 -0
  61. pyauthz-0.9.0/pyauthz/testing/scenario.py +199 -0
  62. pyauthz-0.9.0/pyauthz_django/__init__.py +25 -0
  63. pyauthz-0.9.0/pyauthz_django/decorator.py +66 -0
  64. pyauthz-0.9.0/pyauthz_django/middleware.py +85 -0
  65. pyauthz-0.9.0/pyauthz_fastapi/__init__.py +35 -0
  66. pyauthz-0.9.0/pyauthz_fastapi/decorator.py +159 -0
  67. pyauthz-0.9.0/pyauthz_fastapi/dependencies.py +52 -0
  68. pyauthz-0.9.0/pyauthz_fastapi/middleware.py +105 -0
  69. pyauthz-0.9.0/pyauthz_flask/__init__.py +27 -0
  70. pyauthz-0.9.0/pyauthz_flask/decorator.py +77 -0
  71. pyauthz-0.9.0/pyauthz_flask/extension.py +110 -0
  72. pyauthz-0.9.0/pyauthz_strawberry/__init__.py +31 -0
  73. pyauthz-0.9.0/pyauthz_strawberry/decorator.py +103 -0
  74. pyauthz-0.9.0/pyauthz_strawberry/extension.py +78 -0
  75. pyauthz-0.9.0/pyproject.toml +87 -0
  76. pyauthz-0.9.0/tests/__init__.py +0 -0
  77. pyauthz-0.9.0/tests/test_abac_engine.py +271 -0
  78. pyauthz-0.9.0/tests/test_audit.py +77 -0
  79. pyauthz-0.9.0/tests/test_breakglass.py +93 -0
  80. pyauthz-0.9.0/tests/test_cache.py +170 -0
  81. pyauthz-0.9.0/tests/test_cli.py +196 -0
  82. pyauthz-0.9.0/tests/test_combiners.py +150 -0
  83. pyauthz-0.9.0/tests/test_compliance.py +589 -0
  84. pyauthz-0.9.0/tests/test_conditions.py +285 -0
  85. pyauthz-0.9.0/tests/test_delegation.py +138 -0
  86. pyauthz-0.9.0/tests/test_diff.py +110 -0
  87. pyauthz-0.9.0/tests/test_engine.py +401 -0
  88. pyauthz-0.9.0/tests/test_explain.py +93 -0
  89. pyauthz-0.9.0/tests/test_filtering.py +649 -0
  90. pyauthz-0.9.0/tests/test_hierarchy.py +152 -0
  91. pyauthz-0.9.0/tests/test_lint.py +133 -0
  92. pyauthz-0.9.0/tests/test_models.py +205 -0
  93. pyauthz-0.9.0/tests/test_properties.py +130 -0
  94. pyauthz-0.9.0/tests/test_simulation.py +190 -0
  95. pyauthz-0.9.0/tests/test_storage.py +87 -0
  96. pyauthz-0.9.0/tests/test_temporal.py +110 -0
  97. pyauthz-0.9.0/tests/test_tenancy.py +81 -0
  98. pyauthz-0.9.0/tests/test_testing.py +123 -0
  99. pyauthz-0.9.0/tests/test_watcher.py +90 -0
  100. pyauthz-0.9.0/tests/test_yaml.py +195 -0
  101. pyauthz-0.9.0/tests_pyauthz_django/__init__.py +0 -0
  102. pyauthz-0.9.0/tests_pyauthz_django/test_django.py +272 -0
  103. pyauthz-0.9.0/tests_pyauthz_fastapi/__init__.py +0 -0
  104. pyauthz-0.9.0/tests_pyauthz_fastapi/test_fastapi.py +227 -0
  105. pyauthz-0.9.0/tests_pyauthz_flask/__init__.py +0 -0
  106. pyauthz-0.9.0/tests_pyauthz_flask/test_flask.py +175 -0
  107. pyauthz-0.9.0/tests_pyauthz_strawberry/__init__.py +0 -0
  108. pyauthz-0.9.0/tests_pyauthz_strawberry/test_strawberry.py +140 -0
@@ -0,0 +1,50 @@
1
+ # Python bytecode
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+
16
+ # Testing / coverage
17
+ .pytest_cache/
18
+ .hypothesis/
19
+ .coverage
20
+ htmlcov/
21
+
22
+ # Environment / secrets
23
+ .env
24
+ .env.*
25
+
26
+ # Audit logs (generated at runtime)
27
+ logs/
28
+ *.jsonl
29
+
30
+ # IDE
31
+ .vscode/
32
+ .idea/
33
+ *.swp
34
+ *.swo
35
+
36
+ # OS junk
37
+ Thumbs.db
38
+ Desktop.ini
39
+ .DS_Store
40
+
41
+ # Internal planning docs (local only)
42
+ AGENTS.md
43
+ CONTEXT.md
44
+ Development.md
45
+ Implementation.md
46
+ Progress.md
47
+ GOV_PITCH.md
48
+ rampart-project.md
49
+ bastion-project.md
50
+ pyauthz-project.md
@@ -0,0 +1,124 @@
1
+ # PyAuthz Architecture
2
+
3
+ This document describes the internal architecture of PyAuthz for contributors and advanced users.
4
+
5
+ ## Core Concepts
6
+
7
+ PyAuthz evaluates authorization as: **Can this Subject perform this Action on this Resource?**
8
+
9
+ ```
10
+ Subject ──┐
11
+ Action ──┤──> Engine ──> Decision (allow/deny + reasoning)
12
+ Resource ──┤
13
+ Env ──┘
14
+ ```
15
+
16
+ ## Evaluation Pipeline
17
+
18
+ When `rp.authorize(subject, action, resource)` is called:
19
+
20
+ 1. **Cache check** — If a `DecisionCache` is attached, check for a cached decision.
21
+ 2. **Action resolution** — If the action has no colon, prepend the resource type: `"write"` → `"documents:write"`.
22
+ 3. **Environment merge** — Merge default environment with per-request env kwargs.
23
+ 4. **Role resolution** — Walk the role hierarchy (parents) to compute the subject's effective roles.
24
+ 5. **Policy matching** — Find all applicable policies:
25
+ - Policy is enabled
26
+ - Subject has at least one of the policy's required roles (or policy has no role restriction)
27
+ - Action matches at least one of the policy's permission patterns
28
+ - All conditions evaluate to true (ABAC)
29
+ 6. **Combination** — Run the combiner algorithm on matched policies:
30
+ - `deny-override`: any DENY wins over all ALLOWs
31
+ - `allow-override`: any ALLOW wins over all DENYs
32
+ - `priority-first`: highest priority policy wins
33
+ - `first-match`: first matched policy wins
34
+ 7. **Decision** — Build and return a `Decision` object with the result, reasoning, matched policies, and timing.
35
+ 8. **Cache store** — If caching is enabled, store the decision for future lookups.
36
+
37
+ ## Module Map
38
+
39
+ ### `pyauthz.core.engine` — The Heart
40
+
41
+ The `PyAuthz` class is the central coordinator. It owns storage, cache, audit, and the evaluation pipeline. All authorization flows through `_evaluate()`.
42
+
43
+ ### `pyauthz.models.*` — Data Models
44
+
45
+ Pydantic models for all domain objects. These are the public API surface:
46
+
47
+ - `Subject` — who is requesting access (id, roles, attributes)
48
+ - `Resource` — what is being accessed (id, type, owner_id, attributes)
49
+ - `Role` — named permission set with optional parent hierarchy
50
+ - `Policy` — explicit allow/deny rule with conditions
51
+ - `Permission` — parsed `resource_type:action` with wildcard support
52
+ - `Condition` — ABAC condition (field, operator, value)
53
+ - `Decision` — evaluation result with full reasoning chain
54
+
55
+ ### `pyauthz.core.conditions` — ABAC Engine
56
+
57
+ Evaluates conditions against an `AccessRequest` context. Handles:
58
+ - Dot-notation field resolution (`subject.attributes.department`)
59
+ - Cross-references (`resource.owner_id == subject.id`)
60
+ - 10 operators: `==`, `!=`, `>`, `<`, `>=`, `<=`, `in`, `not_in`, `contains`, `matches`
61
+ - Fail-safe: missing attributes produce DENY, never crash
62
+
63
+ ### `pyauthz.policies.combiners` — Decision Algorithms
64
+
65
+ Pure functions that take a list of matched policies and return (effect, matched_names, reasoning). Each combiner implements a different conflict resolution strategy.
66
+
67
+ ### `pyauthz.storage.*` — Persistence
68
+
69
+ - `StorageBackend` — protocol defining the storage interface
70
+ - `MemoryStorage` — thread-safe in-memory implementation (default)
71
+ - `yaml_file` — YAML loader with schema validation
72
+ - `watcher` — PolicyWatcher for hot-reload on file changes
73
+
74
+ ### `pyauthz.filtering.*` — Query Filters
75
+
76
+ Converts policies into database-compatible filter expressions:
77
+ - `expressions.py` — FilterExpression AST (frozen dataclasses)
78
+ - `compiler.py` — policy-to-AST compilation
79
+ - `sql.py` — SQLAlchemy WHERE clause generation
80
+ - `django.py` — Django Q object generation
81
+ - `raw_sql.py` — Parameterized raw SQL generation
82
+
83
+ ### `pyauthz.core.cache` — Decision Caching
84
+
85
+ TTL-based LRU cache with automatic invalidation when policies or roles change.
86
+
87
+ ### `pyauthz.core.tenancy` — Multi-Tenancy
88
+
89
+ Per-tenant isolated storage instances via `TenantManager`.
90
+
91
+ ### `pyauthz.core.temporal` — Time-Bounded Roles
92
+
93
+ Role assignments with `effective_from` and `expires_at` windows.
94
+
95
+ ### `pyauthz.core.delegation` — Permission Delegation
96
+
97
+ User A grants User B temporary permissions with expiration and audit trail.
98
+
99
+ ### `pyauthz.core.simulation` — Policy Simulation
100
+
101
+ "What would happen if..." analysis by cloning the engine with modified policies/roles.
102
+
103
+ ## Thread Safety
104
+
105
+ The engine uses `threading.RLock` in `MemoryStorage` for all reads and writes. The `DecisionCache` has its own lock. The engine is safe for concurrent use in WSGI workers.
106
+
107
+ ## Async Support
108
+
109
+ Async methods (`aauthorize`, `ais_allowed`, `aauthorize_bulk`) use `asyncio.to_thread()` to run the synchronous engine in a thread pool. The engine itself is not async-native — this keeps the code simple while providing a real async interface.
110
+
111
+ ## Extension Points
112
+
113
+ - **Custom storage backends**: Implement `StorageBackend` for SQL, Redis, etc.
114
+ - **Custom combiners**: Add functions to `COMBINERS` dict
115
+ - **Framework integrations**: Follow the `pyauthz_fastapi`/`pyauthz_flask` pattern
116
+ - **Audit exporters**: Implement custom exporters for the audit logger
117
+
118
+ ## Design Principles
119
+
120
+ 1. **Deny by default** — If nothing explicitly allows access, deny.
121
+ 2. **Fail-safe** — Missing attributes, bad conditions, or errors produce DENY, never ALLOW.
122
+ 3. **Separation of concerns** — Models don't know about the engine. The engine doesn't know about frameworks.
123
+ 4. **Structured decisions** — Every authorization produces a `Decision` with reasoning, not just a boolean.
124
+ 5. **Zero external dependencies for core** — Only Pydantic and PyYAML for the core engine.
@@ -0,0 +1,140 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.9.0] - 2026-03-06
9
+
10
+ ### Added
11
+
12
+ - **Federal Compliance & Hardened Audit (Phase 9)**
13
+ - `AuditEvent` expanded with optional `source_ip`, `request_id`, `service_id` fields (NIST 800-53 AU-3)
14
+ - `MutationEvent` and `MutationAction` models for tracking configuration changes
15
+ - Engine mutation audit hooks — `add_role()`, `remove_role()`, `replace_roles()`, `add_policy()`, `remove_policy()`, `replace_policies()` all emit `MutationEvent` records (AC-2, CM-3)
16
+ - `TemporalRoleStore` and `DelegationStore` accept optional `audit_logger` for temporal/delegation audit events (AC-2)
17
+ - `ComplianceExporter` — enriches events with NIST 800-53 control mappings and event categories
18
+ - `HmacExporter` — HMAC-SHA256 signed log entries with `verify()` for tamper detection (AU-9)
19
+ - `pyauthz.contrib.x509` — X.509 Distinguished Name parsing: `parse_dn()`, `extract_dod_id()`, `subject_from_dn()`
20
+ - `Denied` exception now carries `.suggestions` populated automatically by `explain_denial()`
21
+ - Django integration tests (9 tests)
22
+ - Strawberry GraphQL integration tests (7 tests)
23
+ - 42 new compliance tests (404 total across all suites)
24
+ - `docs/FEDRAMP_GUIDE.md` — authorization patterns for FedRAMP compliance
25
+
26
+ ### Changed
27
+
28
+ - `Denied.__init__` accepts optional `suggestions` parameter
29
+ - Strawberry `PyAuthzExtension` now inherits from `SchemaExtension` (compatible with Strawberry v0.300+)
30
+ - `AuditLogger` gains `log_mutation()` method for configuration change events
31
+
32
+ ## [0.8.0] - 2026-03-06
33
+
34
+ ### Added
35
+
36
+ - `diff_policies()` — compare two policy sets and report authorization outcome changes (allow→deny, deny→allow)
37
+ - `lint_policies()` — detect common policy mistakes: undefined roles (L001), overly broad (L002), contradictory (L003), disabled (L004), orphan roles (L005), undefined parents (L006)
38
+ - `pyauthz lint` CLI command for linting policy files
39
+ - `explain_denial()` — generate actionable suggestions for denied access (which roles would help, which conditions failed)
40
+ - `BreakGlassManager` — emergency access bypass with required justification, audit logging, and authorized subject list (maps to NIST 800-53 AC-2(2))
41
+ - 29 new tests (346 total) covering diff, lint, explain, and break-glass
42
+
43
+ ## [0.7.0] - 2026-03-06
44
+
45
+ ### Added
46
+
47
+ - `pyauthz.testing` module with `AuthScenario`, `must_allow`, `must_deny` for pytest-friendly authorization testing
48
+ - `pyauthz` CLI tool with `check`, `simulate`, `roles`, and `policies` commands
49
+ - `CONTRIBUTING.md` contributor guide
50
+ - `ARCHITECTURE.md` internal architecture documentation
51
+ - 23 new tests (317 total) for testing utilities and CLI
52
+
53
+ ## [0.6.0] - 2026-03-06
54
+
55
+ ### Added
56
+
57
+ - `DecisionCache` — TTL-based LRU decision cache with automatic invalidation on policy/role changes
58
+ - `PyAuthz.set_cache()` — attach a cache to the engine for automatic caching of authorization decisions
59
+ - `TenantManager` — multi-tenancy support with per-tenant isolated roles and policies
60
+ - `TemporalRole` + `TemporalRoleStore` — time-bounded role assignments with `effective_from` and `expires_at`
61
+ - `Delegation` + `DelegationStore` — controlled permission delegation between users with expiration
62
+ - `simulate()` + `SimulationResult` — policy simulation ("what would happen if...") without modifying the live engine
63
+ - All new types exported from `pyauthz.__init__`
64
+ - 50 new tests (294 total) covering cache, multi-tenancy, temporal permissions, delegation, and simulation
65
+
66
+ ## [0.5.0] - 2026-03-06
67
+
68
+ ### Added
69
+
70
+ - `pyauthz_flask` package — Flask extension with before_request subject resolution, `@requires()` decorator, `get_subject()` helper, and Denied -> 403 JSON handler
71
+ - `pyauthz_django` package — Django middleware with subject resolution, `@requires()` view decorator, and Denied -> 403 JSON response
72
+ - `pyauthz_strawberry` package — Strawberry GraphQL extension with context-based subject resolution and `@requires()` resolver decorator (sync + async)
73
+ - 7 Flask integration tests (244 total)
74
+
75
+ ## [0.4.0] - 2026-03-06
76
+
77
+ ### Added
78
+
79
+ - `authorized_filter()` method on `PyAuthz` engine — produces `FilterResult` with AST
80
+ - `FilterExpression` AST: `AllowAll`, `DenyAll`, `Compare`, `And`, `Or`, `Not` nodes
81
+ - `to_sqlalchemy_filter()` — compile FilterExpression to SQLAlchemy WHERE clauses
82
+ - `to_django_q()` — compile FilterExpression to Django ORM Q objects
83
+ - `to_raw_sql()` — compile FilterExpression to parameterized raw SQL
84
+ - Environment conditions flagged as unpushable in `FilterResult.unpushable_conditions`
85
+ - Deny-override combiner produces `AND NOT` clauses for DENY policies
86
+ - Cross-reference conditions resolved to literal values at compile time
87
+ - Performance benchmarks: simple RBAC <500μs, ABAC <2000μs
88
+ - 44 new tests (237 total) covering filtering AST, compilers, and benchmarks
89
+
90
+ ## [0.3.0] - 2026-03-06
91
+
92
+ ### Added
93
+
94
+ - `pyauthz_fastapi` package — first-class FastAPI authorization integration
95
+ - `PyAuthzMiddleware` — ASGI middleware for request-level subject resolution
96
+ - `@requires()` decorator for route-level authorization checks
97
+ - `setup_pyauthz()` convenience function (adds middleware + Denied exception handler)
98
+ - `get_subject()`, `get_decision()`, `get_pyauthz()` FastAPI dependency injection helpers
99
+ - Subject resolver protocol: user-provided async callable `(Request) -> Subject`
100
+ - Resource resolver protocol: user-provided callable for custom resource resolution
101
+ - Denied exception automatically mapped to 403 JSON response with structured error body
102
+ - 9 FastAPI integration tests (193 total)
103
+
104
+ ## [0.2.0] - 2026-03-06
105
+
106
+ ### Added
107
+
108
+ - Role hierarchy with parent inheritance — roles can list `parents` to inherit permissions
109
+ - Cycle detection at `add_role()` time — circular role hierarchies are caught immediately, never at evaluation time
110
+ - Full ABAC condition evaluation with 10 operators: ==, !=, >, <, >=, <=, in, not_in, contains, matches (regex)
111
+ - Dot-notation field resolution for deep attribute access (`subject.attributes.department`, `resource.attributes.classification`)
112
+ - Cross-reference conditions: `Condition(field="resource.owner_id", op="==", value="subject.id")`
113
+ - Fail-safe condition evaluation: missing attributes produce DENY with descriptive reasoning, never crash
114
+ - `allow-override` combiner: any ALLOW wins over all DENYs
115
+ - `priority-first` combiner: highest-priority applicable policy wins
116
+ - `PolicyWatcher` for YAML hot-reload — polls for file changes and auto-reloads policies
117
+ - 75 new tests (184 total) covering ABAC conditions, role hierarchy, new combiners, and the file watcher
118
+
119
+ ## [0.1.0] - 2026-03-06
120
+
121
+ ### Added
122
+
123
+ - Core authorization engine (`PyAuthz`) with sync and async APIs
124
+ - Pydantic data models: `Subject`, `Resource`, `Role`, `Permission`, `Policy`, `Condition`, `Decision`, `AccessRequest`
125
+ - Permission string parsing with wildcard support (`*:*`, `documents:*`, `*:read`)
126
+ - Action shorthand: `"write"` auto-expands to `"documents:write"` via `resource.type`
127
+ - Flat RBAC: roles with permissions, subjects with roles
128
+ - Auto-default policy: allow if role has matching permission (no explicit policies needed)
129
+ - `authorize()` / `aauthorize()` — returns `Decision` on allow, raises `Denied` on deny
130
+ - `is_allowed()` / `ais_allowed()` — returns `bool`, never raises
131
+ - `authorize_bulk()` / `aauthorize_bulk()` — batch authorization checks
132
+ - Policy combination algorithms: deny-override (default), first-match
133
+ - In-memory storage backend with thread-safe locking
134
+ - YAML policy loading with `version: 1` schema validation
135
+ - Structured audit logging with `JsonExporter`
136
+ - Error hierarchy: `Denied`, `SchemaError`, `ConfigError`, `PolicyVersionError`
137
+ - Role and policy management: `add_role()`, `remove_role()`, `replace_roles()`, `add_policy()`, `remove_policy()`, `replace_policies()`
138
+ - Environment context: per-request kwargs and `set_environment()` defaults
139
+ - `py.typed` marker for PEP 561 compliance
140
+ - 109 tests with >95% coverage including Hypothesis property-based tests
@@ -0,0 +1,123 @@
1
+ # Contributing to PyAuthz
2
+
3
+ Thank you for your interest in contributing to PyAuthz! This guide covers everything you need to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ # Clone the repo
9
+ git clone https://github.com/marci/pyauthz.git
10
+ cd pyauthz
11
+
12
+ # Create a virtual environment
13
+ python -m venv .venv
14
+ .venv/Scripts/activate # Windows
15
+ # source .venv/bin/activate # macOS/Linux
16
+
17
+ # Install in editable mode with dev dependencies
18
+ pip install -e ".[dev]"
19
+
20
+ # Install framework extras if working on integrations
21
+ pip install fastapi httpx flask strawberry-graphql
22
+ ```
23
+
24
+ ## Running Tests
25
+
26
+ ```bash
27
+ # Run all tests
28
+ pytest
29
+
30
+ # Run specific test file
31
+ pytest tests/test_engine.py
32
+
33
+ # Run with coverage
34
+ pytest --cov=pyauthz --cov-report=term-missing
35
+
36
+ # Run only core tests (faster)
37
+ pytest tests/
38
+
39
+ # Run framework integration tests
40
+ pytest tests_fastapi/ tests_flask/
41
+ ```
42
+
43
+ ## Code Quality
44
+
45
+ We use `ruff` for linting and formatting:
46
+
47
+ ```bash
48
+ # Lint
49
+ ruff check .
50
+
51
+ # Auto-fix lint issues
52
+ ruff check --fix .
53
+
54
+ # Format
55
+ ruff format .
56
+ ```
57
+
58
+ All code must pass `ruff check` and `ruff format --check` before merge.
59
+
60
+ ## Project Structure
61
+
62
+ ```
63
+ pyauthz/
64
+ ├── core/ # Engine, decision, errors, conditions, cache, etc.
65
+ ├── models/ # Pydantic data models (Subject, Resource, Role, etc.)
66
+ ├── policies/ # Combiner algorithms
67
+ ├── storage/ # Storage backends (memory, YAML loader, watcher)
68
+ ├── filtering/ # Query filter compilation (AST, SQL, Django)
69
+ ├── audit/ # Audit logging
70
+ ├── testing/ # Testing utilities (AuthScenario, assertions)
71
+ ├── cli/ # CLI tool (pyauthz check, simulate)
72
+ ├── middleware/ # Shared middleware types
73
+ pyauthz_fastapi/ # FastAPI integration package
74
+ pyauthz_flask/ # Flask integration package
75
+ pyauthz_django/ # Django integration package
76
+ pyauthz_strawberry/ # Strawberry GraphQL integration package
77
+ tests/ # Core test suite
78
+ tests_fastapi/ # FastAPI integration tests
79
+ tests_flask/ # Flask integration tests
80
+ ```
81
+
82
+ ## Architecture
83
+
84
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for a detailed overview of how the engine works, the evaluation pipeline, and design decisions.
85
+
86
+ ## Making Changes
87
+
88
+ 1. **Read the code first.** Understand the existing patterns before modifying.
89
+ 2. **Write tests.** Every new feature or bug fix should include tests. Tests go in `tests/test_<module>.py`.
90
+ 3. **Keep it simple.** PyAuthz values simplicity over cleverness. If there's a simple way to do it, prefer that.
91
+ 4. **Don't break the public API.** The `pyauthz.__init__` exports define the public API. Changes to these are breaking changes.
92
+
93
+ ## Commit Messages
94
+
95
+ - Explain *why*, not just *what*
96
+ - One logical change per commit
97
+ - Use imperative mood: "Add cache invalidation" not "Added cache invalidation"
98
+
99
+ ## Adding a New Combiner
100
+
101
+ 1. Create the combiner function in `pyauthz/policies/combiners.py`
102
+ 2. Register it in the `COMBINERS` dict
103
+ 3. Add tests in `tests/test_combiners.py`
104
+ 4. Update the engine docstring and README
105
+
106
+ ## Adding a New Framework Integration
107
+
108
+ 1. Create a new namespace package at the project root: `pyauthz_<framework>/`
109
+ 2. Follow the patterns from `pyauthz_fastapi/` or `pyauthz_flask/`
110
+ 3. Add tests in `tests_<framework>/`
111
+ 4. Add the framework to dev dependencies in `pyproject.toml`
112
+
113
+ ## Adding a New Storage Backend
114
+
115
+ 1. Implement the `StorageBackend` protocol from `pyauthz/storage/base.py`
116
+ 2. Add tests that verify thread safety and correctness
117
+ 3. Add the dependency to optional extras in `pyproject.toml`
118
+
119
+ ## Reporting Issues
120
+
121
+ - Use GitHub Issues for bugs and feature requests
122
+ - Include a minimal reproduction case for bugs
123
+ - Include Python version, OS, and PyAuthz version
pyauthz-0.9.0/LICENSE ADDED
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by the Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding any notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 Marci
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.