client.roles.has_permissions(...) returns True for permissions the role does not hold.
Cause
__has_permission in weaviate/rbac/executor.py checks only the status code:
def resp(res: Response) -> bool:
return res.status_code == 200
POST /v1/authz/roles/{id}/has-permission answers 200 whenever the check runs. The answer is the boolean body ("type": "boolean" in the OpenAPI spec). The client ignores the body, so every permission on an existing role reads as held. A missing role (404) still returns False.
The code is the same on main (38f30c1) and in 4.23.1.
Reproduction
Client 4.23.1, server 1.38.19 with RBAC enabled:
import httpx
import weaviate
from weaviate.classes.init import Auth
from weaviate.classes.rbac import Permissions
client = weaviate.connect_to_local(port=8099, grpc_port=50099, auth_credentials=Auth.api_key("admin-key"))
client.roles.create(role_name="reader", permissions=Permissions.collections(collection="Books", read_config=True))
held = Permissions.collections(collection="Books", read_config=True)
not_held = Permissions.collections(collection="Books", delete_collection=True)
print(client.roles.has_permissions(permissions=held, role="reader"))
print(client.roles.has_permissions(permissions=not_held, role="reader"))
r = httpx.post(
"http://localhost:8099/v1/authz/roles/reader/has-permission",
headers={"Authorization": "Bearer admin-key"},
json={"action": "delete_collections", "collections": {"collection": "Books"}},
)
print(r.status_code, r.text.strip())
Output:
The second line should be False. The server returns false for the same check.
Impact
Code that gates on has_permissions treats a missing permission as granted. Code that polls it to wait for a grant to propagate returns at once. The e2e suite in weaviate-e2e-tests has such a helper (wait_for_permission), and it has never waited.
The async client has the same bug, because both paths share __has_permission.
Suggested fix
Read the body on a 200:
def resp(res: Response) -> bool:
return res.status_code == 200 and res.json() is True
integration/test_rbac.py only asserts that has_permissions is true for held permissions. A case that asserts False for a permission the role lacks would have caught this.
client.roles.has_permissions(...)returnsTruefor permissions the role does not hold.Cause
__has_permissioninweaviate/rbac/executor.pychecks only the status code:POST /v1/authz/roles/{id}/has-permissionanswers 200 whenever the check runs. The answer is the boolean body ("type": "boolean"in the OpenAPI spec). The client ignores the body, so every permission on an existing role reads as held. A missing role (404) still returnsFalse.The code is the same on
main(38f30c1) and in 4.23.1.Reproduction
Client 4.23.1, server 1.38.19 with RBAC enabled:
Output:
The second line should be
False. The server returnsfalsefor the same check.Impact
Code that gates on
has_permissionstreats a missing permission as granted. Code that polls it to wait for a grant to propagate returns at once. The e2e suite in weaviate-e2e-tests has such a helper (wait_for_permission), and it has never waited.The async client has the same bug, because both paths share
__has_permission.Suggested fix
Read the body on a 200:
integration/test_rbac.pyonly asserts thathas_permissionsis true for held permissions. A case that assertsFalsefor a permission the role lacks would have caught this.