auth 2.5.0__tar.gz → 2.5.1__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 (57) hide show
  1. {auth-2.5.0 → auth-2.5.1}/PKG-INFO +5 -4
  2. {auth-2.5.0 → auth-2.5.1}/README.md +4 -3
  3. {auth-2.5.0 → auth-2.5.1}/auth/docs_page.py +13 -7
  4. {auth-2.5.0 → auth-2.5.1}/auth/routes.py +7 -1
  5. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/PKG-INFO +5 -4
  6. {auth-2.5.0 → auth-2.5.1}/pyproject.toml +1 -1
  7. {auth-2.5.0 → auth-2.5.1}/tests/test_strict_users.py +13 -3
  8. {auth-2.5.0 → auth-2.5.1}/LICENSE +0 -0
  9. {auth-2.5.0 → auth-2.5.1}/auth/__init__.py +0 -0
  10. {auth-2.5.0 → auth-2.5.1}/auth/api_keys.py +0 -0
  11. {auth-2.5.0 → auth-2.5.1}/auth/audit.py +0 -0
  12. {auth-2.5.0 → auth-2.5.1}/auth/circuit_breaker.py +0 -0
  13. {auth-2.5.0 → auth-2.5.1}/auth/client.py +0 -0
  14. {auth-2.5.0 → auth-2.5.1}/auth/cmd/__init__.py +0 -0
  15. {auth-2.5.0 → auth-2.5.1}/auth/cmd/server.py +0 -0
  16. {auth-2.5.0 → auth-2.5.1}/auth/config.py +0 -0
  17. {auth-2.5.0 → auth-2.5.1}/auth/core/REST/__init__.py +0 -0
  18. {auth-2.5.0 → auth-2.5.1}/auth/core/REST/client.py +0 -0
  19. {auth-2.5.0 → auth-2.5.1}/auth/core/__init__.py +0 -0
  20. {auth-2.5.0 → auth-2.5.1}/auth/core/models/__init__.py +0 -0
  21. {auth-2.5.0 → auth-2.5.1}/auth/database.py +0 -0
  22. {auth-2.5.0 → auth-2.5.1}/auth/decorators.py +0 -0
  23. {auth-2.5.0 → auth-2.5.1}/auth/encryption.py +0 -0
  24. {auth-2.5.0 → auth-2.5.1}/auth/logging_config.py +0 -0
  25. {auth-2.5.0 → auth-2.5.1}/auth/main.py +0 -0
  26. {auth-2.5.0 → auth-2.5.1}/auth/models/sql.py +0 -0
  27. {auth-2.5.0 → auth-2.5.1}/auth/response_format.py +0 -0
  28. {auth-2.5.0 → auth-2.5.1}/auth/sanitizer.py +0 -0
  29. {auth-2.5.0 → auth-2.5.1}/auth/server.py +0 -0
  30. {auth-2.5.0 → auth-2.5.1}/auth/services/service.py +0 -0
  31. {auth-2.5.0 → auth-2.5.1}/auth/validation.py +0 -0
  32. {auth-2.5.0 → auth-2.5.1}/auth/workflow_checker.py +0 -0
  33. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/SOURCES.txt +0 -0
  34. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/dependency_links.txt +0 -0
  35. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/entry_points.txt +0 -0
  36. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/requires.txt +0 -0
  37. {auth-2.5.0 → auth-2.5.1}/auth.egg-info/top_level.txt +0 -0
  38. {auth-2.5.0 → auth-2.5.1}/setup.cfg +0 -0
  39. {auth-2.5.0 → auth-2.5.1}/tests/test_api_keys.py +0 -0
  40. {auth-2.5.0 → auth-2.5.1}/tests/test_api_keys_encryption.py +0 -0
  41. {auth-2.5.0 → auth-2.5.1}/tests/test_audit_integrity.py +0 -0
  42. {auth-2.5.0 → auth-2.5.1}/tests/test_client.py +0 -0
  43. {auth-2.5.0 → auth-2.5.1}/tests/test_client_rest.py +0 -0
  44. {auth-2.5.0 → auth-2.5.1}/tests/test_cmd_server.py +0 -0
  45. {auth-2.5.0 → auth-2.5.1}/tests/test_config.py +0 -0
  46. {auth-2.5.0 → auth-2.5.1}/tests/test_correctness_hardening.py +0 -0
  47. {auth-2.5.0 → auth-2.5.1}/tests/test_docs_page.py +0 -0
  48. {auth-2.5.0 → auth-2.5.1}/tests/test_encryption.py +0 -0
  49. {auth-2.5.0 → auth-2.5.1}/tests/test_encryption_integration.py +0 -0
  50. {auth-2.5.0 → auth-2.5.1}/tests/test_flask.py +0 -0
  51. {auth-2.5.0 → auth-2.5.1}/tests/test_key_rotation.py +0 -0
  52. {auth-2.5.0 → auth-2.5.1}/tests/test_log_redaction.py +0 -0
  53. {auth-2.5.0 → auth-2.5.1}/tests/test_phase_a_hardening.py +0 -0
  54. {auth-2.5.0 → auth-2.5.1}/tests/test_reencryption.py +0 -0
  55. {auth-2.5.0 → auth-2.5.1}/tests/test_routes_errors.py +0 -0
  56. {auth-2.5.0 → auth-2.5.1}/tests/test_server.py +0 -0
  57. {auth-2.5.0 → auth-2.5.1}/tests/test_service_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: auth
