Skip to content

Commit 8a0ac5a

Browse files
Merge pull request #386 from supertokens/fix/tenantid-config-func
fix: Add tenant id in config functions
2 parents 9abfbcb + 54e8866 commit 8a0ac5a

File tree

45 files changed

+285
-495
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+285
-495
lines changed

supertokens_python/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
def raise_general_exception(
2020
msg: Union[str, Exception], previous: Union[None, Exception] = None
21-
):
21+
) -> NoReturn:
2222
if isinstance(msg, SuperTokensError):
2323
raise msg
2424
if isinstance(msg, Exception):

supertokens_python/recipe/dashboard/api/userdetails/user_password_put.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ async def reset_password(
9696
field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
9797
][0]
9898

99-
password_validation_error = await password_form_field.validate(new_password)
99+
password_validation_error = await password_form_field.validate(
100+
new_password, tenant_id
101+
)
100102

101103
if password_validation_error is not None:
102104
return UserPasswordPutAPIInvalidPasswordErrorResponse(

supertokens_python/recipe/dashboard/api/userdetails/user_put.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@
5858

5959

6060
async def update_email_for_recipe_id(
61-
recipe_id: str, user_id: str, email: str, user_context: Dict[str, Any]
61+
recipe_id: str,
62+
user_id: str,
63+
email: str,
64+
tenant_id: str,
65+
user_context: Dict[str, Any],
6266
) -> Union[
6367
UserPutAPIOkResponse,
6468
UserPutAPIInvalidEmailErrorResponse,
@@ -76,7 +80,7 @@ async def update_email_for_recipe_id(
7680
if form_field.id == FORM_FIELD_EMAIL_ID
7781
]
7882

79-
validation_error = await email_form_fields[0].validate(email)
83+
validation_error = await email_form_fields[0].validate(email, tenant_id)
8084

8185
if validation_error is not None:
8286
return UserPutAPIInvalidEmailErrorResponse(validation_error)
@@ -102,7 +106,7 @@ async def update_email_for_recipe_id(
102106
if form_field.id == FORM_FIELD_EMAIL_ID
103107
]
104108

105-
validation_error = await email_form_fields[0].validate(email)
109+
validation_error = await email_form_fields[0].validate(email, tenant_id)
106110

107111
if validation_error is not None:
108112
return UserPutAPIInvalidEmailErrorResponse(validation_error)
@@ -127,12 +131,14 @@ async def update_email_for_recipe_id(
127131
passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
128132

129133
if isinstance(passwordless_config.contact_method, ContactPhoneOnlyConfig):
130-
validation_error = await default_validate_email(email)
134+
validation_error = await default_validate_email(email, tenant_id)
131135

132136
elif isinstance(
133137
passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
134138
):
135-
validation_error = await passwordless_config.validate_email_address(email)
139+
validation_error = await passwordless_config.validate_email_address(
140+
email, tenant_id
141+
)
136142

137143
if validation_error is not None:
138144
return UserPutAPIInvalidEmailErrorResponse(validation_error)
@@ -157,11 +163,13 @@ async def update_email_for_recipe_id(
157163
)
158164

159165
if isinstance(passwordless_config, ContactPhoneOnlyConfig):
160-
validation_error = await default_validate_email(email)
166+
validation_error = await default_validate_email(email, tenant_id)
161167
elif isinstance(
162168
passwordless_config, (ContactEmailOnlyConfig, ContactEmailOrPhoneConfig)
163169
):
164-
validation_error = await passwordless_config.validate_email_address(email)
170+
validation_error = await passwordless_config.validate_email_address(
171+
email, tenant_id
172+
)
165173

166174
if validation_error is not None:
167175
return UserPutAPIInvalidEmailErrorResponse(validation_error)
@@ -183,7 +191,11 @@ async def update_email_for_recipe_id(
183191

184192

185193
async def update_phone_for_recipe_id(
186-
recipe_id: str, user_id: str, phone: str, user_context: Dict[str, Any]
194+
recipe_id: str,
195+
user_id: str,
196+
phone: str,
197+
tenant_id: str,
198+
user_context: Dict[str, Any],
187199
) -> Union[
188200
UserPutAPIOkResponse,
189201
UserPutAPIInvalidPhoneErrorResponse,
@@ -197,11 +209,13 @@ async def update_phone_for_recipe_id(
197209
passwordless_config = PasswordlessRecipe.get_instance().config.contact_config
198210

199211
if isinstance(passwordless_config, ContactEmailOnlyConfig):
200-
validation_error = await default_validate_phone_number(phone)
212+
validation_error = await default_validate_phone_number(phone, tenant_id)
201213
elif isinstance(
202214
passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
203215
):
204-
validation_error = await passwordless_config.validate_phone_number(phone)
216+
validation_error = await passwordless_config.validate_phone_number(
217+
phone, tenant_id
218+
)
205219

206220
if validation_error is not None:
207221
return UserPutAPIInvalidPhoneErrorResponse(validation_error)
@@ -226,12 +240,14 @@ async def update_phone_for_recipe_id(
226240
)
227241

228242
if isinstance(passwordless_config, ContactEmailOnlyConfig):
229-
validation_error = await default_validate_phone_number(phone)
243+
validation_error = await default_validate_phone_number(phone, tenant_id)
230244

231245
elif isinstance(
232246
passwordless_config, (ContactPhoneOnlyConfig, ContactEmailOrPhoneConfig)
233247
):
234-
validation_error = await passwordless_config.validate_phone_number(phone)
248+
validation_error = await passwordless_config.validate_phone_number(
249+
phone, tenant_id
250+
)
235251

236252
if validation_error is not None:
237253
return UserPutAPIInvalidPhoneErrorResponse(validation_error)
@@ -254,7 +270,7 @@ async def update_phone_for_recipe_id(
254270

255271
async def handle_user_put(
256272
_api_interface: APIInterface,
257-
_tenant_id: str,
273+
tenant_id: str,
258274
api_options: APIOptions,
259275
user_context: Dict[str, Any],
260276
) -> Union[
@@ -337,15 +353,15 @@ async def handle_user_put(
337353

338354
if email != "":
339355
email_update_response = await update_email_for_recipe_id(
340-
user_response.recipe, user_id, email, user_context
356+
user_response.recipe, user_id, email, tenant_id, user_context
341357
)
342358

343359
if not isinstance(email_update_response, UserPutAPIOkResponse):
344360
return email_update_response
345361

346362
if phone != "":
347363
phone_update_response = await update_phone_for_recipe_id(
348-
user_response.recipe, user_id, phone, user_context
364+
user_response.recipe, user_id, phone, tenant_id, user_context
349365
)
350366

351367
if not isinstance(phone_update_response, UserPutAPIOkResponse):

supertokens_python/recipe/emailpassword/__init__.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
exceptions = ex
2727
InputOverrideConfig = utils.InputOverrideConfig
28-
InputResetPasswordUsingTokenFeature = utils.InputResetPasswordUsingTokenFeature
2928
InputSignUpFeature = utils.InputSignUpFeature
3029
InputFormField = utils.InputFormField
3130
SMTPService = emaildelivery_services.SMTPService
@@ -38,15 +37,11 @@
3837

3938
def init(
4039
sign_up_feature: Union[utils.InputSignUpFeature, None] = None,
41-
reset_password_using_token_feature: Union[
42-
utils.InputResetPasswordUsingTokenFeature, None
43-
] = None,
4440
override: Union[utils.InputOverrideConfig, None] = None,
4541
email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None,
4642
) -> Callable[[AppInfo], RecipeModule]:
4743
return EmailPasswordRecipe.init(
4844
sign_up_feature,
49-
reset_password_using_token_feature,
5045
override,
5146
email_delivery,
5247
)

supertokens_python/recipe/emailpassword/api/generate_password_reset_token.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ async def handle_generate_password_reset_token_api(
4242
form_fields = await validate_form_fields_or_throw_error(
4343
api_options.config.reset_password_using_token_feature.form_fields_for_generate_token_form,
4444
form_fields_raw,
45+
tenant_id,
4546
)
4647

4748
response = await api_implementation.generate_password_reset_token_post(

supertokens_python/recipe/emailpassword/api/password_reset.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ async def handle_password_reset_api(
4242
form_fields = await validate_form_fields_or_throw_error(
4343
api_options.config.reset_password_using_token_feature.form_fields_for_password_reset_form,
4444
form_fields_raw,
45+
tenant_id,
4546
)
4647

4748
if "token" not in body:

supertokens_python/recipe/emailpassword/api/signin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async def handle_sign_in_api(
4040
raise_bad_input_exception("Please provide a JSON body")
4141
form_fields_raw: Any = body["formFields"] if "formFields" in body else []
4242
form_fields = await validate_form_fields_or_throw_error(
43-
api_options.config.sign_in_feature.form_fields, form_fields_raw
43+
api_options.config.sign_in_feature.form_fields, form_fields_raw, tenant_id
4444
)
4545

4646
response = await api_implementation.sign_in_post(

supertokens_python/recipe/emailpassword/api/signup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async def handle_sign_up_api(
4646
raise_bad_input_exception("Please provide a JSON body")
4747
form_fields_raw: Any = body["formFields"] if "formFields" in body else []
4848
form_fields = await validate_form_fields_or_throw_error(
49-
api_options.config.sign_up_feature.form_fields, form_fields_raw
49+
api_options.config.sign_up_feature.form_fields, form_fields_raw, tenant_id
5050
)
5151

5252
response = await api_implementation.sign_up_post(

supertokens_python/recipe/emailpassword/api/utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929

3030

3131
async def validate_form_or_throw_error(
32-
inputs: List[FormField], config_form_fields: List[NormalisedFormField]
32+
inputs: List[FormField],
33+
config_form_fields: List[NormalisedFormField],
34+
tenant_id: str,
3335
):
3436
validation_errors: List[ErrorFormField] = []
3537
if len(config_form_fields) != len(inputs):
@@ -42,7 +44,7 @@ async def validate_form_or_throw_error(
4244
if input_field is None or (input_field.value == "" and not field.optional):
4345
validation_errors.append(ErrorFormField(field.id, "Field is not optional"))
4446
else:
45-
error = await field.validate(input_field.value)
47+
error = await field.validate(input_field.value, tenant_id)
4648
if error is not None:
4749
validation_errors.append(ErrorFormField(field.id, error))
4850

@@ -52,7 +54,7 @@ async def validate_form_or_throw_error(
5254

5355

5456
async def validate_form_fields_or_throw_error(
55-
config_form_fields: List[NormalisedFormField], form_fields_raw: Any
57+
config_form_fields: List[NormalisedFormField], form_fields_raw: Any, tenant_id: str
5658
) -> List[FormField]:
5759
if form_fields_raw is None:
5860
raise_bad_input_exception("Missing input param: formFields")
@@ -77,5 +79,5 @@ async def validate_form_fields_or_throw_error(
7779
value = value.strip()
7880
form_fields.append(FormField(current_form_field["id"], value))
7981

80-
await validate_form_or_throw_error(form_fields, config_form_fields)
82+
await validate_form_or_throw_error(form_fields, config_form_fields, tenant_id)
8183
return form_fields

supertokens_python/recipe/emailpassword/asyncio/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,18 @@ async def update_email_or_password(
3737
email: Union[str, None] = None,
3838
password: Union[str, None] = None,
3939
apply_password_policy: Union[bool, None] = None,
40+
tenant_id_for_password_policy: Optional[str] = None,
4041
user_context: Union[None, Dict[str, Any]] = None,
4142
):
4243
if user_context is None:
4344
user_context = {}
4445
return await EmailPasswordRecipe.get_instance().recipe_implementation.update_email_or_password(
45-
user_id, email, password, apply_password_policy, user_context
46+
user_id,
47+
email,
48+
password,
49+
apply_password_policy,
50+
tenant_id_for_password_policy or DEFAULT_TENANT_ID,
51+
user_context,
4652
)
4753

4854

supertokens_python/recipe/emailpassword/emaildelivery/services/backward_compatibility/__init__.py

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from __future__ import annotations
1515

1616
from os import environ
17-
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
17+
from typing import Any, Dict
1818

1919
from httpx import AsyncClient
2020

@@ -28,33 +28,26 @@
2828
from supertokens_python.supertokens import AppInfo
2929
from supertokens_python.utils import handle_httpx_client_exceptions
3030

31-
if TYPE_CHECKING:
32-
from supertokens_python.recipe.emailpassword.utils import (
33-
InputResetPasswordUsingTokenFeature,
34-
)
3531

32+
async def create_and_send_email_using_supertokens_service(
33+
app_info: AppInfo, user: User, password_reset_url_with_token: str
34+
) -> None:
35+
if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
36+
return
3637

37-
def default_create_and_send_custom_email(
38-
app_info: AppInfo,
39-
) -> Callable[[User, str, Dict[str, Any]], Awaitable[None]]:
40-
async def func(user: User, password_reset_url_with_token: str, _: Dict[str, Any]):
41-
if ("SUPERTOKENS_ENV" in environ) and (environ["SUPERTOKENS_ENV"] == "testing"):
42-
return
43-
data = {
44-
"email": user.email,
45-
"appName": app_info.app_name,
46-
"passwordResetURL": password_reset_url_with_token,
47-
}
48-
try:
49-
async with AsyncClient() as client:
50-
resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"}) # type: ignore
51-
resp.raise_for_status()
52-
log_debug_message("Password reset email sent to %s", user.email)
53-
except Exception as e:
54-
log_debug_message("Error sending password reset email")
55-
handle_httpx_client_exceptions(e, data)
56-
57-
return func
38+
data = {
39+
"email": user.email,
40+
"appName": app_info.app_name,
41+
"passwordResetURL": password_reset_url_with_token,
42+
}
43+
try:
44+
async with AsyncClient() as client:
45+
resp = await client.post("https://api.supertokens.io/0/st/auth/password/reset", json=data, headers={"api-version": "0"}) # type: ignore
46+
resp.raise_for_status()
47+
log_debug_message("Password reset email sent to %s", user.email)
48+
except Exception as e:
49+
log_debug_message("Error sending password reset email")
50+
handle_httpx_client_exceptions(e, data)
5851

5952

6053
class BackwardCompatibilityService(EmailDeliveryInterface[EmailTemplateVars]):
@@ -64,27 +57,9 @@ def __init__(
6457
self,
6558
app_info: AppInfo,
6659
recipe_interface_impl: RecipeInterface,
67-
reset_password_using_token_feature: Union[
68-
InputResetPasswordUsingTokenFeature, None
69-
] = None,
7060
) -> None:
7161
self.recipe_interface_impl = recipe_interface_impl
72-
73-
reset_password_feature_send_email_func = default_create_and_send_custom_email(
74-
app_info
75-
)
76-
if (
77-
reset_password_using_token_feature
78-
and reset_password_using_token_feature.create_and_send_custom_email
79-
is not None
80-
):
81-
reset_password_feature_send_email_func = (
82-
reset_password_using_token_feature.create_and_send_custom_email
83-
)
84-
85-
self.reset_password_feature_send_email_func = (
86-
reset_password_feature_send_email_func
87-
)
62+
self.app_info = app_info
8863

8964
async def send_email(
9065
self,
@@ -102,8 +77,8 @@ async def send_email(
10277
# will get reset by the getUserById call above.
10378
user.email = template_vars.user.email
10479
try:
105-
await self.reset_password_feature_send_email_func(
106-
user, template_vars.password_reset_link, user_context
80+
await create_and_send_email_using_supertokens_service(
81+
self.app_info, user, template_vars.password_reset_link
10782
)
10883
except Exception:
10984
pass

supertokens_python/recipe/emailpassword/interfaces.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ async def update_email_or_password(
155155
email: Union[str, None],
156156
password: Union[str, None],
157157
apply_password_policy: Union[bool, None],
158+
tenant_id_for_password_policy: str,
158159
user_context: Dict[str, Any],
159160
) -> Union[
160161
UpdateEmailOrPasswordOkResult,

0 commit comments

Comments
 (0)