This commit is contained in:
zandercymatics 2024-05-14 13:49:43 -06:00
parent 54c532f3b2
commit f55d0a655a
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
10 changed files with 38 additions and 43 deletions

View file

@ -50,7 +50,7 @@ class OpenIdConnectBackend(ModelBackend):
user, created = UserModel.objects.get_or_create(**args)
if created:
if created and request is not None:
request.session["is_new_user"] = True
if not created:
@ -63,7 +63,8 @@ class OpenIdConnectBackend(ModelBackend):
try:
user = UserModel.objects.get_by_natural_key(username)
except UserModel.DoesNotExist:
request.session["is_new_user"] = True
if request is not None:
request.session["is_new_user"] = True
return None
# run this callback for a each login
user.on_each_login()

View file

@ -46,4 +46,3 @@ class ContactForm(forms.Form):
label="Phone",
error_messages={"invalid": "Enter a valid 10-digit phone number.", "required": "Enter your phone number."},
)

View file

@ -202,6 +202,7 @@ NameserverFormset = formset_factory(
validate_max=True,
)
# TODO - refactor, wait until daves PR
class ContactForm(forms.ModelForm):
"""Form for updating contacts."""

View file

@ -150,7 +150,7 @@ class OrganizationContactForm(RegistrarForm):
"""Require something to be selected when this is a federal agency."""
federal_agency = self.cleaned_data.get("federal_agency", None)
# need the domain request object to know if this is federal
if self.domain_request is None:
if hasattr(self, "domain_request") and self.domain_request is None:
# hmm, no saved domain request object?, default require the agency
if not federal_agency:
# no answer was selected

View file

@ -3,6 +3,7 @@ from django.db import models
from .utility.time_stamped_model import TimeStampedModel
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
class Contact(TimeStampedModel):
"""Contact information follows a similar pattern for each contact."""

View file

@ -280,4 +280,4 @@ def from_database(form_class, obj):
"""Returns a dict of form field values gotten from `obj`."""
if obj is None:
return {}
return {name: getattr(obj, name) for name in form_class.declared_fields.keys()} # type: ignore
return {name: getattr(obj, name) for name in form_class.declared_fields.keys()} # type: ignore

View file

