Ownership and permission validation for guilds
This commit is contained in:
parent
14a9ebd2f7
commit
4bd0ea77ab
@ -10,6 +10,9 @@ from apps.authentication.models import UserServerLink
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# This DynamicModelSerializer is from a StackOverflow user in an obscure thread.
|
||||
# I wish that I could remember which thread, because god bless that man.
|
||||
|
||||
class DynamicModelSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
For use with GET requests, to specify which fields to include or exclude
|
||||
@ -127,7 +130,7 @@ class UserServerLinkSerializer(DynamicModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = UserServerLink
|
||||
fields = ("id", "server_id", "user", "name", "icon", "icon_url", "permissions")
|
||||
fields = ("id", "server_id", "user", "name", "icon", "icon_url")
|
||||
|
||||
|
||||
class SavedGuildSerializer(DynamicModelSerializer):
|
||||
@ -137,4 +140,4 @@ class SavedGuildSerializer(DynamicModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = SavedGuilds
|
||||
fields = ("id", "guild_id", "name", "icon")
|
||||
fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "owner")
|
||||
|
@ -1,7 +1,10 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.utils import IntegrityError
|
||||
from django.core.exceptions import ValidationError
|
||||
from django_filters import rest_framework as rest_filters
|
||||
@ -189,28 +192,43 @@ class SavedGuild_ListView(generics.ListCreateAPIView):
|
||||
serializer_class = SavedGuildSerializer
|
||||
|
||||
filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
||||
filterset_fields = ["id", "guild_id", "name", "icon"]
|
||||
filterset_fields = ["id", "guild_id", "name", "icon", "added_by", "permissions", "owner"]
|
||||
search_fields = ["name"]
|
||||
|
||||
def get_queryset(self):
|
||||
return SavedGuilds.objects.all()
|
||||
return SavedGuilds.objects.filter(added_by=self.request.user)
|
||||
|
||||
def post(self, request):
|
||||
|
||||
is_owner = request.data["owner"].lower() == "true"
|
||||
|
||||
# Check user is admin in server
|
||||
if not (self.is_server_admin(request.data["permissions"]) or is_owner):
|
||||
return Response(
|
||||
{"detail": "You must be a server administrator"},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
exception=False
|
||||
)
|
||||
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
except IntegrityError:
|
||||
except IntegrityError as err:
|
||||
return Response(
|
||||
{"detail": "SavedGuild must be unique"},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
{"detail": str(err)},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
exception=True
|
||||
)
|
||||
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
def is_server_admin(self, permissions) -> bool:
|
||||
|
||||
return (int(permissions) & 1 << 3) == 1 << 3
|
||||
|
||||
|
||||
class SavedGuild_DetailView(generics.RetrieveDestroyAPIView):
|
||||
"""
|
||||
@ -224,4 +242,6 @@ class SavedGuild_DetailView(generics.RetrieveDestroyAPIView):
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
serializer_class = SavedGuildSerializer
|
||||
queryset = SavedGuilds.objects.all()
|
||||
|
||||
def get_queryset(self):
|
||||
return SavedGuilds.objects.filter(added_by=self.request.user)
|
||||
|
@ -101,7 +101,16 @@ class GuildsView(View):
|
||||
status = response.status_code
|
||||
print(response, content, status)
|
||||
|
||||
return JsonResponse(content, safe=False, status=status)
|
||||
valid_guilds = [guild for guild in response.json() if self._has_permissions(guild)]
|
||||
|
||||
return JsonResponse(valid_guilds, safe=False, status=status)
|
||||
|
||||
def _has_permissions(self, guild):
|
||||
|
||||
permissions = guild["permissions"]
|
||||
is_owner = guild["owner"]
|
||||
|
||||
return (int(permissions) & 1 << 3) == 1 << 3 or is_owner
|
||||
|
||||
|
||||
class GuildChannelsView(View):
|
||||
|
22
apps/home/migrations/0011_savedguilds_added_by.py
Normal file
22
apps/home/migrations/0011_savedguilds_added_by.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.0.1 on 2024-04-16 10:35
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0010_alter_savedguilds_guild_id'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='savedguilds',
|
||||
name='added_by',
|
||||
field=models.ForeignKey(default='1', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
19
apps/home/migrations/0012_savedguilds_permissions.py
Normal file
19
apps/home/migrations/0012_savedguilds_permissions.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0.1 on 2024-04-16 14:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0011_savedguilds_added_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='savedguilds',
|
||||
name='permissions',
|
||||
field=models.CharField(default='1', max_length=64),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
19
apps/home/migrations/0013_savedguilds_owner.py
Normal file
19
apps/home/migrations/0013_savedguilds_owner.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0.1 on 2024-04-16 14:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0012_savedguilds_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='savedguilds',
|
||||
name='owner',
|
||||
field=models.BooleanField(default=True),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -61,6 +61,16 @@ class SavedGuilds(models.Model):
|
||||
max_length=128
|
||||
)
|
||||
|
||||
added_by = models.ForeignKey(to="authentication.DiscordUser", on_delete=models.CASCADE)
|
||||
|
||||
permissions = models.CharField(
|
||||
max_length=64
|
||||
)
|
||||
|
||||
owner = models.BooleanField(
|
||||
default=False
|
||||
)
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
"""
|
||||
|
@ -20,7 +20,7 @@ function addToLoadedServers(server, selectNew=true) {
|
||||
loadedServers[id] = server;
|
||||
|
||||
// Display the loaded server
|
||||
addServerTemplate(id, server.guild_id, server.name, server.icon);
|
||||
addServerTemplate(id, server.guild_id, server.name, server.icon, server.permissions, server.owner);
|
||||
|
||||
// Select the newly added server
|
||||
if (selectNew) {
|
||||
@ -70,6 +70,8 @@ async function loadServerOptions() {
|
||||
value: server.id,
|
||||
text: server.name,
|
||||
"data-icon": server.icon,
|
||||
"data-permissions": server.permissions,
|
||||
"data-isowner": server.owner
|
||||
}));
|
||||
});
|
||||
}
|
||||
@ -103,13 +105,15 @@ async function loadSavedGuilds() {
|
||||
}
|
||||
|
||||
// Create an element for the added server and show it
|
||||
function addServerTemplate(serverPrimaryKey, serverGuildId, serverName, serverIconHash) {
|
||||
function addServerTemplate(serverPrimaryKey, serverGuildId, serverName, serverIconHash, serverPermissions, serverIsOwner) {
|
||||
template = $($("#serverItemTemplate").html());
|
||||
|
||||
template.find("img").attr("src", `https://cdn.discordapp.com/icons/${serverGuildId}/${serverIconHash}.webp?size=80`);
|
||||
template.attr("data-guild-id", serverGuildId);
|
||||
template.attr("data-name", serverName);
|
||||
template.attr("data-icon", serverIconHash);
|
||||
template.attr("data-permissions", serverPermissions);
|
||||
template.attr("data-isowner", serverIsOwner);
|
||||
template.attr("data-id", serverPrimaryKey);
|
||||
|
||||
// Bind the button for selecting this server
|
||||
@ -138,15 +142,19 @@ $("#serverForm").on("submit", async function(event) {
|
||||
serverName = selectedOption.text();
|
||||
serverGuildId = selectedOption.val();
|
||||
serverIconHash = selectedOption.attr("data-icon");
|
||||
serverPermissions = selectedOption.attr("data-permissions");
|
||||
serverIsOwner = selectedOption.attr("data-isowner");
|
||||
|
||||
var serverPrimaryKey = await registerNewServer(serverName, serverGuildId, serverIconHash);
|
||||
var serverPrimaryKey = await registerNewServer(serverName, serverGuildId, serverIconHash, serverPermissions, serverIsOwner);
|
||||
|
||||
if (serverPrimaryKey !== false) {
|
||||
addToLoadedServers({
|
||||
id: serverPrimaryKey,
|
||||
name: serverName,
|
||||
guild_id: serverGuildId,
|
||||
icon: serverIconHash
|
||||
icon: serverIconHash,
|
||||
permissions: serverPermissions,
|
||||
owner: serverIsOwner
|
||||
});
|
||||
}
|
||||
|
||||
@ -155,11 +163,14 @@ $("#serverForm").on("submit", async function(event) {
|
||||
|
||||
// Add a new 'saved guild' based on the info provided
|
||||
// returns `response.id` if successful, else false
|
||||
async function registerNewServer(serverName, serverGuildId, serverIconHash) {
|
||||
async function registerNewServer(serverName, serverGuildId, serverIconHash, serverPermissions, serverIsOwner) {
|
||||
var formData = new FormData();
|
||||
formData.append("name", serverName);
|
||||
formData.append("guild_id", serverGuildId);
|
||||
formData.append("icon", serverIconHash);
|
||||
formData.append("added_by", currentUserId);
|
||||
formData.append("permissions", serverPermissions);
|
||||
formData.append("owner", serverIsOwner === "true");
|
||||
|
||||
try { response = await newSavedGuild(formData); }
|
||||
catch (err) { return false }
|
||||
|
@ -21,6 +21,9 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 form-text">
|
||||
You must be an administrator, or own the selected server.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
|
@ -80,6 +80,7 @@
|
||||
|
||||
<script>
|
||||
const CSRF_MiddlewareToken = "{{ csrf_token }}";
|
||||
const currentUserId = "{{ request.user.id }}";
|
||||
</script>
|
||||
|
||||
{% include 'includes/scripts.html' %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user