Skip to content

Commit

Permalink
gets more tests to pass
Browse files Browse the repository at this point in the history
  • Loading branch information
rishabhpoddar committed Oct 9, 2024
1 parent 9e181dd commit 364ccaa
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 29 deletions.
Empty file added tests/test-server/__init__.py
Empty file.
48 changes: 38 additions & 10 deletions tests/test-server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
from flask import Flask, request, jsonify
from supertokens_python.framework import BaseRequest
from supertokens_python.ingredients.emaildelivery.types import EmailDeliveryConfig
from supertokens_python.recipe import accountlinking
from supertokens_python.recipe import accountlinking, multifactorauth
from supertokens_python.recipe.accountlinking.recipe import AccountLinkingRecipe
from supertokens_python.recipe.multifactorauth.recipe import MultiFactorAuthRecipe
from supertokens_python.recipe.totp.recipe import TOTPRecipe
from utils import init_test_claims
from utils import init_test_claims # pylint: disable=import-error
from supertokens_python.process_state import ProcessState
from supertokens_python.recipe.dashboard.recipe import DashboardRecipe
from supertokens_python.recipe.emailpassword.recipe import EmailPasswordRecipe
Expand All @@ -18,10 +18,17 @@
from supertokens_python.recipe.thirdparty.recipe import ThirdPartyRecipe
from supertokens_python.recipe.usermetadata.recipe import UserMetadataRecipe
from supertokens_python.recipe.userroles.recipe import UserRolesRecipe
from test_functions_mapper import get_func, get_override_params, reset_override_params
from emailpassword import add_emailpassword_routes
from multitenancy import add_multitenancy_routes
from session import add_session_routes
from test_functions_mapper import ( # pylint: disable=import-error
get_func,
get_override_params,
reset_override_params,
) # pylint: disable=import-error
from emailpassword import add_emailpassword_routes # pylint: disable=import-error
from multitenancy import add_multitenancy_routes # pylint: disable=import-error
from emailverification import (
add_emailverification_routes,
) # pylint: disable=import-error
from session import add_session_routes # pylint: disable=import-error
from supertokens_python import (
AppInfo,
Supertokens,
Expand Down Expand Up @@ -52,8 +59,11 @@


def default_st_init():
def origin_func(
request: Optional[BaseRequest] = None, context: Dict[str, Any] = {}
def origin_func( # pylint: disable=unused-argument, dangerous-default-value
request: Optional[BaseRequest] = None,
context: Dict[ # pylint: disable=unused-argument, dangerous-default-value
str, Any
] = {}, # pylint: disable=unused-argument, dangerous-default-value
) -> str:
if request is None:
return "http://localhost:8080"
Expand Down Expand Up @@ -150,7 +160,9 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
impl = get_func(override_name)
else:

async def default_func(*args: Any, **kwargs: Any) -> Any:
async def default_func( # pylint: disable=unused-argument
*args: Any, **kwargs: Any # pylint: disable=unused-argument
) -> Any: # pylint: disable=unused-argument
return default_value

impl = default_func
Expand Down Expand Up @@ -388,6 +400,21 @@ def init_st(config: Dict[str, Any]):
functions=override_functions
)
recipe_list.append(emailverification.init(**ev_config))
elif recipe_id == "multifactorauth":
recipe_config_json = json.loads(recipe_config.get("config", "{}"))
recipe_list.append(
multifactorauth.init(
first_factors=recipe_config_json.get("firstFactors", None),
override=multifactorauth.OverrideConfig(
functions=override_builder_with_logging(
"MultifactorAuth.override.functions",
recipe_config_json.get("override", {}).get(
"functions", None
),
),
),
)
)

interceptor_func = None
if config.get("supertokens", {}).get("networkInterceptor") is not None:
Expand Down Expand Up @@ -536,13 +563,14 @@ def verify_session_route():


@app.errorhandler(404)
def not_found(error: Any) -> Any:
def not_found(error: Any) -> Any: # pylint: disable=unused-argument
return jsonify({"error": f"Route not found: {request.method} {request.path}"}), 404


add_emailpassword_routes(app)
add_multitenancy_routes(app)
add_session_routes(app)
add_emailverification_routes(app)

init_test_claims()

Expand Down
55 changes: 40 additions & 15 deletions tests/test-server/emailpassword.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
UnknownUserIdError,
UpdateEmailOrPasswordEmailChangeNotAllowedError,
UpdateEmailOrPasswordOkResult,
WrongCredentialsError,
)
import supertokens_python.recipe.emailpassword.syncio as emailpassword
from session import convert_session_to_container # pylint: disable=import-error
from utils import ( # pylint: disable=import-error
serialize_user,
serialize_recipe_user_id,
) # pylint: disable=import-error


def add_emailpassword_routes(app: Flask):
Expand All @@ -20,23 +26,35 @@ def emailpassword_signup(): # type: ignore
email = data["email"]
password = data["password"]
user_context = data.get("userContext")
session = (
convert_session_to_container(data["session"]) if "session" in data else None
)

response = emailpassword.sign_up(tenant_id, email, password, user_context)
response = emailpassword.sign_up(
tenant_id, email, password, session, user_context
)

if isinstance(response, SignUpOkResult):
return jsonify(
{
"status": "OK",
"user": {
"id": response.user.id,
"email": response.user.emails[0],
"timeJoined": response.user.time_joined,
"tenantIds": response.user.tenant_ids,
},
**serialize_user(
response.user, request.headers.get("fdi-version", "")
),
**serialize_recipe_user_id(
response.recipe_user_id, request.headers.get("fdi-version", "")
),
}
)
else:
elif isinstance(response, EmailAlreadyExistsError):
return jsonify({"status": "EMAIL_ALREADY_EXISTS_ERROR"})
else:
return jsonify(
{
"status": response.status,
"reason": response.reason,
}
)

@app.route("/test/emailpassword/signin", methods=["POST"]) # type: ignore
def emailpassword_signin(): # type: ignore
Expand All @@ -55,16 +73,23 @@ def emailpassword_signin(): # type: ignore
return jsonify(
{
"status": "OK",
"user": {
"id": response.user.id,
"email": response.user.emails[0],
"timeJoined": response.user.time_joined,
"tenantIds": response.user.tenant_ids,
},
**serialize_user(
response.user, request.headers.get("fdi-version", "")
),
**serialize_recipe_user_id(
response.recipe_user_id, request.headers.get("fdi-version", "")
),
}
)
else:
elif isinstance(response, WrongCredentialsError):
return jsonify({"status": "WRONG_CREDENTIALS_ERROR"})
else:
return jsonify(
{
"status": response.status,
"reason": response.reason,
}
)

@app.route("/test/emailpassword/createresetpasswordlink", methods=["POST"]) # type: ignore
def emailpassword_create_reset_password_link(): # type: ignore
Expand Down
67 changes: 67 additions & 0 deletions tests/test-server/emailverification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from flask import Flask, request, jsonify

from supertokens_python.recipe.emailverification.interfaces import (
CreateEmailVerificationTokenOkResult,
VerifyEmailUsingTokenOkResult,
)
from supertokens_python.recipe.emailverification.syncio import (
create_email_verification_token,
)


def add_emailverification_routes(app: Flask):
@app.route("/test/emailverification/createemailverificationtoken", methods=["POST"]) # type: ignore
def f(): # type: ignore
from supertokens_python import convert_to_recipe_user_id

data = request.json
if data is None:
return jsonify({"status": "MISSING_DATA_ERROR"})

recipe_user_id = convert_to_recipe_user_id(data["recipeUserId"])
tenant_id = data.get("tenantId", "public")
email = None if "email" not in data else data["email"]
user_context = data.get("userContext")

response = create_email_verification_token(
tenant_id, recipe_user_id, email, user_context
)

if isinstance(response, CreateEmailVerificationTokenOkResult):
return jsonify({"status": "OK", "token": response.token})
else:
return jsonify({"status": "EMAIL_ALREADY_VERIFIED_ERROR"})

@app.route("/test/emailverification/verifyemailusingtoken", methods=["POST"]) # type: ignore
def f2(): # type: ignore
from supertokens_python.recipe.emailverification.syncio import (
verify_email_using_token,
)

data = request.json
if data is None:
return jsonify({"status": "MISSING_DATA_ERROR"})

tenant_id = data.get("tenantId", "public")
token = data["token"]
attempt_account_linking = data.get("attemptAccountLinking", False)
user_context = data.get("userContext", {})

response = verify_email_using_token(
tenant_id, token, attempt_account_linking, user_context
)

if isinstance(response, VerifyEmailUsingTokenOkResult):
return jsonify(
{
"status": "OK",
"user": {
"email": response.user.email,
"recipeUserId": {
"recipeUserId": response.user.recipe_user_id.get_as_string()
},
},
}
)
else:
return jsonify({"status": "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR"})
9 changes: 6 additions & 3 deletions tests/test-server/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
parse_jwt_without_signature_verification,
)
from supertokens_python.types import RecipeUserId
from utils import deserialize_validator, get_max_version
from utils import ( # pylint: disable=import-error
deserialize_validator,
get_max_version,
)
from supertokens_python.recipe.session.recipe import SessionRecipe
from supertokens_python.recipe.session.session_class import Session
import supertokens_python.recipe.session.syncio as session
Expand Down Expand Up @@ -175,12 +178,12 @@ def convert_session_to_container(data: Any) -> Session:
jwt_info = parse_jwt_without_signature_verification(data["session"]["accessToken"])
jwt_payload = jwt_info.payload

user_id = jwt_info.version == 2 and jwt_payload["userId"] or jwt_payload["sub"]
user_id = jwt_payload["userId"] if jwt_info.version == 2 else jwt_payload["sub"]
session_handle = jwt_payload["sessionHandle"]

recipe_user_id = RecipeUserId(jwt_payload.get("rsub", user_id))
anti_csrf_token = jwt_payload.get("antiCsrfToken")
tenant_id = jwt_info.version >= 4 and jwt_payload["tId"] or "public"
tenant_id = jwt_payload["tId"] if jwt_info.version >= 4 else "public"

return Session(
recipe_implementation=SessionRecipe.get_instance().recipe_implementation,
Expand Down
2 changes: 1 addition & 1 deletion tests/test-server/test_functions_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def func(*args): # type: ignore
elif eval_str.startswith("accountlinking.init.shouldDoAutomaticAccountLinking"):

async def func(
i: Any, l: Any, o: Any, u: Any, a: Any
i: Any, l: Any, o: Any, u: Any, a: Any # pylint: disable=unused-argument
) -> Union[ShouldNotAutomaticallyLink, ShouldAutomaticallyLink]:
if (
"()=>({shouldAutomaticallyLink:!0,shouldRequireVerification:!1})"
Expand Down
30 changes: 30 additions & 0 deletions tests/test-server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from supertokens_python.recipe.session.claims import SessionClaim

from supertokens_python.recipe.session.interfaces import SessionClaimValidator
from supertokens_python.types import RecipeUserId, User

test_claims: Dict[str, SessionClaim] = {} # type: ignore

Expand Down Expand Up @@ -56,3 +57,32 @@ def get_max_version(v1: str, v2: str) -> str:
return v1

return v2


def serialize_user(user: User, fdi_version: str) -> Dict[str, Any]:
if get_max_version("1.17", fdi_version) == "1.17" or (
get_max_version("2.0", fdi_version) == fdi_version
and get_max_version("3.0", fdi_version) != fdi_version
):
return {
"user": {
"id": user.id,
"email": user.emails[0],
"timeJoined": user.time_joined,
"tenantIds": user.tenant_ids,
}
}
else:
return {"user": user.to_json()}


def serialize_recipe_user_id(
recipe_user_id: RecipeUserId, fdi_version: str
) -> Dict[str, Any]:
if get_max_version("1.17", fdi_version) == "1.17" or (
get_max_version("2.0", fdi_version) == fdi_version
and get_max_version("3.0", fdi_version) != fdi_version
):
return {}
else:
return {"recipeUserId": recipe_user_id.get_as_string()}

0 comments on commit 364ccaa

Please sign in to comment.