Skip to content

feat: Router changes to handle tenant id #364

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 10 commits into from
Jul 12, 2023
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
54 changes: 49 additions & 5 deletions supertokens_python/recipe/dashboard/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
# under the License.
from __future__ import annotations

import re
from os import environ
from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Union

from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.recipe_module import APIHandled, RecipeModule
from supertokens_python.recipe_module import APIHandled, RecipeModule, ApiIdWithTenantId

from .api import (
api_key_protector,
Expand Down Expand Up @@ -45,6 +46,7 @@
from .exceptions import SuperTokensDashboardError
from .interfaces import APIInterface, APIOptions
from .recipe_implementation import RecipeImplementation
from ..multitenancy.constants import DEFAULT_TENANT_ID

if TYPE_CHECKING:
from supertokens_python.framework.request import BaseRequest
Expand Down Expand Up @@ -126,6 +128,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down Expand Up @@ -245,15 +248,56 @@ def reset():

def return_api_id_if_can_handle_request(
self, path: NormalisedURLPath, method: str
) -> Union[str, None]:
) -> Union[ApiIdWithTenantId, None]:
dashboard_bundle_path = self.app_info.api_base_path.append(
NormalisedURLPath(DASHBOARD_API)
)

if is_api_path(path, self.app_info):
return get_api_if_matched(path, method)
base_path_str = self.app_info.api_base_path.get_as_string_dangerous()
path_str = path.get_as_string_dangerous()
regex = rf"^{base_path_str}(?:/([a-zA-Z0-9-]+))?(/.*)$"
# some examples against for above regex:
# books => match = None
# public/books => match = None
# /books => match.group(1) = None, match.group(2) = /dashboard
# /public/books => match.group(1) = 'public', match.group(2) = '/books'
# /public/book/1 => match.group(1) = 'public', match.group(2) = '/book/1'

match = re.match(regex, path_str)
match_group_1 = match.group(1) if match is not None else None
match_group_2 = match.group(2) if match is not None else None

tenant_id: str = DEFAULT_TENANT_ID
remaining_path: Optional[NormalisedURLPath] = None

if (
match is not None
and isinstance(match_group_1, str)
and isinstance(match_group_2, str)
):
tenant_id = match_group_1
remaining_path = NormalisedURLPath(match_group_2)

if is_api_path(path, self.app_info.api_base_path) or (
remaining_path is not None
and is_api_path(
path,
self.app_info.api_base_path.append(NormalisedURLPath(f"/{tenant_id}")),
)
):
# check remainingPath first as path that contains tenantId might match as well
# since getApiIdIfMatched uses endsWith to match
if remaining_path is not None:
id_ = get_api_if_matched(remaining_path, method)
if id_ is not None:
return ApiIdWithTenantId(id_, tenant_id)

id_ = get_api_if_matched(path, method)
if id_ is not None:
return ApiIdWithTenantId(id_, DEFAULT_TENANT_ID)

if path.startswith(dashboard_bundle_path):
return DASHBOARD_API
return ApiIdWithTenantId(DASHBOARD_API, DEFAULT_TENANT_ID)

# tenantId is not supported for bundlePath, so not matching for it
return None
7 changes: 2 additions & 5 deletions supertokens_python/recipe/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

if TYPE_CHECKING:
from supertokens_python.framework.request import BaseRequest
from ...supertokens import AppInfo

from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
from supertokens_python.recipe.emailpassword.asyncio import (
Expand Down Expand Up @@ -195,10 +194,8 @@ def validate_and_normalise_user_input(
)


def is_api_path(path: NormalisedURLPath, app_info: AppInfo) -> bool:
dashboard_recipe_base_path = app_info.api_base_path.append(
NormalisedURLPath(DASHBOARD_API)
)
def is_api_path(path: NormalisedURLPath, base_path: NormalisedURLPath) -> bool:
dashboard_recipe_base_path = base_path.append(NormalisedURLPath(DASHBOARD_API))

if not path.startswith(dashboard_recipe_base_path):
return False
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/emailpassword/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, Any, Dict, List, Union
from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional

from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
Expand Down Expand Up @@ -170,6 +170,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
1 change: 1 addition & 0 deletions supertokens_python/recipe/emailverification/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/jwt/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING, List, Union, Optional

from supertokens_python.querier import Querier
from supertokens_python.recipe.jwt.api.implementation import APIImplementation
Expand Down Expand Up @@ -80,6 +80,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
2 changes: 1 addition & 1 deletion supertokens_python/recipe/multitenancy/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# License for the specific language governing permissions and limitations
# under the License.
LOGIN_METHODS = "/loginmethods"
DEFAULT_TENANT_ID = "defaultTenantId"
DEFAULT_TENANT_ID = "public"
1 change: 1 addition & 0 deletions supertokens_python/recipe/multitenancy/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
5 changes: 3 additions & 2 deletions supertokens_python/recipe/openid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING, List, Union, Optional

from supertokens_python.querier import Querier
from supertokens_python.recipe.jwt import JWTRecipe
Expand Down Expand Up @@ -89,6 +89,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand All @@ -107,7 +108,7 @@ async def handle_api_request(
self.api_implementation, options
)
return await self.jwt_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)

