aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/migrations/0007_tag.py2
-rw-r--r--pydis_site/apps/api/migrations/0009_snakefact.py2
-rw-r--r--pydis_site/apps/api/migrations/0010_snakeidiom.py2
-rw-r--r--pydis_site/apps/api/migrations/0012_specialsnake.py2
-rw-r--r--pydis_site/apps/api/migrations/0018_messagedeletioncontext.py2
-rw-r--r--pydis_site/apps/api/migrations/0019_deletedmessage.py2
-rw-r--r--pydis_site/apps/api/migrations/0020_infraction.py2
-rw-r--r--pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py4
-rw-r--r--pydis_site/apps/api/migrations/0030_reminder.py2
-rw-r--r--pydis_site/apps/api/migrations/0031_nomination.py2
-rw-r--r--pydis_site/apps/api/migrations/0032_botsetting.py2
-rw-r--r--pydis_site/apps/api/migrations/0035_create_table_log_entry.py2
-rw-r--r--pydis_site/apps/api/migrations/0049_offensivemessage.py4
-rw-r--r--pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py33
-rw-r--r--pydis_site/apps/api/migrations/0059_populate_filterlists.py153
-rw-r--r--pydis_site/apps/api/models/__init__.py2
-rw-r--r--pydis_site/apps/api/models/bot/__init__.py1
-rw-r--r--pydis_site/apps/api/models/bot/bot_setting.py2
-rw-r--r--pydis_site/apps/api/models/bot/documentation_link.py2
-rw-r--r--pydis_site/apps/api/models/bot/filter_list.py41
-rw-r--r--pydis_site/apps/api/models/bot/infraction.py2
-rw-r--r--pydis_site/apps/api/models/bot/message.py2
-rw-r--r--pydis_site/apps/api/models/bot/message_deletion_context.py2
-rw-r--r--pydis_site/apps/api/models/bot/nomination.py2
-rw-r--r--pydis_site/apps/api/models/bot/off_topic_channel_name.py2
-rw-r--r--pydis_site/apps/api/models/bot/offensive_message.py2
-rw-r--r--pydis_site/apps/api/models/bot/reminder.py2
-rw-r--r--pydis_site/apps/api/models/bot/role.py2
-rw-r--r--pydis_site/apps/api/models/bot/tag.py2
-rw-r--r--pydis_site/apps/api/models/bot/user.py2
-rw-r--r--pydis_site/apps/api/models/log_entry.py2
-rw-r--r--pydis_site/apps/api/models/mixins.py (renamed from pydis_site/apps/api/models/utils.py)14
-rw-r--r--pydis_site/apps/api/serializers.py44
-rw-r--r--pydis_site/apps/api/tests/test_filterlists.py122
-rw-r--r--pydis_site/apps/api/tests/test_models.py4
-rw-r--r--pydis_site/apps/api/urls.py22
-rw-r--r--pydis_site/apps/api/viewsets/__init__.py1
-rw-r--r--pydis_site/apps/api/viewsets/bot/__init__.py1
-rw-r--r--pydis_site/apps/api/viewsets/bot/filter_list.py97
-rw-r--r--pydis_site/tests/__init__.py0
-rw-r--r--pydis_site/tests/test_utils_account.py81
41 files changed, 591 insertions, 81 deletions
diff --git a/pydis_site/apps/api/migrations/0007_tag.py b/pydis_site/apps/api/migrations/0007_tag.py
index c22715f9..b6d146fe 100644
--- a/pydis_site/apps/api/migrations/0007_tag.py
+++ b/pydis_site/apps/api/migrations/0007_tag.py
@@ -18,6 +18,6 @@ class Migration(migrations.Migration):
('title', models.CharField(help_text='The title of this tag, shown in searches and providing a quick overview over what this embed contains.', max_length=100, primary_key=True, serialize=False)),
('embed', django.contrib.postgres.fields.jsonb.JSONField(help_text='The actual embed shown by this tag.')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0009_snakefact.py b/pydis_site/apps/api/migrations/0009_snakefact.py
index 4fc63bc9..fd583846 100644
--- a/pydis_site/apps/api/migrations/0009_snakefact.py
+++ b/pydis_site/apps/api/migrations/0009_snakefact.py
@@ -16,6 +16,6 @@ class Migration(migrations.Migration):
fields=[
('fact', models.CharField(help_text='A fact about snakes.', max_length=200, primary_key=True, serialize=False)),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0010_snakeidiom.py b/pydis_site/apps/api/migrations/0010_snakeidiom.py
index be089cf4..7d06ce5f 100644
--- a/pydis_site/apps/api/migrations/0010_snakeidiom.py
+++ b/pydis_site/apps/api/migrations/0010_snakeidiom.py
@@ -16,6 +16,6 @@ class Migration(migrations.Migration):
fields=[
('idiom', models.CharField(help_text='A snake idiom', max_length=140, primary_key=True, serialize=False)),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0012_specialsnake.py b/pydis_site/apps/api/migrations/0012_specialsnake.py
index 77072526..ed0c1563 100644
--- a/pydis_site/apps/api/migrations/0012_specialsnake.py
+++ b/pydis_site/apps/api/migrations/0012_specialsnake.py
@@ -17,6 +17,6 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=140, primary_key=True, serialize=False)),
('info', models.TextField()),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py b/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py
index dced1288..7e372d04 100644
--- a/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py
+++ b/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py
@@ -19,6 +19,6 @@ class Migration(migrations.Migration):
('creation', models.DateTimeField(help_text='When this deletion took place.')),
('actor', models.ForeignKey(help_text='The original actor causing this deletion. Could be the author of a manual clean command invocation, the bot when executing automatic actions, or nothing to indicate that the bulk deletion was not issued by us.', null=True, on_delete=django.db.models.deletion.CASCADE, to='api.User')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0019_deletedmessage.py b/pydis_site/apps/api/migrations/0019_deletedmessage.py
index 4b028f0c..33746253 100644
--- a/pydis_site/apps/api/migrations/0019_deletedmessage.py
+++ b/pydis_site/apps/api/migrations/0019_deletedmessage.py
@@ -25,6 +25,6 @@ class Migration(migrations.Migration):
options={
'abstract': False,
},
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0020_infraction.py b/pydis_site/apps/api/migrations/0020_infraction.py
index 6bef6b77..96c71687 100644
--- a/pydis_site/apps/api/migrations/0020_infraction.py
+++ b/pydis_site/apps/api/migrations/0020_infraction.py
@@ -25,6 +25,6 @@ class Migration(migrations.Migration):
('actor', models.ForeignKey(help_text='The user which applied the infraction.', on_delete=django.db.models.deletion.CASCADE, related_name='infractions_given', to='api.User')),
('user', models.ForeignKey(help_text='The user to which the infraction was applied.', on_delete=django.db.models.deletion.CASCADE, related_name='infractions_received', to='api.User')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py
index 0c02cb91..c7fac012 100644
--- a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py
+++ b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py
@@ -1,7 +1,7 @@
# Generated by Django 2.1.4 on 2019-01-06 16:01
-import datetime
from django.db import migrations, models
+from django.utils import timezone
class Migration(migrations.Migration):
@@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='infraction',
name='inserted_at',
- field=models.DateTimeField(default=datetime.datetime.utcnow, help_text='The date and time of the creation of this infraction.'),
+ field=models.DateTimeField(default=timezone.now, help_text='The date and time of the creation of this infraction.'),
),
]
diff --git a/pydis_site/apps/api/migrations/0030_reminder.py b/pydis_site/apps/api/migrations/0030_reminder.py
index 8c42f6dc..e1f1afc3 100644
--- a/pydis_site/apps/api/migrations/0030_reminder.py
+++ b/pydis_site/apps/api/migrations/0030_reminder.py
@@ -22,6 +22,6 @@ class Migration(migrations.Migration):
('expiration', models.DateTimeField(help_text='When this reminder should be sent.')),
('author', models.ForeignKey(help_text='The creator of this reminder.', on_delete=django.db.models.deletion.CASCADE, to='api.User')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0031_nomination.py b/pydis_site/apps/api/migrations/0031_nomination.py
index 75e69701..f39436c1 100644
--- a/pydis_site/apps/api/migrations/0031_nomination.py
+++ b/pydis_site/apps/api/migrations/0031_nomination.py
@@ -21,6 +21,6 @@ class Migration(migrations.Migration):
('inserted_at', models.DateTimeField(auto_now_add=True, help_text='The creation date of this nomination.')),
('author', models.ForeignKey(help_text='The staff member that nominated this user.', on_delete=django.db.models.deletion.CASCADE, related_name='nomination_set', to='api.User')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0032_botsetting.py b/pydis_site/apps/api/migrations/0032_botsetting.py
index 25186a2b..3304edef 100644
--- a/pydis_site/apps/api/migrations/0032_botsetting.py
+++ b/pydis_site/apps/api/migrations/0032_botsetting.py
@@ -18,6 +18,6 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=50, primary_key=True, serialize=False)),
('data', django.contrib.postgres.fields.jsonb.JSONField(help_text='The actual settings of this setting.')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0035_create_table_log_entry.py b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py
index a8256a0e..c9a1ad19 100644
--- a/pydis_site/apps/api/migrations/0035_create_table_log_entry.py
+++ b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py
@@ -24,6 +24,6 @@ class Migration(migrations.Migration):
('line', models.PositiveSmallIntegerField(help_text='The line at which the log line was emitted.')),
('message', models.TextField(help_text='The textual content of the log line.')),
],
- bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0049_offensivemessage.py b/pydis_site/apps/api/migrations/0049_offensivemessage.py
index fe4a1961..f342cec3 100644
--- a/pydis_site/apps/api/migrations/0049_offensivemessage.py
+++ b/pydis_site/apps/api/migrations/0049_offensivemessage.py
@@ -3,7 +3,7 @@
import django.core.validators
from django.db import migrations, models
import pydis_site.apps.api.models.bot.offensive_message
-import pydis_site.apps.api.models.utils
+import pydis_site.apps.api.models.mixins
class Migration(migrations.Migration):
@@ -20,6 +20,6 @@ class Migration(migrations.Migration):
('channel_id', models.BigIntegerField(help_text='The channel ID that the message was sent in, taken from Discord.', validators=[django.core.validators.MinValueValidator(limit_value=0, message='Channel IDs cannot be negative.')])),
('delete_date', models.DateTimeField(help_text='The date on which the message will be auto-deleted.', validators=[pydis_site.apps.api.models.bot.offensive_message.future_date_validator])),
],
- bases=(pydis_site.apps.api.models.utils.ModelReprMixin, models.Model),
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
),
]
diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py
new file mode 100644
index 00000000..aecfdad7
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.0.8 on 2020-07-15 11:23
+
+from django.db import migrations, models
+import pydis_site.apps.api.models.mixins
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('api', '0057_merge_20200716_0751'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='FilterList',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ('type', models.CharField(
+ choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'),
+ ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')],
+ help_text='The type of allowlist this is on.', max_length=50)),
+ ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')),
+ ('content', models.TextField(help_text='The data to add to the allow or denylist.')),
+ ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)),
+ ],
+ bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model),
+ ),
+ migrations.AddConstraint(
+ model_name='filterlist',
+ constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list')
+ )
+ ]
diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py
new file mode 100644
index 00000000..8c550191
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py
@@ -0,0 +1,153 @@
+from django.db import migrations
+
+guild_invite_whitelist = [
+ ("discord.gg/python", "Python Discord", True),
+ ("discord.gg/4JJdJKb", "RLBot", True),
+ ("discord.gg/djPtTRJ", "Kivy", True),
+ ("discord.gg/QXyegWe", "Pyglet", True),
+ ("discord.gg/9XsucTT", "Panda3D", True),
+ ("discord.gg/AP3rq2k", "PyWeek", True),
+ ("discord.gg/vSPsP9t", "Microsoft Python", True),
+ ("discord.gg/bRCvFy9", "Discord.js Official", True),
+ ("discord.gg/9zT7NHP", "Programming Discussions", True),
+ ("discord.gg/ysd6M4r", "JetBrains Community", True),
+ ("discord.gg/4xJeCgy", "Raspberry Pie", True),
+ ("discord.gg/AStb3kZ", "Ren'Py", True),
+ ("discord.gg/t655QNV", "Python Discord: Emojis 1", True),
+ ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True),
+ ("discord.gg/jTtgWuy", "Django", True),
+ ("discord.gg/W9BypZF", "STEM", True),
+ ("discord.gg/dpy", "discord.py", True),
+ ("discord.gg/programming", "Programmers Hangout", True),
+ ("discord.gg/qhGUjGD", "SpeakJS", True),
+ ("discord.gg/eTbWSZj", "Functional Programming", True),
+ ("discord.gg/r8yreB6", "PyGame", True),
+ ("discord.gg/5UBnR3P", "Python Atlanta", True),
+ ("discord.gg/ccyrDKv", "C#", True),
+]
+
+domain_name_blacklist = [
+ ("pornhub.com", None, False),
+ ("liveleak.com", None, False),
+ ("grabify.link", None, False),
+ ("bmwforum.co", None, False),
+ ("leancoding.co", None, False),
+ ("spottyfly.com", None, False),
+ ("stopify.co", None, False),
+ ("yoütu.be", None, False),
+ ("discörd.com", None, False),
+ ("minecräft.com", None, False),
+ ("freegiftcards.co", None, False),
+ ("disçordapp.com", None, False),
+ ("fortnight.space", None, False),
+ ("fortnitechat.site", None, False),
+ ("joinmy.site", None, False),
+ ("curiouscat.club", None, False),
+ ("catsnthings.fun", None, False),
+ ("yourtube.site", None, False),
+ ("youtubeshort.watch", None, False),
+ ("catsnthing.com", None, False),
+ ("youtubeshort.pro", None, False),
+ ("canadianlumberjacks.online", None, False),
+ ("poweredbydialup.club", None, False),
+ ("poweredbydialup.online", None, False),
+ ("poweredbysecurity.org", None, False),
+ ("poweredbysecurity.online", None, False),
+ ("ssteam.site", None, False),
+ ("steamwalletgift.com", None, False),
+ ("discord.gift", None, False),
+ ("lmgtfy.com", None, False),
+]
+
+filter_token_blacklist = [
+ ("\bgoo+ks*\b", None, False),
+ ("\bky+s+\b", None, False),
+ ("\bki+ke+s*\b", None, False),
+ ("\bbeaner+s?\b", None, False),
+ ("\bcoo+ns*\b", None, False),
+ ("\bnig+lets*\b", None, False),
+ ("\bslant-eyes*\b", None, False),
+ ("\btowe?l-?head+s*\b", None, False),
+ ("\bchi*n+k+s*\b", None, False),
+ ("\bspick*s*\b", None, False),
+ ("\bkill* +(?:yo)?urself+\b", None, False),
+ ("\bjew+s*\b", None, False),
+ ("\bsuicide\b", None, False),
+ ("\brape\b", None, False),
+ ("\b(re+)tar+(d+|t+)(ed)?\b", None, False),
+ ("\bta+r+d+\b", None, False),
+ ("\bcunts*\b", None, False),
+ ("\btrann*y\b", None, False),
+ ("\bshemale\b", None, False),
+ ("fa+g+s*", None, False),
+ ("卐", None, False),
+ ("卍", None, False),
+ ("࿖", None, False),
+ ("࿕", None, False),
+ ("࿘", None, False),
+ ("࿗", None, False),
+ ("cuck(?!oo+)", None, False),
+ ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False),
+ ("fag+o+t+s*", None, False),
+]
+
+file_format_whitelist = [
+ (".3gp", None, True),
+ (".3g2", None, True),
+ (".avi", None, True),
+ (".bmp", None, True),
+ (".gif", None, True),
+ (".h264", None, True),
+ (".jpg", None, True),
+ (".jpeg", None, True),
+ (".m4v", None, True),
+ (".mkv", None, True),
+ (".mov", None, True),
+ (".mp4", None, True),
+ (".mpeg", None, True),
+ (".mpg", None, True),
+ (".png", None, True),
+ (".tiff", None, True),
+ (".wmv", None, True),
+ (".svg", None, True),
+ (".psd", "Photoshop", True),
+ (".ai", "Illustrator", True),
+ (".aep", "After Effects", True),
+ (".xcf", "GIMP", True),
+ (".mp3", None, True),
+ (".wav", None, True),
+ (".ogg", None, True),
+ (".webm", None, True),
+ (".webp", None, True),
+]
+
+populate_data = {
+ "FILTER_TOKEN": filter_token_blacklist,
+ "DOMAIN_NAME": domain_name_blacklist,
+ "FILE_FORMAT": file_format_whitelist,
+ "GUILD_INVITE": guild_invite_whitelist,
+}
+
+
+class Migration(migrations.Migration):
+ dependencies = [("api", "0058_create_new_filterlist_model")]
+
+ def populate_filterlists(app, _):
+ FilterList = app.get_model("api", "FilterList")
+
+ for filterlist_type, metadata in populate_data.items():
+ for content, comment, allowed in metadata:
+ FilterList.objects.create(
+ type=filterlist_type,
+ allowed=allowed,
+ content=content,
+ comment=comment,
+ )
+
+ def clear_filterlists(app, _):
+ FilterList = app.get_model("api", "FilterList")
+ FilterList.objects.all().delete()
+
+ operations = [
+ migrations.RunPython(populate_filterlists, clear_filterlists)
+ ]
diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py
index 450d18cd..1d0ab7ea 100644
--- a/pydis_site/apps/api/models/__init__.py
+++ b/pydis_site/apps/api/models/__init__.py
@@ -1,5 +1,6 @@
# flake8: noqa
from .bot import (
+ FilterList,
BotSetting,
DocumentationLink,
DeletedMessage,
@@ -15,4 +16,3 @@ from .bot import (
User
)
from .log_entry import LogEntry
-from .utils import ModelReprMixin
diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py
index 8ae47746..efd98184 100644
--- a/pydis_site/apps/api/models/bot/__init__.py
+++ b/pydis_site/apps/api/models/bot/__init__.py
@@ -1,4 +1,5 @@
# flake8: noqa
+from .filter_list import FilterList
from .bot_setting import BotSetting
from .deleted_message import DeletedMessage
from .documentation_link import DocumentationLink
diff --git a/pydis_site/apps/api/models/bot/bot_setting.py b/pydis_site/apps/api/models/bot/bot_setting.py
index 8d48eac7..2a3944f8 100644
--- a/pydis_site/apps/api/models/bot/bot_setting.py
+++ b/pydis_site/apps/api/models/bot/bot_setting.py
@@ -2,7 +2,7 @@ from django.contrib.postgres import fields as pgfields
from django.core.exceptions import ValidationError
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
def validate_bot_setting_name(name: str) -> None:
diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py
index f844ae04..5a46460b 100644
--- a/pydis_site/apps/api/models/bot/documentation_link.py
+++ b/pydis_site/apps/api/models/bot/documentation_link.py
@@ -1,6 +1,6 @@
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class DocumentationLink(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py
new file mode 100644
index 00000000..d279e137
--- /dev/null
+++ b/pydis_site/apps/api/models/bot/filter_list.py
@@ -0,0 +1,41 @@
+from django.db import models
+
+from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin
+
+
+class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model):
+ """An item that is either allowed or denied."""
+
+ FilterListType = models.TextChoices(
+ 'FilterListType',
+ 'GUILD_INVITE '
+ 'FILE_FORMAT '
+ 'DOMAIN_NAME '
+ 'FILTER_TOKEN '
+ )
+ type = models.CharField(
+ max_length=50,
+ help_text="The type of allowlist this is on.",
+ choices=FilterListType.choices,
+ )
+ allowed = models.BooleanField(
+ help_text="Whether this item is on the allowlist or the denylist."
+ )
+ content = models.TextField(
+ help_text="The data to add to the allow or denylist."
+ )
+ comment = models.TextField(
+ help_text="Optional comment on this entry.",
+ null=True
+ )
+
+ class Meta:
+ """Metaconfig for this model."""
+
+ # This constraint ensures only one filterlist with the
+ # same content can exist. This means that we cannot have both an allow
+ # and a deny for the same item, and we cannot have duplicates of the
+ # same item.
+ constraints = [
+ models.UniqueConstraint(fields=['content', 'type'], name='unique_filter_list'),
+ ]
diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py
index f58e89a3..7660cbba 100644
--- a/pydis_site/apps/api/models/bot/infraction.py
+++ b/pydis_site/apps/api/models/bot/infraction.py
@@ -2,7 +2,7 @@ from django.db import models
from django.utils import timezone
from pydis_site.apps.api.models.bot.user import User
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class Infraction(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py
index 0b279580..78dcbf1d 100644
--- a/pydis_site/apps/api/models/bot/message.py
+++ b/pydis_site/apps/api/models/bot/message.py
@@ -7,7 +7,7 @@ from django.utils import timezone
from pydis_site.apps.api.models.bot.tag import validate_tag_embed
from pydis_site.apps.api.models.bot.user import User
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class Message(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/message_deletion_context.py b/pydis_site/apps/api/models/bot/message_deletion_context.py
index 44a0c8ae..04ae8d34 100644
--- a/pydis_site/apps/api/models/bot/message_deletion_context.py
+++ b/pydis_site/apps/api/models/bot/message_deletion_context.py
@@ -1,7 +1,7 @@
from django.db import models
from pydis_site.apps.api.models.bot.user import User
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class MessageDeletionContext(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py
index cd9951aa..21e34e87 100644
--- a/pydis_site/apps/api/models/bot/nomination.py
+++ b/pydis_site/apps/api/models/bot/nomination.py
@@ -1,7 +1,7 @@
from django.db import models
from pydis_site.apps.api.models.bot.user import User
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class Nomination(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py
index 29280c27..20e77b9f 100644
--- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py
+++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py
@@ -1,7 +1,7 @@
from django.core.validators import RegexValidator
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class OffTopicChannelName(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py
index b466d9c2..6c0e5ffb 100644
--- a/pydis_site/apps/api/models/bot/offensive_message.py
+++ b/pydis_site/apps/api/models/bot/offensive_message.py
@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
def future_date_validator(date: datetime.date) -> None:
diff --git a/pydis_site/apps/api/models/bot/reminder.py b/pydis_site/apps/api/models/bot/reminder.py
index 4b5d15ca..7d968a0e 100644
--- a/pydis_site/apps/api/models/bot/reminder.py
+++ b/pydis_site/apps/api/models/bot/reminder.py
@@ -3,7 +3,7 @@ from django.core.validators import MinValueValidator
from django.db import models
from pydis_site.apps.api.models.bot.user import User
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class Reminder(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py
index 58bbf8b4..721e4815 100644
--- a/pydis_site/apps/api/models/bot/role.py
+++ b/pydis_site/apps/api/models/bot/role.py
@@ -3,7 +3,7 @@ from __future__ import annotations
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class Role(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py
index 5d4cc393..5e53582f 100644
--- a/pydis_site/apps/api/models/bot/tag.py
+++ b/pydis_site/apps/api/models/bot/tag.py
@@ -6,7 +6,7 @@ from django.core.exceptions import ValidationError
from django.core.validators import MaxLengthValidator, MinLengthValidator
from django.db import models
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
def is_bool_validator(value: Any) -> None:
diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py
index 0d8c574a..cd2d58b9 100644
--- a/pydis_site/apps/api/models/bot/user.py
+++ b/pydis_site/apps/api/models/bot/user.py
@@ -4,7 +4,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from pydis_site.apps.api.models.bot.role import Role
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
def _validate_existing_role(value: int) -> None:
diff --git a/pydis_site/apps/api/models/log_entry.py b/pydis_site/apps/api/models/log_entry.py
index 488af48e..752cd2ca 100644
--- a/pydis_site/apps/api/models/log_entry.py
+++ b/pydis_site/apps/api/models/log_entry.py
@@ -1,7 +1,7 @@
from django.db import models
from django.utils import timezone
-from pydis_site.apps.api.models.utils import ModelReprMixin
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class LogEntry(ModelReprMixin, models.Model):
diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/mixins.py
index 0540c4de..5d75b78b 100644
--- a/pydis_site/apps/api/models/utils.py
+++ b/pydis_site/apps/api/models/mixins.py
@@ -1,5 +1,7 @@
from operator import itemgetter
+from django.db import models
+
class ModelReprMixin:
"""Mixin providing a `__repr__()` to display model class name and initialisation parameters."""
@@ -15,3 +17,15 @@ class ModelReprMixin:
if not attribute.startswith('_')
)
return f'<{self.__class__.__name__}({attributes})>'
+
+
+class ModelTimestampMixin(models.Model):
+ """Mixin providing created_at and updated_at fields."""
+
+ created_at = models.DateTimeField(auto_now_add=True)
+ updated_at = models.DateTimeField(auto_now=True)
+
+ class Meta:
+ """Metaconfig for the mixin."""
+
+ abstract = True
diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py
index 80e552a6..52e0d972 100644
--- a/pydis_site/apps/api/serializers.py
+++ b/pydis_site/apps/api/serializers.py
@@ -4,13 +4,20 @@ from rest_framework.validators import UniqueTogetherValidator
from rest_framework_bulk import BulkSerializerMixin
from .models import (
- BotSetting, DeletedMessage,
- DocumentationLink, Infraction,
- LogEntry, MessageDeletionContext,
- Nomination, OffTopicChannelName,
+ BotSetting,
+ DeletedMessage,
+ DocumentationLink,
+ FilterList,
+ Infraction,
+ LogEntry,
+ MessageDeletionContext,
+ Nomination,
+ OffTopicChannelName,
OffensiveMessage,
- Reminder, Role,
- Tag, User
+ Reminder,
+ Role,
+ Tag,
+ User
)
@@ -97,6 +104,31 @@ class DocumentationLinkSerializer(ModelSerializer):
fields = ('package', 'base_url', 'inventory_url')
+class FilterListSerializer(ModelSerializer):
+ """A class providing (de-)serialization of `FilterList` instances."""
+
+ class Meta:
+ """Metadata defined for the Django REST Framework."""
+
+ model = FilterList
+ fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content', 'comment')
+
+ # This validator ensures only one filterlist with the
+ # same content can exist. This means that we cannot have both an allow
+ # and a deny for the same item, and we cannot have duplicates of the
+ # same item.
+ validators = [
+ UniqueTogetherValidator(
+ queryset=FilterList.objects.all(),
+ fields=['content', 'type'],
+ message=(
+ "A filterlist for this item already exists. "
+ "Please note that you cannot add the same item to both allow and deny."
+ )
+ ),
+ ]
+
+
class InfractionSerializer(ModelSerializer):
"""A class providing (de-)serialization of `Infraction` instances."""
diff --git a/pydis_site/apps/api/tests/test_filterlists.py b/pydis_site/apps/api/tests/test_filterlists.py
new file mode 100644
index 00000000..188c0fff
--- /dev/null
+++ b/pydis_site/apps/api/tests/test_filterlists.py
@@ -0,0 +1,122 @@
+from django_hosts.resolvers import reverse
+
+from pydis_site.apps.api.models import FilterList
+from pydis_site.apps.api.tests.base import APISubdomainTestCase
+
+URL = reverse('bot:filterlist-list', host='api')
+JPEG_ALLOWLIST = {
+ "type": 'FILE_FORMAT',
+ "allowed": True,
+ "content": ".jpeg",
+}
+PNG_ALLOWLIST = {
+ "type": 'FILE_FORMAT',
+ "allowed": True,
+ "content": ".png",
+}
+
+
+class UnauthenticatedTests(APISubdomainTestCase):
+ def setUp(self):
+ super().setUp()
+ self.client.force_authenticate(user=None)
+
+ def test_cannot_read_allowedlist_list(self):
+ response = self.client.get(URL)
+
+ self.assertEqual(response.status_code, 401)
+
+
+class EmptyDatabaseTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ FilterList.objects.all().delete()
+
+ def test_returns_empty_object(self):
+ response = self.client.get(URL)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json(), [])
+
+
+class FetchTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ FilterList.objects.all().delete()
+ cls.jpeg_format = FilterList.objects.create(**JPEG_ALLOWLIST)
+ cls.png_format = FilterList.objects.create(**PNG_ALLOWLIST)
+
+ def test_returns_name_in_list(self):
+ response = self.client.get(URL)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json()[0]["content"], self.jpeg_format.content)
+ self.assertEqual(response.json()[1]["content"], self.png_format.content)
+
+ def test_returns_single_item_by_id(self):
+ response = self.client.get(f'{URL}/{self.jpeg_format.id}')
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json().get("content"), self.jpeg_format.content)
+
+ def test_returns_filter_list_types(self):
+ response = self.client.get(f'{URL}/get-types')
+
+ self.assertEqual(response.status_code, 200)
+ for api_type, model_type in zip(response.json(), FilterList.FilterListType.choices):
+ self.assertEquals(api_type[0], model_type[0])
+ self.assertEquals(api_type[1], model_type[1])
+
+
+class CreationTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ FilterList.objects.all().delete()
+
+ def test_returns_400_for_missing_params(self):
+ no_type_json = {
+ "allowed": True,
+ "content": ".jpeg"
+ }
+ no_allowed_json = {
+ "type": "FILE_FORMAT",
+ "content": ".jpeg"
+ }
+ no_content_json = {
+ "allowed": True,
+ "type": "FILE_FORMAT"
+ }
+ cases = [{}, no_type_json, no_allowed_json, no_content_json]
+
+ for case in cases:
+ with self.subTest(case=case):
+ response = self.client.post(URL, data=case)
+ self.assertEqual(response.status_code, 400)
+
+ def test_returns_201_for_successful_creation(self):
+ response = self.client.post(URL, data=JPEG_ALLOWLIST)
+ self.assertEqual(response.status_code, 201)
+
+ def test_returns_400_for_duplicate_creation(self):
+ self.client.post(URL, data=JPEG_ALLOWLIST)
+ response = self.client.post(URL, data=JPEG_ALLOWLIST)
+ self.assertEqual(response.status_code, 400)
+
+
+class DeletionTests(APISubdomainTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ FilterList.objects.all().delete()
+ cls.jpeg_format = FilterList.objects.create(**JPEG_ALLOWLIST)
+ cls.png_format = FilterList.objects.create(**PNG_ALLOWLIST)
+
+ def test_deleting_unknown_id_returns_404(self):
+ response = self.client.delete(f"{URL}/200")
+ self.assertEqual(response.status_code, 404)
+
+ def test_deleting_known_id_returns_204(self):
+ response = self.client.delete(f"{URL}/{self.jpeg_format.id}")
+ self.assertEqual(response.status_code, 204)
+
+ response = self.client.get(f"{URL}/{self.jpeg_format.id}")
+ self.assertNotIn(self.png_format.content, response.json())
diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py
index b4754484..e0e347bb 100644
--- a/pydis_site/apps/api/tests/test_models.py
+++ b/pydis_site/apps/api/tests/test_models.py
@@ -3,13 +3,12 @@ from datetime import datetime as dt
from django.test import SimpleTestCase
from django.utils import timezone
-from ..models import (
+from pydis_site.apps.api.models import (
DeletedMessage,
DocumentationLink,
Infraction,
Message,
MessageDeletionContext,
- ModelReprMixin,
Nomination,
OffTopicChannelName,
OffensiveMessage,
@@ -18,6 +17,7 @@ from ..models import (
Tag,
User
)
+from pydis_site.apps.api.models.mixins import ModelReprMixin
class SimpleClass(ModelReprMixin):
diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py
index 3bb5198e..a4fd5b2e 100644
--- a/pydis_site/apps/api/urls.py
+++ b/pydis_site/apps/api/urls.py
@@ -3,18 +3,28 @@ from rest_framework.routers import DefaultRouter
from .views import HealthcheckView, RulesView
from .viewsets import (
- BotSettingViewSet, DeletedMessageViewSet,
- DocumentationLinkViewSet, InfractionViewSet,
- LogEntryViewSet, NominationViewSet,
+ BotSettingViewSet,
+ DeletedMessageViewSet,
+ DocumentationLinkViewSet,
+ FilterListViewSet,
+ InfractionViewSet,
+ LogEntryViewSet,
+ NominationViewSet,
OffTopicChannelNameViewSet,
- OffensiveMessageViewSet, ReminderViewSet,
- RoleViewSet, TagViewSet, UserViewSet
+ OffensiveMessageViewSet,
+ ReminderViewSet,
+ RoleViewSet,
+ TagViewSet,
+ UserViewSet
)
-
# http://www.django-rest-framework.org/api-guide/routers/#defaultrouter
bot_router = DefaultRouter(trailing_slash=False)
bot_router.register(
+ 'filter-lists',
+ FilterListViewSet
+)
+bot_router.register(
'bot-settings',
BotSettingViewSet
)
diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py
index 3cf9f641..8699517e 100644
--- a/pydis_site/apps/api/viewsets/__init__.py
+++ b/pydis_site/apps/api/viewsets/__init__.py
@@ -1,5 +1,6 @@
# flake8: noqa
from .bot import (
+ FilterListViewSet,
BotSettingViewSet,
DeletedMessageViewSet,
DocumentationLinkViewSet,
diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py
index b3e0fa4d..e64e3988 100644
--- a/pydis_site/apps/api/viewsets/bot/__init__.py
+++ b/pydis_site/apps/api/viewsets/bot/__init__.py
@@ -1,4 +1,5 @@
# flake8: noqa
+from .filter_list import FilterListViewSet
from .bot_setting import BotSettingViewSet
from .deleted_message import DeletedMessageViewSet
from .documentation_link import DocumentationLinkViewSet
diff --git a/pydis_site/apps/api/viewsets/bot/filter_list.py b/pydis_site/apps/api/viewsets/bot/filter_list.py
new file mode 100644
index 00000000..2cb21ab9
--- /dev/null
+++ b/pydis_site/apps/api/viewsets/bot/filter_list.py
@@ -0,0 +1,97 @@
+from rest_framework.decorators import action
+from rest_framework.request import Request
+from rest_framework.response import Response
+from rest_framework.viewsets import ModelViewSet
+
+from pydis_site.apps.api.models.bot.filter_list import FilterList
+from pydis_site.apps.api.serializers import FilterListSerializer
+
+
+class FilterListViewSet(ModelViewSet):
+ """
+ View providing CRUD operations on items allowed or denied by our bot.
+
+ ## Routes
+ ### GET /bot/filter-lists
+ Returns all filterlist items in the database.
+
+ #### Response format
+ >>> [
+ ... {
+ ... 'id': "2309268224",
+ ... 'created_at': "01-01-2020 ...",
+ ... 'updated_at': "01-01-2020 ...",
+ ... 'type': "file_format",
+ ... 'allowed': 'true',
+ ... 'content': ".jpeg",
+ ... 'comment': "Popular image format.",
+ ... },
+ ... ...
+ ... ]
+
+ #### Status codes
+ - 200: returned on success
+ - 401: returned if unauthenticated
+
+ ### GET /bot/filter-lists/<id:int>
+ Returns a specific FilterList item from the database.
+
+ #### Response format
+ >>> {
+ ... 'id': "2309268224",
+ ... 'created_at': "01-01-2020 ...",
+ ... 'updated_at': "01-01-2020 ...",
+ ... 'type': "file_format",
+ ... 'allowed': 'true',
+ ... 'content': ".jpeg",
+ ... 'comment': "Popular image format.",
+ ... }
+
+ #### Status codes
+ - 200: returned on success
+ - 404: returned if the id was not found.
+
+ ### GET /bot/filter-lists/get-types
+ Returns a list of valid list types that can be used in POST requests.
+
+ #### Response format
+ >>> [
+ ... ["GUILD_INVITE","Guild Invite"],
+ ... ["FILE_FORMAT","File Format"],
+ ... ["DOMAIN_NAME","Domain Name"],
+ ... ["FILTER_TOKEN","Filter Token"]
+ ... ]
+
+ #### Status codes
+ - 200: returned on success
+
+ ### POST /bot/filter-lists
+ Adds a single FilterList item to the database.
+
+ #### Request body
+ >>> {
+ ... 'type': str,
+ ... 'allowed': bool,
+ ... 'content': str,
+ ... 'comment': Optional[str],
+ ... }
+
+ #### Status codes
+ - 201: returned on success
+ - 400: if one of the given fields is invalid
+
+ ### DELETE /bot/filter-lists/<id:int>
+ Deletes the FilterList item with the given `id`.
+
+ #### Status codes
+ - 204: returned on success
+ - 404: if a tag with the given `id` does not exist
+ """
+
+ serializer_class = FilterListSerializer
+ queryset = FilterList.objects.all()
+
+ @action(detail=False, url_path='get-types', methods=["get"])
+ def get_types(self, _: Request) -> Response:
+ """Get a list of all the types of FilterLists we support."""
+ return Response(FilterList.FilterListType.choices)
diff --git a/pydis_site/tests/__init__.py b/pydis_site/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pydis_site/tests/__init__.py
diff --git a/pydis_site/tests/test_utils_account.py b/pydis_site/tests/test_utils_account.py
index e8db7b64..6f8338b4 100644
--- a/pydis_site/tests/test_utils_account.py
+++ b/pydis_site/tests/test_utils_account.py
@@ -5,7 +5,7 @@ from allauth.socialaccount.models import SocialAccount, SocialLogin
from django.contrib.auth.models import User
from django.contrib.messages.storage.base import BaseStorage
from django.http import HttpRequest
-from django.test import TestCase
+from django.test import RequestFactory, TestCase
from pydis_site.apps.api.models import Role, User as DiscordUser
from pydis_site.utils.account import AccountAdapter, SocialAccountAdapter
@@ -13,28 +13,43 @@ from pydis_site.utils.account import AccountAdapter, SocialAccountAdapter
class AccountUtilsTests(TestCase):
def setUp(self):
+ # Create the user
self.django_user = User.objects.create(username="user")
+ # Create the roles
+ developers_role = Role.objects.create(
+ id=1,
+ name="Developers",
+ colour=0,
+ permissions=0,
+ position=1
+ )
+ everyone_role = Role.objects.create(
+ id=0,
+ name="@everyone",
+ colour=0,
+ permissions=0,
+ position=0
+ )
+
+ # Create the social accounts
self.discord_account = SocialAccount.objects.create(
user=self.django_user, provider="discord", uid=0
)
-
- self.discord_account_role = SocialAccount.objects.create(
+ self.discord_account_one_role = SocialAccount.objects.create(
user=self.django_user, provider="discord", uid=1
)
-
self.discord_account_two_roles = SocialAccount.objects.create(
user=self.django_user, provider="discord", uid=2
)
-
self.discord_account_not_present = SocialAccount.objects.create(
user=self.django_user, provider="discord", uid=3
)
-
self.github_account = SocialAccount.objects.create(
user=self.django_user, provider="github", uid=0
)
+ # Create DiscordUsers
self.discord_user = DiscordUser.objects.create(
id=0,
name="user",
@@ -44,35 +59,18 @@ class AccountUtilsTests(TestCase):
self.discord_user_role = DiscordUser.objects.create(
id=1,
name="user present",
- discriminator=0
+ discriminator=0,
+ roles=[everyone_role.id]
)
self.discord_user_two_roles = DiscordUser.objects.create(
id=2,
name="user with both roles",
- discriminator=0
+ discriminator=0,
+ roles=[everyone_role.id, developers_role.id]
)
- everyone_role = Role.objects.create(
- id=0,
- name="@everyone",
- colour=0,
- permissions=0,
- position=0
- )
-
- self.discord_user_role.roles.append(everyone_role.id)
- self.discord_user_two_roles.roles.append(everyone_role.id)
-
- developers_role = Role.objects.create(
- id=1,
- name="Developers",
- colour=0,
- permissions=0,
- position=1
- )
-
- self.discord_user_two_roles.roles.append(developers_role.id)
+ self.request_factory = RequestFactory()
def test_account_adapter(self):
"""Test that our Allauth account adapter functions correctly."""
@@ -85,13 +83,13 @@ class AccountUtilsTests(TestCase):
adapter = SocialAccountAdapter()
discord_login = SocialLogin(account=self.discord_account)
- discord_login_role = SocialLogin(account=self.discord_account_role)
- discord_login_two_roles = SocialLogin(account=self.discord_account_two_roles)
+ discord_login_role = SocialLogin(account=self.discord_account_one_role)
discord_login_not_present = SocialLogin(account=self.discord_account_not_present)
+ discord_login_two_roles = SocialLogin(account=self.discord_account_two_roles)
github_login = SocialLogin(account=self.github_account)
- messages_request = HttpRequest()
+ messages_request = self.request_factory.get("/")
messages_request._messages = BaseStorage(messages_request)
with patch("pydis_site.utils.account.reverse") as mock_reverse:
@@ -108,12 +106,14 @@ class AccountUtilsTests(TestCase):
with self.assertRaises(ImmediateHttpResponse):
adapter.is_open_for_signup(messages_request, discord_login_not_present)
+ self.assertTrue(
+ adapter.is_open_for_signup(messages_request, discord_login_two_roles)
+ )
+
self.assertEqual(len(messages_request._messages._queued_messages), 4)
self.assertEqual(mock_redirect.call_count, 4)
self.assertEqual(mock_reverse.call_count, 4)
- self.assertTrue(adapter.is_open_for_signup(HttpRequest(), discord_login_two_roles))
-
def test_social_account_adapter_populate(self):
"""Test that our Allauth social account adapter correctly handles data population."""
adapter = SocialAccountAdapter()
@@ -122,13 +122,18 @@ class AccountUtilsTests(TestCase):
account=self.discord_account,
user=self.django_user
)
-
discord_login.account.extra_data["discriminator"] = "0000"
- user = adapter.populate_user(
- HttpRequest(), discord_login,
+ discord_user = adapter.populate_user(
+ self.request_factory.get("/"), discord_login,
{"username": "user"}
)
+ self.assertEqual(discord_user.username, "user#0000")
+ self.assertEqual(discord_user.first_name, "user#0000")
- self.assertEqual(user.username, "user#0000")
- self.assertEqual(user.first_name, "user#0000")
+ discord_login.account.provider = "not_discord"
+ not_discord_user = adapter.populate_user(
+ self.request_factory.get("/"), discord_login,
+ {"username": "user"}
+ )
+ self.assertEqual(not_discord_user.username, "user")