Files
qdrant/tests/consensus_tests/auth_tests/test_jwt_validation.py
Andrey Vasnetsov dba26be155 Merge pull request #7835
* alt api key

* rm typo

* fix key length check logic

* differnt dirs for tests

* fix tests

* rm unused
2026-01-02 13:18:53 +01:00

240 lines
6.9 KiB
Python

import pytest
import requests
from consensus_tests import fixtures
from .utils import API_KEY_HEADERS, READ_ONLY_API_KEY, REST_URI, SECRET, encode_jwt, ALT_SECRET
COLL_NAME = "jwt_test_collection"
OTHER_COLL_NAME = "jwt_other_test_collection"
@pytest.fixture(scope="module")
def collection_readonly_token():
return encode_jwt(
{
"access": [
{"collection": COLL_NAME, "access": "r"}
]
},
SECRET,
)
@pytest.fixture(scope="module", autouse=True)
def setup(jwt_cluster):
peer_api_uris, peer_dirs, bootstrap_uri = jwt_cluster
fixtures.create_collection(
REST_URI,
collection=COLL_NAME,
headers=API_KEY_HEADERS,
)
fixtures.upsert_random_points(REST_URI, 100, COLL_NAME, headers=API_KEY_HEADERS)
yield peer_api_uris, peer_dirs, bootstrap_uri
fixtures.drop_collection(REST_URI, COLL_NAME, headers=API_KEY_HEADERS)
def create_validation_collection(collection: str, timeout=10):
res = requests.put(
f"{REST_URI}/collections/{collection}?timeout={timeout}",
json={},
headers=API_KEY_HEADERS,
)
res.raise_for_status()
def scroll_with_token(collection: str, token: str) -> requests.Response:
res = requests.post(
f"{REST_URI}/collections/{collection}/points/scroll",
json={
"limit": 10,
},
headers={"Authorization": f"Bearer {token}"},
)
res.raise_for_status()
return res
def test_value_exists_claim():
value_exists_claim_check(SECRET)
value_exists_claim_check(ALT_SECRET)
def value_exists_claim_check(secret):
validation_collection = "jwt_validation_collection"
key = "tokenId"
value = "token_42"
claims = {
"value_exists": {
"collection": validation_collection,
"matches": [{"key": key, "value": value}],
},
}
token = encode_jwt(claims, secret)
# Check that token does not work with unexisting collection
with pytest.raises(requests.HTTPError):
scroll_with_token(COLL_NAME, token)
# Create collection
create_validation_collection(validation_collection)
# Check it still does not work
with pytest.raises(requests.HTTPError):
res = scroll_with_token(COLL_NAME, token)
# Upload validation point
requests.put(
f"{REST_URI}/collections/{validation_collection}/points?wait=true",
json={
"points": [
{
"id": 42,
"vectors": {},
"payload": {key: value},
}
]
},
headers=API_KEY_HEADERS,
).raise_for_status()
# Check that token works now
res = scroll_with_token(COLL_NAME, token)
assert len(res.json()["result"]["points"]) == 10
# Delete validation point
res = requests.post(
f"{REST_URI}/collections/{validation_collection}/points/delete?wait=true",
json={"points": [42]},
headers=API_KEY_HEADERS,
).raise_for_status()
# Check it does not work now
with pytest.raises(requests.HTTPError):
scroll_with_token(COLL_NAME, token)
fixtures.drop_collection(REST_URI, validation_collection, headers=API_KEY_HEADERS)
def test_recommend_collection_lookup(collection_readonly_token):
# delete other collection if exists
fixtures.drop_collection(REST_URI, OTHER_COLL_NAME, headers=API_KEY_HEADERS)
# create other collection
fixtures.create_collection(
REST_URI,
collection=OTHER_COLL_NAME,
headers=API_KEY_HEADERS,
)
# populate collection with points
fixtures.upsert_random_points(REST_URI, 100, COLL_NAME, headers=API_KEY_HEADERS)
# recommending points from same collection should work
res = requests.post(
f"{REST_URI}/collections/{COLL_NAME}/points/recommend",
json={"positive": [1], "limit": 10},
headers={"Authorization": f"Bearer {collection_readonly_token}"},
)
assert res.status_code == 200, res.json()
# recommending points from other collection should fail
res = requests.post(
f"{REST_URI}/collections/{COLL_NAME}/points/recommend",
json={
"positive": [1],
"limit": 10,
"lookup_from": {
"collection": OTHER_COLL_NAME,
}
},
headers={"Authorization": f"Bearer {collection_readonly_token}"},
)
assert res.status_code == 403, res.json()
assert (
res.json()["status"]["error"]
== f"Forbidden: Access to collection {OTHER_COLL_NAME} is required"
)
def test_query_recommendation_collection_lookup(collection_readonly_token):
# delete other collection if exists
fixtures.drop_collection(REST_URI, OTHER_COLL_NAME, headers=API_KEY_HEADERS)
# create other collection
fixtures.create_collection(
REST_URI,
collection=OTHER_COLL_NAME,
headers=API_KEY_HEADERS,
)
# populate collection with points
fixtures.upsert_random_points(REST_URI, 100, COLL_NAME, headers=API_KEY_HEADERS)
# query recommendation with points from same collection should work
res = requests.post(
f"{REST_URI}/collections/{COLL_NAME}/points/query",
json={
"query": {
"recommend": {
"positive": [1],
},
"limit": 10,
}
},
headers={"Authorization": f"Bearer {collection_readonly_token}"},
)
assert res.status_code == 200, res.json()
# query recommending with points from other collection should fail
res = requests.post(
f"{REST_URI}/collections/{COLL_NAME}/points/query",
json={
"recommend": {
"positive": [1],
},
"limit": 10,
"lookup_from": {
"collection": OTHER_COLL_NAME,
}
},
headers={"Authorization": f"Bearer {collection_readonly_token}"},
)
assert res.status_code == 403, res.json()
assert (
res.json()["status"]["error"]
== f"Forbidden: Access to collection {OTHER_COLL_NAME} is required"
)
# query nested recommendation with points from other collection should fail
res = requests.post(
f"{REST_URI}/collections/{COLL_NAME}/points/query",
json={
"prefetch": [
{
"query": {
"recommend": {
"positive": [1],
}
},
"lookup_from": {
"collection": OTHER_COLL_NAME,
}
}
],
"limit": 10,
},
headers={"Authorization": f"Bearer {collection_readonly_token}"},
)
assert res.status_code == 403, res.json()
assert (
res.json()["status"]["error"]
== f"Forbidden: Access to collection {OTHER_COLL_NAME} is required"
)