Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: auth flow #120

Merged
merged 8 commits into from
Jun 19, 2024
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
12 changes: 6 additions & 6 deletions codeforlife/settings/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ def get_databases(base_dir: Path): # pragma: no cover
# https://docs.djangoproject.com/en/3.2/ref/settings/#authentication-backends

AUTHENTICATION_BACKENDS = [
"codeforlife.user.auth.backends.EmailAndPasswordBackend",
"codeforlife.user.auth.backends.EmailBackend",
"codeforlife.user.auth.backends.OtpBackend",
"codeforlife.user.auth.backends.OtpBypassTokenBackend",
"codeforlife.user.auth.backends.UserIdAndLoginIdBackend",
"codeforlife.user.auth.backends.FirstNameAndPasswordAndClassIdBackend",
"codeforlife.user.auth.backends.StudentBackend",
"codeforlife.user.auth.backends.StudentAutoBackend",
]

# Sessions
Expand All @@ -79,7 +79,7 @@ def get_databases(base_dir: Path): # pragma: no cover
SESSION_ENGINE = "codeforlife.user.models.session"
SESSION_SAVE_EVERY_REQUEST = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_NAME = "sessionid_httponly_true"
SESSION_COOKIE_NAME = "session_key"
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_AGE = 60 * 60
SESSION_COOKIE_SECURE = True
Expand Down Expand Up @@ -144,12 +144,12 @@ def get_databases(base_dir: Path): # pragma: no cover
# URLs
# https://docs.djangoproject.com/en/3.2/ref/settings/#root-urlconf

ROOT_URLCONF = "src.service.urls"
ROOT_URLCONF = "src.urls"

# App
# https://docs.djangoproject.com/en/3.2/ref/settings/#wsgi-application

WSGI_APPLICATION = "src.service.wsgi.application"
WSGI_APPLICATION = "main.app"

# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
Expand Down
9 changes: 4 additions & 5 deletions codeforlife/user/auth/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
© Ocado Group
Created on 01/02/2024 at 14:48:57(+00:00).
"""

# TODO: Create a custom auth backend for Django admin permissions
from .email_and_password import EmailAndPasswordBackend
from .first_name_and_password_and_class_id import (
FirstNameAndPasswordAndClassIdBackend,
)
from .email import EmailBackend
from .otp import OtpBackend
from .otp_bypass_token import OtpBypassTokenBackend
from .user_id_and_login_id import UserIdAndLoginIdBackend
from .student import StudentBackend
from .student_auto import StudentAutoBackend
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .base import BaseBackend


class EmailAndPasswordBackend(BaseBackend):
class EmailBackend(BaseBackend):
"""Authenticate a user by checking their email and password."""

def authenticate( # type: ignore[override]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .base import BaseBackend


class FirstNameAndPasswordAndClassIdBackend(BaseBackend):
class StudentBackend(BaseBackend):
"""Authenticate a student using their first name, password and class ID."""

user_class = StudentUser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,34 @@
from .base import BaseBackend


class UserIdAndLoginIdBackend(BaseBackend):
class StudentAutoBackend(BaseBackend):
"""Authenticate a student using their ID and auto-generated password."""

user_class = StudentUser

def authenticate( # type: ignore[override]
self,
request: t.Optional[HttpRequest],
user_id: t.Optional[int] = None,
login_id: t.Optional[str] = None,
student_id: t.Optional[int] = None,
auto_gen_password: t.Optional[str] = None,
**kwargs
):
if user_id is None or login_id is None:
if student_id is None or auto_gen_password is None:

Check warning on line 32 in codeforlife/user/auth/backends/student_auto.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/auth/backends/student_auto.py#L32

Added line #L32 was not covered by tests
return None

user = self.get_user(user_id)
if user:
try:
student = Student.objects.get(id=student_id)
except Student.DoesNotExist:
student = None

Check warning on line 38 in codeforlife/user/auth/backends/student_auto.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/auth/backends/student_auto.py#L35-L38

Added lines #L35 - L38 were not covered by tests

if student:

Check warning on line 40 in codeforlife/user/auth/backends/student_auto.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/auth/backends/student_auto.py#L40

Added line #L40 was not covered by tests
# TODO: refactor this
# Check the url against the student's stored hash.
student = Student.objects.get(new_user=user)
if (
student.login_id
# TODO: refactor this
and get_hashed_login_id(login_id) == student.login_id
student.new_user
and student.login_id
and get_hashed_login_id(auto_gen_password) == student.login_id
):
return user
return student.new_user

Check warning on line 48 in codeforlife/user/auth/backends/student_auto.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/auth/backends/student_auto.py#L48

Added line #L48 was not covered by tests

return None
38 changes: 38 additions & 0 deletions codeforlife/user/management/commands/load_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
© Ocado Group
Created on 10/06/2024 at 10:44:45(+01:00).
"""

import os
import typing as t

Check warning on line 7 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L6-L7

Added lines #L6 - L7 were not covered by tests

from django.apps import apps
from django.core.management import call_command
from django.core.management.base import BaseCommand

Check warning on line 11 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L9-L11

Added lines #L9 - L11 were not covered by tests


# pylint: disable-next=missing-class-docstring
class Command(BaseCommand):
help = "Loads all the fixtures of the specified apps."

Check warning on line 16 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L15-L16

Added lines #L15 - L16 were not covered by tests

def add_arguments(self, parser):
parser.add_argument("app_labels", nargs="*", type=str)

Check warning on line 19 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L18-L19

Added lines #L18 - L19 were not covered by tests

def handle(self, *args, **options):
fixture_labels: t.List[str] = []
for app_label in {*options["app_labels"], "user"}:
app_config = apps.app_configs[app_label]
fixtures_path = os.path.join(app_config.path, "fixtures")

Check warning on line 25 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L21-L25

Added lines #L21 - L25 were not covered by tests

self.stdout.write(f"{app_label} fixtures ({fixtures_path}):")
for fixture_label in os.listdir(fixtures_path):
if fixture_label in fixture_labels:
self.stderr.write(f"Duplicate fixture: {fixture_label}")
return

Check warning on line 31 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L27-L31

Added lines #L27 - L31 were not covered by tests

self.stdout.write(f" - {fixture_label}")
fixture_labels.append(fixture_label)

Check warning on line 34 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L33-L34

Added lines #L33 - L34 were not covered by tests

self.stdout.write()

Check warning on line 36 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L36

Added line #L36 was not covered by tests

call_command("loaddata", *fixture_labels)

Check warning on line 38 in codeforlife/user/management/commands/load_fixtures.py

View check run for this annotation

Codecov / codecov/patch

codeforlife/user/management/commands/load_fixtures.py#L38

Added line #L38 was not covered by tests
3 changes: 3 additions & 0 deletions codeforlife/views/csrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from ..permissions import AllowAny


class CookieView(APIView):
"""A view to get a CSRF cookie."""

http_method_names = ["get"]
permission_classes = [AllowAny]

@method_decorator(ensure_csrf_cookie)
def get(self, request: Request):
Expand Down