async def handle_error(
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/passwordless/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Union
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Union, Optional

from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
Expand Down Expand Up @@ -185,6 +185,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/session/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down Expand Up @@ -215,7 +216,7 @@ async def handle_api_request(
),
)
return await self.openid_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)

async def handle_error(
Expand Down
8 changes: 2 additions & 6 deletions supertokens_python/recipe/thirdparty/providers/apple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@
from jwt import encode # type: ignore
from time import time

from ..provider import (
Provider,
ProviderConfigForClientType,
ProviderInput,
)
from .custom import GenericProvider, NewProvider
from ..provider import Provider, ProviderConfigForClientType, ProviderInput
from .utils import get_actual_client_id_from_development_client_id


Expand Down Expand Up @@ -54,7 +50,7 @@ async def _get_client_secret( # pylint: disable=no-self-use
"Please ensure that keyId, teamId and privateKey are provided in the additionalConfig"
)

payload = {
payload: Dict[str, Any] = {
"iss": config.additional_config.get("teamId"),
"iat": time(),
"exp": time() + (86400 * 180), # 6 months
Expand Down
5 changes: 0 additions & 5 deletions supertokens_python/recipe/thirdparty/providers/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@
from ..provider import Provider, ProviderInput


# TODO Implement when it's done in Node PR
class BitbucketImpl(GenericProvider):
pass


# TODO Finish when it's done in Node PR
def Bitbucket(input: ProviderInput) -> Provider: # pylint: disable=redefined-builtin
if input.config.name is None:
input.config.name = "Bitbucket"
Expand All @@ -40,7 +38,4 @@ def Bitbucket(input: ProviderInput) -> Provider: # pylint: disable=redefined-bu
if input.config.user_info_endpoint is None:
input.config.user_info_endpoint = "https://api.bitbucket.org/2.0/user"

# TODO overrides and working of this
# once done in Node PR

return NewProvider(input, BitbucketImpl)
2 changes: 0 additions & 2 deletions supertokens_python/recipe/thirdparty/providers/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
from ..provider import Provider, ProviderInput


# TODO Implement when it's done in Node PR
class GitlabImpl(GenericProvider):
pass


# TODO Implement when it's done in Node PR
def Gitlab(input: ProviderInput) -> Provider: # pylint: disable=redefined-builtin
return NewProvider(input, GitlabImpl)
3 changes: 2 additions & 1 deletion supertokens_python/recipe/thirdparty/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, Any, Dict, List, Union
from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional

from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.querier import Querier
Expand Down Expand Up @@ -121,6 +121,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
7 changes: 4 additions & 3 deletions supertokens_python/recipe/thirdpartyemailpassword/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING, List, Union, Optional

from supertokens_python.framework.response import BaseResponse
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
Expand Down Expand Up @@ -210,6 +210,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand All @@ -220,7 +221,7 @@ async def handle_api_request(
is not None
):
return await self.email_password_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)
if (
self.third_party_recipe is not None
Expand All @@ -230,7 +231,7 @@ async def handle_api_request(
is not None
):
return await self.third_party_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)
return None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# under the License.
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, Callable
from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Optional

import supertokens_python.recipe.emailpassword.interfaces as EPInterfaces
from supertokens_python.recipe.thirdparty.interfaces import GetProviderOkResult
Expand Down
7 changes: 4 additions & 3 deletions supertokens_python/recipe/thirdpartypasswordless/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from os import environ
from typing import TYPE_CHECKING, Any, Dict, List, Union
from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional

from supertokens_python.framework.response import BaseResponse
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
Expand Down Expand Up @@ -224,6 +224,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand All @@ -234,7 +235,7 @@ async def handle_api_request(
is not None
):
return await self.passwordless_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)
if (
self.third_party_recipe is not None
Expand All @@ -244,7 +245,7 @@ async def handle_api_request(
is not None
):
return await self.third_party_recipe.handle_api_request(
request_id, request, path, method, response
request_id, tenant_id, request, path, method, response
)
return None

Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/usermetadata/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from __future__ import annotations

from os import environ
from typing import List, Union
from typing import List, Union, Optional

from supertokens_python.exceptions import SuperTokensError, raise_general_exception
from supertokens_python.framework import BaseRequest, BaseResponse
Expand Down Expand Up @@ -66,6 +66,7 @@ def get_apis_handled(self) -> List[APIHandled]:
async def handle_api_request(
self,
request_id: str,
tenant_id: Optional[str],
request: BaseRequest,
path: NormalisedURLPath,
method: str,
Expand Down
Loading