From cd53aea38a8b6332fa5d941a497fd02f892f2bab Mon Sep 17 00:00:00 2001 From: Nicholas Bottone Date: Sun, 14 Jul 2024 02:41:59 -0400 Subject: [PATCH 1/4] Add submit_high_stakes function and related checks --- highscores/lib.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/highscores/lib.py b/highscores/lib.py index 7aa71cb..6d8bccb 100644 --- a/highscores/lib.py +++ b/highscores/lib.py @@ -86,6 +86,10 @@ def submit_crescendo(score_obj: Score) -> Union[str, None]: return submit_score(score_obj, crescendo_clean_code_check) +def submit_high_stakes(score_obj: Score) -> Union[str, None]: + return submit_score(score_obj, high_stakes_clean_code_check) + + def decode_time_data(in_string: str) -> str: out_bytes = "" @@ -276,6 +280,10 @@ def crescendo_clean_code_check(score_obj: Score) -> Union[str, None]: return clean_code_check(score_obj, check_crescendo_game_settings, check_subtraction_score) +def high_stakes_clean_code_check(score_obj: Score) -> Union[str, None]: + return clean_code_check(score_obj, check_high_stakes_game_settings, check_score) + + def extract_clean_code_info(score_obj: Score) -> tuple[str, list[str], str, str, str, str, str]: """ Extracts the relevant information from the clean code. :param score_obj: Score object to extract from @@ -466,6 +474,20 @@ def check_crescendo_game_settings(game_options: list, restart_option: str, game_ return None # No error +def check_high_stakes_game_settings(game_options: list, restart_option: str, game_index: str) -> Union[str, None]: + """ Checks if the High Stakes game settings are valid. + :return: None if the settings are valid, or a response with an error message if they are not. + """ + if (game_index != '17'): + return 'Wrong game! This form is for High Stakes.' + if (restart_option != '2'): + return 'You must use restart option 2 (skills challenge) for High Stakes high score submissions.' + if (game_options[0] != '1'): + return 'You must have auto wall enabled for high score submissions.' + + return None # No error + + def check_robot_type(score_obj: Score, robot_model: str) -> Union[str, None]: """ Checks if the robot model is valid. :return: None if the robot model is valid, or a response with an error message if it is not. @@ -631,6 +653,7 @@ def check_time_data(score_obj: Score) -> Union[str, None]: "cs": submit_centerstage, "ou": submit_over_under, "cr": submit_crescendo, + "hs": submit_high_stakes, } game_to_submit_func = { @@ -644,4 +667,5 @@ def check_time_data(score_obj: Score) -> Union[str, None]: "CENTERSTAGE": submit_centerstage, "Over Under": submit_over_under, "Crescendo": submit_crescendo, + "High Stakes": submit_high_stakes, } From 96311d39b458a47b854c55bec5618ed9c70606b3 Mon Sep 17 00:00:00 2001 From: Nicholas Bottone Date: Sun, 14 Jul 2024 02:58:13 -0400 Subject: [PATCH 2/4] Refactor high_stakes_clean_code_check function to use check_skills_challenge_score --- highscores/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/highscores/lib.py b/highscores/lib.py index 6d8bccb..817579e 100644 --- a/highscores/lib.py +++ b/highscores/lib.py @@ -281,7 +281,7 @@ def crescendo_clean_code_check(score_obj: Score) -> Union[str, None]: def high_stakes_clean_code_check(score_obj: Score) -> Union[str, None]: - return clean_code_check(score_obj, check_high_stakes_game_settings, check_score) + return clean_code_check(score_obj, check_high_stakes_game_settings, check_skills_challenge_score) def extract_clean_code_info(score_obj: Score) -> tuple[str, list[str], str, str, str, str, str]: From c7cbe947539869e6cd3a3d227102655ec13501b1 Mon Sep 17 00:00:00 2001 From: Nicholas Bottone Date: Wed, 17 Jul 2024 20:25:25 -0400 Subject: [PATCH 3/4] Fix error on login without email address Fixes #28 --- discordoauth2/auth.py | 6 +++++- discordoauth2/views.py | 14 ++++++++++++-- home/templates/home/login_error.html | 17 +++++++++++++++++ home/urls.py | 1 + home/views.py | 4 ++++ 5 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 home/templates/home/login_error.html diff --git a/discordoauth2/auth.py b/discordoauth2/auth.py index d1b2c6c..eb21d86 100644 --- a/discordoauth2/auth.py +++ b/discordoauth2/auth.py @@ -1,11 +1,15 @@ from django.contrib.auth.backends import RemoteUserBackend from .models import User +from typing import Union + class DiscordAuthenticationBackend(RemoteUserBackend): - def authenticate(self, request, user) -> User: + def authenticate(self, request, user) -> Union[User, None]: found_user = User.objects.filter(id=user['id']) if len(found_user) == 0: # New user (first time login) + if user['email'] is None: + return None new_user = User.objects.create_discord_user(user) return new_user # Returning user diff --git a/discordoauth2/views.py b/discordoauth2/views.py index f4b0fd3..4e5a452 100644 --- a/discordoauth2/views.py +++ b/discordoauth2/views.py @@ -1,3 +1,4 @@ +from typing import Union from django.http import JsonResponse, HttpRequest from django.shortcuts import redirect, render from django.contrib.auth import authenticate, login @@ -36,7 +37,11 @@ def discord_api_login(request: HttpRequest): def discord_login_redirect(request: HttpRequest): user = exchange_code(request.GET.get('code')) + if user is None: + return redirect('/login-error/') discord_user = authenticate(request, user=user) + if discord_user is None: + return redirect('/login-error/') login(request, discord_user) return redirect('/') @@ -44,12 +49,16 @@ def discord_login_redirect(request: HttpRequest): def discord_api_login_redirect(request: HttpRequest): user = exchange_code(request.GET.get( 'code'), redirect_uri=DISCORD_API_REDIRECT_URI) + if user is None: + return redirect('/login-error/') discord_user = authenticate(request, user=user) + if discord_user is None: + return redirect('/login-error/') login(request, discord_user) return redirect('/api/highscores/auth/') -def exchange_code(code: str, redirect_uri: str = DISCORD_REDIRECT_URI) -> requests.Response: +def exchange_code(code: str, redirect_uri: str = DISCORD_REDIRECT_URI) -> Union[requests.Response, None]: data = { 'client_id': DISCORD_CLIENT_ID, 'client_secret': DISCORD_CLIENT_SECRET, @@ -64,7 +73,8 @@ def exchange_code(code: str, redirect_uri: str = DISCORD_REDIRECT_URI) -> reques response = requests.post('%s/oauth2/token' % DISCORD_API_ENDPOINT, data=data, headers=headers) - response.raise_for_status() + if response.status_code != 200: + return None response = requests.get('%s/users/@me' % DISCORD_API_ENDPOINT, headers={ 'Authorization': 'Bearer %s' % response.json()['access_token'] diff --git a/home/templates/home/login_error.html b/home/templates/home/login_error.html new file mode 100644 index 0000000..068093f --- /dev/null +++ b/home/templates/home/login_error.html @@ -0,0 +1,17 @@ +{% extends 'home/base.html' %} {% block content %} +
+
+

