diff --git a/ops/nixos/lib/quotes.bfob.gg.nix b/ops/nixos/lib/quotes.bfob.gg.nix index e31e08d930..f8075c31d5 100644 --- a/ops/nixos/lib/quotes.bfob.gg.nix +++ b/ops/nixos/lib/quotes.bfob.gg.nix @@ -54,6 +54,14 @@ in proxyPass = "http://unix:${sock}"; }; }; + virtualHosts."dev-quotes.bfob.gg" = { + listen = nginxListen; + useACMEHost = "bfob.gg"; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:8000"; + }; + }; }; services.postgresql = { diff --git a/web/quotes/discordguild/__init__.py b/web/quotes/discordguild/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/quotes/discordguild/adapter.py b/web/quotes/discordguild/adapter.py new file mode 100644 index 0000000000..d387e67917 --- /dev/null +++ b/web/quotes/discordguild/adapter.py @@ -0,0 +1,24 @@ +from django.conf import settings +from allauth.account.adapter import DefaultAccountAdapter +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter + + +class BFOBAdapter(DefaultAccountAdapter): + def is_open_for_signup(self, request): + return False + + +class BFOBSocialAdapter(DefaultSocialAccountAdapter): + def is_open_for_signup(self, request): + return True + + def pre_social_login(self, request, sociallogin): + guild_data = sociallogin.account.extra_data.get("guild_data", {}) + roles = set(guild_data.get("roles", [])) + should_be_admin = str(settings.DISCORD_ADMIN_ROLE) in roles + user = sociallogin.user + if user.is_staff != should_be_admin or user.is_superuser != should_be_admin: + user.is_staff = should_be_admin + user.is_superuser = should_be_admin + user.save() + return diff --git a/web/quotes/discordguild/admin.py b/web/quotes/discordguild/admin.py new file mode 100644 index 0000000000..8c38f3f3da --- /dev/null +++ b/web/quotes/discordguild/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/web/quotes/discordguild/apps.py b/web/quotes/discordguild/apps.py new file mode 100644 index 0000000000..dddc5209d3 --- /dev/null +++ b/web/quotes/discordguild/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class DiscordguildConfig(AppConfig): + name = "discordguild" diff --git a/web/quotes/discordguild/migrations/__init__.py b/web/quotes/discordguild/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/quotes/discordguild/models.py b/web/quotes/discordguild/models.py new file mode 100644 index 0000000000..71a8362390 --- /dev/null +++ b/web/quotes/discordguild/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/web/quotes/discordguild/provider.py b/web/quotes/discordguild/provider.py new file mode 100644 index 0000000000..23092d94d4 --- /dev/null +++ b/web/quotes/discordguild/provider.py @@ -0,0 +1,10 @@ +import requests +from django.conf import settings +from allauth.socialaccount.providers.discord.provider import DiscordProvider + + +class DiscordGuildPermissionsProvider(DiscordProvider): + id = "discord" + + +provider_classes = [DiscordGuildPermissionsProvider] diff --git a/web/quotes/discordguild/tests.py b/web/quotes/discordguild/tests.py new file mode 100644 index 0000000000..7ce503c2dd --- /dev/null +++ b/web/quotes/discordguild/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/web/quotes/discordguild/urls.py b/web/quotes/discordguild/urls.py new file mode 100644 index 0000000000..e171bac251 --- /dev/null +++ b/web/quotes/discordguild/urls.py @@ -0,0 +1,5 @@ +from .provider import DiscordGuildPermissionsProvider +from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns + + +urlpatterns = default_urlpatterns(DiscordGuildPermissionsProvider) diff --git a/web/quotes/discordguild/views.py b/web/quotes/discordguild/views.py new file mode 100644 index 0000000000..12a5fc0e97 --- /dev/null +++ b/web/quotes/discordguild/views.py @@ -0,0 +1,33 @@ +from .provider import DiscordGuildPermissionsProvider + +from django.core.exceptions import PermissionDenied +from django.conf import settings +import requests +from allauth.socialaccount.providers.discord.views import DiscordOAuth2Adapter +from allauth.socialaccount.providers.oauth2.views import ( + OAuth2CallbackView, + OAuth2LoginView, +) + + +class DiscordGuildPermissionsOAuth2Adapter(DiscordOAuth2Adapter): + provider_id = DiscordGuildPermissionsProvider.id + + def complete_login(self, request, app, token, **kwargs): + login = super().complete_login(request, app, token, **kwargs) + guild_data = requests.get( + f"https://discord.com/api/guilds/{settings.DISCORD_GUILD_ID}/members/{login.account.uid}", + headers={ + "Authorization": f"Bot {settings.DISCORD_BOT_TOKEN}", + "Content-Type": "application/json", + }, + ) + if guild_data.status_code == 404: + raise PermissionDenied("You're not a BFOBer.") + guild_data.raise_for_status() + login.account.extra_data["guild_data"] = guild_data.json() + return login + + +oauth2_login = OAuth2LoginView.adapter_view(DiscordGuildPermissionsOAuth2Adapter) +oauth2_callback = OAuth2CallbackView.adapter_view(DiscordGuildPermissionsOAuth2Adapter) diff --git a/web/quotes/quotedb/migrations/0002_auto_20210120_0039.py b/web/quotes/quotedb/migrations/0002_auto_20210120_0039.py new file mode 100644 index 0000000000..33536a2b32 --- /dev/null +++ b/web/quotes/quotedb/migrations/0002_auto_20210120_0039.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1.5 on 2021-01-20 00:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("quotedb", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="person", + options={"verbose_name_plural": "people"}, + ), + ] diff --git a/web/quotes/quotesapp/settings.py b/web/quotes/quotesapp/settings.py index e9d52709ac..102af78907 100644 --- a/web/quotes/quotesapp/settings.py +++ b/web/quotes/quotesapp/settings.py @@ -26,7 +26,7 @@ SECRET_KEY = "&(13b=+n^k3px89=%x24_=2593x2*)p_6l7&wu_xph!t=$o9!1" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ["dev-quotes.bfob.gg"] SITE_ID = 1 @@ -44,8 +44,8 @@ INSTALLED_APPS = [ "allauth", "allauth.account", "allauth.socialaccount", - "allauth.socialaccount.providers.discord", "quotes.quotedb", + "quotes.discordguild", ] MIDDLEWARE = [ @@ -134,6 +134,12 @@ STATICFILES_DIRS = [ BASE_DIR / "static", ] +LOGIN_URL = "/accounts/discord/login/" +ACCOUNT_ADAPTER = "quotes.discordguild.adapter.BFOBAdapter" +SOCIALACCOUNT_ADAPTER = "quotes.discordguild.adapter.BFOBSocialAdapter" +DISCORD_GUILD_ID = 547155312071671809 +DISCORD_ADMIN_ROLE = 801258366810456115 +DISCORD_BOT_TOKEN = os.environ.get("DISCORD_BOT_TOKEN", None) SOCIALACCOUNT_PROVIDERS = { "discord": { "SCOPE": [ diff --git a/web/quotes/quotesapp/urls.py b/web/quotes/quotesapp/urls.py index 48814e7176..4cd14ec42a 100644 --- a/web/quotes/quotesapp/urls.py +++ b/web/quotes/quotesapp/urls.py @@ -14,11 +14,15 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin +from django.contrib.auth.decorators import login_required from django.urls import include, re_path, path import allauth.urls import quotes.quotedb.urls +# Monkeypatch the login_required decorator onto /admin. +admin.site.login = login_required(admin.site.login) + urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include(allauth.urls)),