test(cloud-auth): cover authorization failures
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
- [x] 4.2 Add public scopes for task submission/read, pool read, plugin read, and plugin administration and enforce them on every `/v1` route.
|
||||
- [x] 4.3 Add host principals bound to one `host_id` and reject cross-host heartbeat, claim, renewal, or result operations.
|
||||
- [x] 4.4 Make missing production credentials a startup/readiness failure and permit anonymous mode only through the explicit non-production override.
|
||||
- [ ] 4.5 Add authentication tests covering invalid tokens, missing scopes, host impersonation, plugin administration, and secret redaction.
|
||||
- [x] 4.5 Add authentication tests covering invalid tokens, missing scopes, host impersonation, plugin administration, and secret redaction.
|
||||
|
||||
## 5. Host Agent Internal API
|
||||
|
||||
|
||||
@@ -147,3 +147,18 @@ def test_public_principal_cannot_act_as_host() -> None:
|
||||
assert principal is not None
|
||||
with pytest.raises(HostPrincipalRequiredError):
|
||||
principal.require_host("host-a")
|
||||
|
||||
|
||||
def test_authentication_failure_does_not_log_bearer_secret(caplog) -> None:
|
||||
provider = ConfiguredBearerAuthProvider(
|
||||
[BearerCredential(principal_id="integrator", token="valid-secret")]
|
||||
)
|
||||
|
||||
assert (
|
||||
provider.authenticate(
|
||||
_Request(headers={"authorization": "Bearer invalid-secret"})
|
||||
)
|
||||
is None
|
||||
)
|
||||
assert "invalid-secret" not in caplog.text
|
||||
assert "valid-secret" not in caplog.text
|
||||
|
||||
@@ -287,3 +287,56 @@ def test_every_public_route_enforces_its_scope(
|
||||
assert unauthorized.headers["www-authenticate"] == "Bearer"
|
||||
assert forbidden.status_code == 403
|
||||
assert authorized.status_code not in {401, 403}
|
||||
|
||||
|
||||
def test_plugin_admin_scope_is_checked_before_registration(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
provider = ConfiguredBearerAuthProvider(
|
||||
[
|
||||
BearerCredential(
|
||||
principal_id="plugin-reader",
|
||||
token="reader-token",
|
||||
scopes=frozenset({"plugins:read"}),
|
||||
)
|
||||
]
|
||||
)
|
||||
app, _, _, plugin_registry = _build_app(tmp_path, auth_provider=provider)
|
||||
registration_called = False
|
||||
|
||||
def fail_if_called(_manifest) -> None:
|
||||
nonlocal registration_called
|
||||
registration_called = True
|
||||
raise AssertionError("plugin registration must not run before authorization")
|
||||
|
||||
monkeypatch.setattr(plugin_registry, "register", fail_if_called)
|
||||
response = _client_for(app).post(
|
||||
"/v1/plugins",
|
||||
headers={"Authorization": "Bearer reader-token"},
|
||||
json={
|
||||
"name": "forbidden-plugin",
|
||||
"version": "1.0.0",
|
||||
"entry_point_kind": "driver",
|
||||
"target": "secret.module:builder",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert registration_called is False
|
||||
assert "secret.module" not in response.text
|
||||
|
||||
|
||||
def test_invalid_token_is_rejected_by_public_router(tmp_path) -> None:
|
||||
provider = ConfiguredBearerAuthProvider(
|
||||
[BearerCredential(principal_id="integrator", token="valid-token")]
|
||||
)
|
||||
app, _, _, _ = _build_app(tmp_path, auth_provider=provider)
|
||||
|
||||
response = _client_for(app).get(
|
||||
"/v1/devices",
|
||||
headers={"Authorization": "Bearer invalid-token"},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.headers["www-authenticate"] == "Bearer"
|
||||
|
||||
Reference in New Issue
Block a user