mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-19 19:09:22 +02:00
Basic logic
This commit is contained in:
parent
c565655aa8
commit
5cba82b343
4 changed files with 83 additions and 26 deletions
|
@ -490,7 +490,7 @@ class MyUserAdmin(BaseUserAdmin):
|
|||
fieldsets = (
|
||||
(
|
||||
None,
|
||||
{"fields": ("username", "password", "status")},
|
||||
{"fields": ("username", "password", "status", "verification_type")},
|
||||
),
|
||||
("Personal Info", {"fields": ("first_name", "last_name", "email")}),
|
||||
(
|
||||
|
@ -508,6 +508,8 @@ class MyUserAdmin(BaseUserAdmin):
|
|||
("Important dates", {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
|
||||
readonly_fields = ("verification_type")
|
||||
|
||||
# Hide Username (uuid), Groups and Permissions
|
||||
# Q: Now that we're using Groups and Permissions,
|
||||
# do we expose those to analysts to view?
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from enum import Enum
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
@ -23,6 +24,16 @@ class User(AbstractUser):
|
|||
but can be customized later.
|
||||
"""
|
||||
|
||||
class VerificationTypeChoices(models.TextChoices):
|
||||
"""
|
||||
Users achieve access to our system in a few different ways.
|
||||
These choices reflect those pathways.
|
||||
"""
|
||||
GRANDFATHERED = "grandfathered", "Legacy user"
|
||||
VERIFIED_BY_STAFF = "verified_by_staff", "Verified by staff"
|
||||
REGULAR = "regular", "Verified by Login.gov"
|
||||
INVITED = "invited", "Invited by a domain manager"
|
||||
|
||||
# #### Constants for choice fields ####
|
||||
RESTRICTED = "restricted"
|
||||
STATUS_CHOICES = ((RESTRICTED, RESTRICTED),)
|
||||
|
@ -48,6 +59,13 @@ class User(AbstractUser):
|
|||
db_index=True,
|
||||
)
|
||||
|
||||
verification_type = models.CharField(
|
||||
choices=VerificationTypeChoices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="The means through which this user was verified",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
# this info is pulled from Login.gov
|
||||
if self.first_name or self.last_name:
|
||||
|
@ -95,6 +113,22 @@ class User(AbstractUser):
|
|||
def has_contact_info(self):
|
||||
return bool(self.contact.title or self.contact.email or self.contact.phone)
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_existing_user_from_uuid(cls, uuid):
|
||||
existing_user = None
|
||||
try:
|
||||
existing_user = cls.objects.get(username=uuid)
|
||||
if existing_user and UserDomainRole.objects.filter(user=existing_user).exists():
|
||||
return (False, existing_user)
|
||||
except cls.DoesNotExist:
|
||||
# Do nothing when the user is not found, as we're checking for existence.
|
||||
pass
|
||||
except Exception as err:
|
||||
raise err
|
||||
|
||||
return (True, existing_user)
|
||||
|
||||
@classmethod
|
||||
def needs_identity_verification(cls, email, uuid):
|
||||
"""A method used by our oidc classes to test whether a user needs email/uuid verification
|
||||
|
@ -102,33 +136,52 @@ class User(AbstractUser):
|
|||
|
||||
# An existing user who is a domain manager of a domain (that is,
|
||||
# they have an entry in UserDomainRole for their User)
|
||||
try:
|
||||
existing_user = cls.objects.get(username=uuid)
|
||||
if existing_user and UserDomainRole.objects.filter(user=existing_user).exists():
|
||||
return False
|
||||
except cls.DoesNotExist:
|
||||
# Do nothing when the user is not found, as we're checking for existence.
|
||||
pass
|
||||
except Exception as err:
|
||||
raise err
|
||||
user_exists, existing_user = cls.existing_user(uuid)
|
||||
if not user_exists:
|
||||
return False
|
||||
|
||||
# A new incoming user who is a domain manager for one of the domains
|
||||
# that we inputted from Verisign (that is, their email address appears
|
||||
# in the username field of a TransitionDomain)
|
||||
# The user needs identity verification if they don't meet
|
||||
# any special criteria, i.e. we are validating them "regularly"
|
||||
existing_user.verification_type = cls.get_verification_type_from_email(email)
|
||||
return existing_user.verification_type == cls.VerificationTypeChoices.REGULAR
|
||||
|
||||
@classmethod
|
||||
def get_verification_type_from_email(cls, email, invitation_status=DomainInvitation.DomainInvitationStatus.INVITED):
|
||||
"""Retrieves the verification type based off of a provided email address"""
|
||||
|
||||
verification_type = None
|
||||
if TransitionDomain.objects.filter(username=email).exists():
|
||||
return False
|
||||
# A new incoming user who is a domain manager for one of the domains
|
||||
# that we inputted from Verisign (that is, their email address appears
|
||||
# in the username field of a TransitionDomain)
|
||||
verification_type = cls.VerificationTypeChoices.GRANDFATHERED
|
||||
elif VerifiedByStaff.objects.filter(email=email).exists():
|
||||
# New users flagged by Staff to bypass ial2
|
||||
verification_type = cls.VerificationTypeChoices.VERIFIED_BY_STAFF
|
||||
elif DomainInvitation.objects.filter(email=email, status=invitation_status).exists():
|
||||
# A new incoming user who is being invited to be a domain manager (that is,
|
||||
# their email address is in DomainInvitation for an invitation that is not yet "retrieved").
|
||||
verification_type = cls.VerificationTypeChoices.INVITED
|
||||
else:
|
||||
verification_type = cls.VerificationTypeChoices.REGULAR
|
||||
|
||||
# New users flagged by Staff to bypass ial2
|
||||
if VerifiedByStaff.objects.filter(email=email).exists():
|
||||
return False
|
||||
return verification_type
|
||||
|
||||
# A new incoming user who is being invited to be a domain manager (that is,
|
||||
# their email address is in DomainInvitation for an invitation that is not yet "retrieved").
|
||||
invited = DomainInvitation.DomainInvitationStatus.INVITED
|
||||
if DomainInvitation.objects.filter(email=email, status=invited).exists():
|
||||
return False
|
||||
def user_verification_type(self, check_if_user_exists=False):
|
||||
if self.verification_type is None:
|
||||
# Would need to check audit log
|
||||
retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED
|
||||
user_exists, _ = self.existing_user(self.username)
|
||||
verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved)
|
||||
|
||||
return True
|
||||
# This should check if the type is unknown, use check_if_user_exists?
|
||||
if verification_type == self.VerificationTypeChoices.REGULAR and not user_exists:
|
||||
raise ValueError(f"No verification_type was found for {self} with id: {self.pk}")
|
||||
else:
|
||||
self.verification_type = verification_type
|
||||
return self.verification_type
|
||||
else:
|
||||
return self.verification_type
|
||||
|
||||
def check_domain_invitations_on_login(self):
|
||||
"""When a user first arrives on the site, we need to retrieve any domain
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<address class="{% if no_title_top_padding %}margin-top-neg-1__detail-list{% endif %} {% if user.has_contact_info %}margin-bottom-1{% endif %} dja-address-contact-list">
|
||||
|
||||
{% if show_formatted_name %}
|
||||
{% if contact.get_formatted_name %}
|
||||
{% if user.get_formatted_name %}
|
||||
<a href="{% url 'admin:registrar_contact_change' user.id %}">{{ user.get_formatted_name }}</a><br />
|
||||
{% else %}
|
||||
None<br />
|
||||
|
|
|
@ -6,7 +6,9 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
|||
{% endcomment %}
|
||||
{% block field_readonly %}
|
||||
{% with all_contacts=original_object.other_contacts.all %}
|
||||
{% if field.field.name == "other_contacts" %}
|
||||
{% if field.field.name == "creator" %}
|
||||
<div class="readonly">{{ field.contents }} ({{ user.verification_type }})</div>
|
||||
{% elif field.field.name == "other_contacts" %}
|
||||
{% if all_contacts.count > 2 %}
|
||||
<div class="readonly">
|
||||
{% for contact in all_contacts %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue