-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
538 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
""" | ||
These are the common settings that need to be imported in all django projects | ||
throughout our system. Place the following at the END of every settings.py file. | ||
`from codeforlife.settings import *` | ||
If you need to reference a specific CFL setting in a project's setting: | ||
` | ||
# Place this at the top of your file. | ||
from codeforlife import settings as cfl_settings | ||
# Do something with EXAMPLE_SETTING from codeforlife's settings. | ||
cfl_settings.EXAMPLE_SETTING | ||
` | ||
""" | ||
|
||
from .custom import * | ||
from .django import * | ||
from .third_party import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
""" | ||
This file contains all of our custom settings we define for our own purposes. | ||
""" | ||
|
||
import os | ||
|
||
# The name of the current service. | ||
SERVICE_NAME = os.environ["SERVICE_NAME"] # *required | ||
|
||
# If the current service the root service. This will only be true for portal. | ||
SERVICE_IS_ROOT = bool(int(os.getenv("SERVICE_IS_ROOT", "0"))) | ||
|
||
# The protocol, domain and port of the current service. | ||
SERVICE_PROTOCOL = os.getenv("SITE_PROTOCOL", "http") | ||
SERVICE_DOMAIN = os.getenv("SITE_DOMAIN", "localhost") | ||
SERVICE_PORT = int(os.getenv("SITE_PORT", "8000")) | ||
|
||
# The base url of the current service. | ||
# The root service does not need its name included in the base url. | ||
SERVICE_BASE_URL = f"{SERVICE_PROTOCOL}://{SERVICE_DOMAIN}:{SERVICE_PORT}" | ||
if not SERVICE_IS_ROOT: | ||
SERVICE_BASE_URL += f"/{SERVICE_NAME}" | ||
|
||
|
||
# The api url of the current service. | ||
SERVICE_API_URL = f"{SERVICE_BASE_URL}/api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
""" | ||
This file contains all of the settings Django supports out of the box. | ||
https://docs.djangoproject.com/en/3.2/ref/settings/ | ||
""" | ||
|
||
import os | ||
|
||
from .custom import SERVICE_NAME | ||
|
||
# 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") | ||
|
||
# Authentication backends | ||
# https://docs.djangoproject.com/en/3.2/ref/settings/#authentication-backends | ||
|
||
AUTHENTICATION_BACKENDS = [ | ||
"user.auth_backends.EmailAndPasswordBackend", | ||
"user.auth_backends.UserIdAndLoginIdBackend", | ||
"user.auth_backends.UsernameAndPasswordAndClassIdBackend", | ||
] | ||
|
||
# Sessions | ||
# https://docs.djangoproject.com/en/3.2/topics/http/sessions/ | ||
|
||
SESSION_COOKIE_AGE = 60 * 60 | ||
SESSION_SAVE_EVERY_REQUEST = True | ||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True | ||
SESSION_COOKIE_SECURE = True | ||
SESSION_COOKIE_SAMESITE = "None" | ||
SESSION_COOKIE_DOMAIN = "localhost" if DEBUG else "codeforlife.education" | ||
|
||
# Internationalization | ||
# https://docs.djangoproject.com/en/3.2/topics/i18n/ | ||
|
||
LANGUAGE_CODE = "en-us" | ||
TIME_ZONE = "UTC" | ||
USE_I18N = True | ||
USE_L10N = True | ||
USE_TZ = True | ||
|
||
# Default primary key field type | ||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field | ||
|
||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" | ||
|
||
# CSRF | ||
# https://docs.djangoproject.com/en/3.2/ref/csrf/ | ||
|
||
CSRF_COOKIE_NAME = f"{SERVICE_NAME}_csrftoken" | ||
CSRF_COOKIE_SAMESITE = "None" | ||
CSRF_COOKIE_SECURE = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
""" | ||
This file contains custom settings defined by third party extensions. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,60 @@ | ||
from django.contrib import admin | ||
from django.contrib.auth.admin import UserAdmin | ||
|
||
from .models import User | ||
|
||
|
||
class CustomUserAdmin(UserAdmin): | ||
model = User | ||
|
||
list_display = ( | ||
"username", | ||
"email", | ||
"is_active", | ||
"is_staff", | ||
"is_superuser", | ||
"last_login", | ||
) | ||
|
||
list_filter = ("is_active", "is_staff", "is_superuser") | ||
|
||
fieldsets = ( | ||
(None, {"fields": ("username", "email", "password")}), | ||
( | ||
"Permissions", | ||
{ | ||
"fields": ( | ||
"is_staff", | ||
"is_active", | ||
"is_superuser", | ||
"groups", | ||
"user_permissions", | ||
) | ||
}, | ||
), | ||
("Dates", {"fields": ("last_login", "date_joined")}), | ||
) | ||
|
||
add_fieldsets = ( | ||
( | ||
None, | ||
{ | ||
"classes": ("wide",), | ||
"fields": ( | ||
"username", | ||
"email", | ||
"password1", | ||
"password2", | ||
"is_staff", | ||
"is_active", | ||
), | ||
}, | ||
), | ||
) | ||
|
||
search_fields = ("email",) | ||
|
||
ordering = ("email",) | ||
|
||
|
||
admin.site.register(User, CustomUserAdmin) | ||
# from django.contrib import admin | ||
# from django.contrib.auth.admin import UserAdmin | ||
|
||
# from .models import User | ||
|
||
|
||
# class CustomUserAdmin(UserAdmin): | ||
# model = User | ||
|
||
# list_display = ( | ||
# "username", | ||
# "email", | ||
# "is_active", | ||
# "is_staff", | ||
# "is_superuser", | ||
# "last_login", | ||
# ) | ||
|
||
# list_filter = ("is_active", "is_staff", "is_superuser") | ||
|
||
# fieldsets = ( | ||
# (None, {"fields": ("username", "email", "password")}), | ||
# ( | ||
# "Permissions", | ||
# { | ||
# "fields": ( | ||
# "is_staff", | ||
# "is_active", | ||
# "is_superuser", | ||
# "groups", | ||
# "user_permissions", | ||
# ) | ||
# }, | ||
# ), | ||
# ("Dates", {"fields": ("last_login", "date_joined")}), | ||
# ) | ||
|
||
# add_fieldsets = ( | ||
# ( | ||
# None, | ||
# { | ||
# "classes": ("wide",), | ||
# "fields": ( | ||
# "username", | ||
# "email", | ||
# "password1", | ||
# "password2", | ||
# "is_staff", | ||
# "is_active", | ||
# ), | ||
# }, | ||
# ), | ||
# ) | ||
|
||
# search_fields = ("email",) | ||
|
||
# ordering = ("email",) | ||
|
||
|
||
# admin.site.register(User, CustomUserAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from .email_and_password import EmailAndPasswordBackend | ||
from .user_id_and_login_id import UserIdAndLoginIdBackend | ||
from .username_and_password_and_class_id import ( | ||
UsernameAndPasswordAndClassIdBackend, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import typing as t | ||
|
||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.backends import BaseBackend | ||
from django.contrib.auth.base_user import AbstractBaseUser | ||
from django.core.handlers.wsgi import WSGIRequest | ||
|
||
User = get_user_model() | ||
|
||
|
||
class EmailAndPasswordBackend(BaseBackend): | ||
def authenticate( | ||
self, | ||
request: WSGIRequest, | ||
email: t.Optional[str] = None, | ||
password: t.Optional[str] = None, | ||
**kwargs | ||
) -> t.Optional[AbstractBaseUser]: | ||
if email is None or password is None: | ||
return | ||
|
||
try: | ||
user = User.objects.get(email=email) | ||
if getattr(user, "is_active", True) and user.check_password( | ||
password | ||
): | ||
return user | ||
except User.DoesNotExist: | ||
return | ||
|
||
def get_user(self, user_id: int) -> t.Optional[AbstractBaseUser]: | ||
try: | ||
return User.objects.get(id=user_id) | ||
except User.DoesNotExist: | ||
return |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import typing as t | ||
|
||
from common.helpers.generators import get_hashed_login_id | ||
from common.models import Student | ||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.backends import BaseBackend | ||
from django.contrib.auth.base_user import AbstractBaseUser | ||
from django.core.handlers.wsgi import WSGIRequest | ||
|
||
User = get_user_model() | ||
|
||
|
||
class UserIdAndLoginIdBackend(BaseBackend): | ||
def authenticate( | ||
self, | ||
request: WSGIRequest, | ||
user_id: t.Optional[int] = None, | ||
login_id: t.Optional[str] = None, | ||
**kwargs | ||
) -> t.Optional[AbstractBaseUser]: | ||
if user_id is None or login_id is None: | ||
return | ||
|
||
user = self.get_user(user_id) | ||
if user and getattr(user, "is_active", True): | ||
# 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 | ||
): | ||
return user | ||
|
||
def get_user(self, user_id: int) -> t.Optional[AbstractBaseUser]: | ||
try: | ||
return User.objects.get(id=user_id) | ||
except User.DoesNotExist: | ||
return |
39 changes: 39 additions & 0 deletions
39
codeforlife/user/auth_backends/username_and_password_and_class_id.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import typing as t | ||
|
||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.backends import BaseBackend | ||
from django.contrib.auth.base_user import AbstractBaseUser | ||
from django.core.handlers.wsgi import WSGIRequest | ||
|
||
User = get_user_model() | ||
|
||
|
||
class UsernameAndPasswordAndClassIdBackend(BaseBackend): | ||
def authenticate( | ||
self, | ||
request: WSGIRequest, | ||
username: t.Optional[str] = None, | ||
password: t.Optional[str] = None, | ||
class_id: t.Optional[str] = None, | ||
**kwargs | ||
) -> t.Optional[AbstractBaseUser]: | ||
if username is None or password is None or class_id is None: | ||
return | ||
|
||
try: | ||
user = User.objects.get( | ||
username=username, | ||
new_student__class_field__access_code=class_id, | ||
) | ||
if getattr(user, "is_active", True) and user.check_password( | ||
password | ||
): | ||
return user | ||
except User.DoesNotExist: | ||
return | ||
|
||
def get_user(self, user_id: int) -> t.Optional[AbstractBaseUser]: | ||
try: | ||
return User.objects.get(id=user_id) | ||
except User.DoesNotExist: | ||
return |
Oops, something went wrong.