Merge pull request #961 from ae-utbm/auth-backend

Custom auth backend
This commit is contained in:
thomas girod 2024-12-22 06:38:34 +01:00 committed by GitHub
commit eaac0c728f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 166 additions and 42 deletions

37
core/auth_backends.py Normal file
View File

@ -0,0 +1,37 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import Permission
from core.models import Group
if TYPE_CHECKING:
from core.models import User
class SithModelBackend(ModelBackend):
"""Custom auth backend for the Sith.
In fact, it's the exact same backend as `django.contrib.auth.backend.ModelBackend`,
with the exception that group permissions are fetched slightly differently.
Indeed, django tries by default to fetch the permissions associated
with all the `django.contrib.auth.models.Group` of a user ;
however, our User model overrides that, so the actual linked group model
is [core.models.Group][].
Instead of having the relation `auth_perm --> auth_group <-- core_user`,
we have `auth_perm --> auth_group <-- core_group <-- core_user`.
Thus, this backend make the small tweaks necessary to make
our custom models interact with the django auth.
"""
def _get_group_permissions(self, user_obj: User):
groups = user_obj.groups.all()
if user_obj.is_subscribed:
groups = groups.union(
Group.objects.get(pk=settings.SITH_GROUP_SUBSCRIBERS_ID)
)
return Permission.objects.filter(group__group__in=groups)

View File

