Skip to content

Add uid to the generateTenantToken method #484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion meilisearch/client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import re
import base64
import hashlib
import hmac
import json
import datetime
from urllib import parse
from typing import Any, Dict, List, Optional, Union
from xmlrpc.client import Boolean
from meilisearch.index import Index
from meilisearch.config import Config
from meilisearch.task import get_task, get_tasks, wait_for_task
Expand Down Expand Up @@ -473,6 +475,7 @@ def wait_for_task(

def generate_tenant_token(
self,
api_key_uid: str,
search_rules: Union[Dict[str, Any], List[str]],
*,
expires_at: Optional[datetime.datetime] = None,
Expand All @@ -482,6 +485,8 @@ def generate_tenant_token(

Parameters
----------
api_key_uid:
The uid of the API key used as issuer of the token.
search_rules:
A Dictionary or list of string which contains the rules to be enforced at search time for all or specific
accessible indexes for the signing API Key.
Expand All @@ -501,6 +506,8 @@ def generate_tenant_token(
# Validate all fields
if api_key == '' or api_key is None and self.config.api_key is None:
raise Exception('An api key is required in the client or should be passed as an argument.')
if api_key_uid == '' or api_key_uid is None or self._valid_uuid(api_key_uid) is False:
raise Exception('An uid is required and must comply to the uuid4 format.')
if not search_rules or search_rules == ['']:
raise Exception('The search_rules field is mandatory and should be defined.')
if expires_at and expires_at < datetime.datetime.utcnow():
Expand All @@ -516,7 +523,7 @@ def generate_tenant_token(

# Add the required fields to the payload
payload = {
'apiKeyPrefix': api_key[0:8],
'apiKeyUid': api_key_uid,
'searchRules': search_rules,
'exp': int(datetime.datetime.timestamp(expires_at)) if expires_at is not None else None
}
Expand All @@ -542,3 +549,11 @@ def _base64url_encode(
data: bytes
) -> str:
return base64.urlsafe_b64encode(data).decode('utf-8').replace('=','')

@staticmethod
def _valid_uuid(
uuid: str
) -> bool:
uuid4hex = re.compile(r'^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}', re.I)
match = uuid4hex.match(uuid)
return bool(match)
24 changes: 15 additions & 9 deletions tests/client/test_client_tenant_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_generate_tenant_token_with_search_rules(get_private_key, index_with_doc
index_with_documents()
client = meilisearch.Client(BASE_URL, get_private_key['key'])

token = client.generate_tenant_token(search_rules=["*"])
token = client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=["*"])

token_client = meilisearch.Client(BASE_URL, token)
response = token_client.index('indexUID').search('', {
Expand All @@ -28,7 +28,7 @@ def test_generate_tenant_token_with_search_rules_on_one_index(get_private_key, e
empty_index('tenant_token')
client = meilisearch.Client(BASE_URL, get_private_key['key'])

token = client.generate_tenant_token(search_rules=['indexUID'])
token = client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=['indexUID'])

token_client = meilisearch.Client(BASE_URL, token)
response = token_client.index('indexUID').search('')
Expand All @@ -40,7 +40,7 @@ def test_generate_tenant_token_with_search_rules_on_one_index(get_private_key, e
def test_generate_tenant_token_with_api_key(client, get_private_key, empty_index):
"""Tests create a tenant token with search rules and an api key."""
empty_index()
token = client.generate_tenant_token(search_rules=["*"], api_key=get_private_key['key'])
token = client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=["*"], api_key=get_private_key['key'])

token_client = meilisearch.Client(BASE_URL, token)
response = token_client.index('indexUID').search('')
Expand All @@ -53,7 +53,7 @@ def test_generate_tenant_token_with_expires_at(client, get_private_key, empty_in
client = meilisearch.Client(BASE_URL, get_private_key['key'])
tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1)

token = client.generate_tenant_token(search_rules=["*"], expires_at=tomorrow)
token = client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=["*"], expires_at=tomorrow)

token_client = meilisearch.Client(BASE_URL, token)
response = token_client.index('indexUID').search('')
Expand All @@ -65,28 +65,28 @@ def test_generate_tenant_token_with_empty_search_rules_in_list(get_private_key):
client = meilisearch.Client(BASE_URL, get_private_key['key'])

with pytest.raises(Exception):
client.generate_tenant_token(search_rules=[''])
client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=[''])

def test_generate_tenant_token_without_search_rules_in_list(get_private_key):
"""Tests create a tenant token without search rules."""
client = meilisearch.Client(BASE_URL, get_private_key['key'])

with pytest.raises(Exception):
client.generate_tenant_token(search_rules=[])
client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=[])

def test_generate_tenant_token_without_search_rules_in_dict(get_private_key):
"""Tests create a tenant token without search rules."""
client = meilisearch.Client(BASE_URL, get_private_key['key'])

with pytest.raises(Exception):
client.generate_tenant_token(search_rules={})
client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules={})

def test_generate_tenant_token_with_empty_search_rules_in_dict(get_private_key):
"""Tests create a tenant token without search rules."""
client = meilisearch.Client(BASE_URL, get_private_key['key'])

with pytest.raises(Exception):
client.generate_tenant_token(search_rules={''})
client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules={''})

def test_generate_tenant_token_with_bad_expires_at(client, get_private_key):
"""Tests create a tenant token with a bad expires at."""
Expand All @@ -95,7 +95,7 @@ def test_generate_tenant_token_with_bad_expires_at(client, get_private_key):
yesterday = datetime.datetime.utcnow() + datetime.timedelta(days=-1)

with pytest.raises(Exception):
client.generate_tenant_token(search_rules=["*"], expires_at=yesterday)
client.generate_tenant_token(api_key_uid=get_private_key['uid'], search_rules=["*"], expires_at=yesterday)

def test_generate_tenant_token_with_no_api_key(client):
"""Tests create a tenant token with no api key."""
Expand All @@ -104,3 +104,9 @@ def test_generate_tenant_token_with_no_api_key(client):
with pytest.raises(Exception):
client.generate_tenant_token(search_rules=["*"])

def test_generate_tenant_token_with_no_uid(client, get_private_key):
"""Tests create a tenant token with no uid."""
client = meilisearch.Client(BASE_URL, get_private_key['key'])

with pytest.raises(Exception):
client.generate_tenant_token(api_key_uid=None, search_rules=["*"])