mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-10 00:56:19 +01:00
Add blacklist for groups and ignore blacklisted roles in Discord service
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
from django import forms
|
||||
from django.apps import apps
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group as BaseGroup, User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Count
|
||||
from django.db.models.functions import Lower
|
||||
from django.db.models.signals import pre_save, post_save, pre_delete, \
|
||||
post_delete, m2m_changed
|
||||
from django.dispatch import receiver
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import AuthGroup
|
||||
from .models import AuthGroup, ReservedGroupName
|
||||
from .models import GroupRequest
|
||||
|
||||
if 'eve_autogroups' in apps.app_configs:
|
||||
@@ -70,8 +74,7 @@ if _has_auto_groups:
|
||||
managedalliancegroup__isnull=True,
|
||||
managedcorpgroup__isnull=True
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
return queryset
|
||||
|
||||
|
||||
class HasLeaderFilter(admin.SimpleListFilter):
|
||||
@@ -90,11 +93,22 @@ class HasLeaderFilter(admin.SimpleListFilter):
|
||||
return queryset.filter(authgroup__group_leaders__isnull=False)
|
||||
elif value == 'no':
|
||||
return queryset.filter(authgroup__group_leaders__isnull=True)
|
||||
else:
|
||||
return queryset
|
||||
return queryset
|
||||
|
||||
|
||||
class GroupAdminForm(forms.ModelForm):
|
||||
def clean_name(self):
|
||||
my_name = self.cleaned_data['name']
|
||||
if ReservedGroupName.objects.filter(name__iexact=my_name).exists():
|
||||
raise ValidationError(
|
||||
_("This name has been reserved and can not be used for groups."),
|
||||
code='reserved_name'
|
||||
)
|
||||
return my_name
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
form = GroupAdminForm
|
||||
list_select_related = ('authgroup',)
|
||||
ordering = ('name',)
|
||||
list_display = (
|
||||
@@ -209,6 +223,41 @@ class GroupRequestAdmin(admin.ModelAdmin):
|
||||
_leave_request.boolean = True
|
||||
|
||||
|
||||
class ReservedGroupNameAdminForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['created_by'].initial = self.current_user.username
|
||||
self.fields['created_at'].initial = _("(auto)")
|
||||
|
||||
created_by = forms.CharField(disabled=True)
|
||||
created_at = forms.CharField(disabled=True)
|
||||
|
||||
def clean_name(self):
|
||||
my_name = self.cleaned_data['name'].lower()
|
||||
if Group.objects.filter(name__iexact=my_name).exists():
|
||||
raise ValidationError(
|
||||
_("There already exists a group with that name."), code='already_exists'
|
||||
)
|
||||
return my_name
|
||||
|
||||
def clean_created_at(self):
|
||||
return now()
|
||||
|
||||
|
||||
@admin.register(ReservedGroupName)
|
||||
class ReservedGroupNameAdmin(admin.ModelAdmin):
|
||||
form = ReservedGroupNameAdminForm
|
||||
list_display = ("name", "created_by", "created_at")
|
||||
|
||||
def get_form(self, request, *args, **kwargs):
|
||||
form = super().get_form(request, *args, **kwargs)
|
||||
form.current_user = request.user
|
||||
return form
|
||||
|
||||
def has_change_permission(self, *args, **kwargs) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Group)
|
||||
def redirect_pre_save(sender, signal=None, *args, **kwargs):
|
||||
pre_save.send(BaseGroup, *args, **kwargs)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.2.9 on 2021-11-25 18:38
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('groupmanagement', '0017_improve_groups_documentation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ReservedGroupName',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Name that can not be used for groups.', max_length=150, unique=True, verbose_name='name')),
|
||||
('reason', models.TextField(help_text='Reason why this name is reserved.', verbose_name='reason')),
|
||||
('created_by', models.CharField(help_text='Name of the user who created this entry.', max_length=255, verbose_name='created by')),
|
||||
('created_at', models.DateTimeField(default=django.utils.timezone.now, help_text='Date when this entry was created', verbose_name='created at')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -4,8 +4,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from allianceauth.authentication.models import State
|
||||
@@ -181,18 +180,35 @@ class AuthGroup(models.Model):
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Group)
|
||||
def create_auth_group(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Creates the AuthGroup model when a group is created
|
||||
"""
|
||||
if created:
|
||||
AuthGroup.objects.create(group=instance)
|
||||
class ReservedGroupName(models.Model):
|
||||
"""Name that can not be used for groups.
|
||||
|
||||
This enables AA to ignore groups on other services (e.g. Discord) with that name.
|
||||
"""
|
||||
name = models.CharField(
|
||||
_('name'),
|
||||
max_length=150,
|
||||
unique=True,
|
||||
help_text=_("Name that can not be used for groups.")
|
||||
)
|
||||
reason = models.TextField(
|
||||
_('reason'), help_text=_("Reason why this name is reserved.")
|
||||
)
|
||||
created_by = models.CharField(
|
||||
_('created by'),
|
||||
max_length=255,
|
||||
help_text="Name of the user who created this entry."
|
||||
)
|
||||
created_at = models.DateTimeField(
|
||||
_('created at'), default=now, help_text=_("Date when this entry was created")
|
||||
)
|
||||
|
||||
@receiver(post_save, sender=Group)
|
||||
def save_auth_group(sender, instance, **kwargs):
|
||||
"""
|
||||
Ensures AuthGroup model is saved automatically
|
||||
"""
|
||||
instance.authgroup.save()
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
if Group.objects.filter(name__iexact=self.name).exists():
|
||||
raise RuntimeError(
|
||||
f"Save failed. There already exists a group with the name: {self.name}."
|
||||
)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
import logging
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from allianceauth.authentication.signals import state_changed
|
||||
|
||||
from .models import AuthGroup, ReservedGroupName
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Group)
|
||||
def find_new_name_for_conflicting_groups(sender, instance, **kwargs):
|
||||
"""Find new name for a group which name is already reserved."""
|
||||
new_name = instance.name
|
||||
num = 0
|
||||
while ReservedGroupName.objects.filter(name__iexact=new_name).exists():
|
||||
num += 1
|
||||
new_name = f"{instance.name}_{num}"
|
||||
instance.name = new_name
|
||||
|
||||
|
||||
@receiver(post_save, sender=Group)
|
||||
def create_auth_group(sender, instance, created, **kwargs):
|
||||
"""Create the AuthGroup model when a group is created."""
|
||||
if created:
|
||||
AuthGroup.objects.create(group=instance)
|
||||
|
||||
|
||||
@receiver(state_changed)
|
||||
def check_groups_on_state_change(sender, user, state, **kwargs):
|
||||
logger.debug(
|
||||
|
||||
@@ -10,9 +10,10 @@ from allianceauth.authentication.models import CharacterOwnership, State
|
||||
from allianceauth.eveonline.models import (
|
||||
EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
)
|
||||
|
||||
from ..admin import HasLeaderFilter, GroupAdmin, Group
|
||||
from . import get_admin_change_view_url
|
||||
from ..models import ReservedGroupName
|
||||
|
||||
|
||||
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
|
||||
_has_auto_groups = True
|
||||
@@ -396,3 +397,108 @@ class TestGroupAdmin(TestCase):
|
||||
c.login(username='superuser', password='secret')
|
||||
response = c.get(get_admin_change_view_url(self.group_1))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_should_create_new_group(self):
|
||||
# given
|
||||
user = User.objects.create_superuser("bruce")
|
||||
self.client.force_login(user)
|
||||
# when
|
||||
response = self.client.post(
|
||||
"/admin/groupmanagement/group/add/",
|
||||
data={
|
||||
"name": "new group",
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 0,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/groupmanagement/group/")
|
||||
self.assertTrue(Group.objects.filter(name="new group").exists())
|
||||
|
||||
def test_should_not_allow_creating_new_group_with_reserved_name(self):
|
||||
# given
|
||||
ReservedGroupName.objects.create(
|
||||
name="new group", reason="dummy", created_by="bruce"
|
||||
)
|
||||
user = User.objects.create_superuser("bruce")
|
||||
self.client.force_login(user)
|
||||
# when
|
||||
response = self.client.post(
|
||||
"/admin/groupmanagement/group/add/",
|
||||
data={
|
||||
"name": "New group",
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 0,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertContains(
|
||||
response, "This name has been reserved and can not be used for groups"
|
||||
)
|
||||
self.assertFalse(Group.objects.filter(name="new group").exists())
|
||||
|
||||
def test_should_not_allow_changing_name_of_existing_group_to_reserved_name(self):
|
||||
# given
|
||||
ReservedGroupName.objects.create(
|
||||
name="new group", reason="dummy", created_by="bruce"
|
||||
)
|
||||
group = Group.objects.create(name="dummy")
|
||||
user = User.objects.create_superuser("bruce")
|
||||
self.client.force_login(user)
|
||||
# when
|
||||
response = self.client.post(
|
||||
f"/admin/groupmanagement/group/{group.pk}/change/",
|
||||
data={
|
||||
"name": "new group",
|
||||
"authgroup-TOTAL_FORMS": 1,
|
||||
"authgroup-INITIAL_FORMS": 0,
|
||||
"authgroup-MIN_NUM_FORMS": 0,
|
||||
"authgroup-MAX_NUM_FORMS": 1,
|
||||
}
|
||||
)
|
||||
# then
|
||||
self.assertContains(
|
||||
response, "This name has been reserved and can not be used for groups"
|
||||
)
|
||||
self.assertFalse(Group.objects.filter(name="new group").exists())
|
||||
|
||||
|
||||
class TestReservedGroupNameAdmin(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = User.objects.create_superuser("bruce")
|
||||
|
||||
def test_should_create_new_entry(self):
|
||||
# given
|
||||
self.client.force_login(self.user)
|
||||
# when
|
||||
response = self.client.post(
|
||||
"/admin/groupmanagement/reservedgroupname/add/",
|
||||
data={"name": "Test", "reason": "dummy"}
|
||||
)
|
||||
# then
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/groupmanagement/reservedgroupname/")
|
||||
obj = ReservedGroupName.objects.get(name="test")
|
||||
self.assertEqual(obj.name, "test")
|
||||
self.assertEqual(obj.created_by, self.user.username)
|
||||
self.assertTrue(obj.created_at)
|
||||
|
||||
def test_should_not_allow_names_of_existing_groups(self):
|
||||
# given
|
||||
Group.objects.create(name="Already taken")
|
||||
self.client.force_login(self.user)
|
||||
# when
|
||||
response = self.client.post(
|
||||
"/admin/groupmanagement/reservedgroupname/add/",
|
||||
data={"name": "already taken", "reason": "dummy"}
|
||||
)
|
||||
# then
|
||||
self.assertContains(response, "There already exists a group with that name")
|
||||
self.assertFalse(ReservedGroupName.objects.filter(name="already taken").exists())
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.test import TestCase, override_settings
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from ..models import GroupRequest, RequestLog
|
||||
from ..models import GroupRequest, RequestLog, ReservedGroupName
|
||||
|
||||
MODULE_PATH = "allianceauth.groupmanagement.models"
|
||||
|
||||
@@ -284,3 +284,22 @@ class TestAuthGroupRequestApprovers(TestCase):
|
||||
leaders = child_group.authgroup.group_request_approvers()
|
||||
# then
|
||||
self.assertSetEqual(leaders, set())
|
||||
|
||||
|
||||
class TestReservedGroupName(TestCase):
|
||||
def test_should_return_name(self):
|
||||
# given
|
||||
obj = ReservedGroupName(name="test", reason="abc", created_by="xxx")
|
||||
# when
|
||||
result = str(obj)
|
||||
# then
|
||||
self.assertEqual(result, "test")
|
||||
|
||||
def test_should_not_allow_creating_reserved_name_for_existing_group(self):
|
||||
# given
|
||||
Group.objects.create(name="Dummy")
|
||||
# when
|
||||
with self.assertRaises(RuntimeError):
|
||||
ReservedGroupName.objects.create(
|
||||
name="dummy", reason="abc", created_by="xxx"
|
||||
)
|
||||
|
||||
@@ -6,6 +6,27 @@ from allianceauth.eveonline.autogroups.models import AutogroupsConfig
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
|
||||
from ..models import ReservedGroupName
|
||||
|
||||
|
||||
class TestGroupSignals(TestCase):
|
||||
def test_should_create_authgroup_when_group_is_created(self):
|
||||
# when
|
||||
group = Group.objects.create(name="test")
|
||||
# then
|
||||
self.assertEqual(group.authgroup.group, group)
|
||||
|
||||
def test_should_rename_group_that_conflicts_with_reserved_name(self):
|
||||
# given
|
||||
ReservedGroupName.objects.create(name="test", reason="dummy", created_by="xyz")
|
||||
ReservedGroupName.objects.create(name="test_1", reason="dummy", created_by="xyz")
|
||||
# when
|
||||
group = Group.objects.create(name="Test")
|
||||
# then
|
||||
self.assertNotEqual(group.name, "test")
|
||||
self.assertNotEqual(group.name, "test_1")
|
||||
|
||||
|
||||
class TestCheckGroupsOnStateChange(TestCase):
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from django.test import RequestFactory, TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
from esi.models import Token
|
||||
|
||||
from .. import views
|
||||
|
||||
|
||||
Reference in New Issue
Block a user