From 5d7ade9af5e57f3ad67afdc4dad07b87c07077f0 Mon Sep 17 00:00:00 2001 From: Jaimyn Mayer Date: Tue, 13 May 2025 15:31:37 +1000 Subject: [PATCH 1/2] Update registration endpoint to use db transaction to avoid integrity issues also changed duplicate email error to generic error to mitigate against potential username/email enumeration attacks --- memberportal/api_general/views.py | 64 ++++++++++++++++------------ src-frontend/src/i18n/en-AU/index.ts | 1 - src-frontend/src/i18n/sv-SE/index.ts | 64 ++++++++++++---------------- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/memberportal/api_general/views.py b/memberportal/api_general/views.py index 35097209..d1450d9c 100644 --- a/memberportal/api_general/views.py +++ b/memberportal/api_general/views.py @@ -4,6 +4,7 @@ login, logout, ) +from django.db import IntegrityError, transaction import logging from constance import config import json @@ -17,7 +18,6 @@ from rest_framework.views import APIView from .models import Kiosk, SiteSession, EmailVerificationToken from services.discord import post_kiosk_swipe_to_discord -from services.slack import post_kiosk_swipe_to_slack import base64 from urllib.parse import parse_qs, urlencode import hmac @@ -423,8 +423,8 @@ def put(self, request): # check if email is already in use if User.objects.filter(email=email).exists() and email != request.user.email: return Response( - {"message": "error.accountAlreadyExists"}, - status=status.HTTP_409_CONFLICT, + {"message": "error.contactUs"}, + status=status.HTTP_400_BAD_REQUEST, ) # check if screen name is already in use @@ -639,38 +639,46 @@ class Register(APIView): def post(self, request): body = request.data - if User.objects.filter(email=body.get("email").lower()).exists(): - return Response( - {"message": "error.accountAlreadyExists"}, - status=status.HTTP_409_CONFLICT, - ) - - if Profile.objects.filter(screen_name=body.get("screenName").lower()).exists(): + if ( + User.objects.filter(email=body.get("email").lower()).exists() + or Profile.objects.filter( + screen_name=body.get("screenName").lower() + ).exists() + ): return Response( - {"message": "error.screenNameAlreadyExists"}, - status=status.HTTP_409_CONFLICT, + {"message": "error.contactUs"}, + status=status.HTTP_400_BAD_REQUEST, ) - new_user = User.objects.create( - email=body.get("email").lower(), - email_verified=False, - ) + try: + with transaction.atomic(): + new_user = User.objects.create( + email=body.get("email").lower(), + email_verified=False, + ) - new_user.set_password(body.get("password")) - new_user.save() + new_user.set_password(body.get("password")) + new_user.save() - profile = Profile.objects.create( - user=new_user, - first_name=body.get("firstName"), - last_name=body.get("lastName"), - screen_name=body.get("screenName"), - phone=body.get("mobile"), - vehicle_registration_plate=body.get("vehicleRegistrationPlate"), - ) + profile = Profile.objects.create( + user=new_user, + first_name=body.get("firstName"), + last_name=body.get("lastName"), + screen_name=body.get("screenName"), + phone=body.get("mobile"), + vehicle_registration_plate=body.get("vehicleRegistrationPlate"), + ) - profile.save() + profile.save() - verification_token = EmailVerificationToken.objects.create(user=new_user) + verification_token = EmailVerificationToken.objects.create( + user=new_user + ) + except IntegrityError: + return Response( + {"message": "error.contactUs"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + ) url = f"{config.SITE_URL}/profile/email/{verification_token.verification_token}/verify/" verification_token.user.email_link( diff --git a/src-frontend/src/i18n/en-AU/index.ts b/src-frontend/src/i18n/en-AU/index.ts index 15dd1b46..fc8c1515 100644 --- a/src-frontend/src/i18n/en-AU/index.ts +++ b/src-frontend/src/i18n/en-AU/index.ts @@ -63,7 +63,6 @@ export default { error: 'Error', contactUs: 'Please contact us for help if you continue to see this error.', loginFailed: 'Your username or password was incorrect.', - accountAlreadyExists: 'Sorry, that email address has already been used.', screenNameAlreadyExists: 'Sorry, that screen name has already been used.', downloadFailed: 'Failed to download the file.', requestFailed: diff --git a/src-frontend/src/i18n/sv-SE/index.ts b/src-frontend/src/i18n/sv-SE/index.ts index e121b305..3d5df498 100644 --- a/src-frontend/src/i18n/sv-SE/index.ts +++ b/src-frontend/src/i18n/sv-SE/index.ts @@ -48,10 +48,8 @@ export default { usefulResources: 'Medlemsresurser', quickCards: 'Genvägar', quickActions: 'Snabbåtkomst', - signedIn: - 'Du är inloggad. Logga ut när du går.', - signinSuccess: - 'Lyckad inloggning. Kom ihåg att logga ut när du går.', + signedIn: 'Du är inloggad. Logga ut när du går.', + signinSuccess: 'Lyckad inloggning. Kom ihåg att logga ut när du går.', signoutError: 'Det uppstod ett problem vid utloggning. Rapportera felet om det uppstår igen.', signinError: @@ -63,16 +61,13 @@ export default { error: 'Fel', contactUs: 'Kontakta osss om du fortsätter få detta fel.', loginFailed: 'Ditt användarnamn eller lösenord är felaktigt.', - accountAlreadyExists: 'Denna email används redan.', screenNameAlreadyExists: 'Detta användarnamn är redan taget.', downloadFailed: 'Misslyckades att ladda hem filen.', - requestFailed: - "Det har uppstått ett fel. Försök igen senare.", + requestFailed: 'Det har uppstått ett fel. Försök igen senare.', pageNotFound: 'Sidan kan inte hittas', noValue: 'Inget värde', noData: 'Ingen data hittas', - stripeNotConfigured: - 'Betalplattformen Stripe är inte konfigurerad än.', + stripeNotConfigured: 'Betalplattformen Stripe är inte konfigurerad än.', postmarkNotConfigured: 'Mailplattformen Postmark är inte konfigurerad korrekt.', stripeNotConfiguredFeature: @@ -82,12 +77,12 @@ export default { 'Det uppstod ett problem vid kopering. Försök igen eller exportera som csv.', 400: ' Det uppstod ett fel med ditt anrop. (Error 400)', 401: ' Du måste vara inloggad för att komma åt denna sida. (Error 401)', - 403: " Du saknar rättigheter för att komma åt denna sida. (Error 403)", + 403: ' Du saknar rättigheter för att komma åt denna sida. (Error 403)', '403MemberOnly': 'Du måste vara ha ett aktivt medkelsmakp för att komma åt denna sida. (Error 403)', 404: ' Denna sida kan inte hittas. (Error 404)', 500: ' Det uppstod ett fel på servern. Försök igen senare. (Error 500)', - 501: " Denna funktion är ännu inte implementerad. (Error 501)", + 501: ' Denna funktion är ännu inte implementerad. (Error 501)', }, logoutPage: { logoutSuccess: 'Utloggning lyckades.', @@ -95,8 +90,7 @@ export default { 'Det uppstod ett fel vid utloggning. Uppdatera sidan och försök igen.', }, webcams: { - pageDescription: - 'Våra publika webcam foton uppdateras varje minut.', + pageDescription: 'Våra publika webcam foton uppdateras varje minut.', }, about: { title: 'Om MemberMatters', @@ -184,7 +178,7 @@ export default { invalidEmail: 'Ange en giltig email.', invalidPassword: 'Ange ett giltigt lösenord.', invalidPhone: 'Ange ett giltigt telefonnummer.', - passwordNotMatch: "Lösenorden matchar inte.", + passwordNotMatch: 'Lösenorden matchar inte.', cannotBeEmpty: 'Detta fält kan inte vara tomt.', futureDate: 'Datum måste vara idag eller i framtiden.', tooMany: 'Max är {number}.', @@ -211,7 +205,8 @@ export default { defaultAccess: 'Medlemar har åtkomst som default', maintenanceLockout: 'Underhållslåsning är påslagen', playTheme: 'Spela tema på swipe', - exemptSignin: 'Undantag från kravet på inloggning på webbplatsen (om aktiverad)', + exemptSignin: + 'Undantag från kravet på inloggning på webbplatsen (om aktiverad)', hiddenToMembers: 'Dold från medlemmar på deras åtkomst-sida', user: 'Användare', totalSwipes: 'Total Swipes', @@ -237,8 +232,7 @@ export default { form: { saved: 'Sparad', error: 'Fel vid sparning', - pageDescription: - 'Redigera fälten nedan och de kommer sparas automatiskt.', + pageDescription: 'Redigera fälten nedan och de kommer sparas automatiskt.', noResults: 'Inga resultat', allFieldsRequired: 'Alla fält markerade med * är obligatoriska.', featured: 'Utvalda?', @@ -295,9 +289,10 @@ export default { pageDescription: 'Fyll i formuläret nedan för att skapa ett nytt möte.', editDescription: 'Fyll i formuläret nedan för att uppdatera ett möte.', newMeeting: 'Nytt möte', - updatePastMeeting: "Du kan tyvärr inte uppdatera detta fält för gamla möten.", + updatePastMeeting: + 'Du kan tyvärr inte uppdatera detta fält för gamla möten.', noUpdateMeetingType: - "Du kan tyvärr inte uppdatera detta fält för existerande möten.", + 'Du kan tyvärr inte uppdatera detta fält för existerande möten.', meeting: 'Möte', success: 'Mötet skapades.', editSuccess: 'Mötet uppdaterades.', @@ -318,8 +313,8 @@ export default { meeting: 'Möte', yourCity: 'Din stad', - proxyName: "Ombudets namn", - proxyCity: "Ombudets stad", + proxyName: 'Ombudets namn', + proxyCity: 'Ombudets stad', newProxy: 'Nytt ombud', editTitle: 'Redigera ombud', @@ -337,15 +332,14 @@ export default { 'Klicka på en av knapparna för att lägga till pengar på ditt konto. Detta kommer ' + 'omedelbart belasta ditt sparade betal-kort som slutar på {savedCard}.', noSavedBilling: - "Du har inga giltiga betalmetoder. Lägg till en ny betalmetod " + + 'Du har inga giltiga betalmetoder. Lägg till en ny betalmetod ' + 'genom att klicka på knappen nedan.', manageBilling: 'Betalmetoder', selectToContinue: 'Bekräfta din betalmetod', addCard: 'Lägg till kort', addCardDescription: - "Lägg till ditt kort nedan. Vi sparar inte info om ditt betalkort förutom de sista 4 siffrorna och datum för utgång. Vår säkra betal betallösning som vi använder sparar informationen åt oss.", - addCardError: - 'Det uppstod ett fel med ditt kort. Försök igen senare.', + 'Lägg till ditt kort nedan. Vi sparar inte info om ditt betalkort förutom de sista 4 siffrorna och datum för utgång. Vår säkra betal betallösning som vi använder sparar informationen åt oss.', + addCardError: 'Det uppstod ett fel med ditt kort. Försök igen senare.', saveCard: 'Spara betalkort', savedCardTitle: 'Sparade betalkort', savedCardDescription: 'Dina sparade betalkort visas här nedan.', @@ -366,12 +360,12 @@ export default { }, loginRfidCard: { swipeCard: 'Dra ditt kort', - failed: "Vi kunde tyvärr inte logga in dig. Kontrollera ditt kort.", + failed: 'Vi kunde tyvärr inte logga in dig. Kontrollera ditt kort.', }, settings: { title: 'Kiosk-inställningar', description: - "Du har öppnat kiosk-inställningarna. Om detta var ett misstak, vänligen stäng detta " + + 'Du har öppnat kiosk-inställningarna. Om detta var ett misstak, vänligen stäng detta ' + 'fönster.', rfidScanner: { title: 'RFID Scanner', @@ -449,7 +443,7 @@ export default { makeMember: 'Skapa Member', makeMemberSuccess: 'Medlem skapad och välkomstmail skickat.', makeMemberError: 'Okänt fel när medlem skapades.', - makeMemberErrorEmail: "Fel vid utskick av välkomstmail.", + makeMemberErrorEmail: 'Fel vid utskick av välkomstmail.', makeMemberErrorExists: 'Personen är redan medlem. För att kolla profil, ändra filtret till "alla" medlemmar.', makeMemberSuccessDescription: @@ -555,7 +549,7 @@ export default { resumeButton: 'Återstarta medlemskap', cancelling: 'Ditt medlemskap håller på att avslutas', cancellingDescription: - "Ditt medlemskap kommer att avslutas på datumet {date}. Om du vill återstarta ditt medlemskap (se ovan), vänligen klicka nedan.", + 'Ditt medlemskap kommer att avslutas på datumet {date}. Om du vill återstarta ditt medlemskap (se ovan), vänligen klicka nedan.', renewalDate: 'Förnyelsedatum', signupDate: 'Registreringsdatum', subscriptionInfo: 'Abonnemangs-info', @@ -566,16 +560,15 @@ export default { }, signup: { induction: 'Introduktion', - requiredSteps: - 'Du måste färdigställa följande steg för att bli medlem.', + requiredSteps: 'Du måste färdigställa följande steg för att bli medlem.', completeInduction: 'Gå en introduktion', startInduction: 'Starta din introduktion', completedInduction: 'Introduktion genomförd', registerAccessCard: 'Registrera ditt nyckelkort', completeInductionDescription: - "Färdgiställ din introduktion genom att klicka på knappen nedan. Behåll denna sida öppen och kom tillbaka när du är klar.", + 'Färdgiställ din introduktion genom att klicka på knappen nedan. Behåll denna sida öppen och kom tillbaka när du är klar.', canvasEmailWarning: - "Använd samma email-adress som du hade vid registreringen ({email}) för att dina framsteg ska synkroniseras. Detta är en begränsning i Canvas-plattformen.", + 'Använd samma email-adress som du hade vid registreringen ({email}) för att dina framsteg ska synkroniseras. Detta är en begränsning i Canvas-plattformen.', waitingCompletion: 'Väntar på färdigställande...', accessCard: 'Nyckelkort', accessCardNumber: 'Nummer Nyckelkort', @@ -592,13 +585,12 @@ export default { continueToDashboard: 'Forsätt till dashboard', error: 'Fel vid inskickning av medelmsansökan', errorDescription: - "Det blev tyvärr ett oväntat fel när du skickade in din anökan. Vänligen kontakta oss via {email} så hjälper vi dig.", + 'Det blev tyvärr ett oväntat fel när du skickade in din anökan. Vänligen kontakta oss via {email} så hjälper vi dig.', errorMessageDescription: 'Vänligen skicka med felmeddelandet nedan:', requirementsNotMet: 'Krav som inte uppnåtts:', subscriptionFailed: 'Vi har tyvärr problem med att skapa ditt abonnemang. Kontrollera saldo på ditt betalkort, försök igen eller kontakta oss för hjälp.', - existingMemberSubscription: - 'Du har redan ett aktivt abonnemang på Stripe.', + existingMemberSubscription: 'Du har redan ett aktivt abonnemang på Stripe.', }, tiers: { disabledFeature: From 220bd8cb1c494728428f3b0ed79de0de9764a967 Mon Sep 17 00:00:00 2001 From: Jaimyn Mayer Date: Tue, 13 May 2025 15:50:09 +1000 Subject: [PATCH 2/2] fix metrics bug --- memberportal/api_metrics/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memberportal/api_metrics/views.py b/memberportal/api_metrics/views.py index bbb5bfda..a32b5a64 100644 --- a/memberportal/api_metrics/views.py +++ b/memberportal/api_metrics/views.py @@ -107,7 +107,7 @@ def post(self, request): Metric.MetricName.MEMBERBUCKS_BALANCE_TOTAL, Metric.MetricName.MEMBERBUCKS_TRANSACTIONS_TOTAL, ]: - prom_metric = getattr(api_metrics.metrics, metric.name) + prom_metric = getattr(api_metrics.Metric, metric.name) if not prom_metric: logger.error(f"Prometheus metric {metric.name} not found.")