@ -1,17 +1,20 @@
"""
Contains middleware used in settings.py
"""
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
from django.urls import reverse
from django.http import HttpResponseRedirect
from waffle.decorators import flag_is_active
class CheckUserProfileMiddleware:
"""
Checks if the current user has finished_setup = False.
Checks if the current user has finished_setup = False.
If they do, redirect them to the setup page regardless of where they are in
the application.
"""
def __init__(self, get_response):
self.get_response = get_response
@ -21,7 +24,7 @@ class CheckUserProfileMiddleware:
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# Check that the user is "opted-in" to the profile feature flag
has_profile_feature_flag = flag_is_active(request, "profile_feature")
@ -32,10 +35,7 @@ class CheckUserProfileMiddleware:
# Check if setup is not finished
finished_setup = hasattr(request.user, "finished_setup") and request.user.finished_setup
if request.user.is_authenticated and not finished_setup:
setup_page = reverse(
"finish-contact-profile-setup",
kwargs={"pk": request.user.contact.pk}
)
setup_page = reverse("finish-contact-profile-setup", kwargs={"pk": request.user.contact.pk})
logout_page = reverse("logout")
excluded_pages = [
setup_page,
@ -52,7 +52,7 @@ class CheckUserProfileMiddleware:
# Don't redirect on excluded pages (such as the setup page itself)
if not any(request.path.startswith(page) for page in excluded_pages):
# Preserve the original query parameters, and coerce them into a dict
query_params = parse_qs(request.META['QUERY_STRING'])
query_params = parse_qs(request.META["QUERY_STRING"])
if custom_redirect is not None:
# Set the redirect value to our redirect location
@ -65,7 +65,6 @@ class CheckUserProfileMiddleware:
setup_page_parts[4] = urlencode(query_params)
# Reassemble the URL
setup_page = urlunparse(setup_page_parts)
# Redirect to the setup page
return HttpResponseRedirect(setup_page)
@ -88,4 +87,4 @@ class NoCacheMiddleware:
def __call__(self, request):
response = self.get_response(request)
response["Cache-Control"] = "no-cache"
return response
return response

View file

@ -20,6 +20,7 @@ logger = logging.getLogger(__name__)
class BaseContactView(ContactPermissionView):
"""Provides a base view for the contact object. On get, the contact
is saved in the session and on self.object."""
def get(self, request, *args, **kwargs):
"""Sets the current contact in cache, defines the current object as self.object
then returns render_to_response"""
@ -56,6 +57,7 @@ class BaseContactView(ContactPermissionView):
class ContactFormBaseView(BaseContactView, FormMixin):
"""Adds a FormMixin to BaseContactView, and handles post"""
def post(self, request, *args, **kwargs):
"""Form submission posts to this view.
@ -78,8 +80,9 @@ class ContactFormBaseView(BaseContactView, FormMixin):
class ContactProfileSetupView(ContactFormBaseView):
"""This view forces the user into providing additional details that
"""This view forces the user into providing additional details that
we may have missed from Login.gov"""
template_name = "finish_contact_setup.html"
form_class = ContactForm
model = Contact
@ -99,22 +102,23 @@ class ContactProfileSetupView(ContactFormBaseView):
- COMPLETE_SETUP: Indicates that we want to navigate BACK_TO_SELF, but the subsequent
redirect after the next POST should be either HOME or TO_SPECIFIC_PAGE
"""
HOME = "home"
BACK_TO_SELF = "back_to_self"
COMPLETE_SETUP = "complete_setup"
TO_SPECIFIC_PAGE = "domain_request"
# TODO - refactor
@waffle_flag('profile_feature')
@waffle_flag("profile_feature")
@method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
"""
Handles dispatching of the view, applying CSRF protection and checking the 'profile_feature' flag.
This method sets the redirect type based on the 'redirect' query parameter,
This method sets the redirect type based on the 'redirect' query parameter,
defaulting to BACK_TO_SELF if not provided.
It updates the session with the redirect view name if the redirect type is TO_SPECIFIC_PAGE.
Returns:
HttpResponse: The response generated by the parent class's dispatch method.
"""
@ -140,7 +144,7 @@ class ContactProfileSetupView(ContactFormBaseView):
self.RedirectType.HOME,
self.RedirectType.COMPLETE_SETUP,
self.RedirectType.BACK_TO_SELF,
self.RedirectType.TO_SPECIFIC_PAGE
self.RedirectType.TO_SPECIFIC_PAGE,
]
if redirect_type not in default_redirects:
self.redirect_type = self.RedirectType.TO_SPECIFIC_PAGE
@ -154,8 +158,8 @@ class ContactProfileSetupView(ContactFormBaseView):
"""
Returns a URL string based on the current value of self.redirect_type.
Depending on self.redirect_type, constructs a base URL and appends a
'redirect' query parameter. Handles different redirection types such as
Depending on self.redirect_type, constructs a base URL and appends a
'redirect' query parameter. Handles different redirection types such as
HOME, BACK_TO_SELF, COMPLETE_SETUP, and TO_SPECIFIC_PAGE.
Returns:
@ -178,13 +182,11 @@ class ContactProfileSetupView(ContactFormBaseView):
base_url = reverse(desired_view)
except NoReverseMatch as err:
logger.error(err)
logger.error(
"ContactProfileSetupView -> get_redirect_url -> Could not find specified page."
)
logger.error("ContactProfileSetupView -> get_redirect_url -> Could not find specified page.")
base_url = reverse("home")
case _:
base_url = reverse("home")
# Quote cleans up the value so that it can be used in a url
query_params["redirect"] = quote(self.redirect_type)
@ -231,27 +233,24 @@ class ContactProfileSetupView(ContactFormBaseView):
return self.form_invalid(form)
def form_valid(self, form):
completed_states = [
self.RedirectType.TO_SPECIFIC_PAGE,
self.RedirectType.HOME
]
completed_states = [self.RedirectType.TO_SPECIFIC_PAGE, self.RedirectType.HOME]
if self.redirect_type in completed_states:
self.request.user.finished_setup = True
self.request.user.save()
to_database(form=form, obj=self.object)
self._update_session_with_contact()
return super().form_valid(form)
def get_initial(self):
"""The initial value for the form (which is a formset here)."""
db_object = from_database(form_class=self.form_class, obj=self.object)
return db_object
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["email_sublabel_text"] = self._email_sublabel_text()
@ -259,7 +258,7 @@ class ContactProfileSetupView(ContactFormBaseView):
context["confirm_changes"] = True
return context
def _email_sublabel_text(self):
"""Returns the lengthy sublabel for the email field"""
help_url = public_site_url("help/account-management/#get-help-with-login.gov")

View file

@ -819,4 +819,3 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
duplicates = [item for item, count in object_dict.items() if count > 1]
return duplicates

View file

@ -341,7 +341,6 @@ class ContactPermission(PermissionsLoginMixin):
if not self.request.user.is_authenticated:
return False
given_contact_pk = self.kwargs["pk"]
# Grab the user in the DB to do a full object comparision, not just on ids
@ -351,13 +350,10 @@ class ContactPermission(PermissionsLoginMixin):
if current_user.contact.pk != given_contact_pk:
# Don't allow users to modify other users profiles
return False
# Check if the object at the id we're searching on actually exists
requested_user_exists = User.objects.filter(pk=current_user.pk).exists()
requested_contact_exists = Contact.objects.filter(
user=current_user.pk,
pk=given_contact_pk
).exists()
requested_contact_exists = Contact.objects.filter(user=current_user.pk, pk=given_contact_pk).exists()
if not requested_user_exists or not requested_contact_exists:
return False