From f085ad91620300b57ea846d4a0ed4bcb002e0f15 Mon Sep 17 00:00:00 2001 From: Evan Tung Date: Sun, 3 Dec 2023 19:43:37 -0500 Subject: [PATCH] implement Discord Admin permissions --- clients/discord.py | 42 +++++++++++++++++++++++++++++++--------- community/models.py | 22 ++------------------- community/permissions.py | 37 +++++++++++++++++++++++++++++++++++ community/views.py | 6 ++++-- dominator/models.py | 4 ++-- psychopass/models.py | 4 ++-- 6 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 community/permissions.py diff --git a/clients/discord.py b/clients/discord.py index b3b9a86..0350a39 100644 --- a/clients/discord.py +++ b/clients/discord.py @@ -1,22 +1,46 @@ import requests + class DiscordClient(): url = "https://discord.com/api/v10" def __init__(self, access_token): - user = requests.get(f"{self.url}/users/@me", headers={ + self.access_token = access_token + response = requests.get(f"{self.url}/users/@me", headers={ + "Accept": "application/json", "Authorization": f"Bearer {access_token}" - }).json() - user.raise_for_status() - - self.user_id = user["id"] + }) + response.raise_for_status() + self.user = response.json() def is_server_admin(self, guild_id): - response = requests.get( - f"{self.url}/guilds/{guild_id}/members/{self.user_id}").json() + # Check if user is owner + response = requests.get(f"{self.url}/guilds/{guild_id}", headers={ + "Accept": "application/json", + "Authorization": f"Bearer {self.access_token}" + }) + response.raise_for_status() + guild = response.json() + if guild["owner_id"] == self.user["id"]: + return True + + # Check if user is admin + response = requests.get(f"{self.url}/guilds/{guild_id}/members/{self.user['id']}", headers={ + "Accept": "application/json", + "Authorization": f"Bearer {self.access_token}" + }) response.raise_for_status() + member = response.json() + roles = member["roles"] - print(response["permissions"]) + def is_admin(role): + response = requests.get(f"{self.url}/guilds/{guild_id}/roles/{role}", headers={ + "Accept": "application/json", + "Authorization": f"Bearer {self.access_token}" + }) + response.raise_for_status() + role = response.json() + return role["permissions"] & 0x8 == 0x8 - return True + return any(map(is_admin, roles)) diff --git a/community/models.py b/community/models.py index 38b4338..b03f1b9 100644 --- a/community/models.py +++ b/community/models.py @@ -1,10 +1,7 @@ from django.db import models from django.contrib.auth import get_user_model -from rest_framework.views import APIView -from rest_framework.request import Request from rest_framework.serializers import ModelSerializer -from rest_framework.permissions import BasePermission -from clients.discord import DiscordClient + # Create your models here. @@ -18,7 +15,7 @@ class Meta: verbose_name_plural = "Communities" def __str__(self) -> str: - return f"{self.platform.username}/{self.community_id} ({self.id})" + return f"{self.platform.get_username()}/{self.community_id} ({self.id})" # For Discord Servers Only discord_log_channel = models.CharField( @@ -31,18 +28,3 @@ class CommunitySerializer(ModelSerializer): class Meta: model = Community fields = "__all__" - -class DiscordAdmin(BasePermission): - - def has_permission(self, request: Request, view: APIView): - return True - - def has_object_permission(self, request: Request, view: APIView, obj: models.Model) -> bool: - platform, access_token = request.META["Authorization"].split(" ") - - if (platform != "Discord"): return False - if (type(obj) == Community): - return DiscordClient(access_token).is_server_admin(obj.community_id) - - - return False \ No newline at end of file diff --git a/community/permissions.py b/community/permissions.py new file mode 100644 index 0000000..d922847 --- /dev/null +++ b/community/permissions.py @@ -0,0 +1,37 @@ +from django.db import models +from rest_framework.permissions import BasePermission +from rest_framework.views import APIView +from rest_framework.request import Request +from community.models import Community +from dominator.models import MessageDominator, MemberDominator +from clients.discord import DiscordClient + + +class DiscordAdmin(BasePermission): + + def has_permission(self, request: Request, view: APIView): + if request.method == "GET" or request.method == "PUT": + return True + return False + + def has_object_permission(self, request: Request, view: APIView, obj: models.Model) -> bool: + try: + platform, access_token = request.headers["Authorization"].split( + ' ') + + if platform != "Discord": + return False + discord = DiscordClient(access_token) + if isinstance(obj, Community): + if obj.platform.get_username() != "discord": + return False + return discord.is_server_admin(obj.community_id) + if isinstance(obj, (MessageDominator, MemberDominator)): + if obj.community.platform.get_username() != "discord": + return False + return discord.is_server_admin(obj.community.community_id) + + return False + + except Exception: + return False diff --git a/community/views.py b/community/views.py index 6fc9edd..447a6e4 100644 --- a/community/views.py +++ b/community/views.py @@ -4,7 +4,9 @@ from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response -from community.models import Community, CommunitySerializer, DiscordAdmin +from rest_framework.decorators import permission_classes +from community.models import Community, CommunitySerializer +from community.permissions import DiscordAdmin from psychopass.models import CommunityPsychoPass from dominator.models import MessageDominator, MemberDominator @@ -20,7 +22,7 @@ def get(self, request: Request) -> Response: self.check_object_permissions(request, community) return Response(CommunitySerializer(community).data, status=status.HTTP_200_OK) - + def post(self, request: Request) -> Response: community = Community.objects.create( platform=request.user, community_id=request.data.get("communityID")) diff --git a/dominator/models.py b/dominator/models.py index 27e80c9..86c35e7 100644 --- a/dominator/models.py +++ b/dominator/models.py @@ -21,7 +21,7 @@ class Meta: verbose_name_plural = "Message Dominators" def __str__(self) -> str: - return f"{self.community.platform.username}/{self.community.community_id} ({self.id})" + return f"{self.community.platform.get_username()}/{self.community.community_id} ({self.id})" toxicity_action = models.IntegerField( choices=Actions.choices, default=Actions.NOTIFY) @@ -60,7 +60,7 @@ class Meta: verbose_name_plural = "Member Dominators" def __str__(self) -> str: - return f"{self.community.platform.username}/{self.community.community_id} ({self.id})" + return f"{self.community.platform.get_username()}/{self.community.community_id} ({self.id})" crime_coefficient_100_action = models.IntegerField( choices=Actions.choices, default=Actions.NOTIFY) diff --git a/psychopass/models.py b/psychopass/models.py index 0edd7fe..2656fcd 100644 --- a/psychopass/models.py +++ b/psychopass/models.py @@ -27,7 +27,7 @@ class Meta: verbose_name_plural = "Psycho-Passes" def __str__(self) -> str: - return f"{self.platform.username}/{self.user_id} ({self.id})" + return f"{self.platform.get_username()}/{self.user_id} ({self.id})" def ingest_message(self, scores: dict) -> None: self.toxicity = self.update_score( @@ -87,7 +87,7 @@ class Meta: verbose_name_plural = "Community Psycho-Passes" def __str__(self) -> str: - return f"{self.community.platform.username}/{self.community.community_id} ({self.id})" + return f"{self.community.platform.get_username()}/{self.community.community_id} ({self.id})" def area_stress_level(self) -> dict: return {