Skip to content

Commit

Permalink
feat: discord roles with pre-population based with discord data
Browse files Browse the repository at this point in the history
  • Loading branch information
goteusz-maszyk committed Jun 2, 2024
1 parent 9f628af commit fe10f85
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 14 deletions.
12 changes: 11 additions & 1 deletion core/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from core.discord_auth import user_logged_in

SIDEBAR_LINKS = {
"Genshin": [
{"name": "Genshin", "url": "/genshin", "icon": "gamepad"},
Expand Down Expand Up @@ -25,4 +27,12 @@


def sidebar_links(request):
return {"sidebar_links": SIDEBAR_LINKS}
user = user_logged_in(request)
if user is None:
return {}
links = {}
for role in user.roles.all():
if SIDEBAR_LINKS.get(role.permissions) is None:
continue
links[role.permissions] = SIDEBAR_LINKS.get(role.permissions)
return {"sidebar_links": links}
10 changes: 4 additions & 6 deletions core/discord_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
DISCORD_ID_COOKIE,
)
from core.models import User, UserRole
from gwardia_hub.settings import MEMBER_ROLE_ID


def user_logged_in(request: HttpRequest) -> User | None:
Expand Down Expand Up @@ -46,12 +45,12 @@ def wrapper(request: HttpRequest, *args, **kwargs) -> HttpResponse:
return decorator


def require_user(required_role_id: str | None = MEMBER_ROLE_ID):
def require_user(required_permissions: str = None):
"""Decorator that checks if a user is logged in and has the required role.
Adds a second positional argument to the decorated function containting the guild member.
:param required_role_id: A role ID required for the user to be authorised. Defaults to MEMBER_ROLE_ID.
:param required_permissions: The required permissions.
"""

def decorator(func):
Expand All @@ -61,9 +60,8 @@ def wrapper(request: HttpRequest, *args, **kwargs):
if not user:
return temporary_redirect("core:discord_refresh", request)
try:
required_role = UserRole.objects.get(id=required_role_id)
if not user.roles.contains(required_role):
return set_id_cookie(HttpResponse("Unauthorized", status=401), user)
if not user.has_permission(required_permissions):
return redirect("core:profile", request)
except UserRole.DoesNotExist:
pass

Expand Down
2 changes: 1 addition & 1 deletion core/migrations/0002_user_data_valid_until.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Migration(migrations.Migration):
name="data_valid_until",
field=models.DateTimeField(
default=datetime.datetime(
2024, 5, 8, 9, 45, 28, 581656, tzinfo=datetime.timezone.utc
1971, 1, 1, 0, 0, tzinfo=datetime.timezone.utc
)
),
),
Expand Down
20 changes: 20 additions & 0 deletions core/migrations/0003_userrole_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.0.6 on 2024-06-02 11:08

import core.models
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("core", "0002_user_data_valid_until"),
]

operations = [
migrations.AddField(
model_name="userrole",
name="permissions",
field=models.CharField(
choices=core.models.get_permission_choices, default=""
),
),
]
29 changes: 29 additions & 0 deletions core/migrations/0004_userrole_prefill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 5.0.6 on 2024-06-02 11:08

from django.db import migrations


def create_default_roles(apps, schema_editor):
UserRole = apps.get_model("core", "UserRole")
UserRole.objects.create(
name="Genshiniara", id=1215012858346086461, permissions="Genshin"
)
UserRole.objects.create(
name="Członek gwardii", id=1200540359919411380, permissions="Gwardia"
)
UserRole.objects.create(
name="Z klasy", id=1148642506859368448, permissions="Klasowe"
)


def remove_default_roles(apps, schema_editor):
UserRole = apps.get_model("core", "UserRole")
UserRole.objects.all().delete()


class Migration(migrations.Migration):
dependencies = [
("core", "0003_userrole_permissions"),
]

operations = [migrations.RunPython(create_default_roles, remove_default_roles)]
20 changes: 18 additions & 2 deletions core/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
from datetime import timedelta
from datetime import datetime
from django.utils import timezone
from django.db import models

ROLE_PERMISSIONS = ["None", "Gwardia", "Genshin", "Klasowe"]


def get_permission_choices():
return {i: i for i in ROLE_PERMISSIONS}


class UserRole(models.Model):
id = models.BigIntegerField(unique=True, primary_key=True)
name = models.CharField()
permissions = models.CharField(choices=get_permission_choices, default="")

def __str__(self):
return f"{self.name} ({self.id})"
Expand All @@ -21,7 +28,7 @@ class User(models.Model):

# caching data for 10 minutes to prevent Rate-Limits from Discord API
data_valid_until = models.DateTimeField(
default=timezone.now() + timedelta(minutes=10)
default=datetime(1971, 1, 1, tzinfo=timezone.timezone.utc),
)

def avatar_url(self):
Expand All @@ -46,6 +53,15 @@ def to_json(self):
"avatar": self.avatar_url(),
}

def has_permission(self, permission):
if permission is None:
return True
for role in self.roles.all():
if permission == role.permissions:
return True

return False

@classmethod
def exists(cls, user_id: int) -> bool:
try:
Expand Down
4 changes: 2 additions & 2 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def index(request):
return render(request, "core/index.html", context)


@require_user(required_role_id=None)
@require_user()
def profile(request, user, name: str = None):
try:
user = User.objects.get(username=name)
Expand Down Expand Up @@ -99,7 +99,7 @@ def gh_webhook(request):
return HttpResponse("Invalid request", status=500)


@require_user(required_role_id=None)
@require_user()
@require_POST
def logout(request, _user):
return reset_cookies(redirect("core:index"))
4 changes: 2 additions & 2 deletions gwardia/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def index(request, _user):
return redirect("gwardia:meetings")


@require_user()
@require_user("Gwardia")
def meetings(request, user: User):
upcoming_meetings = Meeting.objects.filter(date__gte=timezone.now()).order_by(
"-date"
Expand All @@ -24,6 +24,6 @@ def meetings(request, user: User):
context = {
"upcoming_meetings": upcoming_meetings,
"archival_meetings": archival_meetings,
user: user.to_json(),
"user": user.to_json(),
}
return render(request, "gwardia/meetings.html", context)

0 comments on commit fe10f85

Please sign in to comment.