From 28e3e8b68e5ab66ab81268f291e1caf1c35c8675 Mon Sep 17 00:00:00 2001 From: Sattvik Chakravarthy Date: Thu, 28 Nov 2024 11:41:47 +0530 Subject: [PATCH] fix: dep updates (#545) * fix: dep updates * fix: circle ci unit test * fix: circle ci unit test * fix: backend-sdk-testing * fix: backend-sdk-testing * fix: lsof * fix: missing to_json and other tests * fix: website tests * fix: test servers * fix: deps for chrome * fix: xvfb for chrome * fix: bc * fix: consume code * fix: parallelise unit tests * fix: ci * fix: disable for testing * fix: enable auth-react * fix: update auth-react * fix: update auth-react * fix: mocha * fix: mocha * fix: mocha * fix: auth-react script * fix: stable unit tests and remove retry for auth react * fix: typo in mocha file * fix: mocha artifacts * fix: test reporting * fix: flask server * fix: test and response logging * fix: fast api response logging * ci: update node version * fix: enable all tests * fix: python versions * fix: python version --------- Co-authored-by: Mihaly Lengyel --- .circleci/authReactDjango.sh | 33 +-- .circleci/authReactFastApi.sh | 28 +-- .circleci/authReactFlask.sh | 28 +-- .circleci/config_continue.yml | 220 ++++++++++-------- .circleci/installJava.sh | 10 + .circleci/installNode.sh | 6 + .circleci/setupAndTestWithAuthReact.sh | 65 +++++- .../setupAndTestWithAuthReactWithDjango.sh | 69 +++++- .../setupAndTestWithAuthReactWithFlask.sh | 67 +++++- .circleci/setupAndTestWithFreeCore.sh | 6 +- .circleci/websiteDjango2x.sh | 5 + .circleci/websiteDrfAsync.sh | 5 + .circleci/websiteDrfSync.sh | 5 + .github/workflows/pre-commit-hook-run.yml | 115 ++++++++- .gitignore | 3 +- .pylintrc | 6 +- CHANGELOG.md | 5 + Makefile | 2 +- dev-requirements.txt | 108 ++------- .../project/urls.py | 1 + .../sampleapp/views.py | 3 + .../with-thirdpartyemailpassword/main.py | 4 +- .../with-thirdpartyemailpassword/app.py | 2 +- setup.py | 60 +++-- supertokens_python/auth_utils.py | 10 +- supertokens_python/constants.py | 2 +- .../framework/django/django_middleware.py | 20 +- .../framework/fastapi/fastapi_request.py | 3 + .../framework/fastapi/fastapi_response.py | 4 +- .../framework/flask/flask_middleware.py | 9 +- .../framework/flask/flask_request.py | 9 +- supertokens_python/framework/response.py | 4 +- .../emaildelivery/services/smtp.py | 4 +- supertokens_python/logger.py | 4 +- .../recipe/accountlinking/interfaces.py | 8 +- .../recipe/accountlinking/recipe.py | 7 +- .../recipe/accountlinking/types.py | 12 +- .../api/multitenancy/delete_tenant.py | 4 +- .../multitenancy/get_third_party_config.py | 12 +- .../update_tenant_first_factor.py | 4 +- .../update_tenant_secondary_factor.py | 4 +- .../dashboard/api/userdetails/user_get.py | 5 +- .../recipe/emailpassword/interfaces.py | 6 +- .../recipe/emailverification/recipe.py | 8 +- .../multi_factor_auth_claim.py | 11 +- .../multifactorauth/recipe_implementation.py | 11 +- .../multitenancy/recipe_implementation.py | 6 +- .../recipe/passwordless/api/implementation.py | 12 + .../recipe/passwordless/interfaces.py | 15 +- .../recipe/passwordless/recipe.py | 34 +-- .../passwordless/recipe_implementation.py | 2 +- .../smsdelivery/services/twilio/__init__.py | 2 +- .../recipe/session/api/refresh.py | 2 +- .../recipe/session/api/signout.py | 2 +- .../primitive_array_claim.py | 9 +- .../claim_base_classes/primitive_claim.py | 10 +- .../session/framework/fastapi/__init__.py | 4 +- supertokens_python/recipe/session/jwks.py | 1 + supertokens_python/recipe/session/recipe.py | 5 +- .../recipe/session/session_functions.py | 3 +- .../recipe/thirdparty/provider.py | 8 +- .../recipe/thirdparty/providers/apple.py | 10 +- .../recipe/thirdparty/providers/bitbucket.py | 6 +- .../recipe/thirdparty/providers/custom.py | 10 +- supertokens_python/recipe/totp/interfaces.py | 12 +- .../recipe/totp/recipe_implementation.py | 12 +- supertokens_python/recipe/totp/types.py | 8 +- .../recipe/userroles/recipe_implementation.py | 2 +- supertokens_python/supertokens.py | 16 +- supertokens_python/types.py | 6 +- tests/Django/test_django.py | 18 +- tests/Fastapi/test_fastapi.py | 10 +- tests/Flask/test_flask.py | 18 +- tests/Flask/utils.py | 4 +- .../auth-react/django3x/mysite/middleware.py | 27 ++- tests/auth-react/django3x/mysite/settings.py | 1 + tests/auth-react/django3x/mysite/store.py | 8 + tests/auth-react/django3x/mysite/urls.py | 1 + tests/auth-react/django3x/mysite/utils.py | 8 +- tests/auth-react/django3x/polls/views.py | 38 +-- tests/auth-react/fastapi-server/app.py | 40 +++- tests/auth-react/flask-server/app.py | 29 ++- tests/dashboard/test_dashboard.py | 8 +- tests/emailpassword/test_emaildelivery.py | 2 +- tests/emailpassword/test_emailexists.py | 2 +- tests/emailpassword/test_emailverify.py | 2 +- tests/emailpassword/test_passwordreset.py | 2 +- tests/emailpassword/test_signin.py | 2 +- tests/emailpassword/test_signup.py | 2 +- .../test_updateemailorpassword.py | 2 +- .../django2x/mysite/urls.py | 1 + .../django2x/mysite/wsgi.py | 6 + .../django2x/polls/urls.py | 2 +- .../django2x/polls/views.py | 61 +++-- .../django3x/mysite/asgi.py | 6 + .../django3x/mysite/urls.py | 1 + .../django3x/mysite/wsgi.py | 6 + .../django3x/polls/urls.py | 2 +- .../django3x/polls/views.py | 61 +++-- .../drf_async/mysite/asgi.py | 6 + .../drf_async/mysite/urls.py | 1 + .../drf_async/mysite/wsgi.py | 6 + .../drf_async/polls/urls.py | 2 +- .../drf_async/polls/views.py | 17 +- .../drf_sync/mysite/asgi.py | 6 + .../drf_sync/mysite/urls.py | 1 + .../drf_sync/mysite/wsgi.py | 6 + .../drf_sync/polls/urls.py | 2 +- .../drf_sync/polls/views.py | 17 +- .../frontendIntegration/fastapi-server/app.py | 6 +- tests/frontendIntegration/flask-server/app.py | 10 +- .../input_validation/test_input_validation.py | 9 + tests/jwt/test_create_jwt_feature.py | 1 + tests/jwt/test_get_JWKS.py | 2 +- tests/jwt/test_override.py | 2 +- tests/multitenancy/test_router.py | 2 +- tests/passwordless/test_emaildelivery.py | 10 +- tests/passwordless/test_smsdelivery.py | 6 +- tests/sessions/claims/test_verify_session.py | 8 +- tests/sessions/test_access_token_version.py | 2 +- tests/sessions/test_auth_mode.py | 21 +- tests/sessions/test_jwks.py | 2 +- tests/sessions/test_session_error_handlers.py | 2 +- tests/sessions/test_session_utils.py | 18 +- tests/telemetry/test_telemetry.py | 11 +- tests/test-server/test_functions_mapper.py | 6 +- tests/test-server/thirdparty.py | 11 +- tests/test_config.py | 4 +- tests/test_logger.py | 4 +- tests/test_middleware.py | 2 +- tests/test_misc.py | 4 +- tests/test_network_interceptor.py | 2 +- tests/test_passwordless.py | 2 +- tests/test_session.py | 8 +- tests/test_user_context.py | 2 +- tests/testclient.py | 1 + .../test_authorisation_url_feature.py | 2 +- tests/thirdparty/test_emaildelivery.py | 8 +- tests/thirdparty/test_thirdparty.py | 4 +- tests/utils.py | 27 ++- 140 files changed, 1156 insertions(+), 691 deletions(-) create mode 100755 .circleci/installJava.sh create mode 100755 .circleci/installNode.sh diff --git a/.circleci/authReactDjango.sh b/.circleci/authReactDjango.sh index 5926d6443..62838dc2d 100755 --- a/.circleci/authReactDjango.sh +++ b/.circleci/authReactDjango.sh @@ -1,3 +1,8 @@ +python_version=$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1,2) +if [[ $(echo "$python_version >= 3.13" | bc -l) -eq 1 ]]; then + pip install setuptools legacy-cgi +fi + echo "Starting tests for FDI $1"; if [ -z "$SUPERTOKENS_API_KEY" ]; then @@ -94,26 +99,10 @@ if [[ $frontendDriverVersion == '1.3' || $frontendDriverVersion == '1.8' ]]; the # we skip 1.8 since the SDK with just 1.8 doesn't have the right scripts exit 0 else - tries=1 - while [ $tries -le 3 ] - do - tries=$(( $tries + 1 )) - ./setupAndTestWithAuthReactWithDjango.sh $coreFree $frontendAuthReactTag $nodeTag - if [[ $? -ne 0 ]] - then - if [[ $tries -le 3 ]] - then - rm -rf ../../supertokens-root - rm -rf ../../supertokens-auth-react - echo "failed test.. retrying!" - else - echo "test failed for auth-react tests... exiting!" - exit 1 - fi - else - rm -rf ../../supertokens-root - # we do not delete supertokens-auth-react here cause the test reports are generated in there. - break - fi - done + ./setupAndTestWithAuthReactWithDjango.sh $coreFree $frontendAuthReactTag $nodeTag + if [[ $? -ne 0 ]] + then + echo "test failed for auth-react tests... exiting!" + exit 1 + fi fi \ No newline at end of file diff --git a/.circleci/authReactFastApi.sh b/.circleci/authReactFastApi.sh index e53001099..628607989 100755 --- a/.circleci/authReactFastApi.sh +++ b/.circleci/authReactFastApi.sh @@ -94,26 +94,10 @@ if [[ $frontendDriverVersion == '1.3' || $frontendDriverVersion == '1.8' ]]; the # we skip 1.8 since the SDK with just 1.8 doesn't have the right scripts exit 0 else - tries=1 - while [ $tries -le 3 ] - do - tries=$(( $tries + 1 )) - ./setupAndTestWithAuthReact.sh $coreFree $frontendAuthReactTag $nodeTag - if [[ $? -ne 0 ]] - then - if [[ $tries -le 3 ]] - then - rm -rf ../../supertokens-root - rm -rf ../../supertokens-auth-react - echo "failed test.. retrying!" - else - echo "test failed for auth-react tests... exiting!" - exit 1 - fi - else - rm -rf ../../supertokens-root - # we do not delete supertokens-auth-react here cause the test reports are generated in there. - break - fi - done + ./setupAndTestWithAuthReact.sh $coreFree $frontendAuthReactTag $nodeTag + if [[ $? -ne 0 ]] + then + echo "test failed for auth-react tests... exiting!" + exit 1 + fi fi \ No newline at end of file diff --git a/.circleci/authReactFlask.sh b/.circleci/authReactFlask.sh index 46abeaa5c..e358234bc 100755 --- a/.circleci/authReactFlask.sh +++ b/.circleci/authReactFlask.sh @@ -96,26 +96,10 @@ if [[ $frontendDriverVersion == '1.3' || $frontendDriverVersion == '1.8' ]]; the # we skip 1.8 since the SDK with just 1.8 doesn't have the right scripts exit 0 else - tries=1 - while [ $tries -le 3 ] - do - tries=$(( $tries + 1 )) - ./setupAndTestWithAuthReactWithFlask.sh $coreFree $frontendAuthReactTag $nodeTag - if [[ $? -ne 0 ]] - then - if [[ $tries -le 3 ]] - then - rm -rf ../../supertokens-root - rm -rf ../../supertokens-auth-react - echo "failed test.. retrying!" - else - echo "test failed for auth-react tests... exiting!" - exit 1 - fi - else - rm -rf ../../supertokens-root - # we do not delete supertokens-auth-react here cause the test reports are generated in there. - break - fi - done + ./setupAndTestWithAuthReactWithFlask.sh $coreFree $frontendAuthReactTag $nodeTag + if [[ $? -ne 0 ]] + then + echo "test failed for auth-react tests... exiting!" + exit 1 + fi fi \ No newline at end of file diff --git a/.circleci/config_continue.yml b/.circleci/config_continue.yml index c17d74cbf..8294244bc 100644 --- a/.circleci/config_continue.yml +++ b/.circleci/config_continue.yml @@ -13,62 +13,59 @@ jobs: - run: (cd .circleci/ && ./markDevTagAsTestNotPassed.sh) test-unit: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: cdi-version: type: string + python-version: + type: string + parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof - run: echo "127.0.0.1 localhost.org" >> /etc/hosts + - run: (cd .circleci/ && ./installJava.sh) - run: make dev-install - run: (cd .circleci/ && ./doUnitTests.sh << parameters.cdi-version >>) + - store_test_results: + path: test-results/junit.xml - slack/status test-backend-sdk-testing: docker: - - image: rishabhpoddar/supertokens_node_driver_testing_node_20 + - image: python:<< parameters.python-version >> resource_class: large parameters: cdi-version: type: string fdi-version: type: string + python-version: + type: string steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - - run: - name: Install latest Python and dependencies - command: | - apt-get update - apt-get install -y software-properties-common lsof - add-apt-repository -y ppa:deadsnakes/ppa - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y python3.11 python3-pip python3.11-dev libffi-dev - update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 - update-alternatives --set python3 /usr/bin/python3.11 - python3 --version - python3 -m pip install "cython<3.0.0" wheel - python3 -m pip install --upgrade pip setuptools - python3 -m pip install "pyyaml==5.4.1" --no-build-isolation - run: python3 -m pip install -r dev-requirements.txt - run: (cd .circleci/ && ./doBackendSDKTests.sh << parameters.cdi-version >> << parameters.fdi-version >>) - slack/status test-website-fastapi: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-fastapi @@ -76,16 +73,19 @@ jobs: - slack/status test-website-flask: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-flask @@ -93,16 +93,19 @@ jobs: - slack/status test-website-django: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-django @@ -110,62 +113,59 @@ jobs: - slack/status test-website-drf-async: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - run: echo "unalias pip" >> ~/.bashrc - - run: echo "unalias python" >> ~/.bashrc - - run: apt update && apt install -y python3.8-minimal python3-pip - - run: update-alternatives --install "/usr/bin/python" python /usr/bin/python3.8 2 - - run: python -m pip install --upgrade pip - - run: echo "alias pip3='python -m pip'" >> ~/.bashrc - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-drf - run: (cd .circleci/ && ./websiteDrfAsync.sh << parameters.fdi-version >>) - slack/status test-website-drf-sync: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - run: echo "unalias pip" >> ~/.bashrc - - run: echo "unalias python" >> ~/.bashrc - - run: apt update && apt install -y python3.8-minimal python3-pip - - run: update-alternatives --install "/usr/bin/python" python /usr/bin/python3.8 2 - - run: python -m pip install --upgrade pip - - run: echo "alias pip3='python -m pip'" >> ~/.bashrc - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-drf - run: (cd .circleci/ && ./websiteDrfSync.sh << parameters.fdi-version >>) - slack/status test-website-django2x: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-django2x @@ -173,18 +173,21 @@ jobs: - slack/status test-website-flask-nest-asyncio: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large environment: SUPERTOKENS_NEST_ASYNCIO: "1" parameters: - fdi-version: - type: string + fdi-version: + type: string + python-version: + type: string parallelism: 4 steps: - checkout - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-flask @@ -193,96 +196,96 @@ jobs: - slack/status test-authreact-fastapi: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> environment: - MOCHA_FILE: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + MOCHA_FILE: /root/test_report/report_node-<< parameters.fdi-version >>.xml parameters: fdi-version: type: string + python-version: + type: string parallelism: 4 steps: - checkout - attach_workspace: at: / - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - run: n 16 - - run: node --version - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-fastapi - run: (cd .circleci && ./authReactFastApi.sh << parameters.fdi-version >>) - - run: ls -la ../supertokens-auth-react/test_report || true - store_test_results: - path: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + path: /root/test_report/report_node-<< parameters.fdi-version >>.xml - store_artifacts: - path: ../supertokens-auth-react/test_report/screenshots - destination: failed-test-screenshots + path: ~/test_report/screenshots + destination: screenshots - store_artifacts: - path: ../supertokens-auth-react/test_report/logs - destination: logfiles + path: ~/test_report/react-logs + destination: react-logs test-authreact-flask: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large environment: - MOCHA_FILE: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + MOCHA_FILE: /root/test_report/report_node-<< parameters.fdi-version >>.xml parameters: fdi-version: type: string + python-version: + type: string parallelism: 4 steps: - checkout - attach_workspace: at: / - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - run: n 16 - - run: node --version - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-flask - run: (cd .circleci && ./authReactFlask.sh << parameters.fdi-version >>) - - run: ls -la ../supertokens-auth-react/test_report || true - store_test_results: - path: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + path: /root/test_report/report_node-<< parameters.fdi-version >>.xml - store_artifacts: - path: ../supertokens-auth-react/test_report/screenshots - destination: failed-test-screenshots + path: ~/test_report/screenshots + destination: screenshots - store_artifacts: - path: ../supertokens-auth-react/test_report/logs - destination: logfiles + path: ~/test_report/react-logs + destination: react-logs test-authreact-django: docker: - - image: rishabhpoddar/supertokens_python_driver_testing + - image: python:<< parameters.python-version >> resource_class: large environment: - MOCHA_FILE: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + MOCHA_FILE: /root/test_report/report_node-<< parameters.fdi-version >>.xml parameters: fdi-version: type: string + python-version: + type: string parallelism: 4 steps: - checkout - attach_workspace: at: / - - run: update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2 - - run: update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2 + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - run: n 16 - - run: node --version - run: echo "127.0.0.1 localhost.org" >> /etc/hosts - run: make with-django - run: (cd .circleci && ./authReactDjango.sh << parameters.fdi-version >>) - - run: ls -la ../supertokens-auth-react/test_report || true - store_test_results: - path: ../supertokens-auth-react/test_report/report_node-<< parameters.fdi-version >>.xml + path: /root/test_report/report_node-<< parameters.fdi-version >>.xml - store_artifacts: - path: ../supertokens-auth-react/test_report/screenshots - destination: failed-test-screenshots + path: ~/test_report/screenshots + destination: screenshots - store_artifacts: - path: ../supertokens-auth-react/test_report/logs - destination: logfiles + path: ~/test_report/react-logs + destination: react-logs test-success: docker: - image: rishabhpoddar/supertokens_python_driver_testing @@ -314,6 +317,7 @@ workflows: matrix: parameters: cdi-version: placeholder + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] - test-backend-sdk-testing: requires: - test-dev-tag-as-not-passed @@ -328,6 +332,7 @@ workflows: parameters: cdi-version: placeholder fdi-version: placeholder + python-version: ['3.8', '3.13'] - test-website-fastapi: requires: - test-dev-tag-as-not-passed @@ -336,6 +341,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -349,6 +355,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -362,6 +369,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -375,6 +383,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -388,6 +397,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -401,6 +411,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] filters: tags: only: /dev-v[0-9]+(\.[0-9]+)*/ @@ -419,6 +430,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] - test-authreact-flask: requires: - test-dev-tag-as-not-passed @@ -432,6 +444,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] - test-authreact-django: requires: - test-dev-tag-as-not-passed @@ -445,6 +458,7 @@ workflows: matrix: parameters: fdi-version: placeholder + python-version: ['3.8', '3.13'] - test-success: requires: - test-unit diff --git a/.circleci/installJava.sh b/.circleci/installJava.sh new file mode 100755 index 000000000..1b797ccd5 --- /dev/null +++ b/.circleci/installJava.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +wget https://download.java.net/java/GA/jdk15.0.1/51f4f36ad4ef43e39d0dfdbaf6549e32/9/GPL/openjdk-15.0.1_linux-x64_bin.tar.gz +mkdir /usr/java +mv openjdk-15.0.1_linux-x64_bin.tar.gz /usr/java +cd /usr/java +tar -xzvf openjdk-15.0.1_linux-x64_bin.tar.gz +rm openjdk-15.0.1_linux-x64_bin.tar.gz +ln -s /usr/java/jdk-15.0.1/bin/java /usr/bin/java +ln -s /usr/java/jdk-15.0.1/bin/javac /usr/bin/javac diff --git a/.circleci/installNode.sh b/.circleci/installNode.sh new file mode 100755 index 000000000..c89d42ae9 --- /dev/null +++ b/.circleci/installNode.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export NODE_VERSION=$1 + +curl -fsSL https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - +apt-get install -y nodejs diff --git a/.circleci/setupAndTestWithAuthReact.sh b/.circleci/setupAndTestWithAuthReact.sh index ba92eb35f..66a69f595 100755 --- a/.circleci/setupAndTestWithAuthReact.sh +++ b/.circleci/setupAndTestWithAuthReact.sh @@ -63,8 +63,69 @@ cd ../../../../supertokens-auth-react/ # flag will not be checked because Auth0 is used as a provider so that the Thirdparty tests can run reliably. # In versions lower than 0.18 Github is used as the provider. -SKIP_OAUTH=true npm run test-with-non-node -if [[ $? -ne 0 ]] +# SKIP_OAUTH=true npm run test-with-non-node + +# Exit script from startEndToEnd func. +trap "exit 1" TERM +export EXIT_PID=$$ + +function killServers () { + if [[ "${SERVER_STARTED}" != "true" ]]; then + echo "Kill servers." + lsof -i tcp:8082 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + lsof -i tcp:3031 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + else + echo "Leaving servers running because SERVER_STARTED=true" + fi +} + +trap "killServers" EXIT # Trap to execute on script shutdown + +# Start by killing any servers up on 8082 and 3031 if any. +killServers + +mkdir -p ~/test_report/logs +mkdir -p ~/test_report/react-logs +mkdir -p ~/test_report/screenshots + +apiPort=8083 + +echo "Running tests with React 18" +# Run node server in background. +if [[ "${SERVER_STARTED}" != "true" ]]; then + (cd test/server/ && TEST_MODE=testing INSTALL_PATH=../../../supertokens-root NODE_PORT=8082 node . >> ~/test_report/react-logs/backend.log 2>&1 &) + + (cd ./examples/for-tests/ && cat | CI=true BROWSER=none PORT=3031 REACT_APP_API_PORT=$apiPort npm run start >> ~/test_report/react-logs/frontend.log 2>&1 &) +fi +# Start front end test app and run tests. + +# Wait for the test app to be up before running tests. +while ! curl -s localhost:3031 > /dev/null 2>&1 +do + echo "Waiting for front end test application to start..." + sleep 5 +done + +while ! curl -s localhost:8082 > /dev/null 2>&1 +do + echo "Waiting for backend test application to start..." + sleep 5 +done + +sleep 2 # Because the server is responding does not mean the app is ready. Let's wait another 2secs to make sure the app is up. + + +echo "Start mocha testing" + +export SPEC_FILES=$(circleci tests glob 'test/end-to-end/**/*.test.js' 'test/unit/**/*.test.js') +echo $SPEC_FILES | SCREENSHOT_ROOT=~/test_report/screenshots APP_SERVER=$apiPort TEST_MODE=testing multi="spec=- mocha-junit-reporter=/dev/null" circleci tests run --command="xargs npx mocha mocha --reporter mocha-multi --require @babel/register --require test/test.mocha.env --timeout 40000 --no-config" --verbose --split-by=timings + +testPassed=$?; + +echo "testPassed exit code: $testPassed" +killServers + +if [[ $testPassed -ne 0 ]] then echo "test failed... killing $pid and exiting!" kill -9 $pid diff --git a/.circleci/setupAndTestWithAuthReactWithDjango.sh b/.circleci/setupAndTestWithAuthReactWithDjango.sh index 5f4c835e0..8a4b5f8e8 100755 --- a/.circleci/setupAndTestWithAuthReactWithDjango.sh +++ b/.circleci/setupAndTestWithAuthReactWithDjango.sh @@ -30,6 +30,8 @@ fi pluginInterfaceTag=$(echo $pluginInterfaceInfo | jq .tag | tr -d '"') pluginInterfaceVersion=$(echo $pluginInterfaceInfo | jq .version | tr -d '"') +mkdir -p ~/test_report + echo "Testing with frontend auth-react: $2, node tag: $3, FREE core: $coreVersion, plugin-interface: $pluginInterfaceVersion" cd ../../ @@ -63,8 +65,69 @@ cd ../../../../supertokens-auth-react/ # flag will not be checked because Auth0 is used as a provider so that the Thirdparty tests can run reliably. # In versions lower than 0.18 Github is used as the provider. -SKIP_OAUTH=true npm run test-with-non-node -if [[ $? -ne 0 ]] +# SKIP_OAUTH=true npm run test-with-non-node + +# Exit script from startEndToEnd func. +trap "exit 1" TERM +export EXIT_PID=$$ + +function killServers () { + if [[ "${SERVER_STARTED}" != "true" ]]; then + echo "Kill servers." + lsof -i tcp:8082 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + lsof -i tcp:3031 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + else + echo "Leaving servers running because SERVER_STARTED=true" + fi +} + +trap "killServers" EXIT # Trap to execute on script shutdown + +# Start by killing any servers up on 8082 and 3031 if any. +killServers + +mkdir -p ~/test_report/logs +mkdir -p ~/test_report/react-logs +mkdir -p ~/test_report/screenshots + +apiPort=8083 + +echo "Running tests with React 18" +# Run node server in background. +if [[ "${SERVER_STARTED}" != "true" ]]; then + (cd test/server/ && TEST_MODE=testing INSTALL_PATH=../../../supertokens-root NODE_PORT=8082 node . >> ~/test_report/react-logs/backend.log 2>&1 &) + + (cd ./examples/for-tests/ && cat | CI=true BROWSER=none PORT=3031 REACT_APP_API_PORT=$apiPort npm run start >> ~/test_report/react-logs/frontend.log 2>&1 &) +fi +# Start front end test app and run tests. + +# Wait for the test app to be up before running tests. +while ! curl -s localhost:3031 > /dev/null 2>&1 +do + echo "Waiting for front end test application to start..." + sleep 5 +done + +while ! curl -s localhost:8082 > /dev/null 2>&1 +do + echo "Waiting for backend test application to start..." + sleep 5 +done + +sleep 2 # Because the server is responding does not mean the app is ready. Let's wait another 2secs to make sure the app is up. + + +echo "Start mocha testing" + +export SPEC_FILES=$(circleci tests glob 'test/end-to-end/**/*.test.js' 'test/unit/**/*.test.js') +echo $SPEC_FILES | SCREENSHOT_ROOT=~/test_report/screenshots APP_SERVER=$apiPort TEST_MODE=testing multi="spec=- mocha-junit-reporter=/dev/null" circleci tests run --command="xargs npx mocha mocha --reporter mocha-multi --require @babel/register --require test/test.mocha.env --timeout 40000 --no-config" --verbose --split-by=timings + +testPassed=$?; + +echo "testPassed exit code: $testPassed" +killServers + +if [[ $testPassed -ne 0 ]] then echo "test failed... killing $pid and exiting!" kill -9 $pid @@ -75,4 +138,4 @@ fi echo "all tests passed, killing processes: $pid" kill -9 $pid rm -rf ./test/server/node_modules/supertokens-node -git checkout HEAD -- ./test/server/package.json \ No newline at end of file +git checkout HEAD -- ./test/server/package.json diff --git a/.circleci/setupAndTestWithAuthReactWithFlask.sh b/.circleci/setupAndTestWithAuthReactWithFlask.sh index 485f5fef4..363650d98 100755 --- a/.circleci/setupAndTestWithAuthReactWithFlask.sh +++ b/.circleci/setupAndTestWithAuthReactWithFlask.sh @@ -30,6 +30,8 @@ fi pluginInterfaceTag=$(echo $pluginInterfaceInfo | jq .tag | tr -d '"') pluginInterfaceVersion=$(echo $pluginInterfaceInfo | jq .version | tr -d '"') +mkdir -p ~/test_report + echo "Testing with frontend auth-react: $2, node tag: $3, FREE core: $coreVersion, plugin-interface: $pluginInterfaceVersion" cd ../../ @@ -63,8 +65,69 @@ cd ../../../../supertokens-auth-react/ # flag will not be checked because Auth0 is used as a provider so that the Thirdparty tests can run reliably. # In versions lower than 0.18 Github is used as the provider. -SKIP_OAUTH=true npm run test-with-non-node -if [[ $? -ne 0 ]] +# SKIP_OAUTH=true npm run test-with-non-node + +# Exit script from startEndToEnd func. +trap "exit 1" TERM +export EXIT_PID=$$ + +function killServers () { + if [[ "${SERVER_STARTED}" != "true" ]]; then + echo "Kill servers." + lsof -i tcp:8082 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + lsof -i tcp:3031 | grep -m 1 node | awk '{printf $2}' | cut -c 1- | xargs -I {} kill -9 {} > /dev/null 2>&1 + else + echo "Leaving servers running because SERVER_STARTED=true" + fi +} + +trap "killServers" EXIT # Trap to execute on script shutdown + +# Start by killing any servers up on 8082 and 3031 if any. +killServers + +mkdir -p ~/test_report/logs +mkdir -p ~/test_report/react-logs +mkdir -p ~/test_report/screenshots + +apiPort=8083 + +echo "Running tests with React 18" +# Run node server in background. +if [[ "${SERVER_STARTED}" != "true" ]]; then + (cd test/server/ && TEST_MODE=testing INSTALL_PATH=../../../supertokens-root NODE_PORT=8082 node . >> ~/test_report/react-logs/backend.log 2>&1 &) + + (cd ./examples/for-tests/ && cat | CI=true BROWSER=none PORT=3031 REACT_APP_API_PORT=$apiPort npm run start >> ~/test_report/react-logs/frontend.log 2>&1 &) +fi +# Start front end test app and run tests. + +# Wait for the test app to be up before running tests. +while ! curl -s localhost:3031 > /dev/null 2>&1 +do + echo "Waiting for front end test application to start..." + sleep 5 +done + +while ! curl -s localhost:8082 > /dev/null 2>&1 +do + echo "Waiting for backend test application to start..." + sleep 5 +done + +sleep 2 # Because the server is responding does not mean the app is ready. Let's wait another 2secs to make sure the app is up. + + +echo "Start mocha testing" + +export SPEC_FILES=$(circleci tests glob 'test/end-to-end/**/*.test.js' 'test/unit/**/*.test.js') +echo $SPEC_FILES | SCREENSHOT_ROOT=~/test_report/screenshots APP_SERVER=$apiPort TEST_MODE=testing multi="spec=- mocha-junit-reporter=/dev/null" circleci tests run --command="xargs npx mocha mocha --reporter mocha-multi --require @babel/register --require test/test.mocha.env --timeout 40000 --no-config" --verbose --split-by=timings + +testPassed=$?; + +echo "testPassed exit code: $testPassed" +killServers + +if [[ $testPassed -ne 0 ]] then echo "test failed... killing $pid and exiting!" kill -9 $pid diff --git a/.circleci/setupAndTestWithFreeCore.sh b/.circleci/setupAndTestWithFreeCore.sh index 0ac818fdb..df00bdfaf 100755 --- a/.circleci/setupAndTestWithFreeCore.sh +++ b/.circleci/setupAndTestWithFreeCore.sh @@ -49,4 +49,8 @@ cd ../ echo $SUPERTOKENS_API_KEY > apiPassword ./utils/setupTestEnvLocal cd ../project/ -INSTALL_DIR=../supertokens-root make test \ No newline at end of file + +export INSTALL_DIR=../supertokens-root +mkdir test-results +TEST_FILES=$(circleci tests glob "**/test_*.py") +echo "$TEST_FILES" | circleci tests run --command="xargs pytest -vv -o junit_family=legacy --junitxml=test-results/junit.xml" --verbose --split-by=timings diff --git a/.circleci/websiteDjango2x.sh b/.circleci/websiteDjango2x.sh index 8ffd38a2c..d9a2ed6a6 100755 --- a/.circleci/websiteDjango2x.sh +++ b/.circleci/websiteDjango2x.sh @@ -1,3 +1,8 @@ +python_version=$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1,2) +if [[ $(echo "$python_version >= 3.13" | bc -l) -eq 1 ]]; then + pip install setuptools legacy-cgi +fi + coreDriverJson=`cat ../coreDriverInterfaceSupported.json` coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` coreDriverArray=`echo $coreDriverJson | jq ".versions"` diff --git a/.circleci/websiteDrfAsync.sh b/.circleci/websiteDrfAsync.sh index 849461248..5c3a8255e 100755 --- a/.circleci/websiteDrfAsync.sh +++ b/.circleci/websiteDrfAsync.sh @@ -1,3 +1,8 @@ +python_version=$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1,2) +if [[ $(echo "$python_version >= 3.13" | bc -l) -eq 1 ]]; then + pip install setuptools legacy-cgi +fi + coreDriverJson=`cat ../coreDriverInterfaceSupported.json` coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` coreDriverArray=`echo $coreDriverJson | jq ".versions"` diff --git a/.circleci/websiteDrfSync.sh b/.circleci/websiteDrfSync.sh index bff46dedc..fd7a6026e 100755 --- a/.circleci/websiteDrfSync.sh +++ b/.circleci/websiteDrfSync.sh @@ -1,3 +1,8 @@ +python_version=$(python --version 2>&1 | awk '{print $2}' | cut -d. -f1,2) +if [[ $(echo "$python_version >= 3.13" | bc -l) -eq 1 ]]; then + pip install setuptools legacy-cgi +fi + coreDriverJson=`cat ../coreDriverInterfaceSupported.json` coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` coreDriverArray=`echo $coreDriverJson | jq ".versions"` diff --git a/.github/workflows/pre-commit-hook-run.yml b/.github/workflows/pre-commit-hook-run.yml index 0c25a22bf..464222145 100644 --- a/.github/workflows/pre-commit-hook-run.yml +++ b/.github/workflows/pre-commit-hook-run.yml @@ -9,21 +9,123 @@ on: - synchronize jobs: - pr-title: - name: Pre commit hook check + pr-title-3_8: + name: Pre commit hook check (3.8) runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' + - name: Create virtual environment and install dependencies + run: | + python3 -m venv venv + source venv/bin/activate + make dev-install && rm -rf src + - name: Make a dummy change to README.md + run: | + echo "# Dummy change for PR check" >> README.md + - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' + - run: | + source venv/bin/activate + ./hooks/pre-commit.sh + + pr-title-3_9: + name: Pre commit hook check (3.9) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: Create virtual environment and install dependencies + run: | + python3 -m venv venv + source venv/bin/activate + make dev-install && rm -rf src + - name: Make a dummy change to README.md + run: | + echo "# Dummy change for PR check" >> README.md + - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' + - run: | + source venv/bin/activate + ./hooks/pre-commit.sh + + pr-title-3_10: + name: Pre commit hook check (3.10) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Create virtual environment and install dependencies + run: | + python3 -m venv venv + source venv/bin/activate + make dev-install && rm -rf src + - name: Make a dummy change to README.md + run: | + echo "# Dummy change for PR check" >> README.md + - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' + - run: | + source venv/bin/activate + ./hooks/pre-commit.sh + + pr-title-3_11: + name: Pre commit hook check (3.11) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Create virtual environment and install dependencies + run: | + python3 -m venv venv + source venv/bin/activate + make dev-install && rm -rf src + - name: Make a dummy change to README.md + run: | + echo "# Dummy change for PR check" >> README.md + - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' + - run: | + source venv/bin/activate + ./hooks/pre-commit.sh + + pr-title-3_12: + name: Pre commit hook check (3.12) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Create virtual environment and install dependencies + run: | + python3 -m venv venv + source venv/bin/activate + make dev-install && rm -rf src + - name: Make a dummy change to README.md + run: | + echo "# Dummy change for PR check" >> README.md + - run: git init && git add --all && git -c user.name='test' -c user.email='test@example.com' commit -m 'init for pr action' + - run: | + source venv/bin/activate + ./hooks/pre-commit.sh + + pr-title-3_13: + name: Pre commit hook check (3.13) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' - name: Create virtual environment and install dependencies run: | python3 -m venv venv source venv/bin/activate - python3 -m pip install setuptools - python3 -m pip install "cython<3.0.0" wheel - python3 -m pip install "PyYAML==5.4.1" --no-build-isolation make dev-install && rm -rf src - name: Make a dummy change to README.md run: | @@ -32,4 +134,3 @@ jobs: - run: | source venv/bin/activate ./hooks/pre-commit.sh - \ No newline at end of file diff --git a/.gitignore b/.gitignore index a1f2f4c06..c2ee43a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ bin/ lib/ pyvenv.cfg sqlite.db -.mypy_cache/ \ No newline at end of file +.mypy_cache/ +test-results/ \ No newline at end of file diff --git a/.pylintrc b/.pylintrc index 0c8c9f708..69ed5aab3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -121,9 +121,9 @@ disable=raw-checker-failed, consider-using-f-string, consider-using-in, no-else-return, - no-self-use, no-else-raise, too-many-nested-blocks, + broad-exception-raised, # Enable the message, report, category or checker with the given id(s). You can @@ -603,5 +603,5 @@ min-public-methods=2 # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception diff --git a/CHANGELOG.md b/CHANGELOG.md index 658e959fa..3ffdc661d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.26.0] - 2024-11-20 + +- Not supporting Python 3.7 +- Updating dependencies for vulnerability fixes + ## [0.25.1] - 2024-11-08 - Fixes issues with dashboard - userroles and tenants diff --git a/Makefile b/Makefile index f9afc228e..4b2650022 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ set-up-hooks: chmod +x .git/hooks/pre-commit test: - pytest -vv --reruns 3 --reruns-delay 5 ./tests/ + pytest -vv ./tests/ dev-install: pip install -r dev-requirements.txt diff --git a/dev-requirements.txt b/dev-requirements.txt index 7e3a7d720..a285b8ff0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,89 +1,21 @@ -aiosmtplib>=1.1.6,<4.0.0 -anyio==3.5.0 -asgiref==3.5.2 -astroid==2.9.3 -attrs==21.4.0 -black==22.3.0 -certifi==2021.10.8 -cffi==1.15.0 -chardet==4.0.0 -charset-normalizer==2.0.12 -click==8.1.3 -cryptography==36.0.2 -Deprecated==1.2.13 -Django==3.2.12 -django-cors-headers==3.11.0 -django-stubs==1.9.0 -django-stubs-ext==0.4.0 -fastapi==0.68.1 -filelock==3.6.0 -Flask==2.0.2 -Flask-Cors==3.0.10 -h11==0.12.0 -httpcore==0.15.0 -httpx==0.23.0 -idna==2.10 -importlib-metadata==4.11.3 -iniconfig==1.1.1 -isort==5.10.1 -itsdangerous==2.1.2 -Jinja2==3.1.1 -jsonschema==3.2.0 -lazy-object-proxy==1.7.1 -Mako==1.2.0 -Markdown==3.3.6 -MarkupSafe==2.1.1 -mccabe==0.6.1 -mypy==0.942 -mypy-extensions==0.4.3 -nest-asyncio==1.5.1 -nodeenv==1.6.0 -packaging==21.3 -pathspec==0.9.0 -pdoc3==0.10.0 -phonenumbers==8.13.47 -pkce==1.0.3 -platformdirs==2.5.1 -pluggy==1.0.0 -py==1.11.0 -pycodestyle==2.8.0 -pycparser==2.21 -pycryptodome==3.10.4 -pydantic==1.9.0 -PyJWT==2.6.0 -pylint==2.12.2 -pyparsing==3.0.7 -pyright==1.1.236 -pyrsistent==0.18.1 -pytest==6.2.5 -pytest-asyncio==0.18.0 -pytest-mock==3.8.2 -pytest-rerunfailures==10.3 -python-dotenv==0.19.2 -pytz==2022.1 -PyYAML==5.4.1 -requests==2.25.1 -requests-file==1.5.1 -requests-mock==1.9.3 -respx==0.19.2 -rfc3986==1.5.0 -six==1.16.0 -sniffio==1.2.0 -sqlparse==0.4.2 -starlette==0.14.2 +black==24.8.0 +Django==4.2.6 +django-cors-headers==4.4.0 +django-stubs==4.2.7 +django-stubs-ext==4.2.7 +fastapi==0.115.5 +Flask==3.0.3 +flask-cors==5.0.0 +nest-asyncio==1.6.0 +pylint==3.2.7 +pyright==1.1.389 +python-dotenv==1.0.1 +pytest==8.3.3 +pytest-asyncio==0.24.0 +pytest-mock==3.14.0 +pytest-rerunfailures==14.0 +pyyaml==6.0.2 +requests-mock==1.12.1 +respx==0.21.1 +uvicorn==0.32.0 -e . -tldextract==3.1.0 -toml==0.10.2 -tomli==2.0.1 -twilio==9.3.3 -types-pytz==2021.3.6 -types-PyYAML==6.0.5 -typing_extensions==4.1.1 -tzdata==2021.5 -urllib3==1.26.9 -uvicorn==0.18.2 -Werkzeug==2.0.3 -wrapt==1.13.3 -zipp==3.7.0 -pyotp==2.9.0 -aiofiles==23.2.1 \ No newline at end of file diff --git a/examples/with-django/with-thirdpartyemailpassword/project/urls.py b/examples/with-django/with-thirdpartyemailpassword/project/urls.py index f318fe558..47366a84b 100644 --- a/examples/with-django/with-thirdpartyemailpassword/project/urls.py +++ b/examples/with-django/with-thirdpartyemailpassword/project/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import path, include diff --git a/examples/with-django/with-thirdpartyemailpassword/sampleapp/views.py b/examples/with-django/with-thirdpartyemailpassword/sampleapp/views.py index f9cf1c323..36fb47122 100644 --- a/examples/with-django/with-thirdpartyemailpassword/sampleapp/views.py +++ b/examples/with-django/with-thirdpartyemailpassword/sampleapp/views.py @@ -5,6 +5,9 @@ @verify_session() def get_session_info(request: HttpRequest) -> JsonResponse: + if not isinstance(request.supertokens, SessionContainer): # type: ignore + raise Exception("should never happen") + session_: SessionContainer = request.supertokens # type: ignore return JsonResponse( { diff --git a/examples/with-fastapi/with-thirdpartyemailpassword/main.py b/examples/with-fastapi/with-thirdpartyemailpassword/main.py index b9d19ec92..f47667f6f 100644 --- a/examples/with-fastapi/with-thirdpartyemailpassword/main.py +++ b/examples/with-fastapi/with-thirdpartyemailpassword/main.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv from fastapi import Depends, FastAPI from fastapi.responses import JSONResponse, PlainTextResponse -from starlette.exceptions import ExceptionMiddleware +from starlette.middleware.exceptions import ExceptionMiddleware from starlette.middleware.cors import CORSMiddleware from supertokens_python import ( @@ -153,7 +153,7 @@ def get_website_domain(): telemetry=False, ) -app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers) +app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers) # type: ignore @app.get("/sessioninfo") diff --git a/examples/with-flask/with-thirdpartyemailpassword/app.py b/examples/with-flask/with-thirdpartyemailpassword/app.py index 78f8f4223..4d5794cb2 100644 --- a/examples/with-flask/with-thirdpartyemailpassword/app.py +++ b/examples/with-flask/with-thirdpartyemailpassword/app.py @@ -156,7 +156,7 @@ def get_website_domain(): @app.route("/sessioninfo", methods=["GET"]) # type: ignore @verify_session() def get_session_info(): - session_ = g.supertokens + session_: session.SessionContainer = g.supertokens return jsonify( { "sessionHandle": session_.get_handle(), diff --git a/setup.py b/setup.py index 41ad568ea..6c8337178 100644 --- a/setup.py +++ b/setup.py @@ -15,30 +15,26 @@ # on changes in these frameworks "fastapi": ( [ - "respx==0.19.2", - "Fastapi", - "uvicorn==0.18.2", - "python-dotenv==0.19.2", - "pyotp<3", - "aiofiles==23.2.1", + "respx==0.21.1", + "fastapi", + "uvicorn", + "python-dotenv==1.0.1", ] ), "flask": ( [ - "flask_cors", - "Flask", - "python-dotenv==0.19.2", - "pyotp<3", + "flask-cors", + "flask", + "python-dotenv==1.0.1", ] ), "django": ( [ - "django-cors-headers==3.11.0", + "django-cors-headers", "django>=3", - "django-stubs==1.9.0", - "uvicorn==0.18.2", - "python-dotenv==0.19.2", - "pyotp<3", + "django-stubs", + "uvicorn", + "python-dotenv==1.0.1", ] ), "django2x": ( @@ -46,23 +42,21 @@ "django-cors-headers==3.11.0", "django>=2,<3", "django-stubs==1.9.0", - "gunicorn==20.1.0", - "python-dotenv==0.19.2", - "pyotp<3", + "gunicorn", + "python-dotenv==1.0.1", ] ), "drf": ( [ "adrf", - "django-cors-headers==3.11.0", + "django-cors-headers", "django>=4", - "django-stubs==1.9.0", + "django-stubs", "djangorestframework", - "gunicorn==20.1.0", - "uvicorn==0.18.2", - "python-dotenv==0.19.2", - "tzdata==2021.5", - "pyotp<3", + "gunicorn", + "uvicorn", + "python-dotenv==1.0.1", + "tzdata", ] ), } @@ -89,7 +83,7 @@ setup( name="supertokens_python", - version="0.25.1", + version="0.26.0", author="SuperTokens", license="Apache 2.0", author_email="team@supertokens.com", @@ -105,12 +99,12 @@ }, classifiers=[ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Intended Audience :: Developers", "Topic :: Internet :: WWW/HTTP :: Session", "License :: OSI Approved :: Apache Software License", @@ -122,19 +116,19 @@ # [crypto] ensures that it installs the `cryptography` library as well # based on constraints specified in https://github.com/jpadilla/pyjwt/blob/master/setup.cfg#L50 "PyJWT[crypto]>=2.5.0,<3.0.0", - "httpx>=0.15.0,<=0.26.0", - "pycryptodome==3.10.*", - "tldextract==3.1.0", + "httpx>=0.15.0,<=0.27.2", + "pycryptodome<3.21.0", + "tldextract<5.1.3", "asgiref>=3.4.1,<4", "typing_extensions>=4.1.1,<5.0.0", - "Deprecated==1.2.13", + "Deprecated<1.3.0", "phonenumbers<9", "twilio<10", "aiosmtplib>=1.1.6,<4.0.0", - "pkce==1.0.3", + "pkce<1.1.0", "pyotp<3", ], - python_requires=">=3.7", + python_requires=">=3.8", include_package_data=True, extras_require=extras_require, ) diff --git a/supertokens_python/auth_utils.py b/supertokens_python/auth_utils.py index fe5a03eaf..18b7fbeb1 100644 --- a/supertokens_python/auth_utils.py +++ b/supertokens_python/auth_utils.py @@ -591,7 +591,10 @@ async def link_to_session_if_provided_else_create_primary_user_id_or_link_by_acc session: Union[SessionContainer, None], should_try_linking_with_session_user: Union[bool, None], user_context: Dict[str, Any], -) -> Union[OkResponse2, LinkingToSessionUserFailedError,]: +) -> Union[ + OkResponse2, + LinkingToSessionUserFailedError, +]: log_debug_message( "link_to_session_if_provided_else_create_primary_user_id_or_link_by_account_info called" ) @@ -799,7 +802,10 @@ async def try_linking_by_session( authenticated_user: User, session_user: User, user_context: Dict[str, Any], -) -> Union[OkResponse2, LinkingToSessionUserFailedError,]: +) -> Union[ + OkResponse2, + LinkingToSessionUserFailedError, +]: log_debug_message("tryLinkingBySession called") session_user_has_verified_account_info = any( diff --git a/supertokens_python/constants.py b/supertokens_python/constants.py index 5a8cdf5a5..07d62347f 100644 --- a/supertokens_python/constants.py +++ b/supertokens_python/constants.py @@ -15,7 +15,7 @@ from __future__ import annotations SUPPORTED_CDI_VERSIONS = ["5.1"] -VERSION = "0.25.1" +VERSION = "0.26.0" TELEMETRY = "/telemetry" USER_COUNT = "/users/count" USER_DELETE = "/user/remove" diff --git a/supertokens_python/framework/django/django_middleware.py b/supertokens_python/framework/django/django_middleware.py index 7e523cb02..78dd00e01 100644 --- a/supertokens_python/framework/django/django_middleware.py +++ b/supertokens_python/framework/django/django_middleware.py @@ -14,10 +14,12 @@ from __future__ import annotations import asyncio -from typing import Any, Union +from typing import Any, Optional, Union from asgiref.sync import async_to_sync +from supertokens_python.framework import BaseResponse + def middleware(get_response: Any): from supertokens_python import Supertokens @@ -74,27 +76,35 @@ def __syncMiddleware(request: HttpRequest): user_context = default_user_context(custom_request) try: - result: Union[DjangoResponse, None] = async_to_sync(st.middleware)( + result: Union[BaseResponse, None] = async_to_sync(st.middleware)( custom_request, response, user_context ) if result is None: result = DjangoResponse(get_response(request)) + if not isinstance(result, DjangoResponse): + raise Exception("should never happen") + if hasattr(request, "supertokens") and isinstance( request.supertokens, SessionContainer # type: ignore ): manage_session_post_response( request.supertokens, result, user_context # type: ignore ) + return result.response except SuperTokensError as e: response = DjangoResponse(HttpResponse()) - result: Union[DjangoResponse, None] = async_to_sync( - st.handle_supertokens_error - )(DjangoRequest(request), e, response, user_context) + result: Optional[BaseResponse] = async_to_sync(st.handle_supertokens_error)( + DjangoRequest(request), e, response, user_context + ) + if result is not None: + if not isinstance(result, DjangoResponse): + raise Exception("should never happen") + return result.response raise Exception("Should never come here") diff --git a/supertokens_python/framework/fastapi/fastapi_request.py b/supertokens_python/framework/fastapi/fastapi_request.py index fc9525f3f..c46c72a69 100644 --- a/supertokens_python/framework/fastapi/fastapi_request.py +++ b/supertokens_python/framework/fastapi/fastapi_request.py @@ -67,6 +67,9 @@ def set_session_as_none(self): def get_path(self) -> str: root_path = self.request.scope.get("root_path") + if root_path is None: + raise Exception("should never happen") + url = self.request.url.path # FastAPI seems buggy and it adds an extra root_path (if it matches): # So we trim the extra root_path (from the left) from the url diff --git a/supertokens_python/framework/fastapi/fastapi_response.py b/supertokens_python/framework/fastapi/fastapi_response.py index 70c867ac8..3f6078af3 100644 --- a/supertokens_python/framework/fastapi/fastapi_response.py +++ b/supertokens_python/framework/fastapi/fastapi_response.py @@ -13,7 +13,7 @@ # under the License. import json from math import ceil -from typing import Any, Dict, Optional +from typing import Any, Dict, Literal, Optional from supertokens_python.framework.response import BaseResponse from supertokens_python.utils import get_timestamp_ms @@ -47,7 +47,7 @@ def set_cookie( domain: Optional[str] = None, secure: bool = False, httponly: bool = False, - samesite: str = "lax", + samesite: Literal["lax", "strict", "none"] = "lax", ): # Note: For FastAPI response object, the expires value # doesn't mean the absolute time in ms, but the duration in seconds diff --git a/supertokens_python/framework/flask/flask_middleware.py b/supertokens_python/framework/flask/flask_middleware.py index d4eff2eaf..8a598603e 100644 --- a/supertokens_python/framework/flask/flask_middleware.py +++ b/supertokens_python/framework/flask/flask_middleware.py @@ -14,7 +14,7 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Optional, Union from supertokens_python.async_to_sync_wrapper import sync from supertokens_python.framework import BaseResponse @@ -103,7 +103,7 @@ def _(error: Exception): base_request = FlaskRequest(request) user_context = default_user_context(base_request) - result: BaseResponse = sync( + result: Optional[BaseResponse] = sync( st.handle_supertokens_error( base_request, error, @@ -111,6 +111,9 @@ def _(error: Exception): user_context, ) ) - if isinstance(result, FlaskResponse): + if result is not None: + if not isinstance(result, FlaskResponse): + raise Exception("should never happen") + return result.response raise Exception("Should never come here") diff --git a/supertokens_python/framework/flask/flask_request.py b/supertokens_python/framework/flask/flask_request.py index 0b046f6a2..8697e0480 100644 --- a/supertokens_python/framework/flask/flask_request.py +++ b/supertokens_python/framework/flask/flask_request.py @@ -44,8 +44,7 @@ async def json(self) -> Union[Any, None]: def method(self) -> str: if isinstance(self.request, dict): - temp: str = self.request["REQUEST_METHOD"] - return temp + return str(self.request["REQUEST_METHOD"]) # type: ignore return self.request.method # type: ignore def get_cookie(self, key: str) -> Union[str, None]: @@ -75,8 +74,10 @@ def set_session_as_none(self): def get_path(self) -> str: if isinstance(self.request, dict): - temp: str = self.request["PATH_INFO"] - return temp + if not isinstance(self.request["PATH_INFO"], str): + raise Exception("should never happen") + + return str(self.request["PATH_INFO"]) # type: ignore return self.request.base_url async def form_data(self) -> Dict[str, Any]: diff --git a/supertokens_python/framework/response.py b/supertokens_python/framework/response.py index 966477792..fb5cc3477 100644 --- a/supertokens_python/framework/response.py +++ b/supertokens_python/framework/response.py @@ -13,7 +13,7 @@ # under the License. from abc import ABC, abstractmethod -from typing import Any, Dict, Optional +from typing import Any, Dict, Literal, Optional class BaseResponse(ABC): @@ -34,7 +34,7 @@ def set_cookie( domain: Optional[str] = None, secure: bool = False, httponly: bool = False, - samesite: str = "lax", + samesite: Literal["lax", "strict", "none"] = "lax", ): pass diff --git a/supertokens_python/ingredients/emaildelivery/services/smtp.py b/supertokens_python/ingredients/emaildelivery/services/smtp.py index 685ace28e..a160ca59b 100644 --- a/supertokens_python/ingredients/emaildelivery/services/smtp.py +++ b/supertokens_python/ingredients/emaildelivery/services/smtp.py @@ -56,7 +56,9 @@ async def _connect(self): # Try upgrading to TLS (even if the user opted for secure=False) try: await mail.starttls(tls_context=tls_context) - except aiosmtplib.SMTPException: # TLS wasn't supported by the server, so ignore. + except ( + aiosmtplib.SMTPException + ): # TLS wasn't supported by the server, so ignore. pass if self.smtp_settings.password: diff --git a/supertokens_python/logger.py b/supertokens_python/logger.py index 365ff60ea..e8a967eed 100644 --- a/supertokens_python/logger.py +++ b/supertokens_python/logger.py @@ -13,7 +13,7 @@ # under the License. import json import logging -from datetime import datetime +from datetime import datetime, timezone from os import getenv, path from typing import Union @@ -37,7 +37,7 @@ def enable_debug_logging(): def _get_log_timestamp() -> str: - return datetime.utcnow().isoformat()[:-3] + "Z" + return datetime.now(timezone.utc).isoformat()[:-3] + "Z" class CustomStreamHandler(logging.StreamHandler): # type: ignore diff --git a/supertokens_python/recipe/accountlinking/interfaces.py b/supertokens_python/recipe/accountlinking/interfaces.py index 2058765d3..50368b6b5 100644 --- a/supertokens_python/recipe/accountlinking/interfaces.py +++ b/supertokens_python/recipe/accountlinking/interfaces.py @@ -211,9 +211,9 @@ def __init__( class CanLinkAccountsInputUserNotPrimaryError: def __init__(self, description: Optional[str] = None): - self.status: Literal[ + self.status: Literal["INPUT_USER_IS_NOT_A_PRIMARY_USER"] = ( "INPUT_USER_IS_NOT_A_PRIMARY_USER" - ] = "INPUT_USER_IS_NOT_A_PRIMARY_USER" + ) self.description = description @@ -261,9 +261,9 @@ def __init__( class LinkAccountsInputUserNotPrimaryError: def __init__(self): - self.status: Literal[ + self.status: Literal["INPUT_USER_IS_NOT_A_PRIMARY_USER"] = ( "INPUT_USER_IS_NOT_A_PRIMARY_USER" - ] = "INPUT_USER_IS_NOT_A_PRIMARY_USER" + ) class UnlinkAccountOkResult: diff --git a/supertokens_python/recipe/accountlinking/recipe.py b/supertokens_python/recipe/accountlinking/recipe.py index 2f98825be..bd3eecd6c 100644 --- a/supertokens_python/recipe/accountlinking/recipe.py +++ b/supertokens_python/recipe/accountlinking/recipe.py @@ -58,9 +58,9 @@ def __init__( reason: Literal["OK", "PRIMARY_USER_CONFLICT", "ACCOUNT_TAKEOVER_RISK"], ): self.allowed = allowed - self.reason: Literal[ - "OK", "PRIMARY_USER_CONFLICT", "ACCOUNT_TAKEOVER_RISK" - ] = reason + self.reason: Literal["OK", "PRIMARY_USER_CONFLICT", "ACCOUNT_TAKEOVER_RISK"] = ( + reason + ) class TryLinkingByAccountInfoOrCreatePrimaryUserResult: @@ -685,7 +685,6 @@ async def is_email_change_allowed( ) return EmailChangeAllowedResult(allowed=True, reason="OK") - # pylint:disable=no-self-use async def verify_email_for_recipe_user_if_linked_accounts_are_verified( self, user: User, diff --git a/supertokens_python/recipe/accountlinking/types.py b/supertokens_python/recipe/accountlinking/types.py index 0037c8614..cf20bd218 100644 --- a/supertokens_python/recipe/accountlinking/types.py +++ b/supertokens_python/recipe/accountlinking/types.py @@ -39,9 +39,9 @@ def __init__( third_party: Optional[ThirdPartyInfo] = None, ): super().__init__(email, phone_number, third_party) - self.recipe_id: Literal[ - "emailpassword", "thirdparty", "passwordless" - ] = recipe_id + self.recipe_id: Literal["emailpassword", "thirdparty", "passwordless"] = ( + recipe_id + ) def to_json(self) -> Dict[str, Any]: return { @@ -63,9 +63,9 @@ def __init__( super().__init__(recipe_id, email, phone_number, third_party) self.tenant_ids = tenant_ids self.time_joined = time_joined - self.recipe_id: Literal[ - "emailpassword", "thirdparty", "passwordless" - ] = recipe_id + self.recipe_id: Literal["emailpassword", "thirdparty", "passwordless"] = ( + recipe_id + ) @staticmethod def from_login_method( diff --git a/supertokens_python/recipe/dashboard/api/multitenancy/delete_tenant.py b/supertokens_python/recipe/dashboard/api/multitenancy/delete_tenant.py index f14bf1e1f..6878942a6 100644 --- a/supertokens_python/recipe/dashboard/api/multitenancy/delete_tenant.py +++ b/supertokens_python/recipe/dashboard/api/multitenancy/delete_tenant.py @@ -30,9 +30,9 @@ def to_json(self) -> Dict[str, Any]: class DeleteTenantCannotDeletePublicTenantError(APIResponse): def __init__(self): - self.status: Literal[ + self.status: Literal["CANNOT_DELETE_PUBLIC_TENANT_ERROR"] = ( "CANNOT_DELETE_PUBLIC_TENANT_ERROR" - ] = "CANNOT_DELETE_PUBLIC_TENANT_ERROR" + ) def to_json(self) -> Dict[str, Any]: return {"status": self.status} diff --git a/supertokens_python/recipe/dashboard/api/multitenancy/get_third_party_config.py b/supertokens_python/recipe/dashboard/api/multitenancy/get_third_party_config.py index f2790bf4a..7678a380c 100644 --- a/supertokens_python/recipe/dashboard/api/multitenancy/get_third_party_config.py +++ b/supertokens_python/recipe/dashboard/api/multitenancy/get_third_party_config.py @@ -54,12 +54,12 @@ def __init__( def to_json(self) -> Dict[str, Any]: json_response = self.provider_config.to_json() - json_response[ - "isGetAuthorisationRedirectUrlOverridden" - ] = self.is_get_authorisation_redirect_url_overridden - json_response[ - "isExchangeAuthCodeForOAuthTokensOverridden" - ] = self.is_exchange_auth_code_for_oauth_tokens_overridden + json_response["isGetAuthorisationRedirectUrlOverridden"] = ( + self.is_get_authorisation_redirect_url_overridden + ) + json_response["isExchangeAuthCodeForOAuthTokensOverridden"] = ( + self.is_exchange_auth_code_for_oauth_tokens_overridden + ) json_response["isGetUserInfoOverridden"] = self.is_get_user_info_overridden return { "status": "OK", diff --git a/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_first_factor.py b/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_first_factor.py index 2c77eca4c..0af3632dc 100644 --- a/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_first_factor.py +++ b/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_first_factor.py @@ -36,9 +36,9 @@ def to_json(self) -> Dict[str, Literal["OK"]]: class UpdateTenantFirstFactorRecipeNotConfiguredOnBackendSdkErrorResult(APIResponse): - status: Literal[ + status: Literal["RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR"] = ( "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" - ] = "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" + ) def __init__(self, message: str): self.status = "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" diff --git a/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_secondary_factor.py b/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_secondary_factor.py index ad4947754..772ac0ee7 100644 --- a/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_secondary_factor.py +++ b/supertokens_python/recipe/dashboard/api/multitenancy/update_tenant_secondary_factor.py @@ -46,9 +46,9 @@ def to_json(self) -> Dict[str, Union[Literal["OK"], bool]]: class UpdateTenantSecondaryFactorRecipeNotConfiguredOnBackendSdkErrorResult( APIResponse ): - status: Literal[ + status: Literal["RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR"] = ( "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" - ] = "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" + ) def __init__(self, message: str): self.status = "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR" diff --git a/supertokens_python/recipe/dashboard/api/userdetails/user_get.py b/supertokens_python/recipe/dashboard/api/userdetails/user_get.py index 9d0dcef3e..504cd88ad 100644 --- a/supertokens_python/recipe/dashboard/api/userdetails/user_get.py +++ b/supertokens_python/recipe/dashboard/api/userdetails/user_get.py @@ -21,7 +21,10 @@ async def handle_user_get( _tenant_id: str, api_options: APIOptions, _user_context: Dict[str, Any], -) -> Union[UserGetAPINoUserFoundError, UserGetAPIOkResponse,]: +) -> Union[ + UserGetAPINoUserFoundError, + UserGetAPIOkResponse, +]: user_id = api_options.request.get_query_param("userId") if user_id is None: diff --git a/supertokens_python/recipe/emailpassword/interfaces.py b/supertokens_python/recipe/emailpassword/interfaces.py index 60fad3644..f48b1520e 100644 --- a/supertokens_python/recipe/emailpassword/interfaces.py +++ b/supertokens_python/recipe/emailpassword/interfaces.py @@ -157,7 +157,11 @@ async def sign_in( session: Union[SessionContainer, None], should_try_linking_with_session_user: Union[bool, None], user_context: Dict[str, Any], - ) -> Union[SignInOkResult, WrongCredentialsError, LinkingToSessionUserFailedError,]: + ) -> Union[ + SignInOkResult, + WrongCredentialsError, + LinkingToSessionUserFailedError, + ]: pass @abstractmethod diff --git a/supertokens_python/recipe/emailverification/recipe.py b/supertokens_python/recipe/emailverification/recipe.py index 0fc977b5b..498fd08c4 100644 --- a/supertokens_python/recipe/emailverification/recipe.py +++ b/supertokens_python/recipe/emailverification/recipe.py @@ -671,7 +671,7 @@ def __init__( max_age_in_seconds: Optional[int], ): super().__init__(id_) - self.claim: EmailVerificationClaimClass = claim + self.claim = claim self.ev_claim_validators = ev_claim_validators self.refetch_time_on_false_in_ms = refetch_time_on_false_in_seconds * 1000 self.max_age_in_sec = max_age_in_seconds @@ -686,6 +686,12 @@ async def validate( def should_refetch( self, payload: JSONObject, user_context: Dict[str, Any] ) -> MaybeAwaitable[bool]: + if self.claim is None: + raise Exception("should never happen") + + if not isinstance(self.claim, EmailVerificationClaimClass): + raise Exception("should never happen") + value = self.claim.get_value_from_payload(payload, user_context) if value is None: return True diff --git a/supertokens_python/recipe/multifactorauth/multi_factor_auth_claim.py b/supertokens_python/recipe/multifactorauth/multi_factor_auth_claim.py index 8b45108ba..1ed5d4637 100644 --- a/supertokens_python/recipe/multifactorauth/multi_factor_auth_claim.py +++ b/supertokens_python/recipe/multifactorauth/multi_factor_auth_claim.py @@ -40,17 +40,26 @@ def __init__( requirement_list: MFARequirementList, ): super().__init__(id_) - self.claim: MultiFactorAuthClaimClass = claim + self.claim = claim self.requirement_list = requirement_list def should_refetch( self, payload: Dict[str, Any], user_context: Dict[str, Any] ) -> bool: + if self.claim is None: + raise Exception("should never happen") + 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] ) -> ClaimValidationResult: + if self.claim is None: + raise Exception("should never happen") + + if not isinstance(self.claim, MultiFactorAuthClaimClass): + raise Exception("should never happen") + if len(self.requirement_list) == 0: return ClaimValidationResult(is_valid=True) # no requirements to satisfy diff --git a/supertokens_python/recipe/multifactorauth/recipe_implementation.py b/supertokens_python/recipe/multifactorauth/recipe_implementation.py index 476582eab..5073c5ad9 100644 --- a/supertokens_python/recipe/multifactorauth/recipe_implementation.py +++ b/supertokens_python/recipe/multifactorauth/recipe_implementation.py @@ -53,7 +53,7 @@ def __init__( factor_id: str, ): super().__init__(id_) - self.claim: MultiFactorAuthClaimClass = claim + self.claim = claim self.factors_set_up_for_user = factors_set_up_for_user self.factor_id = factor_id self.mfa_requirement_for_auth = mfa_requirement_for_auth @@ -61,11 +61,20 @@ def __init__( def should_refetch( self, payload: Dict[str, Any], user_context: Dict[str, Any] ) -> bool: + if self.claim is None: + raise Exception("should never happen") + return self.claim.get_value_from_payload(payload) is None async def validate( self, payload: JSONObject, user_context: Dict[str, Any] ) -> ClaimValidationResult: + if self.claim is None: + raise Exception("should never happen") + + if not isinstance(self.claim, MultiFactorAuthClaimClass): + raise Exception("should never happen") + claim_val: MFAClaimValue | None = self.claim.get_value_from_payload(payload) if claim_val is None: diff --git a/supertokens_python/recipe/multitenancy/recipe_implementation.py b/supertokens_python/recipe/multitenancy/recipe_implementation.py index 90578bacb..66ea38cb5 100644 --- a/supertokens_python/recipe/multitenancy/recipe_implementation.py +++ b/supertokens_python/recipe/multitenancy/recipe_implementation.py @@ -139,9 +139,9 @@ async def create_or_update_tenant( if not config.is_first_factors_unchanged(): json_body["firstFactors"] = config.get_first_factors_for_update() if not config.is_required_secondary_factors_unchanged(): - json_body[ - "requiredSecondaryFactors" - ] = config.get_required_secondary_factors_for_update() + json_body["requiredSecondaryFactors"] = ( + config.get_required_secondary_factors_for_update() + ) json_body["coreConfig"] = config.core_config response = await self.querier.send_put_request( diff --git a/supertokens_python/recipe/passwordless/api/implementation.py b/supertokens_python/recipe/passwordless/api/implementation.py index 3aa530f26..ca4cbc397 100644 --- a/supertokens_python/recipe/passwordless/api/implementation.py +++ b/supertokens_python/recipe/passwordless/api/implementation.py @@ -33,6 +33,9 @@ APIInterface, APIOptions, CheckCodeOkResult, + CheckCodeIncorrectUserInputCodeError, + CheckCodeExpiredUserInputCodeError, + CheckCodeRestartFlowError, ConsumeCodeExpiredUserInputCodeError, ConsumeCodeIncorrectUserInputCodeError, ConsumeCodeOkResult, @@ -545,6 +548,15 @@ async def consume_code_post( phone_number=device_info.phone_number, email=device_info.email ) + check_credentials_response: Optional[ + Union[ + CheckCodeOkResult, + CheckCodeIncorrectUserInputCodeError, + CheckCodeExpiredUserInputCodeError, + CheckCodeRestartFlowError, + ] + ] = None + async def check_credentials(_: str): nonlocal check_credentials_response if check_credentials_response is None: diff --git a/supertokens_python/recipe/passwordless/interfaces.py b/supertokens_python/recipe/passwordless/interfaces.py index 877539069..8e14b8ae1 100644 --- a/supertokens_python/recipe/passwordless/interfaces.py +++ b/supertokens_python/recipe/passwordless/interfaces.py @@ -112,12 +112,15 @@ def from_json(json: Dict[str, Any]) -> ConsumedDevice: ) def to_json(self) -> Dict[str, Any]: - return { + result: Dict[str, Any] = { "preAuthSessionId": self.pre_auth_session_id, "failedCodeInputAttemptCount": self.failed_code_input_attempt_count, - "email": self.email, - "phoneNumber": self.phone_number, } + if self.email is not None: + result["email"] = self.email + if self.phone_number is not None: + result["phoneNumber"] = self.phone_number + return result class ConsumeCodeOkResult: @@ -183,6 +186,12 @@ def __init__(self, consumed_device: ConsumedDevice): self.status = "OK" self.consumed_device = consumed_device + def to_json(self): + return { + "status": self.status, + "consumedDevice": self.consumed_device.to_json(), + } + class CheckCodeIncorrectUserInputCodeError(ConsumeCodeIncorrectUserInputCodeError): pass diff --git a/supertokens_python/recipe/passwordless/recipe.py b/supertokens_python/recipe/passwordless/recipe.py index ae0e752e7..3c70d5991 100644 --- a/supertokens_python/recipe/passwordless/recipe.py +++ b/supertokens_python/recipe/passwordless/recipe.py @@ -257,9 +257,9 @@ async def get_emails_for_factor( if FactorIds.OTP_EMAIL in all_factors: factor_id_to_emails_map[FactorIds.OTP_EMAIL] = emails_result if FactorIds.LINK_EMAIL in all_factors: - factor_id_to_emails_map[ - FactorIds.LINK_EMAIL - ] = emails_result + factor_id_to_emails_map[FactorIds.LINK_EMAIL] = ( + emails_result + ) return GetEmailsForFactorOkResult( factor_id_to_emails_map=factor_id_to_emails_map ) @@ -267,13 +267,13 @@ async def get_emails_for_factor( # Return just this email to avoid creating more loginMethods factor_id_to_emails_map = {} if FactorIds.OTP_EMAIL in all_factors: - factor_id_to_emails_map[ - FactorIds.OTP_EMAIL - ] = non_fake_emails_passwordless + factor_id_to_emails_map[FactorIds.OTP_EMAIL] = ( + non_fake_emails_passwordless + ) if FactorIds.LINK_EMAIL in all_factors: - factor_id_to_emails_map[ - FactorIds.LINK_EMAIL - ] = non_fake_emails_passwordless + factor_id_to_emails_map[FactorIds.LINK_EMAIL] = ( + non_fake_emails_passwordless + ) return GetEmailsForFactorOkResult( factor_id_to_emails_map=factor_id_to_emails_map ) @@ -294,7 +294,7 @@ async def get_emails_for_factor( ] ) - factor_id_to_emails_map = {} + factor_id_to_emails_map: Dict[str, List[str]] = {} if FactorIds.OTP_EMAIL in all_factors: factor_id_to_emails_map[FactorIds.OTP_EMAIL] = emails_result if FactorIds.LINK_EMAIL in all_factors: @@ -369,15 +369,15 @@ async def get_phone_numbers_for_factors( ] ) - factor_id_to_phone_number_map = {} + factor_id_to_phone_number_map: Dict[str, List[str]] = {} if FactorIds.OTP_PHONE in all_factors: - factor_id_to_phone_number_map[ - FactorIds.OTP_PHONE - ] = phones_result + factor_id_to_phone_number_map[FactorIds.OTP_PHONE] = ( + phones_result + ) if FactorIds.LINK_PHONE in all_factors: - factor_id_to_phone_number_map[ - FactorIds.LINK_PHONE - ] = phones_result + factor_id_to_phone_number_map[FactorIds.LINK_PHONE] = ( + phones_result + ) return GetPhoneNumbersForFactorsOkResult( factor_id_to_phone_number_map=factor_id_to_phone_number_map diff --git a/supertokens_python/recipe/passwordless/recipe_implementation.py b/supertokens_python/recipe/passwordless/recipe_implementation.py index 371c32a14..48904e095 100644 --- a/supertokens_python/recipe/passwordless/recipe_implementation.py +++ b/supertokens_python/recipe/passwordless/recipe_implementation.py @@ -198,7 +198,7 @@ async def create_code( tenant_id: str, user_context: Dict[str, Any], ) -> CreateCodeOkResult: - input_dict = {} + input_dict: Dict[str, Any] = {} if email: input_dict["email"] = email if phone_number: diff --git a/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/__init__.py b/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/__init__.py index 6cf2448de..92acdf298 100644 --- a/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/__init__.py +++ b/supertokens_python/recipe/passwordless/smsdelivery/services/twilio/__init__.py @@ -51,7 +51,7 @@ def __init__( twilio_settings.account_sid, twilio_settings.auth_token, **otps ) oi = ServiceImplementation(self.twilio_client) # type: ignore - self.service_implementation = oi if override is None else override(oi) + self.service_implementation = oi if override is None else override(oi) # type: ignore async def send_sms( self, diff --git a/supertokens_python/recipe/session/api/refresh.py b/supertokens_python/recipe/session/api/refresh.py index ac986d5a6..f114af90b 100644 --- a/supertokens_python/recipe/session/api/refresh.py +++ b/supertokens_python/recipe/session/api/refresh.py @@ -28,7 +28,7 @@ async def handle_refresh_api( ): if ( api_implementation.disable_refresh_post - or api_implementation.refresh_post is None + or api_implementation.refresh_post is None # type: ignore ): return None diff --git a/supertokens_python/recipe/session/api/signout.py b/supertokens_python/recipe/session/api/signout.py index 9c0c68be0..46867b0dd 100644 --- a/supertokens_python/recipe/session/api/signout.py +++ b/supertokens_python/recipe/session/api/signout.py @@ -35,7 +35,7 @@ async def handle_signout_api( ): if ( api_implementation.disable_signout_post - or api_implementation.signout_post is None + or api_implementation.signout_post is None # type: ignore ): return None diff --git a/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.py b/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.py index 5c13a02b6..a4b7413e7 100644 --- a/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.py +++ b/supertokens_python/recipe/session/claim_base_classes/primitive_array_claim.py @@ -42,7 +42,7 @@ def __init__( max_age_in_sec: Optional[int] = None, ): super().__init__(id_) - self.claim: SessionClaim[PrimitiveList] = claim # TODO:PrimitiveArrayClaim + self.claim = claim self.val = val self.max_age_in_sec = max_age_in_sec @@ -53,6 +53,9 @@ def should_refetch( ) -> bool: claim = self.claim + if claim is None: + raise Exception("should never happen") + return (claim.get_value_from_payload(payload, user_context) is None) or ( self.max_age_in_sec is not None and ( @@ -103,9 +106,7 @@ async def _validate( # Doing this to ensure same code in the upcoming steps irrespective of # whether self.val is Primitive or PrimitiveList - vals: List[JSONPrimitive] = ( - val if isinstance(val, list) else [val] - ) # pyright: reportGeneralTypeIssues=false + vals: List[_T] = val if isinstance(val, list) else [val] claim_val_set = set(claim_val) if is_include and not is_include_any: diff --git a/supertokens_python/recipe/session/claim_base_classes/primitive_claim.py b/supertokens_python/recipe/session/claim_base_classes/primitive_claim.py index 0ce0a2ceb..f34fa2e57 100644 --- a/supertokens_python/recipe/session/claim_base_classes/primitive_claim.py +++ b/supertokens_python/recipe/session/claim_base_classes/primitive_claim.py @@ -37,7 +37,7 @@ def __init__( max_age_in_sec: Optional[int] = None, ): super().__init__(id_) - self.claim: SessionClaim[Primitive] = claim # to fix the type for pyright + self.claim = claim self.val = val self.max_age_in_sec = max_age_in_sec @@ -48,6 +48,9 @@ def should_refetch( ) -> bool: max_age_in_sec = self.max_age_in_sec + if self.claim is None: + raise Exception("should never happen") + # (claim value is None) OR (value has expired) return (self.claim.get_value_from_payload(payload, user_context) is None) or ( (max_age_in_sec is not None) @@ -65,7 +68,10 @@ async def validate( val = self.val max_age_in_sec = self.max_age_in_sec - claim_val: JSONPrimitive = self.claim.get_value_from_payload( + if self.claim is None: + raise Exception("should never happen") + + claim_val: Optional[JSONPrimitive] = self.claim.get_value_from_payload( payload, user_context ) if claim_val is None: diff --git a/supertokens_python/recipe/session/framework/fastapi/__init__.py b/supertokens_python/recipe/session/framework/fastapi/__init__.py index 16d660943..fb36d5c41 100644 --- a/supertokens_python/recipe/session/framework/fastapi/__init__.py +++ b/supertokens_python/recipe/session/framework/fastapi/__init__.py @@ -81,13 +81,13 @@ async def session_exception_handler( Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)` """ base_req = FastApiRequest(request) - base_res = FastApiResponse(JSONResponse()) + base_res = FastApiResponse(JSONResponse(content={})) user_context = default_user_context(base_req) result = await Supertokens.get_instance().handle_supertokens_error( base_req, exc, base_res, user_context ) if isinstance(result, FastApiResponse): - body = json.loads(result.response.body) + body = json.loads(bytes(result.response.body)) return JSONResponse(body, status_code=result.response.status_code) raise Exception("Should never come here") diff --git a/supertokens_python/recipe/session/jwks.py b/supertokens_python/recipe/session/jwks.py index 3b7bb0d3f..cdb4b6faa 100644 --- a/supertokens_python/recipe/session/jwks.py +++ b/supertokens_python/recipe/session/jwks.py @@ -50,6 +50,7 @@ def is_fresh(self): cached_keys: Optional[CachedKeys] = None mutex = RWMutex() + # only for testing purposes def reset_jwks_cache(): with RWLockContext(mutex, read=False): diff --git a/supertokens_python/recipe/session/recipe.py b/supertokens_python/recipe/session/recipe.py index 7e4eb4799..8ac2a9804 100644 --- a/supertokens_python/recipe/session/recipe.py +++ b/supertokens_python/recipe/session/recipe.py @@ -237,10 +237,7 @@ async def handle_error( response: BaseResponse, user_context: Dict[str, Any], ) -> BaseResponse: - if ( - isinstance(err, SuperTokensSessionError) - and err.response_mutators is not None - ): + if isinstance(err, SuperTokensSessionError): for mutator in err.response_mutators: mutator(response, user_context) diff --git a/supertokens_python/recipe/session/session_functions.py b/supertokens_python/recipe/session/session_functions.py index a09200633..bef9a6c49 100644 --- a/supertokens_python/recipe/session/session_functions.py +++ b/supertokens_python/recipe/session/session_functions.py @@ -319,7 +319,8 @@ async def get_session( ] # if the token didn't pass validation, but we got here, it means it was a v2 token that we didn't have the key cached for. ), # This will throw error if others are none and 'expiryTime' key doesn't exist in the payload response["session"].get("tenantId") - or (access_token_info or {}).get("tenantId"), + or (access_token_info or {}).get("tenantId") + or "public", ), ( GetSessionAPIResponseAccessToken( diff --git a/supertokens_python/recipe/thirdparty/provider.py b/supertokens_python/recipe/thirdparty/provider.py index 44e426cd3..65573aba6 100644 --- a/supertokens_python/recipe/thirdparty/provider.py +++ b/supertokens_python/recipe/thirdparty/provider.py @@ -47,14 +47,14 @@ def __init__( self.id = id self.config = config - async def get_config_for_client_type( # pylint: disable=no-self-use + async def get_config_for_client_type( self, client_type: Optional[str], user_context: Dict[str, Any] ) -> ProviderConfigForClient: _ = client_type __ = user_context raise NotImplementedError() - async def get_authorisation_redirect_url( # pylint: disable=no-self-use + async def get_authorisation_redirect_url( self, redirect_uri_on_provider_dashboard: str, user_context: Dict[str, Any], @@ -63,7 +63,7 @@ async def get_authorisation_redirect_url( # pylint: disable=no-self-use __ = user_context raise NotImplementedError() - async def exchange_auth_code_for_oauth_tokens( # pylint: disable=no-self-use + async def exchange_auth_code_for_oauth_tokens( self, redirect_uri_info: RedirectUriInfo, user_context: Dict[str, Any], @@ -72,7 +72,7 @@ async def exchange_auth_code_for_oauth_tokens( # pylint: disable=no-self-use __ = user_context raise NotImplementedError() - async def get_user_info( # pylint: disable=no-self-use + async def get_user_info( self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any], diff --git a/supertokens_python/recipe/thirdparty/providers/apple.py b/supertokens_python/recipe/thirdparty/providers/apple.py index 3bba95ef8..2774e9bf4 100644 --- a/supertokens_python/recipe/thirdparty/providers/apple.py +++ b/supertokens_python/recipe/thirdparty/providers/apple.py @@ -51,9 +51,7 @@ async def get_config_for_client_type( return config - async def _get_client_secret( # pylint: disable=no-self-use - self, config: ProviderConfigForClient - ) -> str: + async def _get_client_secret(self, config: ProviderConfigForClient) -> str: if ( config.additional_config is None or config.additional_config.get("keyId") is None @@ -114,9 +112,9 @@ async def get_user_info( if response.raw_user_info_from_provider.from_id_token_payload is None: response.raw_user_info_from_provider.from_id_token_payload = {} - response.raw_user_info_from_provider.from_id_token_payload[ - "user" - ] = user_dict + response.raw_user_info_from_provider.from_id_token_payload["user"] = ( + user_dict + ) return response diff --git a/supertokens_python/recipe/thirdparty/providers/bitbucket.py b/supertokens_python/recipe/thirdparty/providers/bitbucket.py index 53fa46f41..82e28a673 100644 --- a/supertokens_python/recipe/thirdparty/providers/bitbucket.py +++ b/supertokens_python/recipe/thirdparty/providers/bitbucket.py @@ -71,9 +71,9 @@ async def get_user_info( # checker is not agreeing so doing this: raw_user_info_from_provider.from_id_token_payload = {} - raw_user_info_from_provider.from_id_token_payload[ - "email" - ] = user_info_from_email + raw_user_info_from_provider.from_id_token_payload["email"] = ( + user_info_from_email + ) email = None is_verified = False diff --git a/supertokens_python/recipe/thirdparty/providers/custom.py b/supertokens_python/recipe/thirdparty/providers/custom.py index 4d65ef894..d5b353247 100644 --- a/supertokens_python/recipe/thirdparty/providers/custom.py +++ b/supertokens_python/recipe/thirdparty/providers/custom.py @@ -218,9 +218,7 @@ def __init__(self, provider_config: ProviderConfig): ) super().__init__(input_config.third_party_id, provider_config_for_client) - def _normalize_input( # pylint: disable=no-self-use - self, input_config: ProviderConfig - ) -> ProviderConfig: + def _normalize_input(self, input_config: ProviderConfig) -> ProviderConfig: if input_config.user_info_map is None: input_config.user_info_map = UserInfoMap( from_id_token_payload=UserFields(), @@ -368,9 +366,9 @@ async def exchange_auth_code_for_oauth_tokens( # Transformation needed for dev keys BEGIN if is_using_oauth_development_client_id(self.config.client_id): - access_token_params[ - "client_id" - ] = get_actual_client_id_from_development_client_id(self.config.client_id) + access_token_params["client_id"] = ( + get_actual_client_id_from_development_client_id(self.config.client_id) + ) access_token_params["redirect_uri"] = DEV_OAUTH_REDIRECT_URL # Transformation needed for dev keys END diff --git a/supertokens_python/recipe/totp/interfaces.py b/supertokens_python/recipe/totp/interfaces.py index 64c45f783..3f1fbb80a 100644 --- a/supertokens_python/recipe/totp/interfaces.py +++ b/supertokens_python/recipe/totp/interfaces.py @@ -60,7 +60,11 @@ async def create_device( skew: Optional[int], period: Optional[int], user_context: Dict[str, Any], - ) -> Union[CreateDeviceOkResult, DeviceAlreadyExistsError, UnknownUserIdError,]: + ) -> Union[ + CreateDeviceOkResult, + DeviceAlreadyExistsError, + UnknownUserIdError, + ]: pass @abstractmethod @@ -70,7 +74,11 @@ async def update_device( existing_device_name: str, new_device_name: str, user_context: Dict[str, Any], - ) -> Union[UpdateDeviceOkResult, UnknownDeviceError, DeviceAlreadyExistsError,]: + ) -> Union[ + UpdateDeviceOkResult, + UnknownDeviceError, + DeviceAlreadyExistsError, + ]: pass @abstractmethod diff --git a/supertokens_python/recipe/totp/recipe_implementation.py b/supertokens_python/recipe/totp/recipe_implementation.py index ce213dd88..d19a03eee 100644 --- a/supertokens_python/recipe/totp/recipe_implementation.py +++ b/supertokens_python/recipe/totp/recipe_implementation.py @@ -95,7 +95,11 @@ async def create_device( skew: Optional[int], period: Optional[int], user_context: Dict[str, Any], - ) -> Union[CreateDeviceOkResult, DeviceAlreadyExistsError, UnknownUserIdError,]: + ) -> Union[ + CreateDeviceOkResult, + DeviceAlreadyExistsError, + UnknownUserIdError, + ]: if user_identifier_info is None: email_or_phone_info = await self.get_user_identifier_info_for_user_id( user_id, user_context @@ -137,7 +141,11 @@ async def update_device( existing_device_name: str, new_device_name: str, user_context: Dict[str, Any], - ) -> Union[UpdateDeviceOkResult, UnknownDeviceError, DeviceAlreadyExistsError,]: + ) -> Union[ + UpdateDeviceOkResult, + UnknownDeviceError, + DeviceAlreadyExistsError, + ]: # Prepare the data for the API request data = { "userId": user_id, diff --git a/supertokens_python/recipe/totp/types.py b/supertokens_python/recipe/totp/types.py index f9599bf65..d266d1987 100644 --- a/supertokens_python/recipe/totp/types.py +++ b/supertokens_python/recipe/totp/types.py @@ -43,9 +43,9 @@ def to_json(self) -> Dict[str, Any]: class UserIdentifierInfoDoesNotExistError: def __init__(self): - self.status: Literal[ + self.status: Literal["USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR"] = ( "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" - ] = "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" + ) class CreateDeviceOkResult(OkResult): @@ -66,9 +66,9 @@ def to_json(self) -> Dict[str, Any]: class DeviceAlreadyExistsError(APIResponse): def __init__(self): - self.status: Literal[ + self.status: Literal["DEVICE_ALREADY_EXISTS_ERROR"] = ( "DEVICE_ALREADY_EXISTS_ERROR" - ] = "DEVICE_ALREADY_EXISTS_ERROR" + ) def to_json(self) -> Dict[str, Any]: return {"status": self.status} diff --git a/supertokens_python/recipe/userroles/recipe_implementation.py b/supertokens_python/recipe/userroles/recipe_implementation.py index 955a6ae9d..c4939085b 100644 --- a/supertokens_python/recipe/userroles/recipe_implementation.py +++ b/supertokens_python/recipe/userroles/recipe_implementation.py @@ -163,7 +163,7 @@ async def delete_role( return DeleteRoleOkResult(did_role_exist=response["didRoleExist"]) async def get_all_roles(self, user_context: Dict[str, Any]) -> GetAllRolesOkResult: - params = {} + params: Dict[str, Any] = {} response = await self.querier.send_get_request( NormalisedURLPath("/recipe/roles"), params, diff --git a/supertokens_python/supertokens.py b/supertokens_python/supertokens.py index 6aae8aa9b..53964da2e 100644 --- a/supertokens_python/supertokens.py +++ b/supertokens_python/supertokens.py @@ -345,7 +345,7 @@ def get_all_cors_headers(self) -> List[str]: return list(headers_set) - async def get_user_count( # pylint: disable=no-self-use + async def get_user_count( self, include_recipe_ids: Union[None, List[str]], tenant_id: Optional[str] = None, @@ -367,7 +367,7 @@ async def get_user_count( # pylint: disable=no-self-use return int(response["count"]) - async def create_user_id_mapping( # pylint: disable=no-self-use + async def create_user_id_mapping( self, supertokens_user_id: str, external_user_id: str, @@ -409,7 +409,7 @@ async def create_user_id_mapping( # pylint: disable=no-self-use raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0") - async def get_user_id_mapping( # pylint: disable=no-self-use + async def get_user_id_mapping( self, user_id: str, user_id_type: Optional[UserIDTypes], @@ -443,7 +443,7 @@ async def get_user_id_mapping( # pylint: disable=no-self-use raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0") - async def delete_user_id_mapping( # pylint: disable=no-self-use + async def delete_user_id_mapping( self, user_id: str, user_id_type: Optional[UserIDTypes], @@ -475,7 +475,7 @@ async def delete_user_id_mapping( # pylint: disable=no-self-use raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0") - async def update_or_delete_user_id_mapping_info( # pylint: disable=no-self-use + async def update_or_delete_user_id_mapping_info( self, user_id: str, user_id_type: Optional[UserIDTypes], @@ -505,7 +505,7 @@ async def update_or_delete_user_id_mapping_info( # pylint: disable=no-self-use raise_general_exception("Please upgrade the SuperTokens core to >= 3.15.0") - async def middleware( # pylint: disable=no-self-use + async def middleware( self, request: BaseRequest, response: BaseResponse, user_context: Dict[str, Any] ) -> Union[BaseResponse, None]: log_debug_message("middleware: Started") @@ -634,7 +634,7 @@ async def handle_supertokens_error( err: Exception, response: BaseResponse, user_context: Dict[str, Any], - ): + ) -> Optional[BaseResponse]: log_debug_message("errorHandler: Started") log_debug_message( "errorHandler: Error is from SuperTokens recipe. Message: %s", str(err) @@ -659,7 +659,7 @@ async def handle_supertokens_error( return await recipe.handle_error(request, err, response, user_context) raise err - def get_request_from_user_context( # pylint: disable=no-self-use + def get_request_from_user_context( self, user_context: Optional[Dict[str, Any]] = None, ) -> Optional[BaseRequest]: diff --git a/supertokens_python/types.py b/supertokens_python/types.py index 6c4512fcb..782cbc2ae 100644 --- a/supertokens_python/types.py +++ b/supertokens_python/types.py @@ -75,9 +75,9 @@ def __init__( verified: bool, ): super().__init__(email, phone_number, third_party) - self.recipe_id: Literal[ - "emailpassword", "thirdparty", "passwordless" - ] = recipe_id + self.recipe_id: Literal["emailpassword", "thirdparty", "passwordless"] = ( + recipe_id + ) self.recipe_user_id = RecipeUserId(recipe_user_id) self.tenant_ids: List[str] = tenant_ids self.time_joined = time_joined diff --git a/tests/Django/test_django.py b/tests/Django/test_django.py index 66cd27357..4291524e3 100644 --- a/tests/Django/test_django.py +++ b/tests/Django/test_django.py @@ -120,7 +120,7 @@ async def optional_session(request: HttpRequest): session: Union[None, SessionContainer] = request.supertokens # type: ignore if session is None: return JsonResponse({"s": "empty session"}) - return JsonResponse({"s": session.get_handle()}) + return JsonResponse({"s": session.get_handle()}) # type: ignore @verify_session() @@ -538,7 +538,7 @@ async def test_search_with_multiple_emails(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(emailpassword=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -591,7 +591,7 @@ async def test_search_with_email_t(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(emailpassword=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -642,7 +642,7 @@ async def test_search_with_email_iresh(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(emailpassword=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -700,7 +700,7 @@ async def test_search_with_phone_plus_one(self): await create_users( passwordless=True, ) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -754,7 +754,7 @@ async def test_search_with_phone_one_bracket(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(passwordless=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -849,7 +849,7 @@ async def test_search_with_provider_google(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(emailpassword=False, passwordless=False, thirdparty=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -948,7 +948,7 @@ async def test_search_with_provider_google_and_phone_one(self): if not is_version_gte(cdi_version, "2.20"): pytest.skip() await create_users(emailpassword=False, passwordless=True, thirdparty=True) - headers = { + headers: Dict[str, Any] = { "content_type": "application/json", "HTTP_AUTHORIZATION": "Bearer testapikey", } @@ -990,7 +990,7 @@ async def test_that_verify_session_return_401_if_access_token_is_not_sent_and_mi "public", RecipeUserId("userId"), {}, {} ) access_token = s.get_access_token() - headers = {"HTTP_AUTHORIZATION": "Bearer " + access_token} + headers: Dict[str, Any] = {"HTTP_AUTHORIZATION": "Bearer " + access_token} # Now try with middleware: request = self.factory.get("/verify", {}, **headers) diff --git a/tests/Fastapi/test_fastapi.py b/tests/Fastapi/test_fastapi.py index 6a76a3168..8005ffb92 100644 --- a/tests/Fastapi/test_fastapi.py +++ b/tests/Fastapi/test_fastapi.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import json -from typing import Any, Dict, Union +from typing import Any, Dict, Union, Optional from fastapi import Depends, FastAPI from fastapi.requests import Request @@ -85,7 +85,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client() -> TestClient: app = FastAPI() app.add_middleware(get_middleware()) @@ -121,10 +121,10 @@ async def handle_get(request: Request): # type: ignore return {"s": session.get_handle()} @app.get("/handle-session-optional") - async def handle_get_optional(session: SessionContainer = Depends(verify_session(session_required=False))): # type: ignore + async def handle_get_optional(session: Optional[SessionContainer] = Depends(verify_session(session_required=False))): # type: ignore if session is None: return {"s": "empty session"} - return {"s": session.get_handle()} + return {"s": session.get_handle()} # type: ignore @app.post("/logout") async def custom_logout(request: Request): # type: ignore @@ -551,6 +551,8 @@ def test_fastapi_root_path(fastapi_root_path: str): start_st() # Test with root_path + if fastapi_root_path.startswith("/"): + fastapi_root_path = fastapi_root_path[1:] app = FastAPI(root_path=fastapi_root_path) app.add_middleware(get_middleware()) test_client = TestClient(app) diff --git a/tests/Flask/test_flask.py b/tests/Flask/test_flask.py index ec45aadc3..a84f7fd0a 100644 --- a/tests/Flask/test_flask.py +++ b/tests/Flask/test_flask.py @@ -244,7 +244,7 @@ def custom_logout(): # type: ignore return app -def test_cookie_login_and_refresh(driver_config_app: Any): +def test_cookie_login_and_refresh(driver_config_app: Flask): start_st() set_key_value_in_config(TEST_COOKIE_SAME_SITE_CONFIG_KEY, "None") @@ -284,7 +284,9 @@ def test_cookie_login_and_refresh(driver_config_app: Any): test_client = driver_config_app.test_client() test_client.set_cookie( - "localhost", "sRefreshToken", cookies_1["sRefreshToken"]["value"] + "sRefreshToken", + cookies_1["sRefreshToken"]["value"], + domain="localhost", ) response_2 = test_client.post( "/refresh", headers={"anti-csrf": response_1.headers.get("anti-csrf")} @@ -349,7 +351,9 @@ def test_login_refresh_no_csrf(driver_config_app: Any): test_client = driver_config_app.test_client() test_client.set_cookie( - "localhost", "sRefreshToken", cookies_1["sRefreshToken"]["value"] + "sRefreshToken", + cookies_1["sRefreshToken"]["value"], + domain="localhost", ) # post with csrf token -> no error @@ -403,7 +407,9 @@ def test_login_logout(driver_config_app: Any): test_client = driver_config_app.test_client() test_client.set_cookie( - "localhost", "sAccessToken", cookies_1["sAccessToken"]["value"] + "sAccessToken", + cookies_1["sAccessToken"]["value"], + domain="localhost", ) response_2 = test_client.post( @@ -443,7 +449,9 @@ def test_login_handle(driver_config_app: Any): cookies_1 = extract_all_cookies(response_1) test_client = driver_config_app.test_client() test_client.set_cookie( - "localhost", "sAccessToken", cookies_1["sAccessToken"]["value"] + "sAccessToken", + cookies_1["sAccessToken"]["value"], + domain="localhost", ) response_2 = test_client.get( diff --git a/tests/Flask/utils.py b/tests/Flask/utils.py index 14875b844..f3f75d9b4 100644 --- a/tests/Flask/utils.py +++ b/tests/Flask/utils.py @@ -15,10 +15,10 @@ from typing import Any, Dict -from fastapi import Response +from werkzeug.test import TestResponse -def extract_all_cookies(response: Response) -> Dict[str, Any]: +def extract_all_cookies(response: TestResponse) -> Dict[str, Any]: cookie_headers = response.headers.getlist("Set-Cookie") cookies: Dict[str, Any] = {} for header in cookie_headers: diff --git a/tests/auth-react/django3x/mysite/middleware.py b/tests/auth-react/django3x/mysite/middleware.py index ad282b098..feeb16940 100644 --- a/tests/auth-react/django3x/mysite/middleware.py +++ b/tests/auth-react/django3x/mysite/middleware.py @@ -1,4 +1,5 @@ import asyncio +from typing import Callable from django.http import HttpRequest, HttpResponse @@ -6,20 +7,34 @@ def custom_cors_middleware(get_response): # type: ignore if asyncio.iscoroutinefunction(get_response): # type: ignore - async def __middleware1(request: HttpRequest): - response: HttpResponse = await get_response(request) + async def __middleware1(request: HttpRequest): # type: ignore + response: HttpResponse = await get_response(request) # type: ignore if request.method == "OPTIONS": response.status_code = 204 - return response + return response # type: ignore return __middleware1 else: - def __middleware(request: HttpRequest): - response: HttpResponse = get_response(request) + def __middleware(request: HttpRequest): # type: ignore + response: HttpResponse = get_response(request) # type: ignore if request.method == "OPTIONS": response.status_code = 204 - return response + return response # type: ignore return __middleware + + +def response_logging_middleware(get_response: Callable): # type: ignore + def middleware(request: HttpRequest) -> HttpResponse: + response = get_response(request) # type: ignore + + # Log the response + print(f"Path: {request.path} | Method: {request.method} | Status: {response.status_code}") # type: ignore + if hasattr(response, "content"): # type: ignore + print(f"Response: {response.content.decode('utf-8')}") # type: ignore + + return response # type: ignore + + return middleware diff --git a/tests/auth-react/django3x/mysite/settings.py b/tests/auth-react/django3x/mysite/settings.py index 6ad791b27..24241dab1 100644 --- a/tests/auth-react/django3x/mysite/settings.py +++ b/tests/auth-react/django3x/mysite/settings.py @@ -73,6 +73,7 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "supertokens_python.framework.django.django_middleware.middleware", + # "mysite.middleware.response_logging_middleware", # Uncomment this for response logging ] ROOT_URLCONF = "mysite.urls" diff --git a/tests/auth-react/django3x/mysite/store.py b/tests/auth-react/django3x/mysite/store.py index 3f2ece28c..d1359937e 100644 --- a/tests/auth-react/django3x/mysite/store.py +++ b/tests/auth-react/django3x/mysite/store.py @@ -1,3 +1,4 @@ +import time from typing import Any, Dict, List, Optional, Union from typing_extensions import Literal @@ -10,6 +11,13 @@ def save_url_with_token(url_with_token: str): def get_url_with_token() -> str: + t = 0 + while not latest_url_with_token: + time.sleep(0.5) + t += 1 + if t > 10: + break + return latest_url_with_token diff --git a/tests/auth-react/django3x/mysite/urls.py b/tests/auth-react/django3x/mysite/urls.py index 113b065e4..ec16052eb 100644 --- a/tests/auth-react/django3x/mysite/urls.py +++ b/tests/auth-react/django3x/mysite/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.urls import path, include urlpatterns = [ diff --git a/tests/auth-react/django3x/mysite/utils.py b/tests/auth-react/django3x/mysite/utils.py index d47a28438..cfafd8777 100644 --- a/tests/auth-react/django3x/mysite/utils.py +++ b/tests/auth-react/django3x/mysite/utils.py @@ -204,7 +204,7 @@ async def check_for_general_error( def auth0_provider_override(oi: Provider) -> Provider: - async def get_user_info( # pylint: disable=no-self-use + async def get_user_info( oauth_tokens: Dict[str, Any], user_context: Dict[str, Any], ) -> UserInfo: @@ -944,7 +944,7 @@ async def resync_session_and_fetch_mfa_info_put( }, ] - accountlinking_config_input = { + accountlinking_config_input: Dict[str, Any] = { "enabled": False, "shouldAutoLink": { "shouldAutomaticallyLink": True, @@ -965,10 +965,10 @@ async def should_do_automatic_account_linking( ]: should_auto_link = accountlinking_config_input["shouldAutoLink"] assert isinstance(should_auto_link, dict) - should_automatically_link = should_auto_link["shouldAutomaticallyLink"] + should_automatically_link = should_auto_link["shouldAutomaticallyLink"] # type: ignore assert isinstance(should_automatically_link, bool) if should_automatically_link: - should_require_verification = should_auto_link["shouldRequireVerification"] + should_require_verification = should_auto_link["shouldRequireVerification"] # type: ignore assert isinstance(should_require_verification, bool) return accountlinking.ShouldAutomaticallyLink( should_require_verification=should_require_verification diff --git a/tests/auth-react/django3x/polls/views.py b/tests/auth-react/django3x/polls/views.py index c531c9ddc..cd6b7c9be 100644 --- a/tests/auth-react/django3x/polls/views.py +++ b/tests/auth-react/django3x/polls/views.py @@ -107,10 +107,10 @@ async def session_info(request: HttpRequest): # type: ignore session_: SessionContainer = request.supertokens # type: ignore return JsonResponse( { - "sessionHandle": session_.get_handle(), - "userId": session_.get_user_id(), - "jwtPayload": session_.get_access_token_payload(), - "sessionDataFromDatabase": await session_.get_session_data_from_database(), + "sessionHandle": session_.get_handle(), # type: ignore + "userId": session_.get_user_id(), # type: ignore + "jwtPayload": session_.get_access_token_payload(), # type: ignore + "sessionDataFromDatabase": await session_.get_session_data_from_database(), # type: ignore } ) @@ -119,16 +119,16 @@ async def set_role_api(request: HttpRequest): session_: SessionContainer = request.supertokens # type: ignore body = json.loads(request.body) await create_new_role_or_add_permissions(body["role"], body["permissions"]) - await add_role_to_user("public", session_.get_user_id(), body["role"]) - await session_.fetch_and_set_claim(UserRoleClaim) - await session_.fetch_and_set_claim(PermissionClaim) + await add_role_to_user("public", session_.get_user_id(), body["role"]) # type: ignore + await session_.fetch_and_set_claim(UserRoleClaim) # type: ignore + await session_.fetch_and_set_claim(PermissionClaim) # type: ignore return JsonResponse({"status": "OK"}) @verify_session() async def unverify_email_api(request: HttpRequest): session_: SessionContainer = request.supertokens # type: ignore - await unverify_email(session_.get_recipe_user_id()) - await session_.fetch_and_set_claim(EmailVerificationClaim) + await unverify_email(session_.get_recipe_user_id()) # type: ignore + await session_.fetch_and_set_claim(EmailVerificationClaim) # type: ignore return JsonResponse({"status": "OK"}) @verify_session(override_global_claim_validators=override_global_claim_validators) @@ -163,10 +163,10 @@ def session_info(request: HttpRequest): session_: SessionContainer = request.supertokens # type: ignore return JsonResponse( { - "sessionHandle": session_.get_handle(), - "userId": session_.get_user_id(), - "accessTokenPayload": session_.get_access_token_payload(), - "sessionDataFromDatabase": session_.sync_get_session_data_from_database(), + "sessionHandle": session_.get_handle(), # type: ignore + "userId": session_.get_user_id(), # type: ignore + "accessTokenPayload": session_.get_access_token_payload(), # type: ignore + "sessionDataFromDatabase": session_.sync_get_session_data_from_database(), # type: ignore } ) @@ -175,16 +175,16 @@ def sync_set_role_api(request: HttpRequest): session_: SessionContainer = request.supertokens # type: ignore body = json.loads(request.body) sync_create_new_role_or_add_permissions(body["role"], body["permissions"]) - sync_add_role_to_user("public", session_.get_user_id(), body["role"]) - session_.sync_fetch_and_set_claim(UserRoleClaim) - session_.sync_fetch_and_set_claim(PermissionClaim) + sync_add_role_to_user("public", session_.get_user_id(), body["role"]) # type: ignore + session_.sync_fetch_and_set_claim(UserRoleClaim) # type: ignore + session_.sync_fetch_and_set_claim(PermissionClaim) # type: ignore return JsonResponse({"status": "OK"}) @verify_session() def sync_unverify_email_api(request: HttpRequest): session_: SessionContainer = request.supertokens # type: ignore - sync_unverify_email(session_.get_recipe_user_id()) - session_.sync_fetch_and_set_claim(EmailVerificationClaim) + sync_unverify_email(session_.get_recipe_user_id()) # type: ignore + session_.sync_fetch_and_set_claim(EmailVerificationClaim) # type: ignore return JsonResponse({"status": "OK"}) def sync_delete_user(request: HttpRequest): @@ -425,7 +425,7 @@ async def add_required_factor(request: HttpRequest): return JsonResponse({"error": "Invalid request body"}, status_code=400) await add_to_required_secondary_factors_for_user( - session_.get_user_id(), body["factorId"] + session_.get_user_id(), body["factorId"] # type: ignore ) return JsonResponse({"status": "OK"}) diff --git a/tests/auth-react/fastapi-server/app.py b/tests/auth-react/fastapi-server/app.py index 4a3b2f4b4..cfdaaec10 100644 --- a/tests/auth-react/fastapi-server/app.py +++ b/tests/auth-react/fastapi-server/app.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import os +import time import typing from typing import Any, Awaitable, Callable, Dict, List, Optional, Union @@ -172,6 +173,29 @@ app.add_middleware(get_middleware()) os.environ.setdefault("SUPERTOKENS_ENV", "testing") + +# Uncomment the following for response logging +# @app.middleware("http") +# async def log_response(request: Request, call_next): # type: ignore +# response = await call_next(request) # type: ignore + +# try: +# body_bytes = b"" +# async for chunk in response.body_iterator: # type: ignore +# body_bytes += chunk # type: ignore +# print(f"Response: {body_bytes.decode('utf-8')}") # type: ignore +# response_with_body = Response( +# content=body_bytes, +# status_code=response.status_code, # type: ignore +# headers=response.headers, # type: ignore +# media_type=response.media_type, # type: ignore +# ) +# return response_with_body +# except: +# pass +# return response # type: ignore + + code_store: Dict[str, List[Dict[str, Any]]] = {} accountlinking_config: Dict[str, Any] = {} enabled_providers: Optional[List[Any]] = None @@ -308,7 +332,7 @@ async def validate_age(value: Any, tenant_id: str): def auth0_provider_override(oi: Provider) -> Provider: - async def get_user_info( # pylint: disable=no-self-use + async def get_user_info( oauth_tokens: Dict[str, Any], user_context: Dict[str, Any], ) -> UserInfo: @@ -1001,7 +1025,7 @@ async def resync_session_and_fetch_mfa_info_put( global accountlinking_config - accountlinking_config_input = { + accountlinking_config_input: Dict[str, Any] = { "enabled": False, "shouldAutoLink": { "shouldAutomaticallyLink": True, @@ -1022,10 +1046,10 @@ async def should_do_automatic_account_linking( ]: should_auto_link = accountlinking_config_input["shouldAutoLink"] assert isinstance(should_auto_link, dict) - should_automatically_link = should_auto_link["shouldAutomaticallyLink"] + should_automatically_link = should_auto_link["shouldAutomaticallyLink"] # type: ignore assert isinstance(should_automatically_link, bool) if should_automatically_link: - should_require_verification = should_auto_link["shouldRequireVerification"] + should_require_verification = should_auto_link["shouldRequireVerification"] # type: ignore assert isinstance(should_require_verification, bool) return accountlinking.ShouldAutomaticallyLink( should_require_verification=should_require_verification @@ -1397,6 +1421,14 @@ async def get_session_info(session_: SessionContainer = Depends(verify_session() @app.get("/token") async def get_token(): global latest_url_with_token + t = 0 + + while not latest_url_with_token: + time.sleep(0.5) + t += 1 + if t > 10: + break + return JSONResponse({"latestURLWithToken": latest_url_with_token}) diff --git a/tests/auth-react/flask-server/app.py b/tests/auth-react/flask-server/app.py index 23f91ddef..6c840c9e3 100644 --- a/tests/auth-react/flask-server/app.py +++ b/tests/auth-react/flask-server/app.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import os +import time import traceback from typing import Any, Awaitable, Callable, Dict, List, Optional, Union @@ -314,7 +315,7 @@ async def check_for_general_error( def auth0_provider_override(oi: Provider) -> Provider: - async def get_user_info( # pylint: disable=no-self-use + async def get_user_info( oauth_tokens: Dict[str, Any], user_context: Dict[str, Any], ) -> UserInfo: @@ -1007,7 +1008,7 @@ async def resync_session_and_fetch_mfa_info_put( global accountlinking_config - accountlinking_config_input = { + accountlinking_config_input: Dict[str, Any] = { "enabled": False, "shouldAutoLink": { "shouldAutomaticallyLink": True, @@ -1028,10 +1029,10 @@ async def should_do_automatic_account_linking( ]: should_auto_link = accountlinking_config_input["shouldAutoLink"] assert isinstance(should_auto_link, dict) - should_automatically_link = should_auto_link["shouldAutomaticallyLink"] + should_automatically_link = should_auto_link["shouldAutomaticallyLink"] # type: ignore assert isinstance(should_automatically_link, bool) if should_automatically_link: - should_require_verification = should_auto_link["shouldRequireVerification"] + should_require_verification = should_auto_link["shouldRequireVerification"] # type: ignore assert isinstance(should_require_verification, bool) return accountlinking.ShouldAutomaticallyLink( should_require_verification=should_require_verification @@ -1094,6 +1095,13 @@ def make_default_options_response(): ) +# Uncomment the following for response logging +# @app.after_request +# def after_request(response): # type: ignore +# print(f"Response: {response.get_data(as_text=True)}") # type: ignore +# return response # type: ignore + + @app.route("/ping", methods=["GET"]) # type: ignore def ping(): return "success" @@ -1187,7 +1195,7 @@ def setup_tenant(): raise Exception("Should never come here") tenant_id = body["tenantId"] login_methods = body["loginMethods"] - core_config = "coreConfig" in body and body["coreConfig"] or {} + core_config: Dict[str, Any] = "coreConfig" in body and body["coreConfig"] or {} first_factors: List[str] = [] if login_methods.get("emailPassword", {}).get("enabled") == True: @@ -1284,6 +1292,14 @@ def get_session_info(): @app.route("/token", methods=["GET"]) # type: ignore def get_token(): global latest_url_with_token + + t = 0 + while not latest_url_with_token: + time.sleep(0.5) + t += 1 + if t > 10: + break + return jsonify({"latestURLWithToken": latest_url_with_token}) @@ -1473,7 +1489,8 @@ def check_role_api(): @app.route("/", defaults={"path": ""}) # type: ignore @app.route("/") # type: ignore -def index(_: str): +def index(path: str): + _ = path return "" diff --git a/tests/dashboard/test_dashboard.py b/tests/dashboard/test_dashboard.py index ab8e43763..a37ff4b9b 100644 --- a/tests/dashboard/test_dashboard.py +++ b/tests/dashboard/test_dashboard.py @@ -52,7 +52,7 @@ def teardown_function(_): @fixture(scope="function") -async def app(): +def app(): app = FastAPI() app.add_middleware(get_middleware()) @@ -68,7 +68,7 @@ async def should_allow_access( ) -> bool: return True - oi.should_allow_access = should_allow_access + oi.should_allow_access = should_allow_access # type: ignore return oi st_args = get_st_init_args( @@ -102,7 +102,7 @@ async def should_allow_access( ) -> bool: return True - oi.should_allow_access = should_allow_access + oi.should_allow_access = should_allow_access # type: ignore return oi st_args = get_st_init_args( @@ -227,7 +227,7 @@ async def should_allow_access( ) -> bool: return True - oi.should_allow_access = should_allow_access + oi.should_allow_access = should_allow_access # type: ignore return oi st_args = get_st_init_args( diff --git a/tests/emailpassword/test_emaildelivery.py b/tests/emailpassword/test_emaildelivery.py index 2e51e6df1..b593e83ca 100644 --- a/tests/emailpassword/test_emaildelivery.py +++ b/tests/emailpassword/test_emaildelivery.py @@ -78,7 +78,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_emailexists.py b/tests/emailpassword/test_emailexists.py index 748dde85d..8a6ca193b 100644 --- a/tests/emailpassword/test_emailexists.py +++ b/tests/emailpassword/test_emailexists.py @@ -44,7 +44,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_emailverify.py b/tests/emailpassword/test_emailverify.py index 06314917e..14a511fe9 100644 --- a/tests/emailpassword/test_emailverify.py +++ b/tests/emailpassword/test_emailverify.py @@ -78,7 +78,7 @@ @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_passwordreset.py b/tests/emailpassword/test_passwordreset.py index 26856b78b..dec47d271 100644 --- a/tests/emailpassword/test_passwordreset.py +++ b/tests/emailpassword/test_passwordreset.py @@ -51,7 +51,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_signin.py b/tests/emailpassword/test_signin.py index 42f1b86d7..6745958f4 100644 --- a/tests/emailpassword/test_signin.py +++ b/tests/emailpassword/test_signin.py @@ -55,7 +55,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_signup.py b/tests/emailpassword/test_signup.py index a1acb6030..8313a1abc 100644 --- a/tests/emailpassword/test_signup.py +++ b/tests/emailpassword/test_signup.py @@ -35,7 +35,7 @@ @fixture(scope="function") -async def app(): +def app(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/emailpassword/test_updateemailorpassword.py b/tests/emailpassword/test_updateemailorpassword.py index 71a53112b..c591a94d7 100644 --- a/tests/emailpassword/test_updateemailorpassword.py +++ b/tests/emailpassword/test_updateemailorpassword.py @@ -37,7 +37,7 @@ @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/frontendIntegration/django2x/mysite/urls.py b/tests/frontendIntegration/django2x/mysite/urls.py index 884f78fb7..55980a6ea 100644 --- a/tests/frontendIntegration/django2x/mysite/urls.py +++ b/tests/frontendIntegration/django2x/mysite/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import include, path diff --git a/tests/frontendIntegration/django2x/mysite/wsgi.py b/tests/frontendIntegration/django2x/mysite/wsgi.py index 8b00faa3e..53bc8977f 100644 --- a/tests/frontendIntegration/django2x/mysite/wsgi.py +++ b/tests/frontendIntegration/django2x/mysite/wsgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_wsgi_application() diff --git a/tests/frontendIntegration/django2x/polls/urls.py b/tests/frontendIntegration/django2x/polls/urls.py index 72ae6ebdc..75693f90d 100644 --- a/tests/frontendIntegration/django2x/polls/urls.py +++ b/tests/frontendIntegration/django2x/polls/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.static import static +from django.conf.urls.static import static # type: ignore from django.urls import path from . import views diff --git a/tests/frontendIntegration/django2x/polls/views.py b/tests/frontendIntegration/django2x/polls/views.py index eda19409a..cffbe3e8a 100644 --- a/tests/frontendIntegration/django2x/polls/views.py +++ b/tests/frontendIntegration/django2x/polls/views.py @@ -76,12 +76,12 @@ def custom_decorator_for_test(): # type: ignore def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore - def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): + def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): # type: ignore Test.increment_attempted_refresh() try: - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore if request.headers.get("rid") is None: # type: ignore return HttpResponse(content="refresh failed") Test.increment_refresh() @@ -89,7 +89,7 @@ def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): except Exception as e: raise e - return wrapped_function + return wrapped_function # type: ignore return session_verify_custom_test # type: ignore @@ -100,32 +100,32 @@ def session_verify_custom_test(f): # type: ignore def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "GET": Test.increment_get_session() - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - resp = JsonResponse(session.get_access_token_payload()) + resp = JsonResponse(session.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp else: if request.method == "POST": - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore clearing = {} - for k in session_.get_access_token_payload(): + for k in session_.get_access_token_payload(): # type: ignore if k not in protected_prop_name: clearing[k] = None body = json.loads(request.body) - session_.sync_merge_into_access_token_payload( + session_.sync_merge_into_access_token_payload( # type: ignore {**clearing, **body}, {} ) Test.increment_get_session() - resp = JsonResponse(session_.get_access_token_payload()) + resp = JsonResponse(session_.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp return send_options_api_response() @@ -140,12 +140,12 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "POST": - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore - info = get_session_information(session_.get_handle()) + info = get_session_information(session_.get_handle()) # type: ignore assert info is not None clearing = {} for k in info.custom_claims_in_access_token_payload: @@ -154,10 +154,10 @@ def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore body = json.loads(request.body) merge_into_access_token_payload( - session_.get_handle(), {**clearing, **body} + session_.get_handle(), {**clearing, **body} # type: ignore ) - resp = JsonResponse(session_.get_access_token_payload()) + resp = JsonResponse(session_.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp return send_options_api_response() @@ -172,12 +172,12 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "GET": - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore Test.increment_get_session() session: SessionContainer = request.supertokens # type: ignore - resp = HttpResponse(session.get_user_id()) + resp = HttpResponse(session.get_user_id()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp else: @@ -193,11 +193,11 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "POST": - value: HttpResponse = f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - session.sync_revoke_session() + session.sync_revoke_session() # type: ignore return HttpResponse("success") return send_options_api_response() @@ -390,9 +390,6 @@ def config( assert header in settings.CORS_ALLOW_HEADERS -config(True, False, None) - - def send_file(request: HttpRequest): return render(request, file_path) diff --git a/tests/frontendIntegration/django3x/mysite/asgi.py b/tests/frontendIntegration/django3x/mysite/asgi.py index 38f185c23..6bd612f40 100644 --- a/tests/frontendIntegration/django3x/mysite/asgi.py +++ b/tests/frontendIntegration/django3x/mysite/asgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_asgi_application() diff --git a/tests/frontendIntegration/django3x/mysite/urls.py b/tests/frontendIntegration/django3x/mysite/urls.py index 113b065e4..ec16052eb 100644 --- a/tests/frontendIntegration/django3x/mysite/urls.py +++ b/tests/frontendIntegration/django3x/mysite/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.urls import path, include urlpatterns = [ diff --git a/tests/frontendIntegration/django3x/mysite/wsgi.py b/tests/frontendIntegration/django3x/mysite/wsgi.py index 8b00faa3e..53bc8977f 100644 --- a/tests/frontendIntegration/django3x/mysite/wsgi.py +++ b/tests/frontendIntegration/django3x/mysite/wsgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_wsgi_application() diff --git a/tests/frontendIntegration/django3x/polls/urls.py b/tests/frontendIntegration/django3x/polls/urls.py index 72ae6ebdc..75693f90d 100644 --- a/tests/frontendIntegration/django3x/polls/urls.py +++ b/tests/frontendIntegration/django3x/polls/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.static import static +from django.conf.urls.static import static # type: ignore from django.urls import path from . import views diff --git a/tests/frontendIntegration/django3x/polls/views.py b/tests/frontendIntegration/django3x/polls/views.py index 1e182fb76..4d0bb5acf 100644 --- a/tests/frontendIntegration/django3x/polls/views.py +++ b/tests/frontendIntegration/django3x/polls/views.py @@ -79,12 +79,12 @@ def custom_decorator_for_test(): # type: ignore def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore - async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): + async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): # type: ignore Test.increment_attempted_refresh() try: - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore if request.headers.get("rid") is None: # type: ignore return HttpResponse(content="refresh failed") Test.increment_refresh() @@ -92,7 +92,7 @@ async def wrapped_function(request: HttpRequest, *args: Any, **kwargs: Any): except Exception as e: raise e - return wrapped_function + return wrapped_function # type: ignore return session_verify_custom_test # type: ignore @@ -103,32 +103,32 @@ def session_verify_custom_test(f): # type: ignore async def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "GET": Test.increment_get_session() - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - resp = JsonResponse(session.get_access_token_payload()) + resp = JsonResponse(session.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp else: if request.method == "POST": - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore clearing = {} - for k in session_.get_access_token_payload(): + for k in session_.get_access_token_payload(): # type: ignore if k not in protected_prop_name: clearing[k] = None body = json.loads(request.body) - await session_.merge_into_access_token_payload( + await session_.merge_into_access_token_payload( # type: ignore {**clearing, **body}, {} ) Test.increment_get_session() - resp = JsonResponse(session_.get_access_token_payload()) + resp = JsonResponse(session_.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp return send_options_api_response() @@ -143,12 +143,12 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore async def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "POST": - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore - info = await get_session_information(session_.get_handle()) + info = await get_session_information(session_.get_handle()) # type: ignore assert info is not None clearing = {} for k in info.custom_claims_in_access_token_payload: @@ -157,10 +157,10 @@ async def wrapped_function(request: HttpRequest, *args, **kwargs): # type: igno body = json.loads(request.body) await merge_into_access_token_payload( - session_.get_handle(), {**clearing, **body} + session_.get_handle(), {**clearing, **body} # type: ignore ) - resp = JsonResponse(session_.get_access_token_payload()) + resp = JsonResponse(session_.get_access_token_payload()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp return send_options_api_response() @@ -175,12 +175,12 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore async def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "GET": - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore Test.increment_get_session() session: SessionContainer = request.supertokens # type: ignore - resp = HttpResponse(session.get_user_id()) + resp = HttpResponse(session.get_user_id()) # type: ignore resp["Cache-Control"] = "no-cache, private" return resp else: @@ -196,11 +196,11 @@ def session_verify_custom_test(f): # type: ignore @wraps(f) # type: ignore async def wrapped_function(request: HttpRequest, *args, **kwargs): # type: ignore if request.method == "POST": - value: HttpResponse = await f(request, *args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: HttpResponse = await f(request, *args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - await session.revoke_session() + await session.revoke_session() # type: ignore return HttpResponse("success") return send_options_api_response() @@ -393,9 +393,6 @@ def config( assert header in settings.CORS_ALLOW_HEADERS -config(True, False, None) - - async def send_file(request: HttpRequest): return render(request, file_path) diff --git a/tests/frontendIntegration/drf_async/mysite/asgi.py b/tests/frontendIntegration/drf_async/mysite/asgi.py index 38f185c23..6bd612f40 100644 --- a/tests/frontendIntegration/drf_async/mysite/asgi.py +++ b/tests/frontendIntegration/drf_async/mysite/asgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_asgi_application() diff --git a/tests/frontendIntegration/drf_async/mysite/urls.py b/tests/frontendIntegration/drf_async/mysite/urls.py index 113b065e4..ec16052eb 100644 --- a/tests/frontendIntegration/drf_async/mysite/urls.py +++ b/tests/frontendIntegration/drf_async/mysite/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.urls import path, include urlpatterns = [ diff --git a/tests/frontendIntegration/drf_async/mysite/wsgi.py b/tests/frontendIntegration/drf_async/mysite/wsgi.py index 8b00faa3e..53bc8977f 100644 --- a/tests/frontendIntegration/drf_async/mysite/wsgi.py +++ b/tests/frontendIntegration/drf_async/mysite/wsgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_wsgi_application() diff --git a/tests/frontendIntegration/drf_async/polls/urls.py b/tests/frontendIntegration/drf_async/polls/urls.py index ed371b18b..8d50e2731 100644 --- a/tests/frontendIntegration/drf_async/polls/urls.py +++ b/tests/frontendIntegration/drf_async/polls/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.static import static +from django.conf.urls.static import static # type: ignore from django.urls import path from . import views diff --git a/tests/frontendIntegration/drf_async/polls/views.py b/tests/frontendIntegration/drf_async/polls/views.py index e13848aef..7eb8d2318 100644 --- a/tests/frontendIntegration/drf_async/polls/views.py +++ b/tests/frontendIntegration/drf_async/polls/views.py @@ -123,7 +123,7 @@ async def wrapped_function(request: Request, *args, **kwargs): # type: ignore return value # type: ignore session: SessionContainer = request.supertokens # type: ignore resp = Response( # type: ignore - session.get_access_token_payload(), + session.get_access_token_payload(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -135,18 +135,18 @@ async def wrapped_function(request: Request, *args, **kwargs): # type: ignore session_: SessionContainer = request.supertokens # type: ignore clearing = {} - for k in session_.get_access_token_payload(): + for k in session_.get_access_token_payload(): # type: ignore if k not in protected_prop_name: clearing[k] = None body = request.data # type: ignore - await session_.merge_into_access_token_payload( + await session_.merge_into_access_token_payload( # type: ignore {**clearing, **body}, {} # type: ignore ) Test.increment_get_session() resp = Response( # type: ignore - session_.get_access_token_payload(), + session_.get_access_token_payload(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -167,7 +167,7 @@ async def wrapped_function(request: Request, *args, **kwargs): # type: ignore return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore - info = await get_session_information(session_.get_handle()) + info = await get_session_information(session_.get_handle()) # type: ignore assert info is not None clearing = {} for k in info.custom_claims_in_access_token_payload: @@ -200,7 +200,7 @@ async def wrapped_function(request: Request, *args, **kwargs): # type: ignore Test.increment_get_session() session: SessionContainer = request.supertokens # type: ignore resp = Response( # type: ignore - session.get_user_id(), + session.get_user_id(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -221,7 +221,7 @@ async def wrapped_function(request: Request, *args, **kwargs): # type: ignore if value is not None and value.status_code != 200: # type: ignore return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - await session.revoke_session() + await session.revoke_session() # type: ignore return Response("success") # type: ignore return send_options_api_response() # type: ignore @@ -417,9 +417,6 @@ def config( assert header in settings.CORS_ALLOW_HEADERS -config(True, False, None) - - async def send_file(request: HttpRequest): return render(request, file_path) diff --git a/tests/frontendIntegration/drf_sync/mysite/asgi.py b/tests/frontendIntegration/drf_sync/mysite/asgi.py index 38f185c23..6bd612f40 100644 --- a/tests/frontendIntegration/drf_sync/mysite/asgi.py +++ b/tests/frontendIntegration/drf_sync/mysite/asgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_asgi_application() diff --git a/tests/frontendIntegration/drf_sync/mysite/urls.py b/tests/frontendIntegration/drf_sync/mysite/urls.py index 113b065e4..ec16052eb 100644 --- a/tests/frontendIntegration/drf_sync/mysite/urls.py +++ b/tests/frontendIntegration/drf_sync/mysite/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.urls import path, include urlpatterns = [ diff --git a/tests/frontendIntegration/drf_sync/mysite/wsgi.py b/tests/frontendIntegration/drf_sync/mysite/wsgi.py index 8b00faa3e..53bc8977f 100644 --- a/tests/frontendIntegration/drf_sync/mysite/wsgi.py +++ b/tests/frontendIntegration/drf_sync/mysite/wsgi.py @@ -13,4 +13,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") +if True: + # to help middleware function with uvicorn (to ensure supertokens init is called) + from polls.views import config + + config(True, False, None) + application = get_wsgi_application() diff --git a/tests/frontendIntegration/drf_sync/polls/urls.py b/tests/frontendIntegration/drf_sync/polls/urls.py index ed371b18b..8d50e2731 100644 --- a/tests/frontendIntegration/drf_sync/polls/urls.py +++ b/tests/frontendIntegration/drf_sync/polls/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.static import static +from django.conf.urls.static import static # type: ignore from django.urls import path from . import views diff --git a/tests/frontendIntegration/drf_sync/polls/views.py b/tests/frontendIntegration/drf_sync/polls/views.py index cd776104b..d99bf18a5 100644 --- a/tests/frontendIntegration/drf_sync/polls/views.py +++ b/tests/frontendIntegration/drf_sync/polls/views.py @@ -123,7 +123,7 @@ def wrapped_function(request: Request, *args, **kwargs): # type: ignore return value # type: ignore session: SessionContainer = request.supertokens # type: ignore resp = Response( # type: ignore - session.get_access_token_payload(), + session.get_access_token_payload(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -135,18 +135,18 @@ def wrapped_function(request: Request, *args, **kwargs): # type: ignore session_: SessionContainer = request.supertokens # type: ignore clearing = {} - for k in session_.get_access_token_payload(): + for k in session_.get_access_token_payload(): # type: ignore if k not in protected_prop_name: clearing[k] = None body = request.data # type: ignore - session_.sync_merge_into_access_token_payload( + session_.sync_merge_into_access_token_payload( # type: ignore {**clearing, **body}, {} # type: ignore ) Test.increment_get_session() resp = Response( # type: ignore - session_.get_access_token_payload(), + session_.get_access_token_payload(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -167,7 +167,7 @@ def wrapped_function(request: Request, *args, **kwargs): # type: ignore return value # type: ignore session_: SessionContainer = request.supertokens # type: ignore - info = get_session_information(session_.get_handle()) + info = get_session_information(session_.get_handle()) # type: ignore assert info is not None clearing = {} for k in info.custom_claims_in_access_token_payload: @@ -200,7 +200,7 @@ def wrapped_function(request: Request, *args, **kwargs): # type: ignore Test.increment_get_session() session: SessionContainer = request.supertokens # type: ignore resp = Response( # type: ignore - session.get_user_id(), + session.get_user_id(), # type: ignore headers={"Cache-Control": "no-cache, private"}, # type: ignore ) # type: ignore return resp # type: ignore @@ -221,7 +221,7 @@ def wrapped_function(request: Request, *args, **kwargs): # type: ignore if value is not None and value.status_code != 200: # type: ignore return value # type: ignore session: SessionContainer = request.supertokens # type: ignore - session.sync_revoke_session() + session.sync_revoke_session() # type: ignore return Response("success") # type: ignore return send_options_api_response() # type: ignore @@ -417,9 +417,6 @@ def config( assert header in settings.CORS_ALLOW_HEADERS -config(True, False, None) - - def send_file(request: HttpRequest): return render(request, file_path) diff --git a/tests/frontendIntegration/fastapi-server/app.py b/tests/frontendIntegration/fastapi-server/app.py index addaf27ab..5a9833ff6 100644 --- a/tests/frontendIntegration/fastapi-server/app.py +++ b/tests/frontendIntegration/fastapi-server/app.py @@ -18,10 +18,10 @@ from base64 import b64encode import time -import uvicorn # type: ignore +import uvicorn from fastapi import Depends, FastAPI from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse -from starlette.exceptions import ExceptionMiddleware +from starlette.middleware.exceptions import ExceptionMiddleware from starlette.middleware.cors import CORSMiddleware from starlette.requests import Request from supertokens_python import ( @@ -246,7 +246,7 @@ def config( config(True, False, None) -app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers) +app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers) # type: ignore @app.get("/index.html") diff --git a/tests/frontendIntegration/flask-server/app.py b/tests/frontendIntegration/flask-server/app.py index e829b61fc..a5ce11289 100644 --- a/tests/frontendIntegration/flask-server/app.py +++ b/tests/frontendIntegration/flask-server/app.py @@ -82,9 +82,9 @@ def session_verify_custom_test(f): # type: ignore def wrapped_function(*args, **kwargs): # type: ignore Test.increment_attempted_refresh() try: - value: Response = f(*args, **kwargs) - if value is not None and value.status_code != 200: - return value + value: Response = f(*args, **kwargs) # type: ignore + if value is not None and value.status_code != 200: # type: ignore + return value # type: ignore if request.headers.get("rid") is None: # type: ignore return "refresh failed" Test.increment_refresh() @@ -417,7 +417,7 @@ def update_jwt_post(): if k not in protected_prop_name: clearing[k] = None - body = request.get_json() or {} + body: Any = request.get_json() or {} _session.sync_merge_into_access_token_payload({**clearing, **body}, {}) Test.increment_get_session() resp = make_response(_session.get_access_token_payload()) @@ -436,7 +436,7 @@ def update_jwt_with_handle_post(): for k in info.custom_claims_in_access_token_payload: clearing[k] = None - body = request.get_json() or {} + body: Any = request.get_json() or {} merge_into_access_token_payload(_session.get_handle(), {**clearing, **body}) resp = make_response(_session.get_access_token_payload()) diff --git a/tests/input_validation/test_input_validation.py b/tests/input_validation/test_input_validation.py index f66e22f22..faca83226 100644 --- a/tests/input_validation/test_input_validation.py +++ b/tests/input_validation/test_input_validation.py @@ -18,6 +18,15 @@ GetEmailForUserIdOkResult, ) from supertokens_python.types import RecipeUserId +from tests.utils import reset + + +def setup_function(_): + reset() + + +def teardown_function(_): + reset() @pytest.mark.asyncio diff --git a/tests/jwt/test_create_jwt_feature.py b/tests/jwt/test_create_jwt_feature.py index bc20126f3..debf4d6c6 100644 --- a/tests/jwt/test_create_jwt_feature.py +++ b/tests/jwt/test_create_jwt_feature.py @@ -13,6 +13,7 @@ License for the specific language governing permissions and limitations under the License. """ + import json import time diff --git a/tests/jwt/test_get_JWKS.py b/tests/jwt/test_get_JWKS.py index 20c945ab1..dad540323 100644 --- a/tests/jwt/test_get_JWKS.py +++ b/tests/jwt/test_get_JWKS.py @@ -44,7 +44,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/jwt/test_override.py b/tests/jwt/test_override.py index bb010dc5b..e49c2efe3 100644 --- a/tests/jwt/test_override.py +++ b/tests/jwt/test_override.py @@ -46,7 +46,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/multitenancy/test_router.py b/tests/multitenancy/test_router.py index a08c6c751..bd8a84f67 100644 --- a/tests/multitenancy/test_router.py +++ b/tests/multitenancy/test_router.py @@ -27,7 +27,7 @@ @fixture(scope="function") -async def client(): +def client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/passwordless/test_emaildelivery.py b/tests/passwordless/test_emaildelivery.py index 60f4bf8c9..8cbb60d29 100644 --- a/tests/passwordless/test_emaildelivery.py +++ b/tests/passwordless/test_emaildelivery.py @@ -78,7 +78,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -122,8 +122,8 @@ async def get_content_override( is_html=False, ) - oi.send_raw_email = send_raw_email_override - oi.get_content = get_content_override + oi.send_raw_email = send_raw_email_override # type: ignore + oi.get_content = get_content_override # type: ignore return oi @@ -569,8 +569,8 @@ async def get_content_override( is_html=False, ) - oi.send_raw_email = send_raw_email_override - oi.get_content = get_content_override + oi.send_raw_email = send_raw_email_override # type: ignore + oi.get_content = get_content_override # type: ignore return oi diff --git a/tests/passwordless/test_smsdelivery.py b/tests/passwordless/test_smsdelivery.py index e6f775e5d..d7797fe21 100644 --- a/tests/passwordless/test_smsdelivery.py +++ b/tests/passwordless/test_smsdelivery.py @@ -70,7 +70,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -532,8 +532,8 @@ async def get_content_override( return SMSContent(body=user_input_code, to_phone=template_vars.phone_number) - oi.send_raw_sms = send_raw_email_override - oi.get_content = get_content_override + oi.send_raw_sms = send_raw_email_override # type: ignore + oi.get_content = get_content_override # type: ignore return oi diff --git a/tests/sessions/claims/test_verify_session.py b/tests/sessions/claims/test_verify_session.py index ff5ac669d..31a4c5f22 100644 --- a/tests/sessions/claims/test_verify_session.py +++ b/tests/sessions/claims/test_verify_session.py @@ -59,7 +59,7 @@ async def new_get_global_claim_validators( ): return validators - oi.get_global_claim_validators = new_get_global_claim_validators + oi.get_global_claim_validators = new_get_global_claim_validators # type: ignore return oi return { @@ -87,7 +87,7 @@ async def new_get_global_claim_validators( ): return [*claim_validators_added_by_other_recipes, claim_validator] - oi.get_global_claim_validators = new_get_global_claim_validators + oi.get_global_claim_validators = new_get_global_claim_validators # type: ignore return oi return { @@ -134,7 +134,7 @@ def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]) -> b @fixture(scope="function") -async def fastapi_client(): +def fastapi_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -552,7 +552,7 @@ async def test_should_allow_with_custom_claim_returning_true( @fixture(scope="function") -async def client_without_middleware(): +def client_without_middleware(): app = FastAPI() @app.post("/verify") diff --git a/tests/sessions/test_access_token_version.py b/tests/sessions/test_access_token_version.py index 8b532590a..1e2fa3bdc 100644 --- a/tests/sessions/test_access_token_version.py +++ b/tests/sessions/test_access_token_version.py @@ -70,7 +70,7 @@ async def test_parsing_access_token_v2(): @pytest.fixture(scope="function") -async def app(): +def app(): fast = FastAPI() fast.add_middleware(get_middleware()) diff --git a/tests/sessions/test_auth_mode.py b/tests/sessions/test_auth_mode.py index 69b0b1b86..b3699dbd5 100644 --- a/tests/sessions/test_auth_mode.py +++ b/tests/sessions/test_auth_mode.py @@ -1,4 +1,5 @@ from typing import Any, Dict, Optional, Union +from httpx import Response from typing_extensions import Literal from fastapi import Depends, FastAPI, Request @@ -27,7 +28,7 @@ @fixture(scope="function") -async def app(): +def app(): fast = FastAPI() fast.add_middleware(get_middleware()) @@ -76,8 +77,8 @@ def call_api( ): access_token = info.get("accessTokenFromAny") - headers = {} - cookies = {} + headers: Dict[str, Any] = {} + cookies: Dict[str, Any] = {} if auth_mode_header: headers["st-auth-mode"] = auth_mode_header @@ -87,9 +88,9 @@ def call_api( headers["anti-csrf"] = info["antiCsrf"] if auth_mode in ("header", "both"): - headers[ - "Authorization" - ] = f"Bearer {(access_token)}" # TODO: Might have to add decode_uri() + headers["Authorization"] = ( + f"Bearer {(access_token)}" # TODO: Might have to add decode_uri() + ) app.cookies = RequestsCookieJar() # Reset cookies @@ -516,7 +517,7 @@ async def test_refresh_session_parametrized( # Token transfer method doesn't matter for this test res = create_session(app, "cookies") - auth_mode = "none" + auth_mode: Literal["none", "header", "cookie", "both"] = "none" if auth_cookie and auth_header: auth_mode = "both" elif auth_header: @@ -594,9 +595,9 @@ async def refresh_session( auth_mode_header: TokenTransferMethod, auth_mode: str, res: Dict[str, str], -): - headers = {} - cookies = {} +) -> Response: + headers: Dict[str, Any] = {} + cookies: Dict[str, Any] = {} app.cookies = RequestsCookieJar() # Reset cookies diff --git a/tests/sessions/test_jwks.py b/tests/sessions/test_jwks.py index 7b800210d..f886726b9 100644 --- a/tests/sessions/test_jwks.py +++ b/tests/sessions/test_jwks.py @@ -655,7 +655,7 @@ def callback(): @fixture(scope="function") -async def client(): +def client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/sessions/test_session_error_handlers.py b/tests/sessions/test_session_error_handlers.py index 0cb569297..de21fa904 100644 --- a/tests/sessions/test_session_error_handlers.py +++ b/tests/sessions/test_session_error_handlers.py @@ -53,7 +53,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/sessions/test_session_utils.py b/tests/sessions/test_session_utils.py index 361783c8d..1924e5307 100644 --- a/tests/sessions/test_session_utils.py +++ b/tests/sessions/test_session_utils.py @@ -16,42 +16,42 @@ class TestNormaliseSessionScope: - def test_empty_string(self): # pylint: disable=no-self-use + def test_empty_string(self): try: normalise_session_scope("") assert False except Exception as e: assert str(e) == "Please provide a valid session_scope" - def test_with_leading_dot(self): # pylint: disable=no-self-use + def test_with_leading_dot(self): result = normalise_session_scope(".example.com") assert result == ".example.com" - def test_without_leading_dot(self): # pylint: disable=no-self-use + def test_without_leading_dot(self): result = normalise_session_scope("example.com") assert result == "example.com" - def test_with_http_prefix(self): # pylint: disable=no-self-use + def test_with_http_prefix(self): result = normalise_session_scope("http://example.com") assert result == "example.com" - def test_with_https_prefix(self): # pylint: disable=no-self-use + def test_with_https_prefix(self): result = normalise_session_scope("https://example.com") assert result == "example.com" - def test_with_ip_address(self): # pylint: disable=no-self-use + def test_with_ip_address(self): result = normalise_session_scope("192.168.1.1") assert result == "192.168.1.1" - def test_with_localhost(self): # pylint: disable=no-self-use + def test_with_localhost(self): result = normalise_session_scope("localhost") assert result == "localhost" - def test_with_leading_trailing_whitespace(self): # pylint: disable=no-self-use + def test_with_leading_trailing_whitespace(self): result = normalise_session_scope(" example.com ") assert result == "example.com" - def test_with_subdomain(self): # pylint: disable=no-self-use + def test_with_subdomain(self): assert normalise_session_scope("sub.example.com") == "sub.example.com" assert normalise_session_scope("http://sub.example.com") == "sub.example.com" assert normalise_session_scope("https://sub.example.com") == "sub.example.com" diff --git a/tests/telemetry/test_telemetry.py b/tests/telemetry/test_telemetry.py index 57a8a442a..56c004764 100644 --- a/tests/telemetry/test_telemetry.py +++ b/tests/telemetry/test_telemetry.py @@ -14,6 +14,7 @@ import asyncio import os +import warnings import pytest @@ -25,7 +26,7 @@ @pytest.mark.asyncio async def test_telemetry(): reset() - with pytest.warns(None) as _: + with warnings.catch_warnings(record=True) as warning_list: init( supertokens_config=SupertokensConfig("http://localhost:3567"), app_info=InputAppInfo( @@ -50,13 +51,16 @@ async def test_telemetry(): assert Supertokens.get_instance().telemetry is not None assert Supertokens.get_instance().telemetry + assert ( + len(warning_list) == 0 + ), f"Expected no warnings but got: {[str(w.message) for w in warning_list]}" @pytest.mark.asyncio async def test_read_from_env(): reset() os.environ["TEST_MODE"] = "testing" - with pytest.warns(None) as _: + with warnings.catch_warnings(record=True) as warning_list: init( supertokens_config=SupertokensConfig("http://localhost:3567"), app_info=InputAppInfo( @@ -78,3 +82,6 @@ async def test_read_from_env(): await asyncio.sleep(1) assert not Supertokens.get_instance().telemetry + assert ( + len(warning_list) == 0 + ), f"Expected no warnings but got: {[str(w.message) for w in warning_list]}" diff --git a/tests/test-server/test_functions_mapper.py b/tests/test-server/test_functions_mapper.py index a053986a3..3c98b92ca 100644 --- a/tests/test-server/test_functions_mapper.py +++ b/tests/test-server/test_functions_mapper.py @@ -334,9 +334,9 @@ async def sign_in_up( SignInUpNotAllowed, LinkingToSessionUserFailedError, ]: - user_context[ - "isVerified" - ] = is_verified # this information comes from the third party provider + user_context["isVerified"] = ( + is_verified # this information comes from the third party provider + ) return await og_sign_in_up( third_party_id, third_party_user_id, diff --git a/tests/test-server/thirdparty.py b/tests/test-server/thirdparty.py index 513fd5a6b..d0ba01cb5 100644 --- a/tests/test-server/thirdparty.py +++ b/tests/test-server/thirdparty.py @@ -1,3 +1,4 @@ +from typing import Any, Dict, Optional from flask import Flask, request, jsonify from session import convert_session_to_container # pylint: disable=import-error @@ -71,14 +72,14 @@ def thirdpartymanuallycreateorupdate(): # type: ignore @app.route("/test/thirdparty/getprovider", methods=["POST"]) # type: ignore def get_provider(): # type: ignore - data = request.get_json() + data = request.get_json() # type: ignore if data is None: return jsonify({"status": "MISSING_DATA_ERROR"}) - tenant_id = data.get("tenantId", "public") - third_party_id = data["thirdPartyId"] - client_type = data.get("clientType", None) - user_context = data.get("userContext", {}) + tenant_id: str = data.get("tenantId", "public") + third_party_id: str = data["thirdPartyId"] + client_type: Optional[str] = data.get("clientType", None) + user_context: Dict[str, Any] = data.get("userContext", {}) from supertokens_python.recipe.thirdparty.syncio import get_provider diff --git a/tests/test_config.py b/tests/test_config.py index de831e405..79e0c3ca8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -585,7 +585,7 @@ class OI: def __init__(self): pass - def some_other_func(self): # pylint: disable=no-self-use + def some_other_func(self): nonlocal m m = 1 @@ -620,7 +620,7 @@ def __init__(self): def sign_up(self): self.get_user() - def get_user(self): # pylint: disable=no-self-use + def get_user(self): nonlocal m m = 1 diff --git a/tests/test_logger.py b/tests/test_logger.py index b7979dfe9..24f5a7973 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -28,7 +28,7 @@ def inject_fixtures(self, caplog: pytest.LogCaptureFixture): # caplog is the pytest fixture to capture all logs self._caplog = caplog # pylint: disable=attribute-defined-outside-init - def setup_method(self, _): # pylint: disable=no-self-use + def setup_method(self, _): # Setting the log level to a higher level so debug logs are not printed logging.getLogger(NAMESPACE).setLevel(logging.ERROR) reset() @@ -43,7 +43,7 @@ def teardown_method(self, _): @patch("supertokens_python.logger.datetime", wraps=real_datetime) def test_1_json_msg_format(self, datetime_mock: MagicMock): enable_debug_logging() - datetime_mock.utcnow.return_value = real_datetime(2000, 1, 1) # type: ignore + datetime_mock.now.return_value = real_datetime(2000, 1, 1) with self.assertLogs(level="DEBUG") as captured: log_debug_message("API replied with status 200") diff --git a/tests/test_middleware.py b/tests/test_middleware.py index b7911ee4c..4e25b0da7 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -36,7 +36,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) return TestClient(app) diff --git a/tests/test_misc.py b/tests/test_misc.py index c8f796c3c..7491122be 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -10,7 +10,7 @@ # class TransporterTests(TestCase): -# def test_transporter(self): # pylint: disable=no-self-use +# def test_transporter(self): # local_insecure_smtpd_config_without_auth = SMTPServiceConfig( # host="localhost", # from_=SMTPServiceConfigFrom("Foo bar", "foo@example.com"), @@ -65,7 +65,7 @@ # except TimeoutExpired: # raise Exception('subprocess did not terminate in time') -# def test_transporter_with_gmail(self): # pylint: disable=no-self-use +# def test_transporter_with_gmail(self): # email = getenv("TEST_GMAIL_EMAIL") # password = getenv("TEST_GMAIL_PASS") diff --git a/tests/test_network_interceptor.py b/tests/test_network_interceptor.py index 3be0eb457..3694dc756 100644 --- a/tests/test_network_interceptor.py +++ b/tests/test_network_interceptor.py @@ -24,7 +24,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py index 7237579fe..dbd69a336 100644 --- a/tests/test_passwordless.py +++ b/tests/test_passwordless.py @@ -51,7 +51,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/test_session.py b/tests/test_session.py index 1f88a1417..e7b9f8f2d 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -271,7 +271,7 @@ async def test_creating_many_sessions_for_one_user_and_looping(): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -655,12 +655,12 @@ async def test_token_cookie_expires( response = driver_config_client.post("/create") assert response.status_code == 200 - cookies = extract_all_cookies(response) + cookies = extract_all_cookies(response) # type: ignore assert "sAccessToken" in cookies assert "sRefreshToken" in cookies - for c in response.cookies: + for c in response.cookies.jar: if c.name == "sAccessToken": # 100 years (set by the SDK) # some time must have elasped since the cookie was set. So less than current time assert ( @@ -690,7 +690,7 @@ async def test_token_cookie_expires( assert "sAccessToken" in cookies assert "sRefreshToken" in cookies - for c in response.cookies: + for c in response.cookies.jar: if c.name == "sAccessToken": # 100 years (set by the SDK) # some time must have elasped since the cookie was set. So less than current time assert ( diff --git a/tests/test_user_context.py b/tests/test_user_context.py index 039843f0d..ab7ce93e3 100644 --- a/tests/test_user_context.py +++ b/tests/test_user_context.py @@ -56,7 +56,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/testclient.py b/tests/testclient.py index 3ca188888..93b2011f4 100644 --- a/tests/testclient.py +++ b/tests/testclient.py @@ -1,5 +1,6 @@ from fastapi.testclient import TestClient + # The `fastapi.testclient.TestClient` uses a cookie jar to store cookies between requests. # Our test cases aren't written with this behavior in mind, and for consistency with other SDKs, # we need a `TestClient` that doesn't retain cookies. The class below inherits from `TestClient` diff --git a/tests/thirdparty/test_authorisation_url_feature.py b/tests/thirdparty/test_authorisation_url_feature.py index 2e658c69f..4b557f863 100644 --- a/tests/thirdparty/test_authorisation_url_feature.py +++ b/tests/thirdparty/test_authorisation_url_feature.py @@ -43,7 +43,7 @@ @fixture(scope="function") -async def app(): +def app(): app = FastAPI() app.add_middleware(get_middleware()) diff --git a/tests/thirdparty/test_emaildelivery.py b/tests/thirdparty/test_emaildelivery.py index 207626613..1a3e74db5 100644 --- a/tests/thirdparty/test_emaildelivery.py +++ b/tests/thirdparty/test_emaildelivery.py @@ -71,7 +71,7 @@ def teardown_function(_): @fixture(scope="function") -async def driver_config_client(): +def driver_config_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -95,7 +95,7 @@ async def get_user_info( email=UserInfoEmail(oauth_tokens["email"], True), ) - original_implementation.get_user_info = get_user_info + original_implementation.get_user_info = get_user_info # type: ignore return original_implementation return thirdparty.ProviderInput( @@ -463,8 +463,8 @@ async def get_content_override( is_html=False, ) - oi.send_raw_email = send_raw_email_override - oi.get_content = get_content_override + oi.send_raw_email = send_raw_email_override # type: ignore + oi.get_content = get_content_override # type: ignore return oi diff --git a/tests/thirdparty/test_thirdparty.py b/tests/thirdparty/test_thirdparty.py index ce567f059..bab1371a6 100644 --- a/tests/thirdparty/test_thirdparty.py +++ b/tests/thirdparty/test_thirdparty.py @@ -44,7 +44,7 @@ @fixture(scope="function") -async def fastapi_client(): +def fastapi_client(): app = FastAPI() app.add_middleware(get_middleware()) @@ -88,7 +88,7 @@ async def test_thirdpary_parsing_works(fastapi_client: TestClient): code = "testing" data = {"state": state, "code": code} - res = fastapi_client.post("/auth/callback/apple", data=data) + res = fastapi_client.post("/auth/callback/apple", data=data, allow_redirects=False) assert res.status_code == 303 assert res.content == b"" diff --git a/tests/utils.py b/tests/utils.py index a7430c671..44d987956 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -21,11 +21,12 @@ from signal import SIGTERM from subprocess import DEVNULL, run from time import sleep -from typing import Any, Dict, List, cast, Optional +from typing import Any, Dict, List, Union, cast, Optional from urllib.parse import unquote from fastapi.testclient import TestClient -from requests.models import Response +from requests.models import Response as RequestsResponse +from httpx import Response as HTTPXResponse from yaml import FullLoader, dump, load from supertokens_python import InputAppInfo, Supertokens, SupertokensConfig @@ -232,14 +233,18 @@ def reset(stop_core: bool = True): TOTPRecipe.reset() -def get_cookie_from_response(response: Response, cookie_name: str): +def get_cookie_from_response( + response: Union[RequestsResponse, HTTPXResponse], cookie_name: str +): cookies = extract_all_cookies(response) if cookie_name in cookies: return cookies[cookie_name] return None -def extract_all_cookies(response: Response) -> Dict[str, Any]: +def extract_all_cookies( + response: Union[RequestsResponse, HTTPXResponse] +) -> Dict[str, Any]: if response.headers.get("set-cookie") is None: return {} cookie_headers = SimpleCookie(response.headers.get("set-cookie")) # type: ignore @@ -258,7 +263,7 @@ def extract_all_cookies(response: Response) -> Dict[str, Any]: return cookies -def extract_info(response: Response) -> Dict[str, Any]: +def extract_info(response: Union[RequestsResponse, HTTPXResponse]) -> Dict[str, Any]: cookies = extract_all_cookies(response) access_token = cookies.get("sAccessToken", {}).get("value") refresh_token = cookies.get("sRefreshToken", {}).get("value") @@ -424,7 +429,7 @@ def email_verify_token_request( cookies={ "sAccessToken": accessToken, }, - data=str.encode(userId), + data=userId, # type: ignore ) return resp finally: @@ -466,7 +471,7 @@ async def get_api_version(): try: # If ST has been already initialized: core_version = cast(loop.run_until_complete(asyncio.gather(get_api_version())), str) # type: ignore - return core_version + return core_version # type: ignore except Exception: pass @@ -555,19 +560,17 @@ def is_subset(dict1: Any, dict2: Any) -> bool: """ if isinstance(dict1, list): if isinstance(dict2, list): - for item in dict2: # pyright: reportUnknownVariableType=false + for item in dict2: # type: ignore if item not in dict1: return False return True return False if isinstance(dict1, dict): if isinstance(dict2, dict): - for key, value in dict2.items(): + for key, value in dict2.items(): # type: ignore if key not in dict1: return False - if not is_subset( - dict1[key], value - ): # pyright: reportUnknownArgumentType=false + if not is_subset(dict1[key], value): return False return True return False