diff --git a/html/supertokens_python/constants.html b/html/supertokens_python/constants.html index ddabaf437..433530761 100644 --- a/html/supertokens_python/constants.html +++ b/html/supertokens_python/constants.html @@ -42,7 +42,7 @@
supertokens_python.constants
supertokens_python.framework.django.django_reques
super().__init__()
self.request = request
+ def get_original_url(self) -> str:
+ return self.request.get_raw_uri()
+
def get_query_param(
self, key: str, default: Union[str, None] = None
) -> Union[str, None]:
@@ -126,6 +129,9 @@ Classes
super().__init__()
self.request = request
+ def get_original_url(self) -> str:
+ return self.request.get_raw_uri()
+
def get_query_param(
self, key: str, default: Union[str, None] = None
) -> Union[str, None]:
@@ -217,6 +223,19 @@ Methods
return self.request.META.get(key.upper())
+
+def get_original_url(self) ‑> str
+
def get_original_url(self) -> str:
+ return self.request.get_raw_uri()
+
def get_path(self) ‑> str
form_data
get_cookie
get_header
get_original_url
get_path
get_query_param
get_query_params
supertokens_python.framework.fastapi.fastapi_requ
super().__init__()
self.request = request
+ def get_original_url(self) -> str:
+ return self.request.url.components.geturl()
+
def get_query_param(
self, key: str, default: Union[str, None] = None
) -> Union[str, None]:
@@ -126,6 +129,9 @@ Classes
super().__init__()
self.request = request
+ def get_original_url(self) -> str:
+ return self.request.url.components.geturl()
+
def get_query_param(
self, key: str, default: Union[str, None] = None
) -> Union[str, None]:
@@ -218,6 +224,19 @@ Methods
return self.request.headers.get(key, None)
+
+def get_original_url(self) ‑> str
+
def get_original_url(self) -> str:
+ return self.request.url.components.geturl()
+
def get_path(self) ‑> str
form_data
get_cookie
get_header
get_original_url
get_path
get_query_param
get_query_params
supertokens_python.framework.flask.flask_request<
super().__init__()
self.request = req
+ def get_original_url(self) -> str:
+ return self.request.url
+
def get_query_param(self, key: str, default: Union[str, None] = None):
return self.request.args.get(key, default)
@@ -133,6 +136,9 @@ Classes
super().__init__()
self.request = req
+ def get_original_url(self) -> str:
+ return self.request.url
+
def get_query_param(self, key: str, default: Union[str, None] = None):
return self.request.args.get(key, default)
@@ -233,6 +239,19 @@ Methods
return self.request.headers.get(key) # type: ignore
+
+def get_original_url(self) ‑> str
+
def get_original_url(self) -> str:
+ return self.request.url
+
def get_path(self) ‑> str
form_data
get_cookie
get_header
get_original_url
get_path
get_query_param
get_query_params
supertokens_python.framework.request
+def get_original_url(self) ‑> str
+
@abstractmethod
+def get_original_url(self) -> str:
+ pass
+
def get_path(self) ‑> str
form_data
get_cookie
get_header
get_original_url
get_path
get_query_param
get_query_params
supertokens_python.recipe.dashboard.api.validate_
user_context: Dict[str, Any],
):
- is_valid_key = validate_api_key(
+ is_valid_key = await validate_api_key(
api_options.request, api_options.config, user_context
)
@@ -94,7 +94,7 @@ Functions
user_context: Dict[str, Any],
):
- is_valid_key = validate_api_key(
+ is_valid_key = await validate_api_key(
api_options.request, api_options.config, user_context
)
diff --git a/html/supertokens_python/recipe/dashboard/constants.html b/html/supertokens_python/recipe/dashboard/constants.html
index ab05d20d8..d4762020b 100644
--- a/html/supertokens_python/recipe/dashboard/constants.html
+++ b/html/supertokens_python/recipe/dashboard/constants.html
@@ -36,8 +36,8 @@ Module supertokens_python.recipe.dashboard.constants
diff --git a/html/supertokens_python/recipe/dashboard/exceptions.html b/html/supertokens_python/recipe/dashboard/exceptions.html
index 49844b4ae..55cfffe76 100644
--- a/html/supertokens_python/recipe/dashboard/exceptions.html
+++ b/html/supertokens_python/recipe/dashboard/exceptions.html
@@ -30,6 +30,10 @@ Module supertokens_python.recipe.dashboard.exceptions
@@ -42,6 +46,27 @@ Module supertokens_python.recipe.dashboard.exceptions
Classes
+
+class DashboardOperationNotAllowedError
+(*args, **kwargs)
+
+-
+
Common base class for all non-exit exceptions.
+
+
+Expand source code
+
+class DashboardOperationNotAllowedError(SuperTokensDashboardError):
+ pass
+
+Ancestors
+
+- SuperTokensDashboardError
+- SuperTokensError
+- builtins.Exception
+- builtins.BaseException
+
+
class SuperTokensDashboardError
(*args, **kwargs)
@@ -61,6 +86,10 @@ Ancestors
- builtins.Exception
- builtins.BaseException
+Subclasses
+
@@ -79,6 +108,9 @@ Index
Classes
diff --git a/html/supertokens_python/recipe/dashboard/index.html b/html/supertokens_python/recipe/dashboard/index.html
index 11d46eedd..f5c97daf0 100644
--- a/html/supertokens_python/recipe/dashboard/index.html
+++ b/html/supertokens_python/recipe/dashboard/index.html
@@ -42,7 +42,7 @@ Module supertokens_python.recipe.dashboard
from __future__ import annotations
-from typing import Callable, Optional, Union
+from typing import Callable, Optional, List
from supertokens_python import AppInfo, RecipeModule
@@ -54,11 +54,13 @@ Module supertokens_python.recipe.dashboard
def init(
- api_key: Union[str, None] = None,
+ api_key: Optional[str] = None,
+ admins: Optional[List[str]] = None,
override: Optional[InputOverrideConfig] = None,
) -> Callable[[AppInfo], RecipeModule]:
return DashboardRecipe.init(
api_key,
+ admins,
override,
)
@@ -102,7 +104,7 @@ Sub-modules
Functions
-def init(api_key: Union[str, None] = None, override: Optional[InputOverrideConfig] = None) ‑> Callable[[AppInfo], RecipeModule]
+def init(api_key: Optional[str] = None, admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None) ‑> Callable[[AppInfo], RecipeModule]
-
@@ -111,11 +113,13 @@
Functions
Expand source code
def init(
- api_key: Union[str, None] = None,
+ api_key: Optional[str] = None,
+ admins: Optional[List[str]] = None,
override: Optional[InputOverrideConfig] = None,
) -> Callable[[AppInfo], RecipeModule]:
return DashboardRecipe.init(
api_key,
+ admins,
override,
)
diff --git a/html/supertokens_python/recipe/dashboard/recipe.html b/html/supertokens_python/recipe/dashboard/recipe.html
index 7350f4f2a..a69beb97c 100644
--- a/html/supertokens_python/recipe/dashboard/recipe.html
+++ b/html/supertokens_python/recipe/dashboard/recipe.html
@@ -42,7 +42,7 @@ Module supertokens_python.recipe.dashboard.recipe
from __future__ import annotations
from os import environ
-from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Union, Dict, Any
+from typing import TYPE_CHECKING, Awaitable, Callable, List, Optional, Dict, Any
from supertokens_python.normalised_url_path import NormalisedURLPath
from supertokens_python.recipe_module import APIHandled, RecipeModule
@@ -87,8 +87,8 @@ Module supertokens_python.recipe.dashboard.recipe
from .constants import (
DASHBOARD_ANALYTICS_API,
DASHBOARD_API,
- EMAIL_PASSSWORD_SIGNOUT,
- EMAIL_PASSWORD_SIGN_IN,
+ SIGN_OUT_API,
+ SIGN_IN_API,
SEARCH_TAGS_API,
USER_API,
USER_EMAIL_VERIFY_API,
@@ -115,12 +115,14 @@ Module supertokens_python.recipe.dashboard.recipe
self,
recipe_id: str,
app_info: AppInfo,
- api_key: Union[str, None],
- override: Union[InputOverrideConfig, None] = None,
+ api_key: Optional[str],
+ admins: Optional[List[str]],
+ override: Optional[InputOverrideConfig] = None,
):
super().__init__(recipe_id, app_info)
self.config = validate_and_normalise_user_input(
api_key,
+ admins,
override,
)
recipe_implementation = RecipeImplementation()
@@ -151,11 +153,9 @@ Module supertokens_python.recipe.dashboard.recipe
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSWORD_SIGN_IN)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
"post",
- EMAIL_PASSWORD_SIGN_IN,
+ SIGN_IN_API,
False,
),
APIHandled(
@@ -165,11 +165,9 @@ Module supertokens_python.recipe.dashboard.recipe
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSSWORD_SIGNOUT)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
"post",
- EMAIL_PASSSWORD_SIGNOUT,
+ SIGN_OUT_API,
False,
),
APIHandled(
@@ -242,6 +240,12 @@ Module supertokens_python.recipe.dashboard.recipe
USER_SESSION_API,
False,
),
+ APIHandled(
+ NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+ "post",
+ USER_SESSION_API,
+ False,
+ ),
APIHandled(
NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
"put",
@@ -305,7 +309,7 @@ Module supertokens_python.recipe.dashboard.recipe
return await handle_validate_key_api(
self.api_implementation, api_options, user_context
)
- if request_id == EMAIL_PASSWORD_SIGN_IN:
+ if request_id == SIGN_IN_API:
return await handle_emailpassword_signin_api(
self.api_implementation, api_options, user_context
)
@@ -346,7 +350,7 @@ Module supertokens_python.recipe.dashboard.recipe
api_function = handle_user_password_put
elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
api_function = handle_email_verify_token_post
- elif request_id == EMAIL_PASSSWORD_SIGNOUT:
+ elif request_id == SIGN_OUT_API:
api_function = handle_emailpassword_signout_api
elif request_id == SEARCH_TAGS_API:
api_function = handle_get_tags
@@ -377,8 +381,9 @@ Module supertokens_python.recipe.dashboard.recipe
@staticmethod
def init(
- api_key: Union[str, None],
- override: Union[InputOverrideConfig, None] = None,
+ api_key: Optional[str],
+ admins: Optional[List[str]] = None,
+ override: Optional[InputOverrideConfig] = None,
):
def func(app_info: AppInfo):
if DashboardRecipe.__instance is None:
@@ -386,6 +391,7 @@ Module supertokens_python.recipe.dashboard.recipe
DashboardRecipe.recipe_id,
app_info,
api_key,
+ admins,
override,
)
return DashboardRecipe.__instance
@@ -424,7 +430,7 @@ Classes
class DashboardRecipe
-(recipe_id: str, app_info: AppInfo, api_key: Union[str, None], override: Union[InputOverrideConfig, None] = None)
+(recipe_id: str, app_info: AppInfo, api_key: Optional[str], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None)
-
Helper class that provides a standard way to create an ABC using
@@ -441,12 +447,14 @@
Classes
self,
recipe_id: str,
app_info: AppInfo,
- api_key: Union[str, None],
- override: Union[InputOverrideConfig, None] = None,
+ api_key: Optional[str],
+ admins: Optional[List[str]],
+ override: Optional[InputOverrideConfig] = None,
):
super().__init__(recipe_id, app_info)
self.config = validate_and_normalise_user_input(
api_key,
+ admins,
override,
)
recipe_implementation = RecipeImplementation()
@@ -477,11 +485,9 @@ Classes
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSWORD_SIGN_IN)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
"post",
- EMAIL_PASSWORD_SIGN_IN,
+ SIGN_IN_API,
False,
),
APIHandled(
@@ -491,11 +497,9 @@ Classes
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSSWORD_SIGNOUT)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
"post",
- EMAIL_PASSSWORD_SIGNOUT,
+ SIGN_OUT_API,
False,
),
APIHandled(
@@ -568,6 +572,12 @@ Classes
USER_SESSION_API,
False,
),
+ APIHandled(
+ NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+ "post",
+ USER_SESSION_API,
+ False,
+ ),
APIHandled(
NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
"put",
@@ -631,7 +641,7 @@ Classes
return await handle_validate_key_api(
self.api_implementation, api_options, user_context
)
- if request_id == EMAIL_PASSWORD_SIGN_IN:
+ if request_id == SIGN_IN_API:
return await handle_emailpassword_signin_api(
self.api_implementation, api_options, user_context
)
@@ -672,7 +682,7 @@ Classes
api_function = handle_user_password_put
elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
api_function = handle_email_verify_token_post
- elif request_id == EMAIL_PASSSWORD_SIGNOUT:
+ elif request_id == SIGN_OUT_API:
api_function = handle_emailpassword_signout_api
elif request_id == SEARCH_TAGS_API:
api_function = handle_get_tags
@@ -703,8 +713,9 @@ Classes
@staticmethod
def init(
- api_key: Union[str, None],
- override: Union[InputOverrideConfig, None] = None,
+ api_key: Optional[str],
+ admins: Optional[List[str]] = None,
+ override: Optional[InputOverrideConfig] = None,
):
def func(app_info: AppInfo):
if DashboardRecipe.__instance is None:
@@ -712,6 +723,7 @@ Classes
DashboardRecipe.recipe_id,
app_info,
api_key,
+ admins,
override,
)
return DashboardRecipe.__instance
@@ -775,7 +787,7 @@ Static methods
-def init(api_key: Union[str, None], override: Union[InputOverrideConfig, None] = None)
+def init(api_key: Optional[str], admins: Optional[List[str]] = None, override: Optional[InputOverrideConfig] = None)
-
@@ -785,8 +797,9 @@
Static methods
@staticmethod
def init(
- api_key: Union[str, None],
- override: Union[InputOverrideConfig, None] = None,
+ api_key: Optional[str],
+ admins: Optional[List[str]] = None,
+ override: Optional[InputOverrideConfig] = None,
):
def func(app_info: AppInfo):
if DashboardRecipe.__instance is None:
@@ -794,6 +807,7 @@ Static methods
DashboardRecipe.recipe_id,
app_info,
api_key,
+ admins,
override,
)
return DashboardRecipe.__instance
@@ -857,11 +871,9 @@ Methods
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSWORD_SIGN_IN)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_IN_API)),
"post",
- EMAIL_PASSWORD_SIGN_IN,
+ SIGN_IN_API,
False,
),
APIHandled(
@@ -871,11 +883,9 @@ Methods
False,
),
APIHandled(
- NormalisedURLPath(
- get_api_path_with_dashboard_base(EMAIL_PASSSWORD_SIGNOUT)
- ),
+ NormalisedURLPath(get_api_path_with_dashboard_base(SIGN_OUT_API)),
"post",
- EMAIL_PASSSWORD_SIGNOUT,
+ SIGN_OUT_API,
False,
),
APIHandled(
@@ -948,6 +958,12 @@ Methods
USER_SESSION_API,
False,
),
+ APIHandled(
+ NormalisedURLPath(get_api_path_with_dashboard_base(USER_SESSION_API)),
+ "post",
+ USER_SESSION_API,
+ False,
+ ),
APIHandled(
NormalisedURLPath(get_api_path_with_dashboard_base(USER_PASSWORD_API)),
"put",
@@ -1021,7 +1037,7 @@ Methods
return await handle_validate_key_api(
self.api_implementation, api_options, user_context
)
- if request_id == EMAIL_PASSWORD_SIGN_IN:
+ if request_id == SIGN_IN_API:
return await handle_emailpassword_signin_api(
self.api_implementation, api_options, user_context
)
@@ -1062,7 +1078,7 @@ Methods
api_function = handle_user_password_put
elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
api_function = handle_email_verify_token_post
- elif request_id == EMAIL_PASSSWORD_SIGNOUT:
+ elif request_id == SIGN_OUT_API:
api_function = handle_emailpassword_signout_api
elif request_id == SEARCH_TAGS_API:
api_function = handle_get_tags
diff --git a/html/supertokens_python/recipe/dashboard/recipe_implementation.html b/html/supertokens_python/recipe/dashboard/recipe_implementation.html
index 17557a817..6da507de2 100644
--- a/html/supertokens_python/recipe/dashboard/recipe_implementation.html
+++ b/html/supertokens_python/recipe/dashboard/recipe_implementation.html
@@ -46,10 +46,16 @@ Module supertokens_python.recipe.dashboard.recipe_implem
from supertokens_python.constants import DASHBOARD_VERSION
from supertokens_python.framework import BaseRequest
from supertokens_python.normalised_url_path import NormalisedURLPath
+from supertokens_python.utils import log_debug_message, normalise_http_method
from supertokens_python.querier import Querier
+from supertokens_python.recipe.dashboard.constants import (
+ DASHBOARD_ANALYTICS_API,
+ SIGN_OUT_API,
+)
from .interfaces import RecipeInterface
from .utils import DashboardConfig, validate_api_key
+from .exceptions import DashboardOperationNotAllowedError
class RecipeImplementation(RecipeInterface):
@@ -62,9 +68,9 @@ Module supertokens_python.recipe.dashboard.recipe_implem
config: DashboardConfig,
user_context: Dict[str, Any],
) -> bool:
- if config.auth_mode == "email-password":
+ # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+ if config.api_key is None:
auth_header_value = request.get_header("authorization")
-
if not auth_header_value:
return False
@@ -75,11 +81,48 @@ Module supertokens_python.recipe.dashboard.recipe_implem
{"sessionId": auth_header_value},
)
)
- return (
- "status" in session_verification_response
- and session_verification_response["status"] == "OK"
- )
- return validate_api_key(request, config, user_context)
+ if session_verification_response.get("status") != "OK":
+ return False
+
+ # For all non GET requests we also want to check if the
+ # user is allowed to perform this operation
+ if normalise_http_method(request.method()) != "get":
+ # We dont want to block the analytics API
+ if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+ return True
+
+ # We do not want to block the sign out request
+ if request.get_original_url().endswith(SIGN_OUT_API):
+ return True
+
+ admins = config.admins
+
+ if admins is None:
+ return True
+
+ if len(admins) == 0:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ user_email = session_verification_response.get("email")
+
+ if user_email is None or not isinstance(user_email, str):
+ log_debug_message(
+ "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+ )
+ return False
+
+ if user_email not in admins:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ return True
+
+ return await validate_api_key(request, config, user_context)
@@ -111,9 +154,9 @@ Classes
config: DashboardConfig,
user_context: Dict[str, Any],
) -> bool:
- if config.auth_mode == "email-password":
+ # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+ if config.api_key is None:
auth_header_value = request.get_header("authorization")
-
if not auth_header_value:
return False
@@ -124,11 +167,48 @@ Classes
{"sessionId": auth_header_value},
)
)
- return (
- "status" in session_verification_response
- and session_verification_response["status"] == "OK"
- )
- return validate_api_key(request, config, user_context)
+ if session_verification_response.get("status") != "OK":
+ return False
+
+ # For all non GET requests we also want to check if the
+ # user is allowed to perform this operation
+ if normalise_http_method(request.method()) != "get":
+ # We dont want to block the analytics API
+ if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+ return True
+
+ # We do not want to block the sign out request
+ if request.get_original_url().endswith(SIGN_OUT_API):
+ return True
+
+ admins = config.admins
+
+ if admins is None:
+ return True
+
+ if len(admins) == 0:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ user_email = session_verification_response.get("email")
+
+ if user_email is None or not isinstance(user_email, str):
+ log_debug_message(
+ "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+ )
+ return False
+
+ if user_email not in admins:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ return True
+
+ return await validate_api_key(request, config, user_context)
Ancestors
@@ -165,9 +245,9 @@ Methods
config: DashboardConfig,
user_context: Dict[str, Any],
) -> bool:
- if config.auth_mode == "email-password":
+ # For cases where we're not using the API key, the JWT is being used; we allow their access by default
+ if config.api_key is None:
auth_header_value = request.get_header("authorization")
-
if not auth_header_value:
return False
@@ -178,11 +258,48 @@ Methods
{"sessionId": auth_header_value},
)
)
- return (
- "status" in session_verification_response
- and session_verification_response["status"] == "OK"
- )
- return validate_api_key(request, config, user_context)
+ if session_verification_response.get("status") != "OK":
+ return False
+
+ # For all non GET requests we also want to check if the
+ # user is allowed to perform this operation
+ if normalise_http_method(request.method()) != "get":
+ # We dont want to block the analytics API
+ if request.get_original_url().endswith(DASHBOARD_ANALYTICS_API):
+ return True
+
+ # We do not want to block the sign out request
+ if request.get_original_url().endswith(SIGN_OUT_API):
+ return True
+
+ admins = config.admins
+
+ if admins is None:
+ return True
+
+ if len(admins) == 0:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ user_email = session_verification_response.get("email")
+
+ if user_email is None or not isinstance(user_email, str):
+ log_debug_message(
+ "User Dashboard: Returning UNAUTHORISED_ERROR because no email was provided in headers"
+ )
+ return False
+
+ if user_email not in admins:
+ log_debug_message(
+ "User Dashboard: Throwing OPERATION_NOT_ALLOWED because user is not an admin"
+ )
+ raise DashboardOperationNotAllowedError()
+
+ return True
+
+ return await validate_api_key(request, config, user_context)
diff --git a/html/supertokens_python/recipe/dashboard/utils.html b/html/supertokens_python/recipe/dashboard/utils.html
index 5892ff59c..fe882e84f 100644
--- a/html/supertokens_python/recipe/dashboard/utils.html
+++ b/html/supertokens_python/recipe/dashboard/utils.html
@@ -71,14 +71,14 @@ Module supertokens_python.recipe.dashboard.utils
<
get_user_by_id as tppless_get_user_by_id,
)
from supertokens_python.types import User
-from supertokens_python.utils import Awaitable
+from supertokens_python.utils import Awaitable, log_debug_message, normalise_email
from ...normalised_url_path import NormalisedURLPath
from .constants import (
DASHBOARD_ANALYTICS_API,
DASHBOARD_API,
- EMAIL_PASSSWORD_SIGNOUT,
- EMAIL_PASSWORD_SIGN_IN,
+ SIGN_OUT_API,
+ SIGN_IN_API,
SEARCH_TAGS_API,
USER_API,
USER_EMAIL_VERIFY_API,
@@ -209,9 +209,14 @@ Module supertokens_python.recipe.dashboard.utils
<
class DashboardConfig:
def __init__(
- self, api_key: Union[str, None], override: OverrideConfig, auth_mode: str
+ self,
+ api_key: Optional[str],
+ admins: Optional[List[str]],
+ override: OverrideConfig,
+ auth_mode: str,
):
self.api_key = api_key
+ self.admins = admins
self.override = override
self.auth_mode = auth_mode
@@ -219,14 +224,23 @@ Module supertokens_python.recipe.dashboard.utils
<
def validate_and_normalise_user_input(
# app_info: AppInfo,
api_key: Union[str, None],
+ admins: Optional[List[str]],
override: Optional[InputOverrideConfig] = None,
) -> DashboardConfig:
if override is None:
override = InputOverrideConfig()
+ if api_key is not None and admins is not None:
+ log_debug_message(
+ "User Dashboard: Providing 'admins' has no effect when using an api key."
+ )
+
+ admins = [normalise_email(a) for a in admins] if admins is not None else None
+
return DashboardConfig(
api_key,
+ admins,
OverrideConfig(
functions=override.functions,
apis=override.apis,
@@ -273,10 +287,10 @@ Module supertokens_python.recipe.dashboard.utils
<
return USER_PASSWORD_API
if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
return USER_EMAIL_VERIFY_TOKEN_API
- if path_str.endswith(EMAIL_PASSWORD_SIGN_IN) and method == "post":
- return EMAIL_PASSWORD_SIGN_IN
- if path_str.endswith(EMAIL_PASSSWORD_SIGNOUT) and method == "post":
- return EMAIL_PASSSWORD_SIGNOUT
+ if path_str.endswith(SIGN_IN_API) and method == "post":
+ return SIGN_IN_API
+ if path_str.endswith(SIGN_OUT_API) and method == "post":
+ return SIGN_OUT_API
if path_str.endswith(SEARCH_TAGS_API) and method == "get":
return SEARCH_TAGS_API
if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
@@ -438,7 +452,7 @@ Module supertokens_python.recipe.dashboard.utils
<
return isRecipeInitialised
-def validate_api_key(
+async def validate_api_key(
req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
) -> bool:
api_key_header_value = req.get_header("authorization")
@@ -490,10 +504,10 @@ Functions
return USER_PASSWORD_API
if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
return USER_EMAIL_VERIFY_TOKEN_API
- if path_str.endswith(EMAIL_PASSWORD_SIGN_IN) and method == "post":
- return EMAIL_PASSWORD_SIGN_IN
- if path_str.endswith(EMAIL_PASSSWORD_SIGNOUT) and method == "post":
- return EMAIL_PASSSWORD_SIGNOUT
+ if path_str.endswith(SIGN_IN_API) and method == "post":
+ return SIGN_IN_API
+ if path_str.endswith(SIGN_OUT_API) and method == "post":
+ return SIGN_OUT_API
if path_str.endswith(SEARCH_TAGS_API) and method == "get":
return SEARCH_TAGS_API
if path_str.endswith(DASHBOARD_ANALYTICS_API) and method == "post":
@@ -695,7 +709,7 @@ Functions
-def validate_and_normalise_user_input(api_key: Union[str, None], override: Optional[InputOverrideConfig] = None) ‑> DashboardConfig
+def validate_and_normalise_user_input(api_key: Union[str, None], admins: Optional[List[str]], override: Optional[InputOverrideConfig] = None) ‑> DashboardConfig
-
@@ -706,14 +720,23 @@
Functions
def validate_and_normalise_user_input(
# app_info: AppInfo,
api_key: Union[str, None],
+ admins: Optional[List[str]],
override: Optional[InputOverrideConfig] = None,
) -> DashboardConfig:
if override is None:
override = InputOverrideConfig()
+ if api_key is not None and admins is not None:
+ log_debug_message(
+ "User Dashboard: Providing 'admins' has no effect when using an api key."
+ )
+
+ admins = [normalise_email(a) for a in admins] if admins is not None else None
+
return DashboardConfig(
api_key,
+ admins,
OverrideConfig(
functions=override.functions,
apis=override.apis,
@@ -723,7 +746,7 @@ Functions
-def validate_api_key(req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]) ‑> bool
+async def validate_api_key(req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]) ‑> bool
-
@@ -731,7 +754,7 @@
Functions
Expand source code
-def validate_api_key(
+async def validate_api_key(
req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
) -> bool:
api_key_header_value = req.get_header("authorization")
@@ -749,7 +772,7 @@ Classes
class DashboardConfig
-(api_key: Union[str, None], override: OverrideConfig, auth_mode: str)
+(api_key: Optional[str], admins: Optional[List[str]], override: OverrideConfig, auth_mode: str)
-
@@ -759,9 +782,14 @@
Classes
class DashboardConfig:
def __init__(
- self, api_key: Union[str, None], override: OverrideConfig, auth_mode: str
+ self,
+ api_key: Optional[str],
+ admins: Optional[List[str]],
+ override: OverrideConfig,
+ auth_mode: str,
):
self.api_key = api_key
+ self.admins = admins
self.override = override
self.auth_mode = auth_mode
diff --git a/html/supertokens_python/recipe/session/asyncio/index.html b/html/supertokens_python/recipe/session/asyncio/index.html
index 4bd8ea7f5..4b08c49d3 100644
--- a/html/supertokens_python/recipe/session/asyncio/index.html
+++ b/html/supertokens_python/recipe/session/asyncio/index.html
@@ -69,6 +69,7 @@ Module supertokens_python.recipe.session.asyncio
<
get_session_from_request,
refresh_session_in_request,
)
+from ..constants import protected_props
from ..utils import get_required_claim_validators
from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
@@ -134,6 +135,10 @@ Module supertokens_python.recipe.session.asyncio
<
final_access_token_payload = {**access_token_payload, "iss": issuer}
+ for prop in protected_props:
+ if prop in final_access_token_payload:
+ del final_access_token_payload[prop]
+
for claim in claims_added_by_other_recipes:
update = await claim.build(user_id, tenant_id, user_context)
final_access_token_payload = {**final_access_token_payload, **update}
@@ -668,6 +673,10 @@ Functions
final_access_token_payload = {**access_token_payload, "iss": issuer}
+ for prop in protected_props:
+ if prop in final_access_token_payload:
+ del final_access_token_payload[prop]
+
for claim in claims_added_by_other_recipes:
update = await claim.build(user_id, tenant_id, user_context)
final_access_token_payload = {**final_access_token_payload, **update}
diff --git a/html/supertokens_python/recipe/session/constants.html b/html/supertokens_python/recipe/session/constants.html
index cf4eb291e..1c7197924 100644
--- a/html/supertokens_python/recipe/session/constants.html
+++ b/html/supertokens_python/recipe/session/constants.html
@@ -70,6 +70,7 @@ Module supertokens_python.recipe.session.constants
diff --git a/html/supertokens_python/recipe/session/recipe_implementation.html b/html/supertokens_python/recipe/session/recipe_implementation.html
index 7b84df195..59376da7c 100644
--- a/html/supertokens_python/recipe/session/recipe_implementation.html
+++ b/html/supertokens_python/recipe/session/recipe_implementation.html
@@ -75,6 +75,7 @@ Module supertokens_python.recipe.session.recipe_implemen
from supertokens_python import AppInfo
from .interfaces import SessionContainer
+from .constants import protected_props
from supertokens_python.querier import Querier
from supertokens_python.recipe.multitenancy.constants import DEFAULT_TENANT_ID
@@ -406,8 +407,13 @@ Module supertokens_python.recipe.session.recipe_implemen
if session_info is None:
return False
+ new_access_token_payload = session_info.custom_claims_in_access_token_payload
+ for k in protected_props:
+ if k in new_access_token_payload:
+ del new_access_token_payload[k]
+
new_access_token_payload = {
- **session_info.custom_claims_in_access_token_payload,
+ **new_access_token_payload,
**access_token_payload_update,
}
for k in access_token_payload_update.keys():
@@ -860,8 +866,13 @@ Classes
if session_info is None:
return False
+ new_access_token_payload = session_info.custom_claims_in_access_token_payload
+ for k in protected_props:
+ if k in new_access_token_payload:
+ del new_access_token_payload[k]
+
new_access_token_payload = {
- **session_info.custom_claims_in_access_token_payload,
+ **new_access_token_payload,
**access_token_payload_update,
}
for k in access_token_payload_update.keys():
@@ -1270,8 +1281,13 @@ Methods
if session_info is None:
return False
+ new_access_token_payload = session_info.custom_claims_in_access_token_payload
+ for k in protected_props:
+ if k in new_access_token_payload:
+ del new_access_token_payload[k]
+
new_access_token_payload = {
- **session_info.custom_claims_in_access_token_payload,
+ **new_access_token_payload,
**access_token_payload_update,
}
for k in access_token_payload_update.keys():
diff --git a/html/supertokens_python/recipe/session/session_request_functions.html b/html/supertokens_python/recipe/session/session_request_functions.html
index 50c0f6a92..f7f79339b 100644
--- a/html/supertokens_python/recipe/session/session_request_functions.html
+++ b/html/supertokens_python/recipe/session/session_request_functions.html
@@ -88,6 +88,7 @@ Module supertokens_python.recipe.session.session_request
set_request_in_user_context_if_not_defined,
)
from supertokens_python.supertokens import Supertokens
+from .constants import protected_props
if TYPE_CHECKING:
from supertokens_python.recipe.session.recipe import SessionRecipe
@@ -268,6 +269,10 @@ Module supertokens_python.recipe.session.session_request
final_access_token_payload = {**access_token_payload, "iss": issuer}
+ for prop in protected_props:
+ if prop in final_access_token_payload:
+ del final_access_token_payload[prop]
+
for claim in claims_added_by_other_recipes:
update = await claim.build(user_id, tenant_id, user_context)
final_access_token_payload = {**final_access_token_payload, **update}
@@ -538,6 +543,10 @@ Functions
final_access_token_payload = {**access_token_payload, "iss": issuer}
+ for prop in protected_props:
+ if prop in final_access_token_payload:
+ del final_access_token_payload[prop]
+
for claim in claims_added_by_other_recipes:
update = await claim.build(user_id, tenant_id, user_context)
final_access_token_payload = {**final_access_token_payload, **update}
diff --git a/html/supertokens_python/utils.html b/html/supertokens_python/utils.html
index c6ca29c90..9664de780 100644
--- a/html/supertokens_python/utils.html
+++ b/html/supertokens_python/utils.html
@@ -387,7 +387,11 @@ Module supertokens_python.utils
self.mutex.unlock()
if exc_type is not None:
- raise exc_type(exc_value).with_traceback(traceback)
+ raise exc_type(exc_value).with_traceback(traceback)
+
+
+def normalise_email(email: str) -> str:
+ return email.strip().lower()
@@ -702,6 +706,19 @@ Functions
return _get_max_version(version, minimum_version) == version
+
+def normalise_email(email: str) ‑> str
+
+-
+
+
+
+Expand source code
+
+def normalise_email(email: str) -> str:
+ return email.strip().lower()
+
+
def normalise_http_method(method: str) ‑> str
@@ -1036,6 +1053,7 @@ Index
is_5xx_error
is_an_ip_address
is_version_gte
+normalise_email
normalise_http_method
resolve
send_200_response