From 31ff575485b5a9bd7e28f312c337402ebed0be75 Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Tue, 18 Nov 2025 17:37:36 -0500 Subject: [PATCH 1/3] Actually use jurisdiction `context_processor` Get it to use a general `get_jurisdiction_from_request`, instead of assuming it will always be in the session data. --- efile_app/efile/admin.py | 1 - efile_app/efile/authentication.py | 14 ++--------- efile_app/efile/context_processors.py | 14 ++++------- .../efile/templates/efile/case_details.html | 2 +- .../efile/components/profile_header.html | 25 +++---------------- efile_app/efile/templates/efile/login.html | 4 +-- efile_app/efile/templates/efile/options.html | 2 +- efile_app/efile/utils/jurisdiction_stuff.py | 11 ++++++++ efile_app/efile/views/case_details.py | 7 +----- efile_app/efile/views/confirmation.py | 1 - efile_app/efile/views/expert_form.py | 5 ---- efile_app/efile/views/login.py | 3 +-- efile_app/efile/views/options.py | 5 ---- efile_app/efile/views/register.py | 4 +-- efile_app/efile/views/review.py | 4 --- efile_app/efile/views/upload.py | 5 ---- 16 files changed, 30 insertions(+), 77 deletions(-) delete mode 100644 efile_app/efile/admin.py create mode 100644 efile_app/efile/utils/jurisdiction_stuff.py diff --git a/efile_app/efile/admin.py b/efile_app/efile/admin.py deleted file mode 100644 index 846f6b4..0000000 --- a/efile_app/efile/admin.py +++ /dev/null @@ -1 +0,0 @@ -# Register your models here. diff --git a/efile_app/efile/authentication.py b/efile_app/efile/authentication.py index f7d7dce..bc590b5 100644 --- a/efile_app/efile/authentication.py +++ b/efile_app/efile/authentication.py @@ -3,17 +3,17 @@ from django.contrib.auth import get_user_model from django.contrib.auth.backends import BaseBackend +from efile.utils.jurisdiction_stuff import get_jurisdiction_from_request from efile.utils.proxy_connection import auth_with_tyler_api logger = logging.getLogger(__name__) User = get_user_model() - class SuffolkEFileBackend(BaseBackend): def authenticate(self, request, username=None, password=None, **kwargs): logger.info("Trying auth?") - jurisdiction = kwargs.get("jurisdiction", self._get_jurisdiction_from_request(request)) + jurisdiction = kwargs.get("jurisdiction", get_jurisdiction_from_request(request)) if not username or not password or not jurisdiction: return None @@ -44,16 +44,6 @@ def get_user(self, user_id): except User.DoesNotExist: return None - def _get_jurisdiction_from_request(self, request): - jurisdiction = request.GET.get("jurisdiction") - if jurisdiction: - return jurisdiction.lower() - segments = request.path.split("/") - # TODO(brycew): okay, maybe it makes sense to add an extra segment to the path... - if len(segments) >= 2 and segments[1] not in ["api", "options", "login", "register", "upload", "review"]: - return segments[1].lower() - return None - def _get_or_create_user(self, username, auth_data, jurisdiction): try: user = User.objects.get(username=username) diff --git a/efile_app/efile/context_processors.py b/efile_app/efile/context_processors.py index 721f294..619c337 100644 --- a/efile_app/efile/context_processors.py +++ b/efile_app/efile/context_processors.py @@ -3,20 +3,16 @@ """ from .utils.config_loader import config_loader +from .utils.jurisdiction_stuff import get_jurisdiction_from_request def jurisdiction_context(request): - """Add current jurisdiction to all template contexts""" - current_jurisdiction = request.session.get("jurisdiction", "illinois") + """Add current jurisdiction and it's config to all template contexts.""" - config = config_loader.get_short_jurisdiction_config(current_jurisdiction) - short_configs = { - code: config_loader.get_short_jurisdiction_config(code) for code in config_loader.get_available_jurisdictions() - } + current_jurisdiction = get_jurisdiction_from_request(request) + config = config_loader.load_jurisdiction_config(current_jurisdiction) return { "jurisdiction": current_jurisdiction, - # Returns just a subset of the keys in the config - "jurisdiction_config": config, - "available_jurisdictions": short_configs, + "config": config, } diff --git a/efile_app/efile/templates/efile/case_details.html b/efile_app/efile/templates/efile/case_details.html index 174e102..a5f575a 100644 --- a/efile_app/efile/templates/efile/case_details.html +++ b/efile_app/efile/templates/efile/case_details.html @@ -4,7 +4,7 @@ - Case Details - {{ jurisdiction_config.name }} + Case Details - {{ config.jurisdiction.name }}
- -

{{ jurisdiction_config.name }}

+ +

{{ config.jurisdiction.name }}