3
- Version: 2.5.0
3
+ Version: 2.5.1
4
4
  Summary: Authorization for humans
5
5
  Author-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
6
6
  License-Expression: MIT
@@ -124,9 +124,10 @@ with Client(api_key=KEY, service_url="https://auth.rodmena.app") as c:
124
124
  key backs is scheduled for decommission. The opt-in phase is live (2.5.0):
125
125
  `PUT /api/settings {"strict_users": true}` makes keyless users answer
126
126
  negatively (`user_not_key_backed`), and `POST /api/apikeys/check_permission`
127
- does validate + permission in one round trip. **3.0.0 makes key-backed users
128
- mandatory** — only after every consuming platform confirms.
129
- Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
127
+ does validate + permission in one round trip. **3.0.0 makes strict identity
128
+ the default** (the audited per-tenant opt-out survives for validated
129
+ machine-subject architectures) — and ships only after every consuming
130
+ platform confirms. Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
130
131
 
131
132
  ## Documentation
132
133
 
@@ -68,9 +68,10 @@ with Client(api_key=KEY, service_url="https://auth.rodmena.app") as c:
68
68
  key backs is scheduled for decommission. The opt-in phase is live (2.5.0):
69
69
  `PUT /api/settings {"strict_users": true}` makes keyless users answer
70
70
  negatively (`user_not_key_backed`), and `POST /api/apikeys/check_permission`
71
- does validate + permission in one round trip. **3.0.0 makes key-backed users
72
- mandatory** — only after every consuming platform confirms.
73
- Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
71
+ does validate + permission in one round trip. **3.0.0 makes strict identity
72
+ the default** (the audited per-tenant opt-out survives for validated
73
+ machine-subject architectures) — and ships only after every consuming
74
+ platform confirms. Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
74
75
 
75
76
  ## Documentation
76
77
 
@@ -147,13 +147,19 @@ secrets keep validating afterwards.
147
147
  That is being retired. **Since 2.5.0 the opt-in phase is live**: enable strict
148
148
  mode with `PUT /api/settings` `{{"strict_users": true}}` and authorization
149
149
  decisions about users with no active API key answer negatively (same response
150
- shapes, additive reason `user_not_key_backed`); membership adds for key-less
151
- users return `{{"result": false}}` — create the key first, then grant roles.
152
- Strict mode never blocks key issuance or any delete/revoke path. 3.0.0 makes
153
- strict identity the default, and ships only after every consuming platform has
154
- confirmed readiness. Migrate now: issue keys to your users, derive the user
155
- from `/api/apikeys/validate` — or do both steps in one round trip with
156
- `POST /api/apikeys/check_permission`.
150
+ shapes, additive reason `user_not_key_backed`). Gated decisions: has_permission,
151
+ the membership check, user_permissions (answers `count: 0` + reason), workflow
152
+ can_run. NOT gated: user_roles, members and every other listing. Membership
153
+ adds for key-less subjects answer **409** `{{"result": false, "reason":
154
+ "user_not_key_backed"}}` (since 2.5.1 — a refused grant must not look like
155
+ success; check `result` on writes regardless). Create the key first, then grant
156
+ roles: key creation committing before the grant is a contract, with no eventual
157
+ consistency in between. Strict mode never blocks key issuance or any
158
+ delete/revoke path. 3.0.0 makes strict identity the default — the audited
159
+ per-tenant opt-out survives for platforms that authenticate their own callers —
160
+ and ships only after every consuming platform has confirmed readiness. Migrate
161
+ now: issue keys to your users, derive the user from `/api/apikeys/validate` —
162
+ or do both steps in one round trip with `POST /api/apikeys/check_permission`.
157
163
 
