From dfb8bd6fc1c39b2892f28f3d8f8e3d43b22dcc86 Mon Sep 17 00:00:00 2001 From: zediious Date: Fri, 31 May 2024 20:42:12 -0400 Subject: [PATCH 01/20] Create totp media dir if it does not exist Since the QR images are not placed there through normal means, we need to create the directory manually on the first run. --- raptorWeb/authprofiles/tokens.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/raptorWeb/authprofiles/tokens.py b/raptorWeb/authprofiles/tokens.py index 2c546581..3731b711 100644 --- a/raptorWeb/authprofiles/tokens.py +++ b/raptorWeb/authprofiles/tokens.py @@ -1,5 +1,6 @@ from six import text_type -from os.path import join +from os.path import join, exists +from os import getenv, makedirs from qrcode import make import qrcode.image.svg from tempfile import NamedTemporaryFile @@ -48,6 +49,9 @@ def generate_totp_token(user: AbstractUser) -> str: site_info: SiteInformation = SiteInformation.objects.get_or_create(pk=1)[0] user.totp_token = bytes(random_base32(), 'utf-8') + if not exists(QR_MEDIA_DIR): + makedirs(QR_MEDIA_DIR) + totp = TOTP(user.totp_token) qr_uri = totp.provisioning_uri( name=f'{user.username}/{user.email}', From d68f89346f31122a1e952b6d07376164a0cb62ae Mon Sep 17 00:00:00 2001 From: zediious Date: Fri, 31 May 2024 20:42:37 -0400 Subject: [PATCH 02/20] Remove unused imports from tokens module --- raptorWeb/authprofiles/tokens.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/raptorWeb/authprofiles/tokens.py b/raptorWeb/authprofiles/tokens.py index 3731b711..710f41d9 100644 --- a/raptorWeb/authprofiles/tokens.py +++ b/raptorWeb/authprofiles/tokens.py @@ -1,9 +1,8 @@ from six import text_type from os.path import join, exists -from os import getenv, makedirs +from os import makedirs from qrcode import make import qrcode.image.svg -from tempfile import NamedTemporaryFile from datetime import datetime from logging import Logger, getLogger @@ -11,7 +10,6 @@ from django.conf import settings from django.contrib.auth.models import AbstractUser -from django.core.files import File from django.contrib.auth.tokens import PasswordResetTokenGenerator from raptorWeb.raptormc.models import SiteInformation From ad6b89e4a1edda301ea973f6869d0a07df2854ba Mon Sep 17 00:00:00 2001 From: zediious Date: Fri, 31 May 2024 20:49:40 -0400 Subject: [PATCH 03/20] Move jazzmin settings to own module These take up a lot of space in settings --- config/jazzmin.py | 131 +++++++++++++++++++++++++++++++++++++++++++++ config/settings.py | 127 ++----------------------------------------- 2 files changed, 134 insertions(+), 124 deletions(-) create mode 100644 config/jazzmin.py diff --git a/config/jazzmin.py b/config/jazzmin.py new file mode 100644 index 00000000..2a0030e1 --- /dev/null +++ b/config/jazzmin.py @@ -0,0 +1,131 @@ +from os.path import join +from os import getenv +from pathlib import Path +from dotenv import load_dotenv + +load_dotenv(join(Path(__file__).resolve().parent.parent, 'stack.env')) + +ADMIN_BRAND_NAME = "Default" if getenv('ADMIN_BRAND_NAME') == '' else getenv('ADMIN_BRAND_NAME') + +JAZZMIN_SETTINGS_ORIGINAL = { + + "site_title": f"Admin {ADMIN_BRAND_NAME}", + "site_header": f"{ADMIN_BRAND_NAME}", + "site_brand": f"{ADMIN_BRAND_NAME}", + "site_logo_classes": "img-circle", + "welcome_sign": f"Welcome to {ADMIN_BRAND_NAME}", + "topmenu_links": [ + {"name": "Return to Site", "url": "/", "new_window": False}, + {"name": "Admin", "url": "/admin/", "new_window": False}, + {"name": "Control Panel", "permissions": ["raptormc.panel"], "url": "/panel/", "new_window": False}, + {"name": "Discord Bot", "permissions": ["raptormc.discord_bot"], "url": "/panel/discordbot/", "new_window": False}, + {"name": "Server Actions", "permissions": ["raptormc.server_actions"], "url": "/panel/serveractions/", "new_window": False}, + {"name": "Reporting", "permissions": ["raptormc.reporting"], "url": "/panel/reporting/", "new_window": False}, + {"name": "Donations", "permissions": ["raptormc.donations"], "url": "/panel/donations/", "new_window": False}, + {"app": "raptormc"}, + {"app": "gameservers"}, + {"app": "raptorbot"}, + {"app": "donations"}, + {"app": "staffapps"}, + {"app": "authprofiles"}, + {"name": "Settings", "permissions": ["raptormc.settings"], "url": "/panel/settings/", "new_window": False}, + ], + "usermenu_links": [ + {"model": "auth.user"}, + {"name": "Return to Site", "url": "/", "new_window": False}, + {"name": "Admin", "url": "/admin/", "new_window": False}, + {"name": "Control Panel", "permissions": ["raptormc.panel"], "url": "/panel/", "new_window": False}, + {"name": "Discord Bot", "permissions": ["raptormc.discord_bot"], "url": "/panel/discordbot/", "new_window": False}, + {"name": "Server Actions", "permissions": ["raptormc.server_actions"], "url": "/panel/serveractions/", "new_window": False}, + {"name": "Reporting", "permissions": ["raptormc.reporting"], "url": "/panel/reporting/", "new_window": False}, + {"name": "Donations", "permissions": ["raptormc.donations"], "url": "/panel/donations/", "new_window": False}, + {"name": "Settings", "permissions": ["raptormc.settings"], "url": "/panel/settings/", "new_window": False}, + ], + + # Sidebar + "show_sidebar": True, + "navigation_expanded": False, + "hide_apps": ['ipn'], + "order_with_respect_to": ["raptormc", "gameservers", "raptorbot", 'donations', "staffapps", "authprofiles"], + "custom_links": { + "raptorbot": [{ + "name": "Discord Bot Control Panel", + "url": "/panel/discordbot/", + "icon": "fas fa-terminal", + "permissions": ["raptormc.discord_bot"] + }], + "gameservers": [{ + "name": "Server Actions", + "url": "/panel/serveractions/", + "icon": "fas fa-terminal", + "permissions": ["raptormc.server_actions"] + }], + "donations": [{ + "name": "Completed Donations", + "url": "/panel/donations/", + "icon": "fa fa-check-double", + "permissions": ["raptormc.donations"] + }], + "donations": [{ + "name": "Completed Donations", + "url": "/panel/donations/", + "icon": "fa fa-check-double", + "permissions": ["raptormc.donations"] + }] + }, + "icons": { + "raptormc": "fas fa-book", + "raptormc.InformativeText": "fas fa-scroll", + "raptormc.NavbarLink": "fas fa-map-marker", + "raptormc.NavbarDropdown": "fas fa-map-marker", + "raptormc.NavWidget": "fas fa-map-pin", + "raptormc.NavWidgetBar": "fas fa-map-pin", + "raptormc.NotificationToast": "fas fa-envelope-square", + "raptormc.Page": "fas fa-file", + "gameservers": "fas fa-gamepad", + "gameservers.Player": "fas fa-headset", + "gameservers.Server": "fas fa-server", + "raptorbot": "fas fa-robot", + "raptorbot.SentEmbedMessage": "fas fa-comment", + "raptorbot.GlobalAnnouncement": "fas fa-bullhorn", + "raptorbot.ServerAnnouncement": "fas fa-bullhorn", + "staffapps": "fas fa-book-reader", + "staffapps.ModeratorApplication": "fas fa-book-open", + "staffapps.AdminApplication": "fas fa-book-open", + "authprofiles": "fas fa-users", + "authprofiles.RaptorUserGroup": "fas fa-users", + "authprofiles.RaptorUser": "fas fa-user", + "authprofiles.UserProfileInfo": "fas fa-user-tag", + "authprofiles.DiscordUserInfo": "fas fa-user-tag", + "donations": "fa fa-coins", + "donations.DonationPackage": "fa fa-archive", + "donations.DonationServerCommand": "fa fa-terminal", + "donations.DonationDiscordRole": "fa fa-mask", + + }, + "default_icon_parents": "fas fa-chevron-circle-right", + "default_icon_children": "fas fa-circle", + "use_google_fonts_cdn": True, + "show_ui_builder": False, + +} + +JAZZMIN_UI_TWEAKS_ORIGINAL = { + + "navbar_small_text": True, + "footer_small_text": True, + "body_small_text": False, + "brand_small_text": False, + "brand_colour": False, + "accent": "accent-lightblue", + "navbar": "navbar-gray-dark navbar-dark", + "no_navbar_border": False, + "navbar_fixed": True, + "layout_boxed": False, + "footer_fixed": False, + "sidebar_disable_expand": True, + "theme": "cyborg", + "actions_sticky_top": False, + "sidebar_nav_child_indent": True, + +} \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 9cf825cc..fd97ce94 100644 --- a/config/settings.py +++ b/config/settings.py @@ -6,6 +6,7 @@ from celery.schedules import crontab from config.logging import LOGGING_DEFINITION +from config.jazzmin import JAZZMIN_SETTINGS_ORIGINAL, JAZZMIN_UI_TWEAKS_ORIGINAL # Define project directories BASE_DIR: str = Path(__file__).resolve().parent.parent @@ -231,9 +232,6 @@ BACKGROUND_TASK_RUN_ASYNC: bool = True -# ** Settings for "raptormc" app ** -ADMIN_BRAND_NAME = "Default" if getenv('ADMIN_BRAND_NAME') == '' else getenv('ADMIN_BRAND_NAME') - # ** Settings for "donations" app ** STRIPE_PUBLISHABLE_KEY = '' if str(getenv('STRIPE_PUBLISHABLE_KEY')) == '' else str(getenv('STRIPE_PUBLISHABLE_KEY')) STRIPE_SECRET_KEY = '' if str(getenv('STRIPE_SECRET_KEY')) == '' else str(getenv('STRIPE_SECRET_KEY')) @@ -282,128 +280,9 @@ } # ** Settings for "django-jazzmin" app ** -JAZZMIN_SETTINGS = { - - "site_title": f"Admin {ADMIN_BRAND_NAME}", - "site_header": f"{ADMIN_BRAND_NAME}", - "site_brand": f"{ADMIN_BRAND_NAME}", - "site_logo_classes": "img-circle", - "welcome_sign": f"Welcome to {ADMIN_BRAND_NAME}", - "topmenu_links": [ - {"name": "Return to Site", "url": "/", "new_window": False}, - {"name": "Admin", "url": "/admin/", "new_window": False}, - {"name": "Control Panel", "permissions": ["raptormc.panel"], "url": "/panel/", "new_window": False}, - {"name": "Discord Bot", "permissions": ["raptormc.discord_bot"], "url": "/panel/discordbot/", "new_window": False}, - {"name": "Server Actions", "permissions": ["raptormc.server_actions"], "url": "/panel/serveractions/", "new_window": False}, - {"name": "Reporting", "permissions": ["raptormc.reporting"], "url": "/panel/reporting/", "new_window": False}, - {"name": "Donations", "permissions": ["raptormc.donations"], "url": "/panel/donations/", "new_window": False}, - {"app": "raptormc"}, - {"app": "gameservers"}, - {"app": "raptorbot"}, - {"app": "donations"}, - {"app": "staffapps"}, - {"app": "authprofiles"}, - {"name": "Settings", "permissions": ["raptormc.settings"], "url": "/panel/settings/", "new_window": False}, - ], - "usermenu_links": [ - {"model": "auth.user"}, - {"name": "Return to Site", "url": "/", "new_window": False}, - {"name": "Admin", "url": "/admin/", "new_window": False}, - {"name": "Control Panel", "permissions": ["raptormc.panel"], "url": "/panel/", "new_window": False}, - {"name": "Discord Bot", "permissions": ["raptormc.discord_bot"], "url": "/panel/discordbot/", "new_window": False}, - {"name": "Server Actions", "permissions": ["raptormc.server_actions"], "url": "/panel/serveractions/", "new_window": False}, - {"name": "Reporting", "permissions": ["raptormc.reporting"], "url": "/panel/reporting/", "new_window": False}, - {"name": "Donations", "permissions": ["raptormc.donations"], "url": "/panel/donations/", "new_window": False}, - {"name": "Settings", "permissions": ["raptormc.settings"], "url": "/panel/settings/", "new_window": False}, - ], - - # Sidebar - "show_sidebar": True, - "navigation_expanded": False, - "hide_apps": ['ipn'], - "order_with_respect_to": ["raptormc", "gameservers", "raptorbot", 'donations', "staffapps", "authprofiles"], - "custom_links": { - "raptorbot": [{ - "name": "Discord Bot Control Panel", - "url": "/panel/discordbot/", - "icon": "fas fa-terminal", - "permissions": ["raptormc.discord_bot"] - }], - "gameservers": [{ - "name": "Server Actions", - "url": "/panel/serveractions/", - "icon": "fas fa-terminal", - "permissions": ["raptormc.server_actions"] - }], - "donations": [{ - "name": "Completed Donations", - "url": "/panel/donations/", - "icon": "fa fa-check-double", - "permissions": ["raptormc.donations"] - }], - "donations": [{ - "name": "Completed Donations", - "url": "/panel/donations/", - "icon": "fa fa-check-double", - "permissions": ["raptormc.donations"] - }] - }, - "icons": { - "raptormc": "fas fa-book", - "raptormc.InformativeText": "fas fa-scroll", - "raptormc.NavbarLink": "fas fa-map-marker", - "raptormc.NavbarDropdown": "fas fa-map-marker", - "raptormc.NavWidget": "fas fa-map-pin", - "raptormc.NavWidgetBar": "fas fa-map-pin", - "raptormc.NotificationToast": "fas fa-envelope-square", - "raptormc.Page": "fas fa-file", - "gameservers": "fas fa-gamepad", - "gameservers.Player": "fas fa-headset", - "gameservers.Server": "fas fa-server", - "raptorbot": "fas fa-robot", - "raptorbot.SentEmbedMessage": "fas fa-comment", - "raptorbot.GlobalAnnouncement": "fas fa-bullhorn", - "raptorbot.ServerAnnouncement": "fas fa-bullhorn", - "staffapps": "fas fa-book-reader", - "staffapps.ModeratorApplication": "fas fa-book-open", - "staffapps.AdminApplication": "fas fa-book-open", - "authprofiles": "fas fa-users", - "authprofiles.RaptorUserGroup": "fas fa-users", - "authprofiles.RaptorUser": "fas fa-user", - "authprofiles.UserProfileInfo": "fas fa-user-tag", - "authprofiles.DiscordUserInfo": "fas fa-user-tag", - "donations": "fa fa-coins", - "donations.DonationPackage": "fa fa-archive", - "donations.DonationServerCommand": "fa fa-terminal", - "donations.DonationDiscordRole": "fa fa-mask", - - }, - "default_icon_parents": "fas fa-chevron-circle-right", - "default_icon_children": "fas fa-circle", - "use_google_fonts_cdn": True, - "show_ui_builder": False, +JAZZMIN_SETTINGS = JAZZMIN_SETTINGS_ORIGINAL -} - -JAZZMIN_UI_TWEAKS = { - - "navbar_small_text": True, - "footer_small_text": True, - "body_small_text": False, - "brand_small_text": False, - "brand_colour": False, - "accent": "accent-lightblue", - "navbar": "navbar-gray-dark navbar-dark", - "no_navbar_border": False, - "navbar_fixed": True, - "layout_boxed": False, - "footer_fixed": False, - "sidebar_disable_expand": True, - "theme": "cyborg", - "actions_sticky_top": False, - "sidebar_nav_child_indent": True, - -} +JAZZMIN_UI_TWEAKS = JAZZMIN_UI_TWEAKS_ORIGINAL # ** Settings for "django_bootstrap5" app ** BOOTSTRAP5: dict = { From db58dd7eaf1649448762a959896fb758064c83b2 Mon Sep 17 00:00:00 2001 From: zediious Date: Fri, 31 May 2024 21:50:31 -0400 Subject: [PATCH 04/20] Logger for authprofiles tasks --- config/logging.py | 5 +++++ raptorWeb/authprofiles/tasks.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/config/logging.py b/config/logging.py index e9ab01cb..187727c4 100644 --- a/config/logging.py +++ b/config/logging.py @@ -145,6 +145,11 @@ 'level': 'DEBUG', 'propagate': False, }, + 'authprofiles.tasks': { + 'handlers': ['console', 'log_file', 'mail_admins'], + 'level': 'DEBUG', + 'propagate': False, + }, 'gameservers.views': { 'handlers': ['console', 'log_file', 'mail_admins'], 'level': 'DEBUG', diff --git a/raptorWeb/authprofiles/tasks.py b/raptorWeb/authprofiles/tasks.py index 1a36fb7d..559da3fa 100644 --- a/raptorWeb/authprofiles/tasks.py +++ b/raptorWeb/authprofiles/tasks.py @@ -16,7 +16,7 @@ from raptorWeb.authprofiles.models import RaptorUser, DeletionQueueForUser from raptorWeb.raptormc.models import SiteInformation -LOGGER = getLogger('donations.tasks') +LOGGER = getLogger('authprofiles.tasks') AUTH_TEMPLATE_DIR: str = getattr(settings, 'AUTH_TEMPLATE_DIR') EMAIL_HOST_USER: str = getattr(settings, 'EMAIL_HOST_USER') From 847a5be9fc74c52ed77ec03b76ef43e8bd27ad60 Mon Sep 17 00:00:00 2001 From: zediious Date: Fri, 31 May 2024 21:54:41 -0400 Subject: [PATCH 05/20] remove debug statement from tokens --- raptorWeb/authprofiles/tokens.py | 1 - 1 file changed, 1 deletion(-) diff --git a/raptorWeb/authprofiles/tokens.py b/raptorWeb/authprofiles/tokens.py index 710f41d9..dd3e81f1 100644 --- a/raptorWeb/authprofiles/tokens.py +++ b/raptorWeb/authprofiles/tokens.py @@ -57,7 +57,6 @@ def generate_totp_token(user: AbstractUser) -> str: ) qr_filename = f'{QR_MEDIA_DIR}{hash(datetime.now())}.svg' - LOGGER.debug(qr_filename) qr_code_image = make( qr_uri, From 477855ebea140adaa60fb2ebb766ff6d9f9e39eb Mon Sep 17 00:00:00 2001 From: zediious Date: Sat, 1 Jun 2024 15:04:37 -0400 Subject: [PATCH 06/20] Clear orphaned QR images If a user does not set up MFA after generating the QR, the image will be left behind and not deleted. This new task will check any images in the totp media folder, and delete them if they are older than 15 minutes. This task will run every 5 minutes --- config/settings.py | 4 ++++ raptorWeb/authprofiles/tasks.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/config/settings.py b/config/settings.py index fd97ce94..75250322 100644 --- a/config/settings.py +++ b/config/settings.py @@ -277,6 +277,10 @@ 'task': 'raptorWeb.authprofiles.tasks.check_for_deletable_users', 'schedule': crontab(minute='*/15'), }, + 'check_for_deletable_qr_images': { + 'task': 'raptorWeb.authprofiles.tasks.check_for_deletable_qr_images', + 'schedule': crontab(minute='*/5'), + } } # ** Settings for "django-jazzmin" app ** diff --git a/raptorWeb/authprofiles/tasks.py b/raptorWeb/authprofiles/tasks.py index 559da3fa..7346f238 100644 --- a/raptorWeb/authprofiles/tasks.py +++ b/raptorWeb/authprofiles/tasks.py @@ -3,9 +3,10 @@ """ from logging import getLogger -from os.path import join +from os.path import join, getmtime +from os import listdir, remove from datetime import datetime -from time import sleep +import time from django.core.mail import send_mail from django.template.loader import render_to_string @@ -13,11 +14,12 @@ from celery import shared_task -from raptorWeb.authprofiles.models import RaptorUser, DeletionQueueForUser +from raptorWeb.authprofiles.models import DeletionQueueForUser from raptorWeb.raptormc.models import SiteInformation LOGGER = getLogger('authprofiles.tasks') AUTH_TEMPLATE_DIR: str = getattr(settings, 'AUTH_TEMPLATE_DIR') +QR_MEDIA_DIR: str = join(getattr(settings, 'MEDIA_DIR'), 'totp/') EMAIL_HOST_USER: str = getattr(settings, 'EMAIL_HOST_USER') @shared_task @@ -33,6 +35,16 @@ def check_for_deletable_users(): user.user.delete() user.delete() +@shared_task +def check_for_deletable_qr_images(): + """ + Check if any QR code images have existed for more than an hour, and delete them if so.. + """ + epoch_time = int(time.time()) + + for qr_image in listdir(QR_MEDIA_DIR): + if (epoch_time - int(getmtime(join(QR_MEDIA_DIR, qr_image)))) / 60 >= 15: + remove(join(QR_MEDIA_DIR, qr_image)) @shared_task def send_delete_request_email(deleting_user: list): From 1009467df4ff038e4b4d09b886448cf194afd28f Mon Sep 17 00:00:00 2001 From: zediious Date: Sat, 1 Jun 2024 15:20:22 -0400 Subject: [PATCH 07/20] Remove width from QR submit and account delete buttons These were being squeezed for no reason --- raptorWeb/templates/authprofiles/deletion.html | 2 +- raptorWeb/templates/authprofiles/mfa_setup.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/raptorWeb/templates/authprofiles/deletion.html b/raptorWeb/templates/authprofiles/deletion.html index f6c5e937..ac278112 100644 --- a/raptorWeb/templates/authprofiles/deletion.html +++ b/raptorWeb/templates/authprofiles/deletion.html @@ -30,7 +30,7 @@