-
@@ -46,21 +36,14 @@
diff --git a/efile_app/efile/templates/efile/options.html b/efile_app/efile/templates/efile/options.html index 6322303..48ffd8a 100644 --- a/efile_app/efile/templates/efile/options.html +++ b/efile_app/efile/templates/efile/options.html @@ -4,7 +4,7 @@ - {{ jurisdiction_config.name }} + {{ config.jurisdiction.name }} = 2 and segments[1] not in ["api", "options", "login", "register", "upload", "review"]: + return segments[1].lower() + + return None \ No newline at end of file diff --git a/efile_app/efile/views/case_details.py b/efile_app/efile/views/case_details.py index 287ac11..8503637 100644 --- a/efile_app/efile/views/case_details.py +++ b/efile_app/efile/views/case_details.py @@ -7,8 +7,6 @@ from django.shortcuts import render from django.views.decorators.http import require_http_methods -from efile.utils.config_loader import config_loader - logger = logging.getLogger(__name__) @@ -54,11 +52,8 @@ def case_details(request, jurisdiction): logger.debug("Cache NOT cleared - preserving existing session data") # Get jurisdiction config (can be expanded later for other jurisdictions) - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) - - context = {"jurisdiction_config": jurisdiction_config, "jurisdiction": jurisdiction} + return render(request, "efile/case_details.html") - return render(request, "efile/case_details.html", context) except Exception as e: logger.error(f"Error loading case details page: {str(e)}") diff --git a/efile_app/efile/views/confirmation.py b/efile_app/efile/views/confirmation.py index 8884ae3..338d7af 100644 --- a/efile_app/efile/views/confirmation.py +++ b/efile_app/efile/views/confirmation.py @@ -8,7 +8,6 @@ def filing_confirmation(request, jurisdiction): # or from database if you're storing submitted filings context = { - "jurisdiction": jurisdiction, "page_title": "Filing Confirmation", "success_message": "Your filing has been successfully submitted!", } diff --git a/efile_app/efile/views/expert_form.py b/efile_app/efile/views/expert_form.py index ec991f3..227e262 100644 --- a/efile_app/efile/views/expert_form.py +++ b/efile_app/efile/views/expert_form.py @@ -2,8 +2,6 @@ from django.shortcuts import redirect, render -from efile.utils.config_loader import config_loader - logger = logging.getLogger(__name__) @@ -74,10 +72,7 @@ def efile_expert_form(request, jurisdiction): has_party_info = all(case_data.get(field) for field in party_fields) # Display the form for data collection with existing data populated - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) context = { - "jurisdiction": jurisdiction, - "jurisdiction_config": jurisdiction_config, "case_data": case_data, "auth_tokens": auth_tokens, "can_proceed_to_upload": has_all_required and has_party_info, diff --git a/efile_app/efile/views/login.py b/efile_app/efile/views/login.py index 04ba9e9..ccca19d 100644 --- a/efile_app/efile/views/login.py +++ b/efile_app/efile/views/login.py @@ -47,8 +47,7 @@ def efile_login(request, jurisdiction): except Exception as e: logger.exception("Login request failed") messages.error(request, f"Login failed: {str(e)}") - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) - context = {"login_form": login_form, "jurisdiction": jurisdiction, "jurisdiction_config": jurisdiction_config} + context = {"login_form": login_form} return render(request, "efile/login.html", context) diff --git a/efile_app/efile/views/options.py b/efile_app/efile/views/options.py index b547776..869027c 100644 --- a/efile_app/efile/views/options.py +++ b/efile_app/efile/views/options.py @@ -1,7 +1,5 @@ from django.shortcuts import redirect, render -from efile.utils.config_loader import config_loader - from ..utils.case_data_utils import get_case_data @@ -14,12 +12,9 @@ def efile_options(request, jurisdiction): case_data = get_case_data(request) # Pass case data to template for display - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) context = { "case_data": case_data, "has_case_data": bool(case_data), - "jurisdiction": jurisdiction, - "jurisdiction_config": jurisdiction_config, } return render(request, "efile/options.html", context) diff --git a/efile_app/efile/views/register.py b/efile_app/efile/views/register.py index 4cdbe84..c784ae8 100644 --- a/efile_app/efile/views/register.py +++ b/efile_app/efile/views/register.py @@ -64,7 +64,7 @@ def check_password_strength(pw): ) if form.errors: - return render(request, "efile/register.html", {"form": form, "jurisdiction": jurisdiction}) + return render(request, "efile/register.html", {"form": form}) if form.is_valid(): state_abbr = form.cleaned_data["state"] @@ -131,4 +131,4 @@ def check_password_strength(pw): else: # Always show a blank form on reload form = EFileRegistrationForm() - return render(request, "efile/register.html", {"form": form, "jurisdiction": jurisdiction}) + return render(request, "efile/register.html", {"form": form}) diff --git a/efile_app/efile/views/review.py b/efile_app/efile/views/review.py index d8a5f0b..a12df84 100644 --- a/efile_app/efile/views/review.py +++ b/efile_app/efile/views/review.py @@ -4,7 +4,6 @@ from django.shortcuts import redirect, render from ..utils.case_data_utils import get_case_classification, get_case_data, get_name_sought_info, get_petitioner_info -from ..utils.config_loader import config_loader logger = logging.getLogger(__name__) @@ -92,10 +91,7 @@ def case_review(request, jurisdiction): "items": [{"label": "Selected Services", "value": ", ".join(optional_services)}], } - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) context = { - "jurisdiction": jurisdiction, - "jurisdiction_config": jurisdiction_config, "case_data": case_data, "review_sections": review_sections, "friendly_names": { diff --git a/efile_app/efile/views/upload.py b/efile_app/efile/views/upload.py index 39506d5..e3526d5 100644 --- a/efile_app/efile/views/upload.py +++ b/efile_app/efile/views/upload.py @@ -10,7 +10,6 @@ from django.views.decorators.http import require_http_methods from ..utils.case_data_utils import get_case_classification, get_case_data, get_name_sought_info, get_petitioner_info -from ..utils.config_loader import config_loader from ..utils.s3_upload import s3_handler logger = logging.getLogger(__name__) @@ -160,11 +159,7 @@ def efile_upload(request, jurisdiction): friendly_filing_type = case_data.get("filing_type_name", case_classification["filing_type"]) friendly_court = case_data.get("court_name", case_classification["court"]) - jurisdiction_config = config_loader.get_short_jurisdiction_config(jurisdiction) - context = { - "jurisdiction": jurisdiction, - "jurisdiction_config": jurisdiction_config, "case_data": case_data, "petitioner_info": petitioner_info, "name_sought_info": name_sought_info, From caec4bc437e612720f04b60bbd2b67abf9b111de Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Tue, 18 Nov 2025 17:37:36 -0500 Subject: [PATCH 2/3] Only use UserProfile for user db stuff --- README.md | 6 +- efile_app/efile/api/auth_views.py | 3 - efile_app/efile/authentication.py | 8 +- efile_app/efile/context_processors.py | 2 +- efile_app/efile/models.py | 167 +-------------- efile_app/efile/settings_base.py | 2 + .../efile/components/profile_header.html | 2 +- efile_app/efile/utils/proxy_connection.py | 2 +- efile_app/efile/views.py | 201 ------------------ 9 files changed, 17 insertions(+), 376 deletions(-) delete mode 100644 efile_app/efile/views.py diff --git a/README.md b/README.md index 7491cbb..6f37cc5 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A minimal Django app for form submission and review. The Django project lives un - __2) Initialize the database__ ```bash cd efile_app - uv run python manage.py migrate + uv run python manage.py migrate --run-syncdb ``` - __3) Run the development server__ @@ -47,7 +47,7 @@ A minimal Django app for form submission and review. The Django project lives un ```bash source .venv/bin/activate cd efile_app - python manage.py migrate + python manage.py migrate --run-syncdb python manage.py runserver ``` @@ -55,7 +55,7 @@ A minimal Django app for form submission and review. The Django project lives un ```powershell .venv\Scripts\Activate.ps1 cd efile_app - python manage.py migrate + python manage.py migrate --run-syncdb python manage.py runserver ``` diff --git a/efile_app/efile/api/auth_views.py b/efile_app/efile/api/auth_views.py index 7cc4376..2cc7bc4 100644 --- a/efile_app/efile/api/auth_views.py +++ b/efile_app/efile/api/auth_views.py @@ -35,9 +35,6 @@ def get_tyler_token(request, jurisdiction=None): if jurisdiction is None: jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request) - if request.user.is_authenticated and hasattr(request.user, "tyler_token") and request.user.tyler_token: - return request.user.tyler_token - # Fallback to session # TODO(brycew): should this ever happen? auth_tokens = request.session.get("auth_tokens", {}) diff --git a/efile_app/efile/authentication.py b/efile_app/efile/authentication.py index bc590b5..eb3aad0 100644 --- a/efile_app/efile/authentication.py +++ b/efile_app/efile/authentication.py @@ -28,7 +28,6 @@ def authenticate(self, request, username=None, password=None, **kwargs): user = self._get_or_create_user(username, auth_data, jurisdiction) # TODO(brycew): actually write these? - # self._update_user_profile(user, auth_data, jurisdiction) # if request: # self._store_tokens_in_session(request, auth_data, jurisdiction) @@ -48,24 +47,23 @@ def _get_or_create_user(self, username, auth_data, jurisdiction): try: user = User.objects.get(username=username) except User.DoesNotExist: - # TODO(brycew): should only check username user = None - pass if not user: user_data = self._extract_user_data(auth_data, username, jurisdiction) user = User.objects.create_user( username=username, + tyler_jurisdiction=jurisdiction, + tyler_user_id=user_data.get("user_id", None), email=user_data.get("email", username), first_name=user_data.get("first_name", ""), last_name=user_data.get("last_name", ""), ) - logger.info("Created new user: %s", username) + logger.info("Created new user: %s, %s", user.username, user.email) return user def _extract_user_data(self, auth_data, username, jurisdiction): - # TODO(bryce): continue user_data = {"email": username} if auth_data: user_data["user_id"] = auth_data["tokens"][f"TYLER-ID-{jurisdiction.upper()}"] diff --git a/efile_app/efile/context_processors.py b/efile_app/efile/context_processors.py index 619c337..f0953d6 100644 --- a/efile_app/efile/context_processors.py +++ b/efile_app/efile/context_processors.py @@ -7,7 +7,7 @@ def jurisdiction_context(request): - """Add current jurisdiction and it's config to all template contexts.""" + """Add current jurisdiction and its config to all template contexts.""" current_jurisdiction = get_jurisdiction_from_request(request) config = config_loader.load_jurisdiction_config(current_jurisdiction) diff --git a/efile_app/efile/models.py b/efile_app/efile/models.py index c337461..d9c0dcc 100644 --- a/efile_app/efile/models.py +++ b/efile_app/efile/models.py @@ -1,140 +1,20 @@ # models.py - Optional extension to store additional user information -from django.contrib.auth.models import User +from django.contrib.auth.models import AbstractUser from django.db import models -class UserProfile(models.Model): +class UserProfile(AbstractUser): """ - Extended user profile to store Illinois eFile registration information + Extended user profile to store eFile registration information. """ - user = models.OneToOneField(User, on_delete=models.CASCADE) - - # Name information (stored in User model: first_name, last_name) - middle_name = models.CharField(max_length=100, blank=True) - - # Physical Address - street_address = models.CharField(max_length=255) - street_address_2 = models.CharField(max_length=255, blank=True) - city = models.CharField(max_length=100) - zip_code = models.CharField(max_length=10) - + # TODO(brycew): what happens if someone is trying to do stuff in multiple jurisdictions? + tyler_jurisdiction = models.CharField(max_length=20) tyler_user_id = models.CharField(max_length=100, blank=True, null=True) - tyler_token = models.TextField(blank=True, null=True) # TODO(brycew): uncomment when https://github.com/SuffolkLITLab/EfileProxyServer/issues/334 is in # token_expires_at = models.DateTimeField(blank=True, null=True) - # Illinois Counties choices - COUNTY_CHOICES = [ - ("adams", "Adams"), - ("alexander", "Alexander"), - ("bond", "Bond"), - ("boone", "Boone"), - ("brown", "Brown"), - ("bureau", "Bureau"), - ("calhoun", "Calhoun"), - ("carroll", "Carroll"), - ("cass", "Cass"), - ("champaign", "Champaign"), - ("christian", "Christian"), - ("clark", "Clark"), - ("clay", "Clay"), - ("clinton", "Clinton"), - ("coles", "Coles"), - ("cook", "Cook"), - ("crawford", "Crawford"), - ("cumberland", "Cumberland"), - ("dekalb", "DeKalb"), - ("dewitt", "DeWitt"), - ("douglas", "Douglas"), - ("dupage", "DuPage"), - ("edgar", "Edgar"), - ("edwards", "Edwards"), - ("effingham", "Effingham"), - ("fayette", "Fayette"), - ("ford", "Ford"), - ("franklin", "Franklin"), - ("fulton", "Fulton"), - ("gallatin", "Gallatin"), - ("greene", "Greene"), - ("grundy", "Grundy"), - ("hamilton", "Hamilton"), - ("hancock", "Hancock"), - ("hardin", "Hardin"), - ("henderson", "Henderson"), - ("henry", "Henry"), - ("iroquois", "Iroquois"), - ("jackson", "Jackson"), - ("jasper", "Jasper"), - ("jefferson", "Jefferson"), - ("jersey", "Jersey"), - ("jodaviess", "Jo Daviess"), - ("johnson", "Johnson"), - ("kane", "Kane"), - ("kankakee", "Kankakee"), - ("kendall", "Kendall"), - ("knox", "Knox"), - ("lake", "Lake"), - ("lasalle", "LaSalle"), - ("lawrence", "Lawrence"), - ("lee", "Lee"), - ("livingston", "Livingston"), - ("logan", "Logan"), - ("macon", "Macon"), - ("macoupin", "Macoupin"), - ("madison", "Madison"), - ("marion", "Marion"), - ("marshall", "Marshall"), - ("mason", "Mason"), - ("massac", "Massac"), - ("mcdonough", "McDonough"), - ("mchenry", "McHenry"), - ("mclean", "McLean"), - ("menard", "Menard"), - ("mercer", "Mercer"), - ("monroe", "Monroe"), - ("montgomery", "Montgomery"), - ("morgan", "Morgan"), - ("moultrie", "Moultrie"), - ("ogle", "Ogle"), - ("peoria", "Peoria"), - ("perry", "Perry"), - ("piatt", "Piatt"), - ("pike", "Pike"), - ("pope", "Pope"), - ("pulaski", "Pulaski"), - ("putnam", "Putnam"), - ("randolph", "Randolph"), - ("richland", "Richland"), - ("rock_island", "Rock Island"), - ("saline", "Saline"), - ("sangamon", "Sangamon"), - ("schuyler", "Schuyler"), - ("scott", "Scott"), - ("shelby", "Shelby"), - ("stark", "Stark"), - ("stephenson", "Stephenson"), - ("tazewell", "Tazewell"), - ("union", "Union"), - ("vermilion", "Vermilion"), - ("wabash", "Wabash"), - ("warren", "Warren"), - ("washington", "Washington"), - ("wayne", "Wayne"), - ("white", "White"), - ("whiteside", "Whiteside"), - ("will", "Will"), - ("williamson", "Williamson"), - ("winnebago", "Winnebago"), - ("woodford", "Woodford"), - ] - - county = models.CharField(max_length=50, choices=COUNTY_CHOICES) - - # Contact Information (email stored in User model) - phone = models.CharField(max_length=20, blank=True) - # Communication Preferences email_updates = models.BooleanField(default=False) text_updates = models.BooleanField(default=False) @@ -143,45 +23,10 @@ class UserProfile(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) - def __str__(self): - return f"{self.user.get_full_name()} - {self.county} County" - class Meta: verbose_name = "User Profile" verbose_name_plural = "User Profiles" - -# If using this model, you would also update the registration form's save method: -""" -# Updated save method for EFileRegistrationForm in views.py: - -def save(self): - # Create user with the form data - user = User.objects.create_user( - username=self.cleaned_data['email'], # Use email as username - email=self.cleaned_data['email'], - password=self.cleaned_data['password'], - first_name=self.cleaned_data['first_name'], - last_name=self.cleaned_data['last_name'] - ) - - # Create user profile with additional information - UserProfile.objects.create( - user=user, - middle_name=self.cleaned_data['middle_name'], - street_address=self.cleaned_data['street_address'], - street_address_2=self.cleaned_data['street_address_2'], - city=self.cleaned_data['city'], - zip_code=self.cleaned_data['zip_code'], - county=self.cleaned_data['county'], - phone=self.cleaned_data['phone'], - email_updates=self.cleaned_data['email_updates'], - text_updates=self.cleaned_data['text_updates'], - ) - - return user -""" - # Don't forget to run migrations if you add this model: # python manage.py makemigrations -# python manage.py migrate +# python manage.py migrate --run-syncdb diff --git a/efile_app/efile/settings_base.py b/efile_app/efile/settings_base.py index 2ee3cb3..78c8548 100644 --- a/efile_app/efile/settings_base.py +++ b/efile_app/efile/settings_base.py @@ -21,6 +21,8 @@ # Override in env-specific settings CSRF_TRUSTED_ORIGINS: list[str] = [] +AUTH_USER_MODEL = "efile.UserProfile" + # Application definition INSTALLED_APPS = [ "django.contrib.admin", diff --git a/efile_app/efile/templates/efile/components/profile_header.html b/efile_app/efile/templates/efile/components/profile_header.html index de6e8a6..defe168 100644 --- a/efile_app/efile/templates/efile/components/profile_header.html +++ b/efile_app/efile/templates/efile/components/profile_header.html @@ -40,7 +40,7 @@ account.