158
164
  ## 4. Endpoints
159
165
 
@@ -194,7 +194,13 @@ def register_routes(app):
194
194
  if not result:
195
195
  reason = _strict_reason(auth_service, user)
196
196
  if reason:
197
- return jsonify({"result": result, "reason": reason})
197
+ # 409, not 200-with-false: two consumers independently showed
198
+ # that a refused grant answering 200 gets written past
199
+ # (raise_for_status passes, result goes unchecked) and turns
200
+ # strict mode into silent dead-key provisioning. Strict mode is
201
+ # opt-in, so the shape may differ there; the documented
202
+ # missing-role 200-false below is untouched.
203
+ return jsonify({"result": False, "reason": reason}), 409
198
204
 
199
205
  return jsonify({"result": result})
200
206
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: auth
3
- Version: 2.5.0
3
+ Version: 2.5.1
4
4
  Summary: Authorization for humans
5
5
  Author-email: Farshid Ashouri <farsheed.ashouri@gmail.com>
6
6
  License-Expression: MIT
@@ -124,9 +124,10 @@ with Client(api_key=KEY, service_url="https://auth.rodmena.app") as c:
124
124
  key backs is scheduled for decommission. The opt-in phase is live (2.5.0):
125
125
  `PUT /api/settings {"strict_users": true}` makes keyless users answer
126
126
  negatively (`user_not_key_backed`), and `POST /api/apikeys/check_permission`
127
- does validate + permission in one round trip. **3.0.0 makes key-backed users
128
- mandatory** — only after every consuming platform confirms.
129
- Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
127
+ does validate + permission in one round trip. **3.0.0 makes strict identity
128
+ the default** (the audited per-tenant opt-out survives for validated
129
+ machine-subject architectures) — and ships only after every consuming
130
+ platform confirms. Details: [docs/DEPRECATIONS.md](docs/DEPRECATIONS.md).
130
131
 
131
132
  ## Documentation
132
133
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "auth"
7
- version = "2.5.0"
7
+ version = "2.5.1"
8
8
  description = "Authorization for humans"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -134,9 +134,19 @@ def test_strict_blocks_membership_add_but_never_removal(client):
134
134
  _seed_rbac(client, key, user="bob") # bob is a member while strict is off
135
135
  client.put("/api/settings", headers=h, json={"strict_users": True})
136
136
 
137
- # Adds for keyless users are refused with the 200-false convention.
138
- add = client.post("/api/membership/carol/engineers", headers=h).get_json()
139
- assert add == {"result": False, "reason": "user_not_key_backed"}
137
+ # Adds for keyless users are refused with an unmissable 409 (a 200 here
138
+ # was shown by two consumers to be silently written past).
139
+ add = client.post("/api/membership/carol/engineers", headers=h)
140
+ assert add.status_code == 409
141
+ assert add.get_json() == {"result": False, "reason": "user_not_key_backed"}
142
+ # The documented missing-role no-op keeps its 200-false shape even in
143
+ # strict tenants — only the key-less refusal is a 409.
144
+ ghost_role = client.post("/api/membership/carol/ghosts", headers=h)
145
+ assert ghost_role.status_code == 409 # carol is still key-less: strict wins
146
+ client.post("/api/apikeys/user/dave", headers=h)
147
+ keyed_ghost = client.post("/api/membership/dave/ghosts", headers=h)
148
+ assert keyed_ghost.status_code == 200
149
+ assert keyed_ghost.get_json() == {"result": False}
140
150
 
141
151
  # Removal is NEVER strict-gated: revocation must always work.
142
152
  rm = client.delete("/api/membership/bob/engineers", headers=h).get_json()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes