Free and open source ticket system written in python

remove fbl integration

+11 -486
+2 -3
.github/workflows/docker-image.yml
··· 3 3 on: 4 4 push: 5 5 branches: 6 - - "fbl-integration" 7 - - "staging" 6 + - "main" 8 7 pull_request: 9 - branches: ["fbl-integration"] 8 + branches: ["main"] 10 9 11 10 env: 12 11 REGISTRY: ghcr.io
+2
core/views.py
··· 38 38 39 39 def login_view(request): 40 40 41 + auth_url = None 42 + 41 43 if settings.GOOGLE_OAUTH_ENABLED: 42 44 google_sso = GoogleSSO() 43 45 auth_url, state = google_sso.flow.authorization_url(prompt="consent")
fbl_integration/__init__.py

This is a binary file and will not be displayed.

-13
fbl_integration/admin.py
··· 1 - from django.contrib import admin 2 - from .models import FblAccount 3 - 4 - @admin.register(FblAccount) 5 - class FblAccountAdmin(admin.ModelAdmin): 6 - list_display = ["system_username", "username", "badge_number", "status", "tags_secured", "created_at"] 7 - search_fields = ["badge_number", "username"] 8 - list_filter = ["status"] 9 - readonly_fields = ["badge_number", "account_id", "username", "status", "tags_secured", "created_at"] 10 - 11 - def system_username(self, x): 12 - return x.user.username 13 - system_username.short_description = 'System Username'
-6
fbl_integration/apps.py
··· 1 - from django.apps import AppConfig 2 - 3 - 4 - class FblIntegrationConfig(AppConfig): 5 - default_auto_field = "django.db.models.BigAutoField" 6 - name = "fbl_integration"
-68
fbl_integration/forms.py
··· 1 - from typing import Any 2 - from django import forms 3 - from django.conf import settings 4 - from django.utils.translation import gettext_lazy as _ 5 - from datetime import datetime 6 - 7 - class FblAuthForm(forms.Form): 8 - badge_number = forms.CharField(required=True) 9 - dob = forms.CharField(required=True) 10 - 11 - def clean(self) -> dict[str, Any]: 12 - cleaned_data = super(FblAuthForm, self).clean() 13 - badge_number = cleaned_data.get("badge_number") 14 - dob = cleaned_data.get("dob") 15 - 16 - if not badge_number: 17 - raise forms.ValidationError( 18 - _("Badge number is required") 19 - ) 20 - 21 - try: 22 - int(badge_number) 23 - except ValueError: 24 - raise forms.ValidationError( 25 - _("Badge number must be a number") 26 - ) 27 - 28 - if not dob: 29 - raise forms.ValidationError( 30 - _("Date of birth is required") 31 - ) 32 - try: 33 - dob = datetime.strptime(dob, "%d/%m/%Y").strftime("%Y-%m-%d") 34 - except ValueError: 35 - raise forms.ValidationError( 36 - _("Date of birth must be in the format DD/MM/YYYY") 37 - ) 38 - cleaned_data["dob"] = dob 39 - 40 - return cleaned_data 41 - 42 - class FblAuthCodeForm(FblAuthForm): 43 - validation_code = forms.CharField(required=True) 44 - 45 - def clean(self) -> dict[str, Any]: 46 - cleaned_data = super(FblAuthCodeForm, self).clean() 47 - validation_code = cleaned_data.get("validation_code") 48 - 49 - if not validation_code: 50 - raise forms.ValidationError( 51 - _("The validation code is required. Please check your emails.") 52 - ) 53 - 54 - return cleaned_data 55 - 56 - class RegistrationCompletionForm(forms.Form): 57 - email = forms.EmailField(required=True) 58 - 59 - def clean(self) -> dict[str, Any]: 60 - cleaned_data = super(RegistrationCompletionForm, self).clean() 61 - email = cleaned_data.get("email") 62 - 63 - if not email: 64 - raise forms.ValidationError( 65 - _("A Mail is required for notifications.") 66 - ) 67 - 68 - return cleaned_data
-44
fbl_integration/migrations/0001_initial.py
··· 1 - # Generated by Django 5.0.3 on 2024-04-06 18:26 2 - 3 - import django.db.models.deletion 4 - from django.conf import settings 5 - from django.db import migrations, models 6 - 7 - 8 - class Migration(migrations.Migration): 9 - 10 - initial = True 11 - 12 - dependencies = [ 13 - migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 - ] 15 - 16 - operations = [ 17 - migrations.CreateModel( 18 - name="FblAccount", 19 - fields=[ 20 - ( 21 - "id", 22 - models.BigAutoField( 23 - auto_created=True, 24 - primary_key=True, 25 - serialize=False, 26 - verbose_name="ID", 27 - ), 28 - ), 29 - ("badge_number", models.IntegerField()), 30 - ("account_id", models.CharField(max_length=255)), 31 - ("username", models.CharField(max_length=255)), 32 - ("tags_secured", models.CharField(max_length=255)), 33 - ("created_at", models.DateTimeField(auto_now_add=True)), 34 - ("status", models.CharField(max_length=64)), 35 - ( 36 - "user", 37 - models.OneToOneField( 38 - on_delete=django.db.models.deletion.CASCADE, 39 - to=settings.AUTH_USER_MODEL, 40 - ), 41 - ), 42 - ], 43 - ), 44 - ]
fbl_integration/migrations/__init__.py

This is a binary file and will not be displayed.

-35
fbl_integration/models.py
··· 1 - from django.db import models 2 - from core.models import PawUser 3 - 4 - class FblAccount(models.Model): 5 - user = models.OneToOneField(PawUser, on_delete=models.CASCADE) 6 - badge_number = models.IntegerField() 7 - account_id = models.CharField(max_length=255) 8 - username = models.CharField(max_length=255) 9 - tags_secured = models.CharField(max_length=255) 10 - created_at = models.DateTimeField(auto_now_add=True) 11 - status = models.CharField(max_length=64) 12 - 13 - def tags_list(self): 14 - return self.tags_secured.split(',') 15 - 16 - @classmethod 17 - def create_user(cls, username) -> PawUser: 18 - if not PawUser.objects.filter(username=username).exists(): 19 - user = PawUser.objects.create(username=username) 20 - else: 21 - counter = 1 22 - while True: 23 - new_username = f"{username}_{counter}" 24 - if not PawUser.objects.filter(username=new_username).exists(): 25 - user = PawUser.objects.create(username=new_username) 26 - break 27 - counter += 1 28 - 29 - user.set_unusable_password() 30 - user.save() 31 - return user 32 - 33 - 34 - def __str__(self): 35 - return self.user.username
-43
fbl_integration/templates/complete_registration.html
··· 1 - {% extends 'base.html' %} 2 - {% block content %} 3 - {% load i18n %} 4 - <div class="self-center w-full max-w-xl mx-auto"> 5 - <div class="flex items-center"> 6 - <h1 class="text-3xl font-bold p-2">{% trans 'FurryBlacklight Login' %}</h1> 7 - </div> 8 - <div class="bg-base-200 rounded p-8"> 9 - <ul class="steps w-full mb-6"> 10 - <li class="step step-accent">{% trans 'Authenticate' %}</li> 11 - <li class="step step-accent">{% trans 'Confirm Code' %}</li> 12 - <li class="step step-accent">{% trans 'Account Completion' %}</li> 13 - <li class="step step-success">{% trans 'Done' %}</li> 14 - </ul> 15 - 16 - <div role="alert" class="alert alert-info mb-4"> 17 - {% trans 'Please set up a mail address. This will be used for notifications.' %} 18 - </div> 19 - 20 - <form method="post" action=""> 21 - {% csrf_token %} 22 - 23 - {% if form.non_field_errors %} 24 - <div role="alert" class="alert alert-error mb-4"> 25 - <svg xmlns="http://www.w3.org/2000/svg" class="hidden sm:block stroke-current shrink-0 h-6 w-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10l4 4m0 -4l-4 4" /><path d="M12 3c7.2 0 9 1.8 9 9s-1.8 9 -9 9s-9 -1.8 -9 -9s1.8 -9 9 -9z" /></svg> 26 - <span>{{ form.non_field_errors }}</span> 27 - </div> 28 - {% endif %} 29 - 30 - <div class="form-control w-full"> 31 - <label class="label"> 32 - <span class="text-base label-text" for="{{ form.email.id_for_label }}">{% trans 'Mail Address' %}</span> 33 - </label> 34 - <input type="email" name="email" class="w-full input input-bordered" /> 35 - </div> 36 - 37 - <div class="flex justify-end mt-6"> 38 - <button type="submit" class="btn btn-accent mb-4">{% trans 'Save Changes' %}</button> 39 - </div> 40 - </form> 41 - </div> 42 - </div> 43 - {% endblock %}
-58
fbl_integration/templates/fbl_auth_get_code.html
··· 1 - {% extends 'base.html' %} 2 - {% block content %} 3 - {% load i18n %} 4 - <div class="self-center w-full max-w-xl mx-auto"> 5 - <div class="flex items-center"> 6 - <h1 class="text-3xl font-bold p-2">{% trans 'FurryBlacklight Login' %}</h1> 7 - <div class="flex-grow"></div> 8 - <a href="{% url 'login' %}" class="btn btn-sm btn-neutral">{% trans 'Go Back' %}</a> 9 - </div> 10 - <div class="bg-base-200 rounded p-8"> 11 - <ul class="steps w-full mb-6"> 12 - <li class="step step-accent">{% trans 'Authenticate' %}</li> 13 - <li class="step step-accent">{% trans 'Confirm Code' %}</li> 14 - <li class="step">{% trans 'Account Completion' %}</li> 15 - <li class="step step-success">{% trans 'Done' %}</li> 16 - </ul> 17 - 18 - <div role="alert" class="alert alert-info mb-4"> 19 - {% trans 'We sent you an email that contains a code for your login. Please enter the code in the field below.' %} 20 - </div> 21 - <form method="post" action="/fbl/auth_get_code"> 22 - {% csrf_token %} 23 - 24 - {% if get_code_form.non_field_errors %} 25 - <div role="alert" class="alert alert-error mb-4"> 26 - <svg xmlns="http://www.w3.org/2000/svg" class="hidden sm:block stroke-current shrink-0 h-6 w-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10l4 4m0 -4l-4 4" /><path d="M12 3c7.2 0 9 1.8 9 9s-1.8 9 -9 9s-9 -1.8 -9 -9s1.8 -9 9 -9z" /></svg> 27 - <span>{{ get_code_form.non_field_errors }}</span> 28 - </div> 29 - {% endif %} 30 - 31 - <div class="form-control w-full"> 32 - <label class="label"> 33 - <span class="text-base label-text" for="{{ get_code_form.username.id_for_label }}">{% trans 'Badge Number' %}</span> 34 - </label> 35 - <input type="number" name="badge_number" class="w-full input input-bordered" value="{{ get_code_form.badge_number.value }}" readonly /> 36 - </div> 37 - <div class="form-control w-full"> 38 - <label class="label"> 39 - <span class="text-base label-text" for="{{ get_code_form.dob.id_for_label }}">{% trans 'Date of Birth' %}</span> 40 - </label> 41 - <input type="text" name="dob" class="w-full input input-bordered" value="{{ get_code_form.dob.value }}" readonly /> 42 - </div> 43 - 44 - <script src="https://cdn.jsdelivr.net/npm/@alpinejs/mask@3.x.x/dist/cdn.min.js"></script> 45 - <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> 46 - <div class="form-control w-full"> 47 - <label class="label"> 48 - <span class="text-base label-text" for="{{ get_code_form.username.id_for_label }}">{% trans 'Verify Code' %}</span> 49 - </label> 50 - <input type="text" name="validation_code" x-mask="999999" placeholder="000000" class="w-full input input-bordered text-2xl" /> 51 - </div> 52 - <div class="flex justify-end mt-6"> 53 - <button type="submit" class="btn btn-accent mb-4">{% trans 'Confirm Code' %}</button> 54 - </div> 55 - </form> 56 - </div> 57 - </div> 58 - {% endblock %}
-10
fbl_integration/templates/fbl_auth_start.html
··· 1 - <!-- templates/core/login.html --> 2 - {% extends 'base.html' %} 3 - {% block content %} 4 - {% load i18n %} 5 - <div class="self-center w-full max-w-xl mx-auto"> 6 - <div class="bg-base-200 rounded p-8"> 7 - {% include 'partials/attendee_login.html' with fbl_auth_form=form %} 8 - </div> 9 - </div> 10 - {% endblock %}
-21
fbl_integration/templates/partials/attendee_info.html
··· 1 - {% block attendee_info %} 2 - {% load i18n %} 3 - {% if fbl_account %} 4 - <h2 class="font-semibold text-xs mt-4 mb-2">{% trans 'Rawrgister Info' %}</h2> 5 - 6 - <div class="text-sm flex flex-col"> 7 - <div class="my-1 flex items-center"> 8 - {{ fbl_account.username }} <div class="ml-2 badge badge-accent">#{{ fbl_account.badge_number }}</div> 9 - </div> 10 - <div class="my-1 flex items-center"> 11 - <label class="font-semibold text-xs">{% trans 'Status' %}:</label> <div class="ml-2 badge badge-info">{{ fbl_account.status }}</div> 12 - </div> 13 - <div> 14 - <h3 class="font-semibold text-xs my-1">{% trans 'Tags' %}</h3> 15 - {% for tag in fbl_account.tags_list %} 16 - <div class="badge badge-accent badge-outline mr-2">{{ tag }}</div> 17 - {% endfor %} 18 - </div> 19 - </div> 20 - {% endif %} 21 - {% endblock %}
-32
fbl_integration/templates/partials/attendee_login.html
··· 1 - {% block attendee_login %} 2 - {% load i18n %} 3 - <form method="post" action="/fbl/auth_start"> 4 - {% csrf_token %} 5 - 6 - {% if fbl_auth_form.non_field_errors %} 7 - <div role="alert" class="alert alert-error mb-4"> 8 - <svg xmlns="http://www.w3.org/2000/svg" class="hidden sm:block stroke-current shrink-0 h-6 w-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 10l4 4m0 -4l-4 4" /><path d="M12 3c7.2 0 9 1.8 9 9s-1.8 9 -9 9s-9 -1.8 -9 -9s1.8 -9 9 -9z" /></svg> 9 - <span>{{ fbl_auth_form.non_field_errors }}</span> 10 - </div> 11 - {% endif %} 12 - 13 - <div> 14 - <label class="label"> 15 - <span class="text-base label-text" for="{{ fbl_auth_form.username.id_for_label }}">{% trans 'Badge Number' %}</span> 16 - </label> 17 - <input type="number" name="badge_number" placeholder="420" class="w-full input input-bordered" /> 18 - </div> 19 - <script src="https://cdn.jsdelivr.net/npm/@alpinejs/mask@3.x.x/dist/cdn.min.js"></script> 20 - <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> 21 - <label class="form-control w-full"> 22 - <div class="label"> 23 - <span class="text-base label-text" for="{{ fbl_auth_form.dob.id_for_label }}">{% trans 'Date of Birth' %}</span> 24 - </div> 25 - <input type="text" x-mask="99/99/9999" name="dob" placeholder="DD/MM/YYYY" class="w-full input input-bordered"> 26 - </label> 27 - <div class="flex justify-end mt-6"> 28 - <button type="submit" class="btn btn-accent mb-4">{% trans 'Log In with FBL' %}</button> 29 - </div> 30 - </form> 31 - {% if general_login %}<div class="divider">{% trans "I don\'t have a seat for this years FBL" %}</div>{% endif %} 32 - {% endblock %}
-1
fbl_integration/templates/partials/fbl_logo.html
··· 1 - <svg version="1.1" xml:space="preserve" class="w-full h-full" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2177.33 277.33"><defs><clipPath clipPathUnits="userSpaceOnUse" id="a"><path d="M0 456h1920V0H0z"/></clipPath></defs><g clip-path="url(#a)" transform="matrix(1.33333 0 0 -1.33333 -191.33 442.67)"><path d="M378.41 303.43h68.51v-.23c-6.1-5.61-11.87-8.42-17.3-8.42H407.1c-3.15 0-5.12-1.21-5.92-3.64l-1.36-4.1h35.5v-.23c-6.82-5.91-12.44-8.87-16.84-8.87h-21.85c-.3 0-1.74-4.1-4.32-12.3-.84-2.38-1.45-3.75-1.82-4.09H370.9l-.45.23a936.16 936.16 0 0111.6 34.82v1.37c0 1.55-1.21 3.3-3.64 5.23z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M442.71 303.43h17.3c5.16 0 7.74-1.6 7.74-4.78v-.91c0-.99-2.2-7.9-6.6-20.71-.76-2.5-1.14-4.1-1.14-4.78.45-1.07 1.14-1.6 2.05-1.6h21.84c2.93 0 5.05 3.27 6.38 9.8 3.34 9.36 5 14.82 5 16.38v.23c0 2.69-1.29 4.66-3.86 5.91v.46h18.66c3.07 0 5.12-1.37 6.14-4.1v-2.05c-5.3-17.56-8.57-27.2-9.78-28.9-3.76-4.55-13.17-6.83-28.23-6.83H460.7c-14.87 0-22.3 2.58-22.3 7.74 0 .5 2.8 9.44 8.42 26.85v1.83c0 2-1.37 3.67-4.1 5zM617.45 287.04h33.23c1.06 3.19 1.6 5 1.6 5.46v.69c0 1.06-.54 1.59-1.6 1.59h-25.95c-3.15 0-5.12-1.21-5.92-3.64zm-21.4 16.39h66.24c4.24 0 7.66-2.05 10.24-6.15.3-1.44.45-2.58.45-3.41 0-2.7-1.14-5.96-3.41-9.79-4.67-4.1-12.48-6.14-23.44-6.14v-.23c5.69-3.45 13.8-10.28 24.35-20.48 4.4-5.2 6.6-7.86 6.6-7.97.6-.76.91-1.21.91-1.37h-.23c-26.7 15.82-46.8 25.84-60.31 30.05h-3.19c-.3 0-1.74-4.1-4.32-12.3-.84-2.38-1.44-3.75-1.82-4.09h-19.58l-.45.23a934.2 934.2 0 0111.6 34.82v1.37c0 1.55-1.2 3.3-3.64 5.23z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M673.5 303.43h21.16c9.45-6.26 14.6-9.75 15.48-10.47 10.4 6.98 16 10.47 16.84 10.47h30.27v-.46a3333.1 3333.1 0 01-91.27-55.08h-.22v.23c3.75 7.06 10.58 15.93 20.48 26.63.19 0 2.39 1.97 6.6 5.92l7.28 5.69-26.63 16.61z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M657.31 223.71h53.86c.62 0 1.74 3.22 3.35 9.66v1.12c0 1.73-.87 2.6-2.6 2.6h-47.55c-2.66 0-4.64-3.35-5.94-10.03-.38 0-.75-1.12-1.12-3.35m-4.83-13.37c0-.56-.99-3.78-2.97-9.66.37-1.98 1.36-2.97 2.97-2.97h47.55c2.72 0 4.58 2.97 5.57 8.92.25 0 .74 1.23 1.49 3.71zm-29.34 41.23h104.38c10.46 0 17.27-3.96 20.43-11.88.24-1.68.37-3.04.37-4.09-1.3-7.68-2.54-11.52-3.72-11.52-1.6-2.72-5.32-4.82-11.14-6.3h-.37v-.38c4.4 0 7.12-1.86 8.17-5.57v-1.12c-4.15-15.35-7.62-23.03-10.4-23.03-3.9-2.97-7.86-4.45-11.89-4.45H610.14c12.38 37.08 18.57 56.02 18.57 56.83v3.71c0 2.42-1.86 4.77-5.57 7.06zM753.15 251.57h32.31c4.15 0 6.63-2.6 7.43-7.8 0-2.85-4.45-17.08-13.37-42.72v-.74c0-1.11 1.36-1.98 4.09-2.6h38.63c11.08 0 19.62-4.58 25.63-13.74v-.74H740.89l-.74.37c7.18 20.3 13.5 39.25 18.94 56.83v2.23c0 2.54-1.98 5.38-5.94 8.54z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M892.81 220h54.6c2.73 8.1 4.1 12.57 4.1 13.37v1.12c0 1.73-.87 2.6-2.6 2.6h-47.55c-2.67 0-4.65-3.35-5.95-10.03-.18 0-1.05-2.36-2.6-7.06m-32.68 31.57H964.5c10.47 0 17.28-3.96 20.43-11.88.25-1.68.38-3.04.38-4.09 0-4.83-5.45-22.29-16.35-52.37H935.9l-.74.37c4.46 12.07 7.06 19.5 7.8 22.28h-54.23c-.5 0-2.85-6.68-7.06-20.05-.68-1.74-1.18-2.6-1.49-2.6h-33.05c12.38 37.08 18.57 56.02 18.57 56.83v3.71c0 2.42-1.86 4.77-5.58 7.06zM988.83 251.57h112.55v-.37c-8.6-9.41-20.37-14.11-35.28-14.11h-33.06c-4.03 0-6.63-3.6-7.8-10.78-5.45-15.1-8.18-23.65-8.18-25.63.75-1.73 1.86-2.6 3.35-2.6h27.11c16.97 0 27.24-2.23 30.83-6.68 2.42-2.1 4.52-4.7 6.32-7.8l-.74-.37h-65.75c-24.27 0-36.4 4.2-36.4 12.62 0 .8 4.58 15.42 13.74 43.84v2.97c0 3.28-2.23 6-6.69 8.17z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M1098.5 251.57h32.32c4.15 0 6.62-2.6 7.43-7.8 0-3.1-2.97-12.88-8.91-29.34l55.71 37.14h34.18v-.37c-3.78-2.78-21.12-13.8-52-33.06 13.18-7.06 30.39-20.68 51.63-40.86 6.68-7.49 11.14-12.94 13.37-16.34h-.37c-47.86 28.17-82.4 45.13-103.64 50.89-6-19.07-9.35-28.6-10.03-28.6h-31.94l-.75.37a1524.9 1524.9 0 0118.95 56.83v2.23c0 2.54-1.98 5.38-5.95 8.54zM1262.6 251.57h32.31c4.15 0 6.62-2.6 7.43-7.8 0-2.85-4.46-17.08-13.37-42.72v-.74c0-1.11 1.36-1.98 4.08-2.6h38.63c11.09 0 19.63-4.58 25.64-13.74v-.74h-106.98l-.75.37a1524.9 1524.9 0 0118.95 56.83v2.23c0 2.54-1.98 5.38-5.95 8.54z" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M1369.2 251.57h32.32c4.15 0 6.62-2.6 7.43-7.8 0-2.85-4.7-17.83-14.12-44.94-3.34-9.85-5.32-15.05-5.94-15.6h-31.95l-.74.37c7.18 20.3 13.5 39.25 18.94 56.83v2.23c0 2.54-1.98 5.38-5.94 8.54zM1428.36 251.57h95.83v-.37c-8.92-9.9-22.29-14.86-40.12-14.86h-30.46c-3.15 0-5.38-3.47-6.68-10.4-.25-.3-3.35-9.35-9.29-27.11h52.38c2.47 0 4.45 2.47 5.94 7.43 0 2.72-2.1 4.08-6.32 4.08h-41.23v.74c11.7 8.92 20.5 13.38 26.38 13.38h26.74c19.56-1.24 29.35-5.08 29.35-11.52-2.98-13.12-5.45-19.68-7.43-19.68-6-6.7-20.5-10.03-43.46-10.03h-49.03c-17.53 1.11-27.06 4.2-28.6 9.28a8.61 8.61 0 00-.75 3.34c9.9 32.32 15.35 49.65 16.34 52 2.36 2.48 5.82 3.72 10.4 3.72M1552.24 251.57h32.31c.5-.06.74-.3.74-.74l-8.54-26.37h42.72c5.45 17.52 8.54 26.56 9.28 27.11h31.95c.5-.06.74-.3.74-.74-13.12-41.1-20.43-63.64-21.91-67.6h-33.06c-.5 0-.75.24-.75.74a483.9 483.9 0 018.55 26.37h-42.35c-5.7-18.08-8.79-27.11-9.28-27.11h-33.43c14.6 45.56 22.28 68.34 23.03 68.34M1663.95 251.57h112.55v-.37c-8.8-9.41-20.3-14.11-34.54-14.11l-18.58-53.86h-32.31l-.75.37 18.2 53.49h-.74c-18.94 0-33.55 4.7-43.83 14.11zM541.15 291.14c.8 2.43 2.76 3.64 5.91 3.64h25.95c1.06 0 1.6-.53 1.6-1.6v-.68c0-.45-.54-2.27-1.6-5.46h-33.23zm65.27-110.6l1.18 3.53c8.13 24.37 12.82 38.6 15.44 46.69-8.27 14.48-25.08 19-54.58 46.95v.23c10.96 0 18.77 2.05 23.44 6.14 2.28 3.83 3.41 7.1 3.41 9.79 0 .83-.15 1.97-.45 3.41-2.58 4.1-6 6.15-10.24 6.15h-66.23v-.23c2.42-1.93 3.64-3.68 3.64-5.23v-1.37a935.52 935.52 0 00-11.61-34.82l.46-.23h19.57c.38.34.98 1.7 1.82 4.1 2.58 8.2 4.02 12.29 4.32 12.29h3.19c13.5-4.21 37.18-16.15 47.9-23.29 15.29-10.2 35.7-38.38 7.13-49.09 4.62-1.05 8.3-.13 11.25 1.87-28.2-42.92-56.77-9-56.77-2.76-16.95-47.3 35.7-49.98 43.74-43.73-2.23-5.86-11.6-8.03-14.28-8.03 19.89-2.85 40.33 12.97 47.85 27.64zM202.8 242.6c-.83 13.59 9.06 26.35 22.65 37.06-.83 4.12.82 9.88.82 9.88s-24.7-11.53-30.47-31.3c2.06-4.94 1.23-11.52 1.23-11.52s-1.23 13.17-9.06 21.4c0 0-1.64-17.7-7.4-29.23-5.77-11.53-9.48-31.3 8.64-48.18a17.74 17.74 0 003.7 2.47s-22.23 14-8.23 38.71c0 0 .41 3.7 8.65-4.12.82 3.3 3.04 11.4 9.47 14.83M278.16 184.53s2.06-2.88.82-7c3.3 1.65 6.18 6.6 6.18 6.6s-4.53 1.23-7 .4M313.57 197.3c2.47 4.12 1.65 11.12-3.3 9.47 6.09 5.06 11.54-1.23 3.3-9.47m-6.59 19.35c-11.53-16.88 5.36-24.7 7-23.47 22.24 18.12 8.25 25.88-7 23.47M263.33 245.48c6.6 2.88 5.77 7.41 7.83 21 7.82 10.7 26.35 4.53 26.35 4.53s-11.53-.82-14-6.59c1.65 2.89 12.36 2.47 16.89 2.47 4.53 0 13.58 6.18 9.06 15.65 8.23-.41 17.7-27.59-17.3-42.41-3.7-1.65-6.59-3.71-11.12-1.24-4.53 2.47-13.18 7-17.7 6.59M253.45 220.36s2.06 4.94 5.35 4.12c3.3-.82 2.89-4.94 2.89-4.94s-4.95 1.23-8.24.82" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M270.33 217.89c-1.23-2.88-7.4.41-12.35 1.23-4.94.83-9.47-1.64-9.47 1.65 0 3.3 10.35 9.78 17.3 8.24 7.4-1.65 5.76-8.24 4.52-11.12m-34.18 35.41c-1.64-12.76-7.82-6.58-7-2.47 4.19 20.92 34.18 41.6 34.18 41.6-11.53-10.3-25.53-26.36-27.18-39.13m69.19-35.82s-19.36 5.35-24.71 1.64c-5.35-3.7-3.7-.4-.82 1.24-.36.5 3.16 8.03 4.11 12.35 1.31 5.94-17.7 10.71-22.65 11.12-4.94.41-15.64-5.35-15.64-5.35s0 2.06 11.11 6.59c17.98 7.32 6.18 19.76 20.18 51.47 10.26 23.24-10.47 14.92-50.65-19.76-39.12-33.77-14-51.07-7.41-49.42-8.24-2.47-15.24 5.35-15.78 12.44-4.81-1.73-8.52-12.85-8.52-12.85 1.65-2.88 6.18-6.59 8.65-7.83-8.24 1.65-15.92 13.52-17.7 10.3-10.3-18.53 4.52-32.94 13.17-37.06-10.3-1.65-10.7-8.65-10.7-8.65 13.58 6.18 17.7-4.94 26.76-5.76-10.7 11.94 4.12 26.35 4.12 26.35s-10.3-16.06-.41-25.12c14.62-13.4 18.12 4.53 55.18-11.53 16.92-7.33 23.88 8.65 25.12 9.88 1.23 1.24-7 5.77-12.36 5.77a19.86 19.86 0 00-8.23-7.83s.41 5.36-2.47 8.24c-2.47 0-13.18 0-19.36 5.77-6.17 5.76-14.82 9.88-21 10.3 11.94 2.87 21-3.72 21-3.72s-4.53.83-4.94 0c6.18-2.88 3.3-10.29 20.18-9.88 18.95.46 28.41-6.59 28.41-6.59s8.24 4.12 10.3 11.53c-17.7 13.18-4.94 26.36-4.94 26.36" fill="#000" fill-opacity="1" fill-rule="nonzero"/><path d="M253.68 136.35c-54.03 0-97.83 43.8-97.83 97.83 0 53 42.17 96.16 94.8 97.77-1.05.03-2.1.05-3.15.05-57.44 0-104-46.56-104-104s46.56-104 104-104 104 46.56 104 104c0 1.05-.02 2.1-.05 3.14-1.6-52.62-44.76-94.79-97.77-94.79" fill="#000" fill-opacity="1" fill-rule="nonzero"/></g></svg>
-3
fbl_integration/tests.py
··· 1 - from django.test import TestCase 2 - 3 - # Create your tests here.
-9
fbl_integration/urls.py
··· 1 - from django.urls import path 2 - 3 - from .views import fbl_authentication_start, fbl_authentication_get_code, complete_registration 4 - 5 - urlpatterns = [ 6 - path("auth_start", fbl_authentication_start, name="fbl_auth_start"), 7 - path("auth_get_code", fbl_authentication_get_code, name="fbl_auth_get_code"), 8 - path("complete_registration", complete_registration, name="fbl_complete_registration"), 9 - ]
-55
fbl_integration/utils.py
··· 1 - import requests 2 - from django.conf import settings 3 - from .models import FblAccount 4 - 5 - def fbl_auth_request_code(badge_number: int, dob: str) -> bool: 6 - res = requests.post(f'{settings.FBL_AUTH_SERVER}/auth/get-code', data={ 7 - "badge": badge_number, 8 - "password": dob 9 - }) 10 - if res.status_code != 201: 11 - print(res.status_code) 12 - return False 13 - return True 14 - 15 - def fbl_auth_validate_code(badge_number: str, dob: str, validation_code: str) -> str | None: 16 - res = requests.post(f'{settings.FBL_AUTH_SERVER}/auth/signin', json={ 17 - "badge": badge_number, 18 - "password": dob, 19 - "code": validation_code 20 - }) 21 - if res.status_code != 201: 22 - print(res.status_code) 23 - return None 24 - 25 - return res.json()["accessToken"] 26 - 27 - def fbl_auth_get_account(jwt_token: str) -> dict[str, str] | None: 28 - res = requests.get(f'{settings.FBL_AUTH_SERVER}/attendees/me', headers={ 29 - "Authorization": f"Bearer {jwt_token}" 30 - }) 31 - if res.status_code != 200: 32 - print(res.status_code) 33 - return None 34 - 35 - return res.json() 36 - 37 - def get_or_create_account(account_info: dict[str, str]) -> FblAccount: 38 - """Get or create a FblAccount object based on the account info""" 39 - if FblAccount.objects.filter(account_id=account_info["account_id"]).exists(): 40 - # Get existing user and update status 41 - user = FblAccount.objects.get(account_id=account_info["account_id"]) 42 - user.status = account_info["status"] 43 - user.save() 44 - return user 45 - 46 - # Create new user with (generic) username 47 - user = FblAccount.create_user(account_info["username"]) 48 - return FblAccount.objects.create( 49 - user=user, 50 - badge_number=account_info["badge"], 51 - account_id=account_info["account_id"], 52 - status=account_info["status"], 53 - username=account_info["username"], 54 - tags_secured=','.join(account_info["tags_secured"]) 55 - )
-62
fbl_integration/views.py
··· 1 - from django.shortcuts import render, redirect 2 - from django.contrib.auth import login 3 - from datetime import datetime 4 - from .forms import FblAuthForm, FblAuthCodeForm, RegistrationCompletionForm 5 - from .utils import fbl_auth_request_code, fbl_auth_validate_code, fbl_auth_get_account, get_or_create_account 6 - 7 - def fbl_authentication_start(request): 8 - print('fbl auth started') 9 - auth_form = FblAuthForm(request.POST or None) 10 - if request.method == "POST": 11 - if auth_form.is_valid(): 12 - 13 - valid_info = fbl_auth_request_code( 14 - badge_number=auth_form.cleaned_data["badge_number"], 15 - dob=auth_form.cleaned_data["dob"] 16 - ) 17 - if valid_info: 18 - code_form = FblAuthCodeForm(initial={ 19 - **auth_form.cleaned_data, 20 - "dob": datetime.strptime(auth_form.cleaned_data["dob"], "%Y-%m-%d").strftime("%d/%m/%Y"), 21 - }) 22 - 23 - return render(request, "fbl_auth_get_code.html", {"get_code_form": code_form}) 24 - 25 - auth_form.add_error(None, "Invalid badge number or date of birth") 26 - 27 - return render(request, "fbl_auth_start.html", {"form": auth_form}) 28 - 29 - 30 - def fbl_authentication_get_code(request): 31 - code_form = FblAuthCodeForm(request.POST or None) 32 - if request.method == "POST": 33 - if code_form.is_valid(): 34 - access_token = fbl_auth_validate_code( 35 - badge_number=code_form.cleaned_data["badge_number"], 36 - dob=code_form.cleaned_data["dob"], 37 - validation_code=code_form.cleaned_data["validation_code"] 38 - ) 39 - if access_token: 40 - account_info = fbl_auth_get_account(access_token) 41 - fbl_account = get_or_create_account(account_info) 42 - 43 - login(request=request, user=fbl_account.user) 44 - 45 - if not fbl_account.user.email: 46 - return redirect("fbl_complete_registration") 47 - else: 48 - return redirect("all_tickets") 49 - 50 - code_form.add_error(None, "Invalid validation code") 51 - 52 - 53 - return render(request, "fbl_auth_get_code.html", {"get_code_form": code_form}) 54 - 55 - def complete_registration(request): 56 - form = RegistrationCompletionForm(request.POST or None) 57 - if request.method == "POST": 58 - if form.is_valid(): 59 - request.user.email = form.cleaned_data["email"] 60 - request.user.save() 61 - return redirect("all_tickets") 62 - return render(request, "complete_registration.html", {"form": form})
+1 -2
paw/__init__.py
··· 1 1 from django import get_version 2 2 3 3 PAW_VERSION = (0, 5, 10, "final", 0) 4 - FBL_ITERATION = 4 5 4 6 - __version__ = f"{get_version(PAW_VERSION)}-fbl{FBL_ITERATION}" 5 + __version__ = get_version(PAW_VERSION)
+5 -11
paw/settings.py
··· 45 45 "django.contrib.messages", 46 46 "django.contrib.staticfiles", 47 47 "status", 48 - "fbl_integration", 49 48 ] 50 49 51 50 AUTH_USER_MODEL = "core.PawUser" ··· 68 67 "BACKEND": "django.template.backends.django.DjangoTemplates", 69 68 "DIRS": [ 70 69 BASE_DIR / 'paw' / 'templates', 71 - BASE_DIR / 'fbl_integration' / 'templates', 72 70 ], 73 71 "APP_DIRS": True, 74 72 "OPTIONS": { ··· 186 184 187 185 # Google SSO 188 186 GOOGLE_OAUTH_ENABLED = environ.get('GOOGLE_OAUTH_ENABLED').lower() == 'true' 189 - GOOGLE_OAUTH_CLIENT_ID = environ['GOOGLE_OAUTH_CLIENT_ID'] 190 - GOOGLE_OAUTH_PROJECT_ID = environ['GOOGLE_OAUTH_PROJECT_ID'] 191 - GOOGLE_OAUTH_CLIENT_SECRET = environ['GOOGLE_OAUTH_CLIENT_SECRET'] 192 - GOOGLE_OAUTH_REDIRECT_URI = environ['GOOGLE_OAUTH_REDIRECT_URI'] 193 - GOOGLE_OAUTH_SCOPES = environ.get('GOOGLE_OAUTH_SCOPES', '').split(",") 194 - 195 - # FBL Integration 196 - FBL_AUTH_ENABLED = environ.get('FBL_AUTH_ENABLED').lower() == 'true' 197 - FBL_AUTH_SERVER = environ['FBL_AUTH_SERVER'] 187 + GOOGLE_OAUTH_CLIENT_ID = environ.get('GOOGLE_OAUTH_CLIENT_ID') 188 + GOOGLE_OAUTH_PROJECT_ID = environ.get('GOOGLE_OAUTH_PROJECT_ID') 189 + GOOGLE_OAUTH_CLIENT_SECRET = environ.get('GOOGLE_OAUTH_CLIENT_SECRET') 190 + GOOGLE_OAUTH_REDIRECT_URI = environ.get('GOOGLE_OAUTH_REDIRECT_URI') 191 + GOOGLE_OAUTH_SCOPES = environ.get('GOOGLE_OAUTH_SCOPES', '').split(",")
+1 -6
paw/templates/core/login.html
··· 3 3 {% block content %} 4 4 {% load i18n %} 5 5 <div class="self-center w-full max-w-xl mx-auto"> 6 - <div class="p-4"> 7 - {% include 'partials/fbl_logo.html' %} 8 - </div> 9 6 <div class="flex items-center"> 10 7 <h1 class="text-3xl font-bold p-2">{% trans 'Log In' %}</h1> 11 8 <div class="flex-grow"></div> 12 9 <a href="{% url 'register' %}" class="btn btn-sm btn-neutral">{% trans 'Register Account' %}</a> 13 10 </div> 14 11 <div class="bg-base-200 rounded p-8"> 15 - 16 - {% include 'partials/attendee_login.html' with general_login=True %} 17 12 18 13 <form method="post"> 19 14 {% csrf_token %} ··· 46 41 </div> 47 42 </form> 48 43 {% if google_sso_enabled %} 49 - <div class="divider">Staff Login</div> 44 + <div class="divider"></div> 50 45 <a href="{{ google_sso_auth_url }}" class="btn w-full bg-[#4285F4] hover:bg-[#4285F4]/90 border-[#4285F4] text-white"> 51 46 <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a9.96 9.96 0 0 1 6.29 2.226a1 1 0 0 1 .04 1.52l-1.51 1.362a1 1 0 0 1 -1.265 .06a6 6 0 1 0 2.103 6.836l.001 -.004h-3.66a1 1 0 0 1 -.992 -.883l-.007 -.117v-2a1 1 0 0 1 1 -1h6.945a1 1 0 0 1 .994 .89c.04 .367 .061 .737 .061 1.11c0 5.523 -4.477 10 -10 10s-10 -4.477 -10 -10s4.477 -10 10 -10z" stroke-width="0" fill="currentColor" /></svg> 52 47 {% trans 'Log in with Google' %}
-2
paw/templates/ticketing/ticket_detail.html
··· 159 159 </div> 160 160 {% endif %} 161 161 162 - {% include 'partials/attendee_info.html' with fbl_account=ticket.user.fblaccount %} 163 - 164 162 <div class="divider"></div> 165 163 <h2 class="font-semibold mb-4">{% trans 'Category' %}</h2> 166 164 <div class="text-base-content/85 flex items-center text-sm mb-6">
-1
paw/urls.py
··· 26 26 path("", include("core.urls")), 27 27 path("", include("ticketing.urls")), 28 28 path("status", include("status.urls")), 29 - path("fbl/", include("fbl_integration.urls")), 30 29 ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 31 30 32 31 if settings.DEBUG:
-1
theme/tailwind.config.js
··· 2 2 module.exports = { 3 3 content: [ 4 4 "../paw/templates/**/*.{html,js}", 5 - "../fbl_integration/templates/**/*.{html,js}", 6 5 "../status/templates/**/*.{html,js}", 7 6 ], 8 7 theme: {