- Current email: {{profile_info.email}} + Current email: {{user.email}}
Current Jurisdiction: {{jurisdiction}} diff --git a/efile_app/efile/utils/proxy_connection.py b/efile_app/efile/utils/proxy_connection.py index e415d07..cd38312 100644 --- a/efile_app/efile/utils/proxy_connection.py +++ b/efile_app/efile/utils/proxy_connection.py @@ -5,7 +5,6 @@ logger = logging.getLogger(__name__) - def auth_with_tyler_api(username, password, jurisdiction): url = f"{settings.EFSP_URL}/authenticate" try: @@ -21,3 +20,4 @@ def auth_with_tyler_api(username, password, jurisdiction): logger.debug("Auth endpoint failed: %s - %s", url, str(e)) return None + diff --git a/efile_app/efile/views.py b/efile_app/efile/views.py deleted file mode 100644 index dfadc36..0000000 --- a/efile_app/efile/views.py +++ /dev/null @@ -1,201 +0,0 @@ -import logging - -# views.py - Complete updated file for Illinois eFile system -from django.http import JsonResponse -from django.views.decorators.http import require_http_methods - -logger = logging.getLogger(__name__) - -# Preserving original imports for when we un-comment all the other methods. -# from django.contrib.auth import authenticate, login -# from django.views.generic import TemplateView -# from .forms import EFileLoginForm, EFileRegistrationForm -# from django.contrib.auth.models import User - -# Login view (simplified - no longer handles registration) -# def efile_login(request): -# """ -# Handle login and redirect to separate registration page -# """ -# login_form = EFileLoginForm() - -# if request.method == 'POST': -# if 'login_submit' in request.POST: -# # Handle login -# login_form = EFileLoginForm(request.POST) -# if login_form.is_valid(): -# email = login_form.cleaned_data['email'] -# password = login_form.cleaned_data['password'] - -# # Try to find user by email -# try: -# user = User.objects.get(email=email) -# user = authenticate(request, username=user.username, password=password) -# if user is not None: -# login(request, user) -# messages.success(request, 'Successfully logged in!') -# # Redirect to next page if specified, otherwise dashboard -# next_page = request.GET.get('next', 'dashboard') -# return redirect(next_page) -# else: -# messages.error(request, 'Invalid email or password.') -# except User.DoesNotExist: -# messages.error(request, 'No account found with this email address.') - -# context = { -# 'login_form': login_form, -# } -# return render(request, 'efile/login.html', context) - -# # New separate registration view -# def efile_register(request): -# import requests -# if request.method == 'POST': -# form = EFileRegistrationForm(request.POST) -# if form.is_valid(): -# # Prepare payload for external API -# data = { -# "registrationType": "INDIVIDUAL", -# "firstName": form.cleaned_data["first_name"], -# "middleName": form.cleaned_data.get("middle_name", ""), -# "lastName": form.cleaned_data["last_name"], -# "streetAddressLine1": form.cleaned_data["street_address"], -# "streetAddressLine2": form.cleaned_data.get("street_address_2", ""), -# "city": form.cleaned_data["city"], -# "state": form.cleaned_data["state"], -# "zipCode": form.cleaned_data["zip_code"], -# "countryCode": "US", -# "county": form.cleaned_data["county"], -# "email": form.cleaned_data["email"], -# "phoneNumber": form.cleaned_data.get("phone", ""), -# # "emailUpdates": form.cleaned_data.get("email_updates", False), -# # "textUpdates": form.cleaned_data.get("text_updates", False), -# "password": form.cleaned_data["password"], -# } -# try: -# response = requests.post( -# f"{settings.EFSP_URL}/jurisdictions/illinois/adminusers/users", -# json=data, -# timeout=10 -# ) -# if response.status_code == 201: -# messages.success( -# request, -# "Registration successful! Please log in with your new account." -# ) -# return redirect("efile_login") -# else: -# # Try to get error message from API -# try: -# error_msg = response.json().get("error") or response.text -# except Exception: -# error_msg = response.text -# messages.error(request, f"Registration failed: {error_msg}") -# except Exception as e: -# messages.error(request, f"Registration failed: {str(e)}") -# else: -# messages.error(request, "Please correct the errors below.") -# else: -# form = EFileRegistrationForm() -# return render(request, "efile/register.html", {"form": form}) - -# # Dashboard view after successful login -# def dashboard(request): -# """ -# Dashboard view after successful login -# """ -# if not request.user.is_authenticated: -# messages.info(request, 'Please log in to access your dashboard.') -# return redirect('efile_login') - -# context = { -# 'user': request.user, -# # If using UserProfile model: -# # 'profile': getattr(request.user, 'userprofile', None) -# } -# return render(request, 'efile/dashboard.html', context) - -# # Class-based view alternative (if you prefer) -# class EFileLoginRegisterView(TemplateView): -# """ -# Class-based view for the eFile login page -# """ -# template_name = 'efile/login.html' - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) -# context['login_form'] = EFileLoginForm() -# return context - -# def post(self, request, *args, **kwargs): -# login_form = EFileLoginForm(request.POST) -# if login_form.is_valid(): -# email = login_form.cleaned_data['email'] -# password = login_form.cleaned_data['password'] - -# try: -# user = User.objects.get(email=email) -# user = authenticate(request, username=user.username, password=password) -# if user is not None: -# login(request, user) -# messages.success(request, 'Successfully logged in!') -# next_page = request.GET.get('next', 'dashboard') -# return redirect(next_page) -# else: -# messages.error(request, 'Invalid email or password.') -# except User.DoesNotExist: -# messages.error(request, 'No account found with this email address.') - -# context = self.get_context_data() -# context['login_form'] = login_form -# return render(request, self.template_name, context) - -# # Password reset view (optional) -# def password_reset_request(request): -# """ -# Handle password reset requests -# """ -# if request.method == 'POST': -# email = request.POST.get('email') -# try: -# user = User.objects.get(email=email) -# # Here you would typically send a password reset email -# # For now, we'll just show a success message -# messages.success( -# request, -# 'If an account with this email exists, you will receive password reset instructions.' -# ) -# return redirect('efile_login') -# except User.DoesNotExist: -# # Don't reveal whether the email exists or not for security -# messages.success( -# request, -# 'If an account with this email exists, you will receive password reset instructions.' -# ) -# return redirect('efile_login') - -# return render(request, 'efile/password_reset.html') - - -# API Endpoints for User Profile and Authentication - - -@require_http_methods(["GET"]) -def api_user_profile(request): - """ - API endpoint to get user profile information - """ - try: - # Mock user profile data - replace with actual user data - profile_data = { - "username": "john_doe", - "first_name": "John", - "last_name": "Doe", - "email": "john.doe@example.com", - "preferred_county": "cook", - "location": {"county": "Cook County", "state": "Illinois"}, - } - - return JsonResponse({"success": True, "data": profile_data}) - except Exception as e: - return JsonResponse({"success": False, "error": str(e)}, status=500) From 4214210347ca756744c65e09b5d5b607cdb46159 Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Wed, 19 Nov 2025 16:54:36 -0500 Subject: [PATCH 3/3] More general cleaning De-duping random functions like Jurisdiction get and auth tokens --- efile_app/efile/api/auth_views.py | 66 +++++-------------- efile_app/efile/api/base.py | 18 ----- efile_app/efile/api/dropdown_views.py | 30 +-------- efile_app/efile/api/filing_views.py | 49 ++++---------- efile_app/efile/api/suffolk_api_views.py | 42 ++++-------- efile_app/efile/authentication.py | 29 +++++++- efile_app/efile/models.py | 1 + efile_app/efile/static/js/api-utils.js | 18 ++--- .../efile/components/profile_header.html | 4 +- efile_app/efile/tests/tests.py | 4 +- efile_app/efile/utils/case_config.py | 0 efile_app/efile/utils/case_type_config.py | 0 efile_app/efile/utils/jurisdiction_stuff.py | 2 +- efile_app/efile/utils/proxy_connection.py | 2 +- efile_app/efile/views/case_details.py | 1 - efile_app/efile/views/login.py | 30 +++------ 16 files changed, 94 insertions(+), 202 deletions(-) delete mode 100644 efile_app/efile/utils/case_config.py delete mode 100644 efile_app/efile/utils/case_type_config.py diff --git a/efile_app/efile/api/auth_views.py b/efile_app/efile/api/auth_views.py index 2cc7bc4..79b2d91 100644 --- a/efile_app/efile/api/auth_views.py +++ b/efile_app/efile/api/auth_views.py @@ -7,11 +7,14 @@ import requests from django.conf import settings -from django.contrib.auth import authenticate, login, logout +from django.contrib.auth import authenticate, login from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from requests.exceptions import RequestException, Timeout +from efile.api.suffolk_api_views import get_tyler_token +from efile.utils.jurisdiction_stuff import get_jurisdiction_from_request + from .base import APIResponseMixin logger = logging.getLogger(__name__) @@ -20,35 +23,6 @@ class AuthAPIViews(APIResponseMixin): """API views for authentication""" - @staticmethod - def get_jurisdiction_from_request(request): - """Extract jurisdiction from request URL or parameters""" - # Try to get jurisdiction from query parameters first - jurisdiction = request.GET.get("jurisdiction") - if jurisdiction: - return jurisdiction.lower() - return None - - @staticmethod - def get_tyler_token(request, jurisdiction=None): - """Helper method to retrieve Tyler token from various sources""" - if jurisdiction is None: - jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request) - - # Fallback to session - # TODO(brycew): should this ever happen? - auth_tokens = request.session.get("auth_tokens", {}) - logger.debug(f"Auth tokens in session: {auth_tokens}") - - # Try different Tyler token key formats - tyler_token = ( - auth_tokens.get(f"TYLER-TOKEN-{jurisdiction.upper()}") - or auth_tokens.get(f"tyler_token_{jurisdiction}") - or auth_tokens.get(f"tyler-token-{jurisdiction}") - ) - - return tyler_token - @staticmethod @require_http_methods(["POST"]) @csrf_exempt @@ -68,7 +42,6 @@ def user_login(request): if user is not None: login(request, user) - request.session["user_email"] = user.email return AuthAPIViews.success_response( {"user_id": user.id, "username": user.username, "email": user.email, "is_authenticated": True}, "Login successful", @@ -86,12 +59,10 @@ def user_login(request): @csrf_exempt def user_logout(request): """Handle user logout""" + from efile.authentication import SuffolkEFileBackend + try: - logout(request) - session_keys_to_keep = ["csrftoken"] - session_data = {k: v for k, v in request.session.items() if k in session_keys_to_keep} - request.session.clear() - request.session.update(session_data) + SuffolkEFileBackend.logout(request) return AuthAPIViews.success_response({}, "Logout successful") except Exception as e: return AuthAPIViews.error_response(f"Error: {str(e)}") @@ -103,8 +74,8 @@ def user_profile(request): try: # TODO(brycew): get some of this from the existing logged in user # Get jurisdiction and Tyler token dynamically - jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request) - tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction) + jurisdiction = get_jurisdiction_from_request(request) + tyler_token = get_tyler_token(request, jurisdiction) api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None) headers = { @@ -113,9 +84,6 @@ def user_profile(request): "X-API-Key": api_key if api_key else "", } - auth_tokens = request.session.get("auth_tokens", {}) - logger.debug(f"Auth tokens in session: {auth_tokens}") - # Add Tyler token if available if tyler_token: headers[f"tyler-token-{jurisdiction}"] = tyler_token @@ -123,8 +91,8 @@ def user_profile(request): # Log that no token was found for debugging logger.info("No Tyler token found for state '%s' in Suffolk eFile API request", jurisdiction) - if auth_tokens.get(f"TYLER-ID-{jurisdiction.upper()}"): - headers[f"TYLER-ID-{jurisdiction.upper()}"] = auth_tokens.get(f"TYLER-ID-{jurisdiction.upper()}") + if request.user.tyler_user_id: + headers[f"TYLER-ID-{jurisdiction.upper()}"] = request.user.tyler_user_id url = f"{settings.EFSP_URL}/jurisdictions/{jurisdiction}/firmattorneyservice/firm" logger.debug("GET %s header keys=%s", url, list(headers.keys())) @@ -195,8 +163,8 @@ def user_profile(request): return AuthAPIViews.error_response("Unable to retrieve profile", 500) except Timeout: return AuthAPIViews.error_response("External API request timed out", 408) - except Exception as e: - logger.warn("Request exception: %s", str(e)) + except Exception: + logger.exception("Request exception") return AuthAPIViews.error_response("Request Exception", 500) @staticmethod @@ -235,8 +203,8 @@ def tyler_token(request): """Get Tyler token and API key for external form submissions""" try: # Get jurisdiction and Tyler token dynamically - jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request) - tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction) + jurisdiction = get_jurisdiction_from_request(request) + tyler_token = get_tyler_token(request, jurisdiction) api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None) return AuthAPIViews.success_response( @@ -251,8 +219,8 @@ def payment_accounts(request): """Get payment accounts from Suffolk eFile API with proper authentication""" try: # Get jurisdiction and Tyler token dynamically - jurisdiction = AuthAPIViews.get_jurisdiction_from_request(request) - tyler_token = AuthAPIViews.get_tyler_token(request, jurisdiction) + jurisdiction = get_jurisdiction_from_request(request) + tyler_token = get_tyler_token(request, jurisdiction) api_key = getattr(settings, "SUFFOLK_EFILE_API_KEY", None) headers = { diff --git a/efile_app/efile/api/base.py b/efile_app/efile/api/base.py index a6eadaa..39af4ae 100644 --- a/efile_app/efile/api/base.py +++ b/efile_app/efile/api/base.py @@ -20,21 +20,3 @@ def success_response(data, message=None): def error_response(error_message, status_code=400): """Return an error API response""" return JsonResponse({"success": False, "error": error_message}, status=status_code) - - -def get_auth_tokens(request): - """Helper function to get auth tokens from session""" - return request.session.get("auth_tokens", None) - - -def validate_request(request, required_params=None): - """Validate API request and return any missing parameters""" - if required_params is None: - required_params = [] - - missing_params = [] - for param in required_params: - if param not in request.GET and param not in request.POST: - missing_params.append(param) - - return missing_params diff --git a/efile_app/efile/api/dropdown_views.py b/efile_app/efile/api/dropdown_views.py index f9fbb07..3a8eb4a 100644 --- a/efile_app/efile/api/dropdown_views.py +++ b/efile_app/efile/api/dropdown_views.py @@ -11,7 +11,7 @@ from django.views.decorators.http import require_http_methods from ..utils.zip_to_county_il import get_county_by_zip -from .base import APIResponseMixin, get_auth_tokens +from .base import APIResponseMixin logger = logging.getLogger(__name__) @@ -24,8 +24,6 @@ class DropdownAPIViews(APIResponseMixin): def get_case_categories(request): """Get available case categories from Suffolk LIT Lab API""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters court_code = request.GET.get("court") jurisdiction = request.GET.get("jurisdiction", "illinois") @@ -38,8 +36,6 @@ def get_case_categories(request): # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) response = requests.get(api_url, headers=headers, timeout=10) @@ -78,8 +74,6 @@ def get_case_categories(request): def get_case_types(request): """Get case types based on selected category from Suffolk LIT Lab API""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters court_code = request.GET.get("court") category_id = request.GET.get("parent") # category_id from case category dropdown @@ -97,8 +91,6 @@ def get_case_types(request): # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) response = requests.get(api_url, headers=headers, timeout=10) @@ -131,8 +123,6 @@ def get_case_types(request): def get_filing_types(request): """Get filing types based on selected case type from Suffolk LIT Lab API""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters - support both parameter names for flexibility court_code = request.GET.get("court") case_type_id = request.GET.get("case_type") or request.GET.get("parent") # Support both flows @@ -165,8 +155,6 @@ def get_filing_types(request): # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) response = requests.get(api_url, headers=headers, timeout=10) @@ -203,8 +191,6 @@ def get_filing_types(request): def get_courts(request): """Get available courts based on user location/preferences""" try: - auth_tokens = get_auth_tokens(request) - jurisdiction = request.GET.get("jurisdiction", "") user_zip = request.GET.get("user_zip") user_county = request.GET.get("user_county") @@ -215,8 +201,6 @@ def get_courts(request): try: # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) @@ -463,8 +447,6 @@ def _prioritize_courts_by_location(courts, user_zip=None, user_county=None): def get_document_types(request): """Get document types based on selected filing type from Suffolk LIT Lab API""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters court_code = request.GET.get("court") filing_type_id = request.GET.get("parent") # filing type ID from filing type dropdown @@ -485,8 +467,6 @@ def get_document_types(request): # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" response = requests.get(api_url, headers=headers, timeout=10) @@ -520,8 +500,6 @@ def get_document_types(request): def get_optional_services(request): """Get optional services for a filing type from Suffolk LIT Lab API""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters court_code = request.GET.get("court") filing_type_id = request.GET.get("filing_type_id") @@ -543,8 +521,6 @@ def get_optional_services(request): try: # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" response = requests.get(api_url, headers=headers, timeout=10) @@ -596,8 +572,6 @@ def get_optional_services(request): def get_party_types(request): """Get available party types from Suffolk LIT Lab API based on case type""" try: - auth_tokens = get_auth_tokens(request) - # Get required parameters court_code = request.GET.get("court") case_type_code = request.GET.get("case_type") @@ -612,8 +586,6 @@ def get_party_types(request): # Make the API request with auth tokens if available headers = {} - if auth_tokens and "token" in auth_tokens: - headers["Authorization"] = f"Bearer {auth_tokens['token']}" response = requests.get(api_url, headers=headers, timeout=10) diff --git a/efile_app/efile/api/filing_views.py b/efile_app/efile/api/filing_views.py index 5e956a9..05cb291 100644 --- a/efile_app/efile/api/filing_views.py +++ b/efile_app/efile/api/filing_views.py @@ -9,10 +9,14 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods -from .base import APIResponseMixin, get_auth_tokens +from .base import APIResponseMixin logger = logging.getLogger(__name__) +# TODO(brycew): this file doesn't work in it's current state. Keeping +# around for later refactors, when we inevitably want to start letting users +# handle filings themselves / see current status, etc. + class FilingAPIViews(APIResponseMixin): """API views for filing operations""" @@ -22,15 +26,9 @@ class FilingAPIViews(APIResponseMixin): def get_filings(request): """Get user's filings""" try: - auth_tokens = get_auth_tokens(request) - if not auth_tokens: - return FilingAPIViews.error_response("Not authenticated", 401) - # API call to get user's filings api_url = "https://suffolkefile.com/api/filings" - headers = {"Authorization": f"Bearer {auth_tokens['access_token']}"} - logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) - response = requests.get(api_url, headers=headers, timeout=30) + response = requests.get(api_url, timeout=30) logger.debug( "Get filings response: status=%s content_type=%s", response.status_code, @@ -52,10 +50,6 @@ def get_filings(request): def create_filing(request): """Create a new filing""" try: - auth_tokens = get_auth_tokens(request) - if not auth_tokens: - return FilingAPIViews.error_response("Not authenticated", 401) - data = json.loads(request.body) # Validate required fields @@ -67,9 +61,8 @@ def create_filing(request): # API call to create filing api_url = "https://suffolkefile.com/api/filings" - headers = {"Authorization": f"Bearer {auth_tokens['access_token']}"} - logger.debug("POST %s header keys=%s payload keys=%s", api_url, list(headers.keys()), list(data.keys())) - response = requests.post(api_url, json=data, headers=headers, timeout=30) + logger.debug("POST %s payload keys=%s", api_url, list(data.keys())) + response = requests.post(api_url, json=data, timeout=30) logger.debug( "Create filing response: status=%s content_type=%s", response.status_code, @@ -92,15 +85,9 @@ def create_filing(request): def get_filing_detail(request, filing_id): """Get details for a specific filing""" try: - auth_tokens = get_auth_tokens(request) - if not auth_tokens: - return FilingAPIViews.error_response("Not authenticated", 401) - # API call to get filing details api_url = f"https://suffolkefile.com/api/filings/{filing_id}" - headers = {"Authorization": f"Bearer {auth_tokens['access_token']}"} - logger.debug("GET %s header keys=%s", api_url, list(headers.keys())) - response = requests.get(api_url, headers=headers, timeout=30) + response = requests.get(api_url, timeout=30) logger.debug( "Filing detail response: status=%s content_type=%s", response.status_code, @@ -124,17 +111,12 @@ def get_filing_detail(request, filing_id): def update_filing(request, filing_id): """Update an existing filing""" try: - auth_tokens = get_auth_tokens(request) - if not auth_tokens: - return FilingAPIViews.error_response("Not authenticated", 401) - data = json.loads(request.body) # API call to update filing api_url = f"https://suffolkefile.com/api/filings/{filing_id}" - headers = {"Authorization": f"Bearer {auth_tokens['access_token']}"} - logger.debug("PUT %s header keys=%s payload keys=%s", api_url, list(headers.keys()), list(data.keys())) - response = requests.put(api_url, json=data, headers=headers, timeout=30) + logger.debug("PUT %s payload keys=%s", api_url, list(data.keys())) + response = requests.put(api_url, json=data, timeout=30) logger.debug( "Update filing response: status=%s content_type=%s", response.status_code, @@ -160,15 +142,10 @@ def update_filing(request, filing_id): def delete_filing(request, filing_id): """Delete a filing""" try: - auth_tokens = get_auth_tokens(request) - if not auth_tokens: - return FilingAPIViews.error_response("Not authenticated", 401) - # API call to delete filing api_url = f"https://suffolkefile.com/api/filings/{filing_id}" - headers = {"Authorization": f"Bearer {auth_tokens['access_token']}"} - logger.debug("DELETE %s header keys=%s", api_url, list(headers.keys())) - response = requests.delete(api_url, headers=headers, timeout=30) + logger.debug("DELETE %s", api_url) + response = requests.delete(api_url, timeout=30) logger.debug( "Delete filing response: status=%s content_type=%s", response.status_code, diff --git a/efile_app/efile/api/suffolk_api_views.py b/efile_app/efile/api/suffolk_api_views.py index c0e44b2..40ea2a7 100644 --- a/efile_app/efile/api/suffolk_api_views.py +++ b/efile_app/efile/api/suffolk_api_views.py @@ -12,48 +12,28 @@ from django.views.decorators.http import require_http_methods from requests.exceptions import RequestException -logger = logging.getLogger(__name__) - - -def get_state_from_request(request): - """Extract state from request URL or parameters""" - # Try to get state from query parameters first - state = request.GET.get("state") - if state: - return state.lower() +from efile.utils.jurisdiction_stuff import get_jurisdiction_from_request - # Try to extract from URL path (e.g., /jurisdictions/illinois/) - path = request.path - if "/jurisdictions/" in path: - path_parts = path.split("/jurisdictions/") - if len(path_parts) > 1: - state_part = path_parts[1].split("/")[0] - if state_part: - return state_part.lower() - - # Default to Illinois if no state found - return "illinois" +logger = logging.getLogger(__name__) -def get_tyler_token(request, state=None): +def get_tyler_token(request, jurisdiction=None): """Helper method to retrieve Tyler token from various sources""" - if state is None: - state = get_state_from_request(request) + if jurisdiction is None: + jurisdiction = get_jurisdiction_from_request(request) + # Fallback to session auth_tokens = request.session.get("auth_tokens", {}) logger.debug(f"Auth tokens in session: {auth_tokens}") # Try different Tyler token key formats tyler_token = ( - auth_tokens.get(f"TYLER-TOKEN-{state.upper()}") - or auth_tokens.get(f"tyler_token_{state}") - or auth_tokens.get(f"tyler-token-{state}") + auth_tokens.get(f"TYLER-TOKEN-{jurisdiction.upper()}") + or auth_tokens.get(f"tyler_token_{jurisdiction}") + or auth_tokens.get(f"tyler-token-{jurisdiction}") ) - if tyler_token: - return tyler_token - - return None + return tyler_token @csrf_exempt @@ -100,7 +80,7 @@ def lookup_case(request): if jurisdiction: state = jurisdiction.lower() else: - state = get_state_from_request(request) + state = get_jurisdiction_from_request(request) api_url = f"{settings.EFSP_URL}/jurisdictions/{state}/cases/courts/{court}/cases" # Add query parameter for docket number diff --git a/efile_app/efile/authentication.py b/efile_app/efile/authentication.py index eb3aad0..1978e13 100644 --- a/efile_app/efile/authentication.py +++ b/efile_app/efile/authentication.py @@ -9,6 +9,7 @@ logger = logging.getLogger(__name__) User = get_user_model() + class SuffolkEFileBackend(BaseBackend): def authenticate(self, request, username=None, password=None, **kwargs): logger.info("Trying auth?") @@ -32,6 +33,7 @@ def authenticate(self, request, username=None, password=None, **kwargs): # self._store_tokens_in_session(request, auth_data, jurisdiction) logger.info("Successfully auth'd user: %s", username) + request.session["user_email"] = user.email return user except Exception: logger.exception("Error during auth for user: %s", username) @@ -49,8 +51,8 @@ def _get_or_create_user(self, username, auth_data, jurisdiction): except User.DoesNotExist: user = None + user_data = self._extract_user_data(auth_data, username, jurisdiction) if not user: - user_data = self._extract_user_data(auth_data, username, jurisdiction) user = User.objects.create_user( username=username, tyler_jurisdiction=jurisdiction, @@ -59,6 +61,11 @@ def _get_or_create_user(self, username, auth_data, jurisdiction): first_name=user_data.get("first_name", ""), last_name=user_data.get("last_name", ""), ) + else: + user.tyler_jurisdiction = (jurisdiction,) + user.tyler_user_id = (user_data.get("user_id", None),) + user.email = (user_data.get("email", username),) + user.save() logger.info("Created new user: %s, %s", user.username, user.email) return user @@ -69,3 +76,23 @@ def _extract_user_data(self, auth_data, username, jurisdiction): user_data["user_id"] = auth_data["tokens"][f"TYLER-ID-{jurisdiction.upper()}"] user_data["tyler_token"] = auth_data["tokens"][f"TYLER-TOKEN-{jurisdiction.upper()}"] return user_data + + @staticmethod + def logout(request): + """Log the current user out. Here for symmetry. + + If logout fails, will raise an exception. + """ + from django.contrib.auth import logout + from django.contrib.messages.api import get_messages + + # Clear any existing messages first + storage = get_messages(request) + for _message in storage: + pass # This consumes all messages + + logout(request) + session_keys_to_keep = ["csrftoken"] + session_data = {k: v for k, v in request.session.items() if k in session_keys_to_keep} + request.session.clear() + request.session.update(session_data) diff --git a/efile_app/efile/models.py b/efile_app/efile/models.py index d9c0dcc..11a6518 100644 --- a/efile_app/efile/models.py +++ b/efile_app/efile/models.py @@ -27,6 +27,7 @@ class Meta: verbose_name = "User Profile" verbose_name_plural = "User Profiles" + # Don't forget to run migrations if you add this model: # python manage.py makemigrations # python manage.py migrate --run-syncdb diff --git a/efile_app/efile/static/js/api-utils.js b/efile_app/efile/static/js/api-utils.js index 249f014..30ca1bd 100644 --- a/efile_app/efile/static/js/api-utils.js +++ b/efile_app/efile/static/js/api-utils.js @@ -23,20 +23,14 @@ class ApiUtils { //const result = await response.json(); // Try to get from jurisdiction selector first - const jurisdictionSelect = document.getElementById("jurisdictionSelect"); - if (jurisdictionSelect && jurisdictionSelect.value) { - return jurisdictionSelect.value; + const jurisdiction = document.getElementById("currentJurisdiction"); + if (jurisdiction && jurisdiction.textContent) { + return jurisdiction.textContent; } - // Try to get from profile modal selector - const profileSelect = document.getElementById("profileJurisdictionSelect"); - if (profileSelect && profileSelect.value) { - return profileSelect.value; - } - - // Default to Illinois if nothing found - console.log("Returning illinois for current, likely shouldn't") - return "illinois"; + // Default to null if nothing found; rather that than weird bugs from a default state + console.log("Returning null for current, likely shouldn't") + return null; } getCache() { diff --git a/efile_app/efile/templates/efile/components/profile_header.html b/efile_app/efile/templates/efile/components/profile_header.html index defe168..25e0d42 100644 --- a/efile_app/efile/templates/efile/components/profile_header.html +++ b/efile_app/efile/templates/efile/components/profile_header.html @@ -1,5 +1,7 @@ {% load static %} + +
@@ -40,7 +42,7 @@ account.

- Current email: {{user.email}} + Current email: {{user.username}}
Current Jurisdiction: {{jurisdiction}} diff --git a/efile_app/efile/tests/tests.py b/efile_app/efile/tests/tests.py index 88f65f9..6ac0fac 100644 --- a/efile_app/efile/tests/tests.py +++ b/efile_app/efile/tests/tests.py @@ -3,11 +3,13 @@ from unittest.mock import Mock, patch import pytest -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from django.test import Client from efile.utils.config_loader import config_loader +User = get_user_model() + # ===================================================== # Secrets loaded at runtime diff --git a/efile_app/efile/utils/case_config.py b/efile_app/efile/utils/case_config.py deleted file mode 100644 index e69de29..0000000 diff --git a/efile_app/efile/utils/case_type_config.py b/efile_app/efile/utils/case_type_config.py deleted file mode 100644 index e69de29..0000000 diff --git a/efile_app/efile/utils/jurisdiction_stuff.py b/efile_app/efile/utils/jurisdiction_stuff.py index 441ffeb..ea4b763 100644 --- a/efile_app/efile/utils/jurisdiction_stuff.py +++ b/efile_app/efile/utils/jurisdiction_stuff.py @@ -8,4 +8,4 @@ def get_jurisdiction_from_request(request): if len(segments) >= 2 and segments[1] not in ["api", "options", "login", "register", "upload", "review"]: return segments[1].lower() - return None \ No newline at end of file + return None diff --git a/efile_app/efile/utils/proxy_connection.py b/efile_app/efile/utils/proxy_connection.py index cd38312..e415d07 100644 --- a/efile_app/efile/utils/proxy_connection.py +++ b/efile_app/efile/utils/proxy_connection.py @@ -5,6 +5,7 @@ logger = logging.getLogger(__name__) + def auth_with_tyler_api(username, password, jurisdiction): url = f"{settings.EFSP_URL}/authenticate" try: @@ -20,4 +21,3 @@ def auth_with_tyler_api(username, password, jurisdiction): logger.debug("Auth endpoint failed: %s - %s", url, str(e)) return None - diff --git a/efile_app/efile/views/case_details.py b/efile_app/efile/views/case_details.py index 8503637..dec0bc3 100644 --- a/efile_app/efile/views/case_details.py +++ b/efile_app/efile/views/case_details.py @@ -54,7 +54,6 @@ def case_details(request, jurisdiction): # Get jurisdiction config (can be expanded later for other jurisdictions) return render(request, "efile/case_details.html") - except Exception as e: logger.error(f"Error loading case details page: {str(e)}") return render(request, "efile/case_details.html", {"error": "An error occurred loading the page"}) diff --git a/efile_app/efile/views/login.py b/efile_app/efile/views/login.py index ccca19d..a2cb98d 100644 --- a/efile_app/efile/views/login.py +++ b/efile_app/efile/views/login.py @@ -1,3 +1,9 @@ +""" +Actual pages / paths for logging in and out. + +Depend on the api.auth_views that implement the actual functionality. +""" + import logging from django.contrib import messages @@ -28,20 +34,9 @@ def efile_login(request, jurisdiction): if user is not None: # response.status_code == 200: login(request, user) - # data = response.json() request.session["user_email"] = user.email - logger.info("User is auth: %s", user.is_authenticated) - logger.info("Session info: %s", request.session.keys()) - logger.info("User authenticated and session tokens stored") messages.success(request, "Successfully logged in!") return redirect(f"/{jurisdiction}/options/") - # if data.get("tokens"): - # Save tokens in session - # request.session["auth_tokens"] = data["tokens"] - # Save user email for use in forms - # request.session["user_email"] = email - # else: - # messages.error(request, data.get("message", "Invalid email or password.")) else: messages.error(request, "Login service error. Please try again later.") except Exception as e: @@ -55,16 +50,9 @@ def efile_logout(request, jurisdiction): """ Custom logout view """ - from django.contrib.auth import logout - from django.contrib.messages.api import get_messages + from efile.authentication import SuffolkEFileBackend - # Clear any existing messages first - storage = get_messages(request) - for _message in storage: - pass # This consumes all messages + SuffolkEFileBackend.logout(request) - logout(request) - # Clear session data - request.session.flush() messages.success(request, "You have been successfully logged out.") - return redirect("efile_login") + return redirect("efile_login", jurisdiction=jurisdiction)