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.
- {auth-2.5.0 → auth-2.5.1}/PKG-INFO +5 -4
- {auth-2.5.0 → auth-2.5.1}/README.md +4 -3
- {auth-2.5.0 → auth-2.5.1}/auth/docs_page.py +13 -7
- {auth-2.5.0 → auth-2.5.1}/auth/routes.py +7 -1
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/PKG-INFO +5 -4
- {auth-2.5.0 → auth-2.5.1}/pyproject.toml +1 -1
- {auth-2.5.0 → auth-2.5.1}/tests/test_strict_users.py +13 -3
- {auth-2.5.0 → auth-2.5.1}/LICENSE +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/__init__.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/api_keys.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/audit.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/circuit_breaker.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/client.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/cmd/__init__.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/cmd/server.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/config.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/core/REST/__init__.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/core/REST/client.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/core/__init__.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/core/models/__init__.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/database.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/decorators.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/encryption.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/logging_config.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/main.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/models/sql.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/response_format.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/sanitizer.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/server.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/services/service.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/validation.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth/workflow_checker.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/SOURCES.txt +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/dependency_links.txt +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/entry_points.txt +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/requires.txt +0 -0
- {auth-2.5.0 → auth-2.5.1}/auth.egg-info/top_level.txt +0 -0
- {auth-2.5.0 → auth-2.5.1}/setup.cfg +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_api_keys.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_api_keys_encryption.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_audit_integrity.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_client.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_client_rest.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_cmd_server.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_config.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_correctness_hardening.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_docs_page.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_encryption.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_encryption_integration.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_flask.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_key_rotation.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_log_redaction.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_phase_a_hardening.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_reencryption.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_routes_errors.py +0 -0
- {auth-2.5.0 → auth-2.5.1}/tests/test_server.py +0 -0
- {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.
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
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`)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
|
@@ -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
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|