@ -23,7 +23,7 @@
from datetime import date, timedelta from datetime import date, timedelta
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from typing import ClassVar from typing import ClassVar, NamedTuple
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@ -31,6 +31,7 @@ from django.contrib.sites.models import Site
from django.core.management import call_command from django.core.management import call_command
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db import connection from django.db import connection
from django.db.models import Q
from django.utils import timezone from django.utils import timezone
from django.utils.timezone import localdate from django.utils.timezone import localdate
from PIL import Image from PIL import Image
@ -56,6 +57,18 @@ from sas.models import Album, PeoplePictureRelation, Picture
from subscription.models import Subscription from subscription.models import Subscription
class PopulatedGroups(NamedTuple):
root: Group
public: Group
subscribers: Group
old_subscribers: Group
sas_admin: Group
com_admin: Group
counter_admin: Group
accounting_admin: Group
pedagogy_admin: Group
class Command(BaseCommand): class Command(BaseCommand):
ROOT_PATH: ClassVar[Path] = Path(__file__).parent.parent.parent.parent ROOT_PATH: ClassVar[Path] = Path(__file__).parent.parent.parent.parent
SAS_FIXTURE_PATH: ClassVar[Path] = ( SAS_FIXTURE_PATH: ClassVar[Path] = (
@ -79,25 +92,7 @@ class Command(BaseCommand):
Sith.objects.create(weekmail_destinations="etudiants@git.an personnel@git.an") Sith.objects.create(weekmail_destinations="etudiants@git.an personnel@git.an")
Site.objects.create(domain=settings.SITH_URL, name=settings.SITH_NAME) Site.objects.create(domain=settings.SITH_URL, name=settings.SITH_NAME)
groups = self._create_groups()
root_group = Group.objects.create(name="Root")
public_group = Group.objects.create(name="Public")
subscribers = Group.objects.create(name="Subscribers")
old_subscribers = Group.objects.create(name="Old subscribers")
Group.objects.create(name="Accounting admin")
Group.objects.create(name="Communication admin")
Group.objects.create(name="Counter admin")
Group.objects.create(name="Banned from buying alcohol")
Group.objects.create(name="Banned from counters")
Group.objects.create(name="Banned to subscribe")
Group.objects.create(name="SAS admin")
Group.objects.create(name="Forum admin")
Group.objects.create(name="Pedagogy admin")
self.reset_index("core", "auth")
change_billing = Permission.objects.get(codename="change_billinginfo")
add_billing = Permission.objects.get(codename="add_billinginfo")
root_group.permissions.add(change_billing, add_billing)
root = User.objects.create_superuser( root = User.objects.create_superuser(
id=0, id=0,
@ -155,7 +150,7 @@ class Command(BaseCommand):
Counter.edit_groups.through.objects.bulk_create(bar_groups) Counter.edit_groups.through.objects.bulk_create(bar_groups)
self.reset_index("counter") self.reset_index("counter")
subscribers.viewable_files.add(home_root, club_root) groups.subscribers.viewable_files.add(home_root, club_root)
Weekmail().save() Weekmail().save()
@ -260,21 +255,11 @@ class Command(BaseCommand):
) )
User.groups.through.objects.bulk_create( User.groups.through.objects.bulk_create(
[ [
User.groups.through( User.groups.through(group=groups.counter_admin, user=counter),
group_id=settings.SITH_GROUP_COUNTER_ADMIN_ID, user=counter User.groups.through(group=groups.accounting_admin, user=comptable),
), User.groups.through(group=groups.com_admin, user=comunity),
User.groups.through( User.groups.through(group=groups.pedagogy_admin, user=tutu),
group_id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID, user=comptable User.groups.through(group=groups.sas_admin, user=skia),
),
User.groups.through(
group_id=settings.SITH_GROUP_COM_ADMIN_ID, user=comunity
),
User.groups.through(
group_id=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID, user=tutu
),
User.groups.through(
group_id=settings.SITH_GROUP_SAS_ADMIN_ID, user=skia
),
] ]
) )
for user in richard, sli, krophil, skia: for user in richard, sli, krophil, skia:
@ -335,7 +320,7 @@ Welcome to the wiki page!
content="Fonctionnement de la laverie", content="Fonctionnement de la laverie",
) )
public_group.viewable_page.set( groups.public.viewable_page.set(
[syntax_page, services_page, index_page, laundry_page] [syntax_page, services_page, index_page, laundry_page]
) )
@ -512,8 +497,10 @@ Welcome to the wiki page!
club=main_club, club=main_club,
limit_age=18, limit_age=18,
) )
subscribers.products.add(cotis, cotis2, refill, barb, cble, cors, carolus) groups.subscribers.products.add(
old_subscribers.products.add(cotis, cotis2) cotis, cotis2, refill, barb, cble, cors, carolus
)
groups.old_subscribers.products.add(cotis, cotis2)
mde = Counter.objects.get(name="MDE") mde = Counter.objects.get(name="MDE")
mde.products.add(barb, cble, cons, dcons) mde.products.add(barb, cble, cons, dcons)
@ -616,10 +603,10 @@ Welcome to the wiki page!
start_date="1942-06-12 10:28:45+01", start_date="1942-06-12 10:28:45+01",
end_date="7942-06-12 10:28:45+01", end_date="7942-06-12 10:28:45+01",
) )
el.view_groups.add(public_group) el.view_groups.add(groups.public)
el.edit_groups.add(ae_board_group) el.edit_groups.add(ae_board_group)
el.candidature_groups.add(subscribers) el.candidature_groups.add(groups.subscribers)
el.vote_groups.add(subscribers) el.vote_groups.add(groups.subscribers)
liste = ElectionList.objects.create(title="Candidature Libre", election=el) liste = ElectionList.objects.create(title="Candidature Libre", election=el)
listeT = ElectionList.objects.create(title="Troll", election=el) listeT = ElectionList.objects.create(title="Troll", election=el)
pres = Role.objects.create( pres = Role.objects.create(
@ -898,3 +885,102 @@ Welcome to the wiki page!
start=s.subscription_start, start=s.subscription_start,
) )
s.save() s.save()
def _create_groups(self) -> PopulatedGroups:
perms = Permission.objects.all()
root_group = Group.objects.create(name="Root")
root_group.permissions.add(*list(perms.values_list("pk", flat=True)))
# public has no permission.
# Its purpose is not to link users to permissions,
# but to other objects (like products)
public_group = Group.objects.create(name="Public")
subscribers = Group.objects.create(name="Subscribers")
old_subscribers = Group.objects.create(name="Old subscribers")
old_subscribers.permissions.add(
*list(
perms.filter(
codename__in=[
"view_user",
"view_picture",
"view_album",
"view_peoplepicturerelation",
"add_peoplepicturerelation",
]
)
)
)
accounting_admin = Group.objects.create(name="Accounting admin")
accounting_admin.permissions.add(
*list(
perms.filter(
Q(content_type__app_label="accounting")
| Q(
codename__in=[
"view_customer",
"view_product",
"change_product",
"add_product",
"view_producttype",
"change_producttype",
"add_producttype",
"delete_selling",
]
)
).values_list("pk", flat=True)
)
)
com_admin = Group.objects.create(name="Communication admin")
com_admin.permissions.add(
*list(
perms.filter(content_type__app_label="com").values_list("pk", flat=True)
)
)
counter_admin = Group.objects.create(name="Counter admin")
counter_admin.permissions.add(
*list(
perms.filter(
Q(content_type__app_label__in=["counter", "launderette"])
& ~Q(codename__in=["delete_product", "delete_producttype"])
)
)
)
Group.objects.create(name="Banned from buying alcohol")
Group.objects.create(name="Banned from counters")
Group.objects.create(name="Banned to subscribe")
sas_admin = Group.objects.create(name="SAS admin")
sas_admin.permissions.add(
*list(
perms.filter(content_type__app_label="sas").values_list("pk", flat=True)
)
)
forum_admin = Group.objects.create(name="Forum admin")
forum_admin.permissions.add(
*list(
perms.filter(content_type__app_label="forum").values_list(
"pk", flat=True
)
)
)
pedagogy_admin = Group.objects.create(name="Pedagogy admin")
pedagogy_admin.permissions.add(
*list(
perms.filter(content_type__app_label="pedagogy").values_list(
"pk", flat=True
)
)
)
self.reset_index("core", "auth")
return PopulatedGroups(
root=root_group,
public=public_group,
subscribers=subscribers,
old_subscribers=old_subscribers,
com_admin=com_admin,
counter_admin=counter_admin,
accounting_admin=accounting_admin,
sas_admin=sas_admin,
pedagogy_admin=pedagogy_admin,
)

View File

@ -290,6 +290,7 @@ STORAGES = {
# Auth configuration # Auth configuration
AUTH_USER_MODEL = "core.User" AUTH_USER_MODEL = "core.User"
AUTH_ANONYMOUS_MODEL = "core.models.AnonymousUser" AUTH_ANONYMOUS_MODEL = "core.models.AnonymousUser"
AUTHENTICATION_BACKENDS = ["core.auth_backends.SithModelBackend"]
LOGIN_URL = "/login" LOGIN_URL = "/login"
LOGOUT_URL = "/logout" LOGOUT_URL = "/logout"
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"