Login error

+

Ensure there is an email address attached to your discord account, and you select authorize.

+
+
+{% endblock %} diff --git a/home/urls.py b/home/urls.py index 137256c..c8c98d4 100644 --- a/home/urls.py +++ b/home/urls.py @@ -24,4 +24,5 @@ path("privacy/", views.privacy, name="privacy"), path("logopack/", views.logos, name="logos"), path("link-success/", views.link_success, name="link success"), + path("login-error/", views.login_error, name="login error"), ] diff --git a/home/views.py b/home/views.py index 3e0981b..83e3af5 100644 --- a/home/views.py +++ b/home/views.py @@ -81,6 +81,10 @@ def login_page(request): return redirect('/oauth2/login') +def login_error(request): + return render(request, "home/login_error.html", {}) + + def logout_user(request): logout(request) return redirect('/') From 5026adbb7393528a78920315a90200fb2fc5d82f Mon Sep 17 00:00:00 2001 From: Nicholas Bottone Date: Wed, 17 Jul 2024 20:26:25 -0400 Subject: [PATCH 4/4] Disable ip search email --- highscores/lib.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/highscores/lib.py b/highscores/lib.py index 817579e..31d6aa6 100644 --- a/highscores/lib.py +++ b/highscores/lib.py @@ -577,23 +577,23 @@ def search_for_reused_code(score_obj: Score) -> Union[str, None]: return 'That clean code has already been submitted by another player.' - # same ip but different player - ip_search = CleanCodeSubmission.objects.filter( - ip=score_obj.ip).exclude(player=score_obj.player) - - if ip_search.exists(): - # Uh oh, there are multiple users submitting from the same IP. - # Report this via email. - - message = f"{score_obj.player} ({score_obj.ip}) submitted a score (successfully): [{score_obj.score}] - {score_obj.leaderboard}\n\n This IP has also been used by {ip_search[0].player} ({ip_search[0].ip})\n\n {score_obj.source}\n\nhttps://secondrobotics.org/admin/highscores/score/" - try: - if (not DEBUG): - send_mail(f"Duplicate IP usage from {score_obj.player}", - message, EMAIL_HOST_USER, ADMIN_EMAILS, fail_silently=False) - except Exception as ex: - print(ex) - - # Still allow the score to be submitted. + # # same ip but different player + # ip_search = CleanCodeSubmission.objects.filter( + # ip=score_obj.ip).exclude(player=score_obj.player) + + # if ip_search.exists(): + # # Uh oh, there are multiple users submitting from the same IP. + # # Report this via email. + + # message = f"{score_obj.player} ({score_obj.ip}) submitted a score (successfully): [{score_obj.score}] - {score_obj.leaderboard}\n\n This IP has also been used by {ip_search[0].player} ({ip_search[0].ip})\n\n {score_obj.source}\n\nhttps://secondrobotics.org/admin/highscores/score/" + # try: + # if (not DEBUG): + # send_mail(f"Duplicate IP usage from {score_obj.player}", + # message, EMAIL_HOST_USER, ADMIN_EMAILS, fail_silently=False) + # except Exception as ex: + # print(ex) + + # # Still allow the score to be submitted. return None # No error