Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 6 additions & 15 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ name = "pypi"

[packages]
celery = {version = "==5.4.0", extras = ["sqs"]}
cryptography = "==44.0.1"
cryptography = "==46.0.5"
boto3 = "==1.36.14"
django = "==5.1.15"
djangorestframework = "==3.16.0"
django = "==5.2.11"
djangorestframework = "==3.16.1"
django-filter = "==25.1"
django-countries = "==7.6.1"
django-two-factor-auth = "==1.17.0"
django-cors-headers = "==4.7.0"
django-csp = "==3.8"
django-import-export = "==4.2.0"
django-storages = {version = "==1.14.6", extras = ["s3"]}
pyotp = "==2.9.0"
python-dotenv = "==1.0.1"
Expand All @@ -26,13 +24,6 @@ gunicorn = "==23.0.0"
uvicorn-worker = "==0.2.0"
pyjwt = "==2.6.0" # TODO: upgrade to latest version.
psutil = "==7.0.0"
importlib-metadata = "==4.13.0" # TODO: remove. needed by old portal
django-formtools = "==2.5.1" # TODO: remove. needed by old portal
# https://pypi.org/user/codeforlife/
cfl-common = "==8.9.19" # TODO: remove
codeforlife-portal = "==8.9.19" # TODO: remove
rapid-router = "==7.6.18" # TODO: remove
phonenumbers = "==8.12.12" # TODO: remove
google-auth = "==2.48.0"
google-cloud-bigquery = "==3.38.0"
tink = {version = "==1.13.0", extras = ["gcpkms"]}
Expand All @@ -51,11 +42,11 @@ django-extensions = "==3.2.1"
django-test-migrations = "==1.2.0"
pyparsing = "==3.0.9"
pydot = "==1.4.2"
pylint = "==3.2.7"
pylint-django = "==2.5.5"
pylint = "==4.0.4"
pylint-django = "==2.7.0"
isort = "==5.13.2"
mypy = "==1.15.0"
django-stubs = {version = "==5.1.3", extras = ["compatible-mypy"]}
django-stubs = {version = "==5.2.9", extras = ["compatible-mypy"]}
djangorestframework-stubs = {version = "==3.15.3", extras = ["compatible-mypy"]}
types-regex = "==2024.11.6.*"
types-psutil = "==7.0.0.20250601"
Expand Down
1,182 changes: 278 additions & 904 deletions Pipfile.lock

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions codeforlife/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,6 @@ def set_up_settings(service_base_dir: Path, service_name: str):
secrets_file.write(secrets_file_comment)

secrets = dotenv_values(secrets_path)
secrets.setdefault(
# NOTE: This is only used locally for testing purposes.
"SECRET_KEY",
"XTgWqMlZCMI_E5BvCArkif9nrJIIhe_6Ic6Q_UcWJDk=",
)
else:
# pylint: disable-next=import-outside-toplevel
import boto3
Expand Down
2 changes: 1 addition & 1 deletion codeforlife/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def clean(self):
"Incorrect user class.",
code="incorrect_user_class",
)
if not user.is_active:
if not user.is_active: # type: ignore[attr-defined]
raise ValidationError(
"User is not active",
code="user_not_active",
Expand Down
4 changes: 2 additions & 2 deletions codeforlife/mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Preference:
is_opted_in: t.Optional[bool] = None


# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def add_contact(
email: str,
opt_in_type: t.Optional[
Expand Down Expand Up @@ -218,7 +218,7 @@ class EmailAttachment:
content: str


# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def send_mail(
campaign_id: int,
to_addresses: t.List[str],
Expand Down
2 changes: 1 addition & 1 deletion codeforlife/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def django_dev_server_is_running(self):
and sys.argv[1] == "runserver"
)

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def __init__(
self,
mode: Mode = t.cast(Mode, os.getenv("SERVER_MODE", "django")),
Expand Down
7 changes: 7 additions & 0 deletions codeforlife/settings/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,10 @@ def get_redis_url():

# The URL to connect to the Redis cache.
REDIS_URL = get_redis_url()

# A flag to indicate whether the old system is the current runtime to
# conditionally run code that is still needed for the old system to work but is
# no longer needed in the new system. Once the old system is fully deprecated,
# this flag and all code that depends on it should be removed.
# WARN: This setting should never be imported in the old system.
OLD_SYSTEM = False
11 changes: 8 additions & 3 deletions codeforlife/settings/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.getenv("DEBUG", "1")))

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv("SECRET_KEY", "replace-me")

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/

