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 = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
{"fields": ("username", "password", "status")},
|
{"fields": ("username", "password", "status", "verification_type")},
|
||||||
),
|
),
|
||||||
("Personal Info", {"fields": ("first_name", "last_name", "email")}),
|
("Personal Info", {"fields": ("first_name", "last_name", "email")}),
|
||||||
(
|
(
|
||||||
|
@ -508,6 +508,8 @@ class MyUserAdmin(BaseUserAdmin):
|
||||||
("Important dates", {"fields": ("last_login", "date_joined")}),
|
("Important dates", {"fields": ("last_login", "date_joined")}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
readonly_fields = ("verification_type")
|
||||||
|
|
||||||
# Hide Username (uuid), Groups and Permissions
|
# Hide Username (uuid), Groups and Permissions
|
||||||
# Q: Now that we're using Groups and Permissions,
|
# Q: Now that we're using Groups and Permissions,
|
||||||
# do we expose those to analysts to view?
|
# do we expose those to analysts to view?
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
@ -23,6 +24,16 @@ class User(AbstractUser):
|
||||||
but can be customized later.
|
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 ####
|
# #### Constants for choice fields ####
|
||||||
RESTRICTED = "restricted"
|
RESTRICTED = "restricted"
|
||||||
STATUS_CHOICES = ((RESTRICTED, RESTRICTED),)
|
STATUS_CHOICES = ((RESTRICTED, RESTRICTED),)
|
||||||
|
@ -48,6 +59,13 @@ class User(AbstractUser):
|
||||||
db_index=True,
|
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):
|
def __str__(self):
|
||||||
# this info is pulled from Login.gov
|
# this info is pulled from Login.gov
|
||||||
if self.first_name or self.last_name:
|
if self.first_name or self.last_name:
|
||||||
|
@ -95,6 +113,22 @@ class User(AbstractUser):
|
||||||
def has_contact_info(self):
|
def has_contact_info(self):
|
||||||
return bool(self.contact.title or self.contact.email or self.contact.phone)
|
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
|
@classmethod
|
||||||
def needs_identity_verification(cls, email, uuid):
|
def needs_identity_verification(cls, email, uuid):
|
||||||
"""A method used by our oidc classes to test whether a user needs email/uuid verification
|
"""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,
|
# An existing user who is a domain manager of a domain (that is,
|
||||||
# they have an entry in UserDomainRole for their User)
|
# they have an entry in UserDomainRole for their User)
|
||||||
try:
|
user_exists, existing_user = cls.existing_user(uuid)
|
||||||
existing_user = cls.objects.get(username=uuid)
|
if not user_exists:
|
||||||
if existing_user and UserDomainRole.objects.filter(user=existing_user).exists():
|
|
||||||
return False
|
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
|
|
||||||
|
|
||||||
|
# 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():
|
||||||
# A new incoming user who is a domain manager for one of the domains
|
# 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
|
# that we inputted from Verisign (that is, their email address appears
|
||||||
# in the username field of a TransitionDomain)
|
# in the username field of a TransitionDomain)
|
||||||
if TransitionDomain.objects.filter(username=email).exists():
|
verification_type = cls.VerificationTypeChoices.GRANDFATHERED
|
||||||
return False
|
elif VerifiedByStaff.objects.filter(email=email).exists():
|
||||||
|
|
||||||
# New users flagged by Staff to bypass ial2
|
# New users flagged by Staff to bypass ial2
|
||||||
if VerifiedByStaff.objects.filter(email=email).exists():
|
verification_type = cls.VerificationTypeChoices.VERIFIED_BY_STAFF
|
||||||
return False
|
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,
|
# 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").
|
# their email address is in DomainInvitation for an invitation that is not yet "retrieved").
|
||||||
invited = DomainInvitation.DomainInvitationStatus.INVITED
|
verification_type = cls.VerificationTypeChoices.INVITED
|
||||||
if DomainInvitation.objects.filter(email=email, status=invited).exists():
|
else:
|
||||||
return False
|
verification_type = cls.VerificationTypeChoices.REGULAR
|
||||||
|
|
||||||
return True
|
return verification_type
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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):
|
def check_domain_invitations_on_login(self):
|
||||||
"""When a user first arrives on the site, we need to retrieve any domain
|
"""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">
|
<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 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 />
|
<a href="{% url 'admin:registrar_contact_change' user.id %}">{{ user.get_formatted_name }}</a><br />
|
||||||
{% else %}
|
{% else %}
|
||||||
None<br />
|
None<br />
|
||||||
|
|
|
@ -6,7 +6,9 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
{% block field_readonly %}
|
{% block field_readonly %}
|
||||||
{% with all_contacts=original_object.other_contacts.all %}
|
{% 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 %}
|
{% if all_contacts.count > 2 %}
|
||||||
<div class="readonly">
|
<div class="readonly">
|
||||||
{% for contact in all_contacts %}
|
{% for contact in all_contacts %}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue