Skip to content

Commit

Permalink
Merge pull request #528 from supertokens/auto-transpiling-e2e-tests
Browse files Browse the repository at this point in the history
auto transpile: pr 1
  • Loading branch information
rishabhpoddar authored Sep 30, 2024
2 parents 8e3a7b2 + 91dcf57 commit cfe84f5
Show file tree
Hide file tree
Showing 33 changed files with 790 additions and 148 deletions.
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ disable=raw-checker-failed,
consider-using-in,
no-else-return,
no-self-use,
no-else-raise
no-else-raise,
too-many-nested-blocks,


# Enable the message, report, category or checker with the given id(s). You can
Expand Down
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
"FLASK_DEBUG": "1"
},
"jinja": true
},
{
"name": "Python: Flask, supertokens-auth-react tests",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/tests/auth-react/flask-server/app.py",
"args": [
"--port",
"8083"
],
"cwd": "${workspaceFolder}/tests/auth-react/flask-server",
"env": {
"FLASK_DEBUG": "1"
},
"jinja": true
}
]
}
5 changes: 5 additions & 0 deletions supertokens_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing_extensions import Literal

from supertokens_python.framework.request import BaseRequest
from supertokens_python.types import RecipeUserId

from . import supertokens
from .recipe_module import RecipeModule
Expand Down Expand Up @@ -49,3 +50,7 @@ def get_request_from_user_context(
user_context: Optional[Dict[str, Any]],
) -> Optional[BaseRequest]:
return Supertokens.get_instance().get_request_from_user_context(user_context)


