Skip to content

fix: Userroles recipe multitenancy features #375

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 4 commits into from
Jul 24, 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
28 changes: 19 additions & 9 deletions supertokens_python/recipe/userroles/asyncio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Union
from typing import Any, Dict, List, Union, Optional

from supertokens_python.recipe.userroles.interfaces import (
AddRoleToUserOkResult,
Expand All @@ -17,44 +17,54 @@


async def add_role_to_user(
user_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
if user_context is None:
user_context = {}
return await UserRolesRecipe.get_instance().recipe_implementation.add_role_to_user(
user_id, role, user_context
user_id, role, tenant_id, user_context
)


async def remove_user_role(
user_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
if user_context is None:
user_context = {}
return await UserRolesRecipe.get_instance().recipe_implementation.remove_user_role(
user_id, role, user_context
user_id, role, tenant_id, user_context
)


async def get_roles_for_user(
user_id: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
user_context: Union[Dict[str, Any], None] = None,
) -> GetRolesForUserOkResult:
if user_context is None:
user_context = {}
return (
await UserRolesRecipe.get_instance().recipe_implementation.get_roles_for_user(
user_id, user_context
user_id, tenant_id, user_context
)
)


async def get_users_that_have_role(
role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
if user_context is None:
user_context = {}
return await UserRolesRecipe.get_instance().recipe_implementation.get_users_that_have_role(
role, user_context
role, tenant_id, user_context
)


Expand Down
18 changes: 13 additions & 5 deletions supertokens_python/recipe/userroles/interfaces.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Union
from typing import Any, Dict, List, Union, Optional


class AddRoleToUserOkResult:
Expand Down Expand Up @@ -58,25 +58,33 @@ def __init__(self, roles: List[str]):
class RecipeInterface(ABC):
@abstractmethod
async def add_role_to_user(
self, user_id: str, role: str, user_context: Dict[str, Any]
self,
user_id: str,
role: str,
tenant_id: Optional[str],
user_context: Dict[str, Any],
) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
pass

@abstractmethod
async def remove_user_role(
self, user_id: str, role: str, user_context: Dict[str, Any]
self,
user_id: str,
role: str,
tenant_id: Optional[str],
user_context: Dict[str, Any],
) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
pass

@abstractmethod
async def get_roles_for_user(
self, user_id: str, user_context: Dict[str, Any]
self, user_id: str, tenant_id: Optional[str], user_context: Dict[str, Any]
) -> GetRolesForUserOkResult:
pass

@abstractmethod
async def get_users_that_have_role(
self, role: str, user_context: Dict[str, Any]
self, role: str, tenant_id: Optional[str], user_context: Dict[str, Any]
) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
pass

Expand Down
8 changes: 4 additions & 4 deletions supertokens_python/recipe/userroles/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ def __init__(self) -> None:
default_max_age_in_sec = 300

async def fetch_value(
user_id: str, _tenant_id: str, user_context: Dict[str, Any]
user_id: str, tenant_id: str, user_context: Dict[str, Any]
) -> List[str]:
recipe = UserRolesRecipe.get_instance()

user_roles = await recipe.recipe_implementation.get_roles_for_user(
user_id, user_context
tenant_id, user_id, user_context
)

user_permissions: Set[str] = set()
Expand Down Expand Up @@ -181,11 +181,11 @@ def __init__(self) -> None:
default_max_age_in_sec = 300

async def fetch_value(
user_id: str, _tenant_id: str, user_context: Dict[str, Any]
user_id: str, tenant_id: str, user_context: Dict[str, Any]
) -> List[str]:
recipe = UserRolesRecipe.get_instance()
res = await recipe.recipe_implementation.get_roles_for_user(
user_id, user_context
tenant_id, user_id, user_context
)
return res.roles

Expand Down
33 changes: 24 additions & 9 deletions supertokens_python/recipe/userroles/recipe_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# under the License.


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

from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.querier import Querier
Expand All @@ -32,6 +32,7 @@
RemoveUserRoleOkResult,
UnknownRoleError,
)
from ..multitenancy.constants import DEFAULT_TENANT_ID


class RecipeImplementation(RecipeInterface):
Expand All @@ -40,11 +41,16 @@ def __init__(self, querier: Querier):
self.querier = querier

async def add_role_to_user(
self, user_id: str, role: str, user_context: Dict[str, Any]
self,
user_id: str,
role: str,
tenant_id: Optional[str],
user_context: Dict[str, Any],
) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
params = {"userId": user_id, "role": role}
response = await self.querier.send_put_request(
NormalisedURLPath("/recipe/user/role"), params
NormalisedURLPath(f"{tenant_id or DEFAULT_TENANT_ID}/recipe/user/role"),
params,
)
if response.get("status") == "OK":
return AddRoleToUserOkResult(
Expand All @@ -53,11 +59,18 @@ async def add_role_to_user(
return UnknownRoleError()

async def remove_user_role(
self, user_id: str, role: str, user_context: Dict[str, Any]
self,
user_id: str,
role: str,
tenant_id: Optional[str],
user_context: Dict[str, Any],
) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
params = {"userId": user_id, "role": role}
response = await self.querier.send_post_request(
NormalisedURLPath("/recipe/user/role/remove"), params
NormalisedURLPath(
f"{tenant_id or DEFAULT_TENANT_ID}/recipe/user/role/remove"
),
params,
)
if response["status"] == "OK":
return RemoveUserRoleOkResult(
Expand All @@ -66,20 +79,22 @@ async def remove_user_role(
return UnknownRoleError()

async def get_roles_for_user(
self, user_id: str, user_context: Dict[str, Any]
self, user_id: str, tenant_id: Optional[str], user_context: Dict[str, Any]
) -> GetRolesForUserOkResult:
params = {"userId": user_id}
response = await self.querier.send_get_request(
NormalisedURLPath("/recipe/user/roles"), params
NormalisedURLPath(f"{tenant_id or DEFAULT_TENANT_ID}/recipe/user/roles"),
params,
)
return GetRolesForUserOkResult(roles=response["roles"])

async def get_users_that_have_role(
self, role: str, user_context: Dict[str, Any]
self, role: str, tenant_id: Optional[str], user_context: Dict[str, Any]
) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
params = {"role": role}
response = await self.querier.send_get_request(
NormalisedURLPath("/recipe/role/users"), params
NormalisedURLPath(f"{tenant_id or DEFAULT_TENANT_ID}/recipe/role/users"),
params,
)
if response.get("status") == "OK":
return GetUsersThatHaveRoleOkResult(users=response["users"])
Expand Down
28 changes: 19 additions & 9 deletions supertokens_python/recipe/userroles/syncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.

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

from supertokens_python.async_to_sync_wrapper import sync
from supertokens_python.recipe.userroles.interfaces import (
Expand All @@ -31,35 +31,45 @@


def add_role_to_user(
user_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[AddRoleToUserOkResult, UnknownRoleError]:
from supertokens_python.recipe.userroles.asyncio import add_role_to_user

return sync(add_role_to_user(user_id, role, user_context))
return sync(add_role_to_user(tenant_id, user_id, role, user_context))


def remove_user_role(
user_id: str, role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[RemoveUserRoleOkResult, UnknownRoleError]:
from supertokens_python.recipe.userroles.asyncio import remove_user_role

return sync(remove_user_role(user_id, role, user_context))
return sync(remove_user_role(tenant_id, user_id, role, user_context))


def get_roles_for_user(
user_id: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
user_id: str,
user_context: Union[Dict[str, Any], None] = None,
) -> GetRolesForUserOkResult:
from supertokens_python.recipe.userroles.asyncio import get_roles_for_user

return sync(get_roles_for_user(user_id, user_context))
return sync(get_roles_for_user(tenant_id, user_id, user_context))


def get_users_that_have_role(
role: str, user_context: Union[Dict[str, Any], None] = None
tenant_id: Optional[str],
role: str,
user_context: Union[Dict[str, Any], None] = None,
) -> Union[GetUsersThatHaveRoleOkResult, UnknownRoleError]:
from supertokens_python.recipe.userroles.asyncio import get_users_that_have_role

return sync(get_users_that_have_role(role, user_context))
return sync(get_users_that_have_role(tenant_id, role, user_context))


def create_new_role_or_add_permissions(
Expand Down