Skip to content

Commit

Permalink
Merge pull request #142 from wazo-platform/WP-1541-share-verified-tenant
Browse files Browse the repository at this point in the history
validate and share tenant_uuid from verify_token with autodetect

why: will remove a query to wazo-auth when Wazo-Tenant is present

Reviewed-by: Pascal Cadotte Michaud
Reviewed-by: wazo-community-zuul[bot]
  • Loading branch information
wazo-community-zuul[bot] authored Jul 16, 2024
2 parents a361295 + edcded7 commit ec78da7
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 16 deletions.
7 changes: 5 additions & 2 deletions xivo/flask/auth_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from ..auth_verifier import AuthVerifierHelpers
from ..http_exceptions import InvalidTokenAPIException, Unauthorized
from ..tenant_flask_helpers import auth_client, token
from .headers import extract_token_id_from_header
from .headers import extract_tenant_id_from_header, extract_token_id_from_header

R = TypeVar('R')

Expand All @@ -33,14 +33,17 @@ def wrapper(*args: Any, **kwargs: Any) -> R | None:
self.set_token_extractor(func)
token_uuid = token.uuid
required_acl = self.helpers.extract_required_acl(func, kwargs)
tenant_uuid = None # FIXME: Logic not implemented
tenant_uuid = extract_tenant_id_from_header() or None

self.helpers.validate_token(
auth_client,
token_uuid,
required_acl,
tenant_uuid,
)

g.verified_tenant_uuid = tenant_uuid

return func(*args, **kwargs)

return wrapper
Expand Down
4 changes: 4 additions & 0 deletions xivo/flask/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ def extract_token_id_from_query_string() -> str:

def extract_token_id_from_query_or_header() -> str:
return extract_token_id_from_query_string() or extract_token_id_from_header()


def extract_tenant_id_from_header() -> str:
return request.headers.get('Wazo-Tenant', '')
11 changes: 8 additions & 3 deletions xivo/flask/tests/test_auth_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@ def test_verify_token_decorator(self):
'token_extractor': None,
}
mock_g.get.side_effect = lambda x: g_data[x]
tenant_extractor = Mock(return_value=s.tenant)

@auth_verifier.verify_token
@required_acl('foo')
def decorated():
return s.result

with patch('xivo.flask.auth_verifier.g', mock_g):
with patch('xivo.tenant_flask_helpers.g', mock_g):
result = decorated()
with patch(
'xivo.flask.auth_verifier.extract_tenant_id_from_header',
tenant_extractor,
):
with patch('xivo.flask.auth_verifier.g', mock_g):
with patch('xivo.tenant_flask_helpers.g', mock_g):
result = decorated()

assert result == s.result

Expand Down
5 changes: 5 additions & 0 deletions xivo/tenant_flask_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ def autodetect(cls: type[Self], include_query: bool = False) -> Self:
else:
logger.debug('Found tenant "%s" from header', tenant.uuid)

verified_tenant_uuid = g.get('verified_tenant_uuid')
if verified_tenant_uuid and tenant and verified_tenant_uuid == tenant.uuid:
logger.debug('Tenant already validated by Flask verify_token')
return cls(uuid=tenant.uuid)

if not tenant:
tenant = cls.from_token(token)
logger.debug('Found tenant "%s" from token', tenant.uuid)
Expand Down
10 changes: 6 additions & 4 deletions xivo/tenant_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
try:
from flask import request

from .flask.headers import extract_token_id_from_header
from .flask.headers import (
extract_tenant_id_from_header,
extract_token_id_from_header,
)
except ImportError:
pass

Expand Down Expand Up @@ -71,9 +74,8 @@ def from_query(cls: type[Self]) -> Self:

@classmethod
def from_headers(cls: type[Self]) -> Self:
try:
tenant_uuid = request.headers['Wazo-Tenant']
except KeyError:
tenant_uuid = extract_tenant_id_from_header()
if not tenant_uuid:
raise InvalidTenant()
return cls(uuid=tenant_uuid)

Expand Down
18 changes: 18 additions & 0 deletions xivo/tests/test_tenant_flask_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from unittest.mock import Mock, patch
from unittest.mock import sentinel as s

from ..tenant_flask_helpers import Tenant
from ..tenant_flask_helpers import auth_client as auth_client_proxy


Expand All @@ -27,3 +28,20 @@ def test_config_deleted(self, auth_client):

expected_config = {'host': s.host}
auth_client.assert_called_once_with(**expected_config)


class TestTenant(unittest.TestCase):
@patch('xivo.tenant_flask_helpers.AuthClient')
def test_autodetect_when_verified_tenant_uuid(self, auth_client):
g_mock = Mock()
g_data = {'verified_tenant_uuid': s.tenant}
g_mock.get.side_effect = lambda x: g_data[x]
request_mock = Mock()
request_mock.headers.get.return_value = s.tenant
request_mock.args = []

with patch('xivo.tenant_flask_helpers.g', g_mock):
with patch('xivo.flask.headers.request', request_mock):
tenant = Tenant.autodetect()

assert tenant.uuid == s.tenant
14 changes: 7 additions & 7 deletions xivo/tests/test_tenant_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

class TestTenantAutodetect(TestCase):
@patch('xivo.tenant_helpers.Token')
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_no_token_when_autodetect_then_raise(self, request, Token):
auth = Mock()
Token.from_headers = Mock()
Expand All @@ -35,7 +35,7 @@ def test_given_no_token_when_autodetect_then_raise(self, request, Token):
)

@patch('xivo.tenant_helpers.Token')
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_token_no_tenant_when_autodetect_then_return_tenant_from_token(
self, request, Token
):
Expand All @@ -51,7 +51,7 @@ def test_given_token_no_tenant_when_autodetect_then_return_tenant_from_token(
assert_that(result.uuid, equal_to(tenant))

@patch('xivo.tenant_helpers.Token')
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_token_and_tenant_when_autodetect_then_return_given_tenant(
self, request, Token
):
Expand All @@ -67,7 +67,7 @@ def test_given_token_and_tenant_when_autodetect_then_return_given_tenant(
assert_that(result.uuid, equal_to(tenant))

@patch('xivo.tenant_helpers.Token')
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_token_unknown_tenant_and_user_in_tenant_when_autodetect_then_return_tenant(
self, request, Token
):
Expand All @@ -85,7 +85,7 @@ def test_given_token_unknown_tenant_and_user_in_tenant_when_autodetect_then_retu
assert_that(result.uuid, equal_to(tenant))

@patch('xivo.tenant_helpers.Token')
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_token_unknown_tenant_and_user_not_in_tenant_when_autodetect_then_raise(
self, request, Token
):
Expand All @@ -112,13 +112,13 @@ def test_given_visible_tenants_called_twice_with_same_tenant(self):


class TestTenantFromHeaders(TestCase):
@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_no_tenant_when_from_headers_then_raise(self, request):
request.headers = {}

assert_that(calling(Tenant.from_headers), raises(InvalidTenant))

@patch('xivo.tenant_helpers.request', spec={})
@patch('xivo.flask.headers.request', spec={})
def test_given_tenant_when_from_headers_then_return_tenant(self, request):
tenant = 'tenant'
request.headers = {'Wazo-Tenant': tenant}
Expand Down

0 comments on commit ec78da7

Please sign in to comment.