Expand Down Expand Up @@ -235,6 +238,11 @@ def get_databases():

WSGI_APPLICATION = "application.django_wsgi"

# Custom user model
# https://docs.djangoproject.com/en/6.0/topics/auth/customizing/#auth-custom-user

AUTH_USER_MODEL = "user.User"

# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators

Expand Down Expand Up @@ -275,9 +283,6 @@ def get_databases():
"django.contrib.messages",
"django.contrib.sites",
"django.contrib.staticfiles",
"game", # TODO: remove
"portal", # TODO: remove
"common", # TODO: remove
"src",
"codeforlife.user",
"corsheaders",
Expand Down
3 changes: 2 additions & 1 deletion codeforlife/tasks/bigquery_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

# pylint: disable-next=too-many-instance-attributes,too-many-public-methods
class TestLoadDataIntoBigQueryTask(CeleryTestCase):
fixtures = ["school_1"]

append_users: BigQueryTask
truncate_users: BigQueryTask
Expand Down Expand Up @@ -180,7 +181,7 @@ def _assert_csv_file_loaded_into_bigquery(

# settings

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def _test_settings(
self,
code: str,
Expand Down
4 changes: 2 additions & 2 deletions codeforlife/tests/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ def setUpClass(cls):

return super().setUpClass()

def _pre_setup(self):
def _setup_and_call(self, result, debug=False):
# pylint: disable-next=protected-access
self.client_class._test_case = self
super()._pre_setup() # type: ignore[misc]
super()._setup_and_call(result, debug) # type: ignore[misc]


class APITestCase(
Expand Down
7 changes: 4 additions & 3 deletions codeforlife/tests/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def _make_assertions():

StatusCodeAssertion = t.Optional[t.Union[int, t.Callable[[int], bool]]]

# pylint: disable=too-many-arguments,redefined-builtin
# pylint: disable=too-many-arguments,redefined-builtin,too-many-positional-arguments

def generic(
self,
Expand Down Expand Up @@ -304,7 +304,7 @@ def options( # type: ignore[override]
**extra,
)

# pylint: enable=too-many-arguments,redefined-builtin
# pylint: enable=too-many-arguments,redefined-builtin,too-many-positional-arguments


class APIClient(
Expand Down Expand Up @@ -558,7 +558,8 @@ def login_as(self, user: "TypedUser", password: str = "password"):
auth_user = self.login_teacher(user.email, password)
elif isinstance(user, StudentUser):
auth_user = self.login_student(
user.student.class_field.access_code,
# pylint: disable-next=line-too-long
user.student.class_field.access_code, # type: ignore[union-attr,arg-type]
user.first_name,
password,
)
Expand Down
10 changes: 4 additions & 6 deletions codeforlife/tests/api_request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def request(self, user: t.Optional[AnyAbstractBaseUser] = None, **kwargs):

return request

# pylint: disable-next=too-many-arguments
# pylint: disable=too-many-arguments,too-many-positional-arguments

def generic( # type: ignore[override]
self,
method: str,
Expand Down Expand Up @@ -100,7 +101,6 @@ def get( # type: ignore[override]
),
)

# pylint: disable-next=too-many-arguments
def post( # type: ignore[override]
self,
path: t.Optional[str] = None,
Expand All @@ -126,7 +126,6 @@ def post( # type: ignore[override]
),
)

# pylint: disable-next=too-many-arguments
def put( # type: ignore[override]
self,
path: t.Optional[str] = None,
Expand All @@ -152,7 +151,6 @@ def put( # type: ignore[override]
),
)

# pylint: disable-next=too-many-arguments
def patch( # type: ignore[override]
self,
path: t.Optional[str] = None,
Expand All @@ -178,7 +176,6 @@ def patch( # type: ignore[override]
),
)

# pylint: disable-next=too-many-arguments
def delete( # type: ignore[override]
self,
path: t.Optional[str] = None,
Expand All @@ -204,7 +201,6 @@ def delete( # type: ignore[override]
),
)

# pylint: disable-next=too-many-arguments
def options( # type: ignore[override]
self,
path: t.Optional[str] = None,
Expand All @@ -230,6 +226,8 @@ def options( # type: ignore[override]
),
)

# pylint: enable=too-many-arguments,too-many-positional-arguments


class APIRequestFactory(
BaseAPIRequestFactory[Request[AnyUser], AnyUser],
Expand Down
2 changes: 1 addition & 1 deletion codeforlife/tests/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class InterruptPipelineError(Exception):
"""

@classmethod
# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def run(
cls,
test_case: "TestCase",
Expand Down
2 changes: 1 addition & 1 deletion codeforlife/tests/model_view_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def reverse_action(
# Assertion Helpers
# --------------------------------------------------------------------------

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def assert_serialized_model_equals_json_model(
self,
model: AnyModel,
Expand Down
12 changes: 6 additions & 6 deletions codeforlife/tests/model_view_set_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def retrieve(

return response

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def list(
self,
models: t.Collection[AnyModel],
Expand Down Expand Up @@ -268,7 +268,7 @@ def _make_assertions(response_json: JsonDict):
# Partial Update (HTTP PATCH)
# --------------------------------------------------------------------------

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def _assert_update(
self,
model: AnyModel,
Expand All @@ -282,7 +282,7 @@ def _assert_update(
model, json_model, action, request_method, contains_subset=partial
)

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def partial_update(
self,
model: AnyModel,
Expand Down Expand Up @@ -333,7 +333,7 @@ def partial_update(

return response

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def bulk_partial_update(
self,
models: t.Union[t.List[AnyModel], QuerySet[AnyModel]],
Expand Down Expand Up @@ -394,7 +394,7 @@ def _make_assertions(json_models: t.List[JsonDict]):
# Update (HTTP PUT)
# --------------------------------------------------------------------------

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def update(
self,
model: AnyModel,
Expand Down Expand Up @@ -445,7 +445,7 @@ def update(

return response

# pylint: disable-next=too-many-arguments
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def bulk_update(
self,
models: t.Union[t.List[AnyModel], QuerySet[AnyModel]],
Expand Down
60 changes: 31 additions & 29 deletions codeforlife/user/auth/backends/otp_bypass_token_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,37 @@
Created on 10/04/2024 at 13:17:18(+01:00).
"""

from ....tests import APIRequestFactory, TestCase
from ...models import AuthFactor, User
from .otp_bypass_token import OtpBypassTokenBackend
# TODO: uncomment test once CSE is implemented

# from ....tests import APIRequestFactory, TestCase
# from ...models import AuthFactor, User
# from .otp_bypass_token import OtpBypassTokenBackend


# pylint: disable-next=missing-class-docstring,too-many-instance-attributes
class TestTokenBackend(TestCase):
fixtures = ["school_2", "school_2_sessions"]

def setUp(self):
self.backend = OtpBypassTokenBackend()
self.request_factory = APIRequestFactory(User)

user = User.objects.filter(
otp_bypass_tokens__isnull=False,
session__isnull=False,
session__auth_factors__auth_factor__type__in=[AuthFactor.Type.OTP],
).first()
assert user
self.user = user

def test_authenticate(self):
"""Can authenticate by bypassing a user's enabled OTP auth factor."""
otp_bypass_token_count = self.user.otp_bypass_tokens.count()

user = self.backend.authenticate(
request=self.request_factory.post("/", user=self.user),
token="aaaaaaaa",
)

assert user == self.user
assert user.otp_bypass_tokens.count() == otp_bypass_token_count - 1
# class TestTokenBackend(TestCase):
# fixtures = ["school_2", "school_2_sessions"]

# def setUp(self):
# self.backend = OtpBypassTokenBackend()
# self.request_factory = APIRequestFactory(User)

# user = User.objects.filter(
# otp_bypass_tokens__isnull=False,
# session__isnull=False,
# session__auth_factors__auth_factor__type__in=[AuthFactor.Type.OTP],
# ).first()
# assert user
# self.user = user

# def test_authenticate(self):
# """Can authenticate by bypassing a user's enabled OTP auth factor."""
# otp_bypass_token_count = self.user.otp_bypass_tokens.count()

# user = self.backend.authenticate(
# request=self.request_factory.post("/", user=self.user),
# token="aaaaaaaa",
# )

# assert user == self.user
# assert user.otp_bypass_tokens.count() == otp_bypass_token_count - 1
Loading
Loading