def convert_to_recipe_user_id(user_id: str) -> RecipeUserId:
return RecipeUserId(user_id)
7 changes: 5 additions & 2 deletions supertokens_python/auth_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -912,19 +912,22 @@ async def filter_out_invalid_second_factors_or_throw_if_all_are_invalid(
factors_set_up_for_user_prom: Optional[List[str]] = None
mfa_info_prom = None

async def get_factors_set_up_for_user():
async def get_factors_set_up_for_user() -> List[str]:
nonlocal factors_set_up_for_user_prom
if factors_set_up_for_user_prom is None:
factors_set_up_for_user_prom = await mfa_instance.recipe_implementation.get_factors_setup_for_user(
user=session_user, user_context=user_context
)
assert factors_set_up_for_user_prom is not None
return factors_set_up_for_user_prom

async def get_mfa_requirements_for_auth():
nonlocal mfa_info_prom
if mfa_info_prom is None:

mfa_info_prom = await update_and_get_mfa_related_info_in_session(
input_session=session, user_context=user_context
input_session=session,
user_context=user_context,
)
return mfa_info_prom.mfa_requirements_for_auth

Expand Down
35 changes: 35 additions & 0 deletions supertokens_python/recipe/multifactorauth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations

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

from supertokens_python.recipe.multifactorauth.types import OverrideConfig

from .recipe import MultiFactorAuthRecipe

if TYPE_CHECKING:
from supertokens_python.supertokens import AppInfo

from ...recipe_module import RecipeModule


def init(
first_factors: Optional[List[str]] = None,
override: Union[OverrideConfig, None] = None,
) -> Callable[[AppInfo], RecipeModule]:
return MultiFactorAuthRecipe.init(
first_factors,
override,
)
25 changes: 12 additions & 13 deletions supertokens_python/recipe/multifactorauth/api/implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,22 @@
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations
import importlib

from typing import TYPE_CHECKING, Any, Dict, List, Union
from typing import Any, Dict, List, Union
from supertokens_python.recipe.multifactorauth.multi_factor_auth_claim import (
MultiFactorAuthClaim,
)

from supertokens_python.recipe.session import SessionContainer
from supertokens_python.recipe.multifactorauth.utils import (
update_and_get_mfa_related_info_in_session,
)
from supertokens_python.recipe.multitenancy.asyncio import get_tenant
from ..multi_factor_auth_claim import MultiFactorAuthClaim
from supertokens_python.asyncio import get_user
from supertokens_python.recipe.session.exceptions import (
InvalidClaimsError,
SuperTokensSessionError,
UnauthorisedError,
)

if TYPE_CHECKING:
from supertokens_python.recipe.multifactorauth.interfaces import (
APIInterface,
APIOptions,
)

from supertokens_python.types import GeneralErrorResponse
from ..interfaces import (
APIInterface,
Expand All @@ -50,14 +44,19 @@ async def resync_session_and_fetch_mfa_info_put(
session: SessionContainer,
user_context: Dict[str, Any],
) -> Union[ResyncSessionAndFetchMFAInfoPUTOkResult, GeneralErrorResponse]:

module = importlib.import_module(
"supertokens_python.recipe.multifactorauth.utils"
)

session_user = await get_user(session.get_user_id(), user_context)

if session_user is None:
raise UnauthorisedError(
"Session user not found",
)

mfa_info = await update_and_get_mfa_related_info_in_session(
mfa_info = await module.update_and_get_mfa_related_info_in_session(
input_session=session,
user_context=user_context,
)
Expand Down Expand Up @@ -144,7 +143,7 @@ async def get_mfa_requirements_for_auth():
)
return ResyncSessionAndFetchMFAInfoPUTOkResult(
factors=NextFactors(
next=next_factors,
next_=next_factors,
already_setup=factors_setup_for_user,
allowed_to_setup=factors_allowed_to_setup,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@


async def handle_resync_session_and_fetch_mfa_info_api(
tenant_id: str,
_tenant_id: str,
api_implementation: APIInterface,
api_options: APIOptions,
user_context: Dict[str, Any],
Expand Down
10 changes: 6 additions & 4 deletions supertokens_python/recipe/multifactorauth/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from ..types import (
MFARequirementList,
)
from ..recipe import MultiFactorAuthRecipe
from ..utils import update_and_get_mfa_related_info_in_session
from supertokens_python.recipe.accountlinking.asyncio import get_user

Expand All @@ -41,6 +40,7 @@ async def assert_allowed_to_setup_factor_else_throw_invalid_claim_error(
factors_set_up_for_user = await get_factors_setup_for_user(
session.get_user_id(), user_context
)
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()

Expand Down Expand Up @@ -81,6 +81,7 @@ async def mark_factor_as_complete_in_session(
) -> None:
if user_context is None:
user_context = {}
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()
await recipe.recipe_implementation.mark_factor_as_complete_in_session(
Expand All @@ -100,6 +101,7 @@ async def get_factors_setup_for_user(
user = await get_user(user_id, user_context)
if user is None:
raise Exception("Unknown user id")
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()
return await recipe.recipe_implementation.get_factors_setup_for_user(
Expand All @@ -114,6 +116,7 @@ async def get_required_secondary_factors_for_user(
) -> List[str]:
if user_context is None:
user_context = {}
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()
return await recipe.recipe_implementation.get_required_secondary_factors_for_user(
Expand All @@ -129,6 +132,7 @@ async def add_to_required_secondary_factors_for_user(
) -> None:
if user_context is None:
user_context = {}
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()
await recipe.recipe_implementation.add_to_required_secondary_factors_for_user(
Expand All @@ -145,13 +149,11 @@ async def remove_from_required_secondary_factors_for_user(
) -> None:
if user_context is None:
user_context = {}
from ..recipe import MultiFactorAuthRecipe

recipe = MultiFactorAuthRecipe.get_instance_or_throw_error()
await recipe.recipe_implementation.remove_from_required_secondary_factors_for_user(
user_id=user_id,
factor_id=factor_id,
user_context=user_context,
)


init = MultiFactorAuthRecipe.init
6 changes: 3 additions & 3 deletions supertokens_python/recipe/multifactorauth/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ async def resync_session_and_fetch_mfa_info_put(

class NextFactors:
def __init__(
self, next: List[str], already_setup: List[str], allowed_to_setup: List[str]
self, next_: List[str], already_setup: List[str], allowed_to_setup: List[str]
):
self.next = next
self.next_ = next_
self.already_setup = already_setup
self.allowed_to_setup = allowed_to_setup

def to_json(self) -> Dict[str, Any]:
return {
"next": self.next,
"next": self.next_,
"alreadySetup": self.already_setup,
"allowedToSetup": self.allowed_to_setup,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
# Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import annotations
import importlib

from typing import Any, Dict, Optional, Set

Expand All @@ -15,7 +30,6 @@
MFAClaimValue,
MFARequirementList,
)
from .utils import update_and_get_mfa_related_info_in_session


class HasCompletedRequirementListSCV(SessionClaimValidator):
Expand All @@ -29,14 +43,10 @@ def __init__(
self.claim: MultiFactorAuthClaimClass = claim
self.requirement_list = requirement_list

async def should_refetch(
def should_refetch(
self, payload: Dict[str, Any], user_context: Dict[str, Any]
) -> bool:
return (
True
if self.claim.key not in payload or not payload[self.claim.key]
else False
)
return bool(self.claim.key not in payload or not payload[self.claim.key])

async def validate(
self, payload: JSONObject, user_context: Dict[str, Any]
Expand Down Expand Up @@ -65,7 +75,7 @@ async def validate(

factor_ids = next_set_of_unsatisfied_factors.factor_ids

if next_set_of_unsatisfied_factors.type == "string":
if next_set_of_unsatisfied_factors.type_ == "string":
return ClaimValidationResult(
is_valid=False,
reason={
Expand All @@ -74,7 +84,7 @@ async def validate(
},
)

elif next_set_of_unsatisfied_factors.type == "oneOf":
elif next_set_of_unsatisfied_factors.type_ == "oneOf":
return ClaimValidationResult(
is_valid=False,
reason={
Expand All @@ -101,15 +111,11 @@ def __init__(
super().__init__(id_)
self.claim = claim

async def should_refetch(
def should_refetch(
self, payload: Dict[str, Any], user_context: Dict[str, Any]
) -> bool:
assert self.claim is not None
return (
True
if self.claim.key not in payload or not payload[self.claim.key]
else False
)
return bool(self.claim.key not in payload or not payload[self.claim.key])

async def validate(
self, payload: JSONObject, user_context: Dict[str, Any]
Expand Down Expand Up @@ -161,13 +167,17 @@ def __init__(self, key: Optional[str] = None):
key = key or "st-mfa"

async def fetch_value(
user_id: str,
_user_id: str,
recipe_user_id: RecipeUserId,
tenant_id: str,
current_payload: Dict[str, Any],
user_context: Dict[str, Any],
) -> MFAClaimValue:
mfa_info = await update_and_get_mfa_related_info_in_session(
module = importlib.import_module(
"supertokens_python.recipe.multifactorauth.utils"
)

mfa_info = await module.update_and_get_mfa_related_info_in_session(
input_session_recipe_user_id=recipe_user_id,
input_tenant_id=tenant_id,
input_access_token_payload=current_payload,
Expand Down Expand Up @@ -209,9 +219,11 @@ def get_next_set_of_unsatisfied_factors(
)

if len(next_factors) > 0:
return FactorIdsAndType(factor_ids=list(next_factors), type=factor_type)
return FactorIdsAndType(
factor_ids=list(next_factors), type_=factor_type
)

return FactorIdsAndType(factor_ids=[], type="string")
return FactorIdsAndType(factor_ids=[], type_="string")

def add_to_payload_(
self,
Expand Down
Loading

0 comments on commit cfe84f5

Please sign in to comment.