merged main and resolved conflict

This commit is contained in:
Alysia Broddrick 2024-06-11 13:07:21 -07:00
commit 1c1ae51023
No known key found for this signature in database
GPG key ID: 03917052CD0F06B7
14 changed files with 461 additions and 110 deletions

View file

@ -25,6 +25,9 @@ jobs:
|| startsWith(github.head_ref, 'meoward/')
|| startsWith(github.head_ref, 'bob/')
|| startsWith(github.head_ref, 'cb/')
|| startsWith(github.head_ref, 'hotgov/')
|| startsWith(github.head_ref, 'litterbox/')
|| startsWith(github.head_ref, 'ag/')
outputs:
environment: ${{ steps.var.outputs.environment}}
runs-on: "ubuntu-latest"

View file

@ -17,6 +17,8 @@ on:
- staging
- development
- ag
- litterbox
- hotgov
- cb
- bob
- meoward

View file

@ -17,6 +17,8 @@ on:
- staging
- development
- ag
- litterbox
- hotgov
- cb
- bob
- meoward

View file

@ -1,11 +1,15 @@
# Complete model documentation
This is an auto-generated diagram of our data models generated with the
[django-model2puml](https://github.com/sen-den/django-model2puml) library
using the command
[django-model2puml](https://github.com/sen-den/django-model2puml) library.
## How to generate the puml
1. Uncomment `puml_generator` from `INSTALLED_APPS` in settings.py and docker-compose down and up
2. Run the following command to generate a puml file
```bash
$ docker compose app ./manage.py generate_puml --include registrar
docker compose exec app ./manage.py generate_puml --include registrar
```
![Complete data models diagram](./models_diagram.svg)
@ -13,12 +17,19 @@ $ docker compose app ./manage.py generate_puml --include registrar
<details>
<summary>PlantUML source code</summary>
To regenerate this image using Docker, run
## How To regenerate the database svg image
1. Copy your puml file contents into the bottom of this file and replace the current code marked by `plantuml`
2. Run the following command
```bash
$ docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md
docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md
```
3. Remove the puml file from earlier (if you still have it)
4. Commit the new image and the md file
```plantuml
@startuml
class "registrar.Contact <Registrar>" as registrar.Contact #d6f4e9 {
@ -28,17 +39,97 @@ class "registrar.Contact <Registrar>" as registrar.Contact #d6f4e9 {
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
~ user (OneToOneField)
+ first_name (TextField)
+ middle_name (TextField)
+ last_name (TextField)
+ title (TextField)
+ email (TextField)
+ first_name (CharField)
+ middle_name (CharField)
+ last_name (CharField)
+ title (CharField)
+ email (EmailField)
+ phone (PhoneNumberField)
--
}
registrar.Contact -- registrar.User
class "registrar.Host <Registrar>" as registrar.Host #d6f4e9 {
host
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
~ domain (ForeignKey)
--
}
registrar.Host -- registrar.Domain
class "registrar.HostIP <Registrar>" as registrar.HostIP #d6f4e9 {
host ip
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ address (CharField)
~ host (ForeignKey)
--
}
registrar.HostIP -- registrar.Host
class "registrar.PublicContact <Registrar>" as registrar.PublicContact #d6f4e9 {
public contact
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ contact_type (CharField)
+ registry_id (CharField)
~ domain (ForeignKey)
+ name (CharField)
+ org (CharField)
+ street1 (CharField)
+ street2 (CharField)
+ street3 (CharField)
+ city (CharField)
+ sp (CharField)
+ pc (CharField)
+ cc (CharField)
+ email (EmailField)
+ voice (CharField)
+ fax (CharField)
+ pw (CharField)
--
}
registrar.PublicContact -- registrar.Domain
class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
domain
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (DomainField)
+ state (FSMField)
+ expiration_date (DateField)
+ security_contact_registry_id (TextField)
+ deleted (DateField)
+ first_ready (DateField)
--
}
class "registrar.FederalAgency <Registrar>" as registrar.FederalAgency #d6f4e9 {
Federal agency
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ agency (CharField)
--
}
class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
domain request
--
@ -46,24 +137,25 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ status (FSMField)
+ rejection_reason (TextField)
~ federal_agency (ForeignKey)
~ creator (ForeignKey)
~ investigator (ForeignKey)
+ generic_org_type (CharField)
+ is_election_board (BooleanField)
+ organization_type (CharField)
+ federally_recognized_tribe (BooleanField)
+ state_recognized_tribe (BooleanField)
+ tribe_name (TextField)
+ federal_agency (TextField)
+ tribe_name (CharField)
+ federal_type (CharField)
+ is_election_board (BooleanField)
+ organization_name (TextField)
+ address_line1 (TextField)
+ organization_name (CharField)
+ address_line1 (CharField)
+ address_line2 (CharField)
+ city (TextField)
+ city (CharField)
+ state_territory (CharField)
+ zipcode (CharField)
+ urbanization (TextField)
+ type_of_work (TextField)
+ more_organization_information (TextField)
+ urbanization (CharField)
+ about_your_organization (TextField)
~ authorizing_official (ForeignKey)
~ approved_domain (OneToOneField)
~ requested_domain (OneToOneField)
@ -71,17 +163,23 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
+ purpose (TextField)
+ no_other_contacts_rationale (TextField)
+ anything_else (TextField)
+ has_anything_else_text (BooleanField)
+ cisa_representative_email (EmailField)
+ has_cisa_representative (BooleanField)
+ is_policy_acknowledged (BooleanField)
+ submission_date (DateField)
+ notes (TextField)
# current_websites (ManyToManyField)
# alternative_domains (ManyToManyField)
# other_contacts (ManyToManyField)
--
}
registrar.DomainRequest -- registrar.FederalAgency
registrar.DomainRequest -- registrar.User
registrar.DomainRequest -- registrar.User
registrar.DomainRequest -- registrar.Contact
registrar.DomainRequest -- registrar.DraftDomain
registrar.DomainRequest -- registrar.Domain
registrar.DomainRequest -- registrar.DraftDomain
registrar.DomainRequest -- registrar.Contact
registrar.DomainRequest *--* registrar.Website
registrar.DomainRequest *--* registrar.Website
@ -94,35 +192,37 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
~ federal_agency (ForeignKey)
~ creator (ForeignKey)
~ domain_request (OneToOneField)
+ generic_org_type (CharField)
+ organization_type (CharField)
+ federally_recognized_tribe (BooleanField)
+ state_recognized_tribe (BooleanField)
+ tribe_name (TextField)
+ federal_agency (TextField)
+ tribe_name (CharField)
+ federal_type (CharField)
+ is_election_board (BooleanField)
+ organization_name (TextField)
+ address_line1 (TextField)
+ organization_name (CharField)
+ address_line1 (CharField)
+ address_line2 (CharField)
+ city (TextField)
+ city (CharField)
+ state_territory (CharField)
+ zipcode (CharField)
+ urbanization (TextField)
+ type_of_work (TextField)
+ more_organization_information (TextField)
+ urbanization (CharField)
+ about_your_organization (TextField)
~ authorizing_official (ForeignKey)
~ domain (OneToOneField)
~ submitter (ForeignKey)
+ purpose (TextField)
+ no_other_contacts_rationale (TextField)
+ anything_else (TextField)
+ cisa_representative_email (EmailField)
+ is_policy_acknowledged (BooleanField)
+ security_email (EmailField)
+ notes (TextField)
# other_contacts (ManyToManyField)
--
}
registrar.DomainInformation -- registrar.FederalAgency
registrar.DomainInformation -- registrar.User
registrar.DomainInformation -- registrar.DomainRequest
registrar.DomainInformation -- registrar.Contact
@ -142,43 +242,6 @@ class "registrar.DraftDomain <Registrar>" as registrar.DraftDomain #d6f4e9 {
}
class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
domain
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
--
}
class "registrar.HostIP <Registrar>" as registrar.HostIP #d6f4e9 {
host ip
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ address (CharField)
~ host (ForeignKey)
--
}
registrar.HostIP -- registrar.Host
class "registrar.Host <Registrar>" as registrar.Host #d6f4e9 {
host
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
~ domain (ForeignKey)
--
}
registrar.Host -- registrar.Domain
class "registrar.UserDomainRole <Registrar>" as registrar.UserDomainRole #d6f4e9 {
user domain role
--
@ -208,47 +271,49 @@ class "registrar.DomainInvitation <Registrar>" as registrar.DomainInvitation #d6
registrar.DomainInvitation -- registrar.Domain
class "registrar.Nameserver <Registrar>" as registrar.Nameserver #d6f4e9 {
nameserver
class "registrar.TransitionDomain <Registrar>" as registrar.TransitionDomain #d6f4e9 {
transition domain
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
~ domain (ForeignKey)
~ host_ptr (OneToOneField)
+ username (CharField)
+ domain_name (CharField)
+ status (CharField)
+ email_sent (BooleanField)
+ processed (BooleanField)
+ generic_org_type (CharField)
+ organization_name (CharField)
+ federal_type (CharField)
+ federal_agency (CharField)
+ epp_creation_date (DateField)
+ epp_expiration_date (DateField)
+ first_name (CharField)
+ middle_name (CharField)
+ last_name (CharField)
+ title (CharField)
+ email (EmailField)
+ phone (CharField)
+ address_line (CharField)
+ city (CharField)
+ state_territory (CharField)
+ zipcode (CharField)
--
}
registrar.Nameserver -- registrar.Domain
registrar.Nameserver -- registrar.Host
class "registrar.PublicContact <Registrar>" as registrar.PublicContact #d6f4e9 {
public contact
class "registrar.VerifiedByStaff <Registrar>" as registrar.VerifiedByStaff #d6f4e9 {
verified by staff
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ contact_type (CharField)
+ registry_id (CharField)
~ domain (ForeignKey)
+ name (TextField)
+ org (TextField)
+ street1 (TextField)
+ street2 (TextField)
+ street3 (TextField)
+ city (TextField)
+ sp (TextField)
+ pc (TextField)
+ cc (TextField)
+ email (TextField)
+ voice (TextField)
+ fax (TextField)
+ pw (TextField)
+ email (EmailField)
~ requestor (ForeignKey)
+ notes (TextField)
--
}
registrar.PublicContact -- registrar.Domain
registrar.VerifiedByStaff -- registrar.User
class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
@ -265,7 +330,11 @@ class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
+ is_staff (BooleanField)
+ is_active (BooleanField)
+ date_joined (DateTimeField)
+ status (CharField)
+ phone (PhoneNumberField)
+ middle_name (CharField)
+ title (CharField)
+ verification_type (CharField)
# groups (ManyToManyField)
# user_permissions (ManyToManyField)
# domains (ManyToManyField)
@ -274,6 +343,17 @@ class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
registrar.User *--* registrar.Domain
class "registrar.UserGroup <Registrar>" as registrar.UserGroup #d6f4e9 {
User group
--
- id (AutoField)
+ name (CharField)
~ group_ptr (OneToOneField)
# permissions (ManyToManyField)
--
}
class "registrar.Website <Registrar>" as registrar.Website #d6f4e9 {
website
--
@ -285,6 +365,29 @@ class "registrar.Website <Registrar>" as registrar.Website #d6f4e9 {
}
class "registrar.WaffleFlag <Registrar>" as registrar.WaffleFlag #d6f4e9 {
waffle flag
--
+ id (BigAutoField)
+ name (CharField)
+ everyone (BooleanField)
+ percent (DecimalField)
+ testing (BooleanField)
+ superusers (BooleanField)
+ staff (BooleanField)
+ authenticated (BooleanField)
+ languages (TextField)
+ rollout (BooleanField)
+ note (TextField)
+ created (DateTimeField)
+ modified (DateTimeField)
# groups (ManyToManyField)
# users (ManyToManyField)
--
}
registrar.WaffleFlag *--* registrar.User
@enduml
```

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Before After
Before After

View file

@ -0,0 +1,32 @@
---
applications:
- name: getgov-hotgov
buildpacks:
- python_buildpack
path: ../../src
instances: 1
memory: 512M
stack: cflinuxfs4
timeout: 180
command: ./run.sh
health-check-type: http
health-check-http-endpoint: /health
health-check-invocation-timeout: 40
env:
# Send stdout and stderr straight to the terminal without buffering
PYTHONUNBUFFERED: yup
# Tell Django where to find its configuration
DJANGO_SETTINGS_MODULE: registrar.config.settings
# Tell Django where it is being hosted
DJANGO_BASE_URL: https://getgov-hotgov.app.cloud.gov
# Tell Django how much stuff to log
DJANGO_LOG_LEVEL: INFO
# default public site location
GETGOV_PUBLIC_SITE_URL: https://get.gov
# Flag to disable/enable features in prod environments
IS_PRODUCTION: False
routes:
- route: getgov-hotgov.app.cloud.gov
services:
- getgov-credentials
- getgov-hotgov-database

View file

@ -0,0 +1,32 @@
---
applications:
- name: getgov-litterbox
buildpacks:
- python_buildpack
path: ../../src
instances: 1
memory: 512M
stack: cflinuxfs4
timeout: 180
command: ./run.sh
health-check-type: http
health-check-http-endpoint: /health
health-check-invocation-timeout: 40
env:
# Send stdout and stderr straight to the terminal without buffering
PYTHONUNBUFFERED: yup
# Tell Django where to find its configuration
DJANGO_SETTINGS_MODULE: registrar.config.settings
# Tell Django where it is being hosted
DJANGO_BASE_URL: https://getgov-litterbox.app.cloud.gov
# Tell Django how much stuff to log
DJANGO_LOG_LEVEL: INFO
# default public site location
GETGOV_PUBLIC_SITE_URL: https://get.gov
# Flag to disable/enable features in prod environments
IS_PRODUCTION: False
routes:
- route: getgov-litterbox.app.cloud.gov
services:
- getgov-credentials
- getgov-litterbox-database

View file

@ -15,6 +15,7 @@ from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from dateutil.relativedelta import relativedelta # type: ignore
from epplibwrapper.errors import ErrorCode, RegistryError
from registrar.models.user_domain_role import UserDomainRole
from waffle.admin import FlagAdmin
from waffle.models import Sample, Switch
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
@ -588,6 +589,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
resource_classes = [UserResource]
form = MyUserAdminForm
change_form_template = "django/admin/user_change_form.html"
class Meta:
"""Contains meta information about this class"""
@ -627,7 +629,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
None,
{"fields": ("username", "password", "status", "verification_type")},
),
("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}),
("Personal info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}),
(
"Permissions",
{
@ -706,8 +708,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
ordering = ["first_name", "last_name", "email"]
search_help_text = "Search by first name, last name, or email."
change_form_template = "django/admin/email_clipboard_change_form.html"
def get_search_results(self, request, queryset, search_term):
"""
Override for get_search_results. This affects any upstream model using autocomplete_fields,
@ -787,6 +787,23 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
# users who might not belong to groups
return self.analyst_readonly_fields
def change_view(self, request, object_id, form_url="", extra_context=None):
"""Add user's related domains and requests to context"""
obj = self.get_object(request, object_id)
domain_requests = DomainRequest.objects.filter(creator=obj).exclude(
Q(status=DomainRequest.DomainRequestStatus.STARTED) | Q(status=DomainRequest.DomainRequestStatus.WITHDRAWN)
)
sort_by = request.GET.get("sort_by", "requested_domain__name")
domain_requests = domain_requests.order_by(sort_by)
user_domain_roles = UserDomainRole.objects.filter(user=obj)
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
domains = Domain.objects.filter(id__in=domain_ids).exclude(state=Domain.State.DELETED)
extra_context = {"domain_requests": domain_requests, "domains": domains}
return super().change_view(request, object_id, form_url, extra_context)
class HostIPInline(admin.StackedInline):
"""Edit an ip address on the host page."""

View file

@ -130,7 +130,7 @@ html[data-theme="light"] {
// Sets darker color on delete page links.
// Remove when dark mode successfully applies to Django delete page.
.delete-confirmation .content a:not(.button) {
color: #005288;
color: color('primary');
}
}
@ -159,7 +159,7 @@ html[data-theme="dark"] {
// Sets darker color on delete page links.
// Remove when dark mode successfully applies to Django delete page.
.delete-confirmation .content a:not(.button) {
color: #005288;
color: color('primary');
}
}
@ -186,6 +186,14 @@ div#content > h2 {
margin: units(2) 0 units(1) 0;
}
.module ul.padding-0 {
padding: 0 !important;
}
.module ul.margin-0 {
margin: 0 !important;
}
.change-list {
.usa-table--striped tbody tr:nth-child(odd) td,
.usa-table--striped tbody tr:nth-child(odd) th,
@ -732,7 +740,7 @@ div.dja__model-description{
a, a:link, a:visited {
font-size: medium;
color: #005288 !important;
color: color('primary') !important;
}
&.dja__model-description--no-overflow {
@ -761,3 +769,7 @@ div.dja__model-description{
.usa-summary-box h3 {
color: #{$dhs-blue-60};
}
.module caption, .inline-group h2 {
text-transform: capitalize;
}

View file

@ -660,6 +660,8 @@ ALLOWED_HOSTS = [
"getgov-staging.app.cloud.gov",
"getgov-development.app.cloud.gov",
"getgov-ag.app.cloud.gov",
"getgov-litterbox.app.cloud.gov",
"getgov-hotgov.app.cloud.gov",
"getgov-cb.app.cloud.gov",
"getgov-bob.app.cloud.gov",
"getgov-meoward.app.cloud.gov",

View file

@ -106,6 +106,12 @@ class UserFixture:
"last_name": "Orr",
"email": "riley+320@truss.works",
},
{
"username": "76612d84-66b0-4ae9-9870-81e98b9858b6",
"first_name": "Anna",
"last_name": "Gingle",
"email": "annagingle@truss.works",
},
]
STAFF = [
@ -194,6 +200,12 @@ class UserFixture:
"last_name": "Orr-Analyst",
"email": "riley+321@truss.works",
},
{
"username": "e1e350b1-cfc1-4753-a6cb-3ae6d912f99c",
"first_name": "Anna-Analyst",
"last_name": "Gingle-Analyst",
"email": "annagingle+analyst@truss.works",
},
]
def load_users(cls, users, group_name, are_superusers=False):

View file

@ -0,0 +1,36 @@
{% extends 'django/admin/email_clipboard_change_form.html' %}
{% load i18n static %}
{% block after_related_objects %}
<div class="module aligned padding-3">
<h2>Associated requests and domains</h2>
<div class="grid-row grid-gap mobile:padding-x-1 desktop:padding-x-4">
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
<h3>Domain requests</h3>
<ul class="margin-0 padding-0">
{% for domain_request in domain_requests %}
<li>
<a href="{% url 'admin:registrar_domainrequest_change' domain_request.pk %}">
{{ domain_request.requested_domain }}
</a>
({{ domain_request.status }})
</li>
{% endfor %}
</ul>
</div>
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
<h3>Domains</h3>
<ul class="margin-0 padding-0">
{% for domain in domains %}
<li>
<a href="{% url 'admin:registrar_domain_change' domain.pk %}">
{{ domain.name }}
</a>
({{ domain.state }})
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}

View file

@ -667,7 +667,7 @@ class MockDb(TestCase):
is_election_board=False,
)
meoward_user = get_user_model().objects.create(
self.meoward_user = get_user_model().objects.create(
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
)
@ -676,7 +676,7 @@ class MockDb(TestCase):
)
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
user=self.meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
@ -688,19 +688,21 @@ class MockDb(TestCase):
)
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
user=self.meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
user=self.meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
user=self.meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
)
_, created = DomainInvitation.objects.get_or_create(
email=meoward_user.email, domain=self.domain_1, status=DomainInvitation.DomainInvitationStatus.RETRIEVED
email=self.meoward_user.email,
domain=self.domain_1,
status=DomainInvitation.DomainInvitationStatus.RETRIEVED,
)
_, created = DomainInvitation.objects.get_or_create(

View file

@ -47,6 +47,7 @@ from registrar.models import (
from registrar.models.user_domain_role import UserDomainRole
from registrar.models.verified_by_staff import VerifiedByStaff
from .common import (
MockDb,
MockSESClient,
AuditedAdminMockData,
completed_domain_request,
@ -3438,16 +3439,19 @@ class TestListHeaderAdmin(TestCase):
User.objects.all().delete()
class TestMyUserAdmin(TestCase):
class TestMyUserAdmin(MockDb):
def setUp(self):
super().setUp()
admin_site = AdminSite()
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
self.client = Client(HTTP_HOST="localhost:8080")
self.superuser = create_superuser()
self.staffuser = create_user()
self.test_helper = GenericTestHelper(admin=self.admin)
def tearDown(self):
super().tearDown()
DomainRequest.objects.all().delete()
User.objects.all().delete()
@less_console_noise_decorator
@ -3472,7 +3476,7 @@ class TestMyUserAdmin(TestCase):
"""
Tests for the correct helper text on this page
"""
user = create_user()
user = self.staffuser
p = "adminpass"
self.client.login(username="superuser", password=p)
@ -3493,10 +3497,11 @@ class TestMyUserAdmin(TestCase):
]
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
@less_console_noise_decorator
def test_list_display_without_username(self):
with less_console_noise():
request = self.client.request().wsgi_request
request.user = create_user()
request.user = self.staffuser
list_display = self.admin.get_list_display(request)
expected_list_display = [
@ -3522,7 +3527,7 @@ class TestMyUserAdmin(TestCase):
def test_get_fieldsets_cisa_analyst(self):
with less_console_noise():
request = self.client.request().wsgi_request
request.user = create_user()
request.user = self.staffuser
fieldsets = self.admin.get_fieldsets(request)
expected_fieldsets = (
(
@ -3540,6 +3545,97 @@ class TestMyUserAdmin(TestCase):
)
self.assertEqual(fieldsets, expected_fieldsets)
def test_analyst_can_see_related_domains_and_requests_in_user_form(self):
"""Tests if an analyst can see the related domains and domain requests for a user in that user's form"""
# From MockDb, we have self.meoward_user which we'll use as creator
# Create fake domain requests
domain_request_started = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED, user=self.meoward_user, name="started.gov"
)
domain_request_submitted = completed_domain_request(
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.meoward_user, name="submitted.gov"
)
domain_request_in_review = completed_domain_request(
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.meoward_user, name="in-review.gov"
)
domain_request_withdrawn = completed_domain_request(
status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=self.meoward_user, name="withdrawn.gov"
)
domain_request_approved = completed_domain_request(
status=DomainRequest.DomainRequestStatus.APPROVED, user=self.meoward_user, name="approved.gov"
)
domain_request_rejected = completed_domain_request(
status=DomainRequest.DomainRequestStatus.REJECTED, user=self.meoward_user, name="rejected.gov"
)
domain_request_ineligible = completed_domain_request(
status=DomainRequest.DomainRequestStatus.INELIGIBLE, user=self.meoward_user, name="ineligible.gov"
)
# From MockDb, we have sel.meoward_user who's admin on
# self.domain_1 - READY
# self.domain_2 - DNS_NEEDED
# self.domain_11 - READY
# self.domain_12 - READY
# DELETED:
domain_deleted, _ = Domain.objects.get_or_create(
name="domain_deleted.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2024, 4, 2))
)
_, created = UserDomainRole.objects.get_or_create(
user=self.meoward_user, domain=domain_deleted, role=UserDomainRole.Roles.MANAGER
)
p = "userpass"
self.client.login(username="staffuser", password=p)
response = self.client.get(
"/admin/registrar/user/{}/change/".format(self.meoward_user.id),
follow=True,
)
# Make sure the page loaded and contains the expected domain request names and links to the domain requests
self.assertEqual(response.status_code, 200)
self.assertContains(response, domain_request_submitted.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_submitted.pk])
self.assertContains(response, expected_href)
self.assertContains(response, domain_request_in_review.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_in_review.pk])
self.assertContains(response, expected_href)
self.assertContains(response, domain_request_approved.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_approved.pk])
self.assertContains(response, expected_href)
self.assertContains(response, domain_request_rejected.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_rejected.pk])
self.assertContains(response, expected_href)
self.assertContains(response, domain_request_ineligible.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_ineligible.pk])
self.assertContains(response, expected_href)
# We filter out those requests
# STARTED
self.assertNotContains(response, domain_request_started.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_started.pk])
self.assertNotContains(response, expected_href)
# WITHDRAWN
self.assertNotContains(response, domain_request_withdrawn.requested_domain.name)
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_withdrawn.pk])
self.assertNotContains(response, expected_href)
# Make sure the page contains the expected domain names and links to the domains
self.assertContains(response, self.domain_1.name)
expected_href = reverse("admin:registrar_domain_change", args=[self.domain_1.pk])
self.assertContains(response, expected_href)
# We filter out DELETED
self.assertNotContains(response, domain_deleted.name)
expected_href = reverse("admin:registrar_domain_change", args=[domain_deleted.pk])
self.assertNotContains(response, expected_href)
class AuditedAdminTest(TestCase):
def setUp(self):