Merge pull request #1981 from cisagov/za/1948-remove-draft-domain-and-websites

(on getgov-bob) Ticket #1948: Remove draft domain and websites for analysts
This commit is contained in:
zandercymatics 2024-04-12 09:22:01 -06:00 committed by GitHub
commit 21638ad364
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 258 additions and 15 deletions

View file

@ -779,6 +779,46 @@ class WebsiteAdmin(ListHeaderAdmin):
] ]
search_help_text = "Search by website." search_help_text = "Search by website."
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
if analyst_perm and not superuser_perm:
return {}
return super().get_model_perms(request)
def has_change_permission(self, request, obj=None):
"""
Allow analysts to access the change form directly via URL.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
if analyst_perm and not superuser_perm:
return True
return super().has_change_permission(request, obj)
def response_change(self, request, obj):
"""
Override to redirect users back to the previous page after saving.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
return_path = request.GET.get("return_path")
# First, call the super method to perform the standard operations and capture the response
response = super().response_change(request, obj)
# Don't redirect to the website page on save if the user is an analyst.
# Rather, just redirect back to the originating page.
if (analyst_perm and not superuser_perm) and return_path:
# Redirect to the return path if it exists
return HttpResponseRedirect(return_path)
# If no redirection is needed, return the original response
return response
class UserDomainRoleAdmin(ListHeaderAdmin): class UserDomainRoleAdmin(ListHeaderAdmin):
"""Custom user domain role admin class.""" """Custom user domain role admin class."""
@ -1466,7 +1506,10 @@ class DomainInformationInline(admin.StackedInline):
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
"""Custom has_change_permission override so that we can specify that """Custom has_change_permission override so that we can specify that
analysts can edit this through this inline, but not through the model normally""" analysts can edit this through this inline, but not through the model normally"""
if request.user.has_perm("registrar.analyst_access_permission"):
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
if analyst_perm and not superuser_perm:
return True return True
return super().has_change_permission(request, obj) return super().has_change_permission(request, obj)
@ -1892,6 +1935,46 @@ class DraftDomainAdmin(ListHeaderAdmin):
# in autocomplete_fields for user # in autocomplete_fields for user
ordering = ["name"] ordering = ["name"]
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
if analyst_perm and not superuser_perm:
return {}
return super().get_model_perms(request)
def has_change_permission(self, request, obj=None):
"""
Allow analysts to access the change form directly via URL.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
if analyst_perm and not superuser_perm:
return True
return super().has_change_permission(request, obj)
def response_change(self, request, obj):
"""
Override to redirect users back to the previous page after saving.
"""
superuser_perm = request.user.has_perm("registrar.full_access_permission")
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
return_path = request.GET.get("return_path")
# First, call the super method to perform the standard operations and capture the response
response = super().response_change(request, obj)
# Don't redirect to the website page on save if the user is an analyst.
# Rather, just redirect back to the originating page.
if (analyst_perm and not superuser_perm) and return_path:
# Redirect to the return path if it exists
return HttpResponseRedirect(return_path)
# If no redirection is needed, return the original response
return response
class PublicContactAdmin(ListHeaderAdmin): class PublicContactAdmin(ListHeaderAdmin):
"""Custom PublicContact admin class.""" """Custom PublicContact admin class."""

View file

@ -0,0 +1,37 @@
# This migration creates the create_full_access_group and create_cisa_analyst_group groups
# It is dependent on 0079 (which populates federal agencies)
# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS
# in the user_group model then:
# [NOT RECOMMENDED]
# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions
# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups
# step 3: fake run the latest migration in the migrations list
# [RECOMMENDED]
# Alternatively:
# step 1: duplicate the migration that loads data
# step 2: docker-compose exec app ./manage.py migrate
from django.db import migrations
from registrar.models import UserGroup
from typing import Any
# For linting: RunPython expects a function reference,
# so let's give it one
def create_groups(apps, schema_editor) -> Any:
UserGroup.create_cisa_analyst_group(apps, schema_editor)
UserGroup.create_full_access_group(apps, schema_editor)
class Migration(migrations.Migration):
dependencies = [
("registrar", "0083_alter_contact_email_alter_publiccontact_email"),
]
operations = [
migrations.RunPython(
create_groups,
reverse_code=migrations.RunPython.noop,
atomic=True,
),
]

View file

@ -41,11 +41,6 @@ class UserGroup(Group):
"model": "domain", "model": "domain",
"permissions": ["view_domain"], "permissions": ["view_domain"],
}, },
{
"app_label": "registrar",
"model": "draftdomain",
"permissions": ["change_draftdomain"],
},
{ {
"app_label": "registrar", "app_label": "registrar",
"model": "user", "model": "user",
@ -56,11 +51,6 @@ class UserGroup(Group):
"model": "domaininvitation", "model": "domaininvitation",
"permissions": ["add_domaininvitation", "view_domaininvitation"], "permissions": ["add_domaininvitation", "view_domaininvitation"],
}, },
{
"app_label": "registrar",
"model": "website",
"permissions": ["change_website"],
},
{ {
"app_label": "registrar", "app_label": "registrar",
"model": "userdomainrole", "model": "userdomainrole",

View file

@ -27,6 +27,10 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
</dl> </dl>
</div> </div>
{% endif %} {% endif %}
{% elif field.field.name == "requested_domain" %}
{% with current_path=request.get_full_path %}
<a class="margin-top-05 padding-top-05" href="{% url 'admin:registrar_draftdomain_change' original.requested_domain.id %}?{{ 'return_path='|add:current_path }}">{{ original.requested_domain }}</a>
{% endwith%}
{% elif field.field.name == "current_websites" %} {% elif field.field.name == "current_websites" %}
{% comment %} {% comment %}
The "website" model is essentially just a text field. The "website" model is essentially just a text field.
@ -49,9 +53,11 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
</div> </div>
{% elif field.field.name == "alternative_domains" %} {% elif field.field.name == "alternative_domains" %}
<div class="readonly"> <div class="readonly">
{% with current_path=request.get_full_path %}
{% for alt_domain in original.alternative_domains.all %} {% for alt_domain in original.alternative_domains.all %}
<a href="{% url 'admin:registrar_website_change' alt_domain.id %}">{{ alt_domain }}</a>{% if not forloop.last %}, {% endif %} <a href="{% url 'admin:registrar_website_change' alt_domain.id %}?{{ 'return_path='|add:current_path }}">{{ alt_domain }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
{% endwith %}
</div> </div>
{% else %} {% else %}
<div class="readonly">{{ field.contents }}</div> <div class="readonly">{{ field.contents }}</div>

View file

@ -21,7 +21,16 @@ from registrar.admin import (
UserDomainRoleAdmin, UserDomainRoleAdmin,
VerifiedByStaffAdmin, VerifiedByStaffAdmin,
) )
from registrar.models import Domain, DomainRequest, DomainInformation, User, DomainInvitation, Contact, Website from registrar.models import (
Domain,
DomainRequest,
DomainInformation,
User,
DomainInvitation,
Contact,
Website,
DraftDomain,
)
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from registrar.models.verified_by_staff import VerifiedByStaff from registrar.models.verified_by_staff import VerifiedByStaff
from .common import ( from .common import (
@ -697,6 +706,126 @@ class TestDomainRequestAdmin(MockEppLib):
) )
self.mock_client = MockSESClient() self.mock_client = MockSESClient()
@less_console_noise_decorator
def test_analyst_can_see_and_edit_alternative_domain(self):
"""Tests if an analyst can still see and edit the alternative domain field"""
# Create fake creator
_creator = User.objects.create(
username="MrMeoward",
first_name="Meoward",
last_name="Jones",
)
# Create a fake domain request
_domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
fake_website = Website.objects.create(website="thisisatest.gov")
_domain_request.alternative_domains.add(fake_website)
_domain_request.save()
p = "userpass"
self.client.login(username="staffuser", password=p)
response = self.client.get(
"/admin/registrar/domainrequest/{}/change/".format(_domain_request.pk),
follow=True,
)
# Make sure the page loaded, and that we're on the right page
self.assertEqual(response.status_code, 200)
self.assertContains(response, _domain_request.requested_domain.name)
# Test if the page has the alternative domain
self.assertContains(response, "thisisatest.gov")
# Check that the page contains the url we expect
expected_href = reverse("admin:registrar_website_change", args=[fake_website.id])
self.assertContains(response, expected_href)
# Navigate to the website to ensure that we can still edit it
response = self.client.get(
"/admin/registrar/website/{}/change/".format(fake_website.pk),
follow=True,
)
# Make sure the page loaded, and that we're on the right page
self.assertEqual(response.status_code, 200)
self.assertContains(response, "thisisatest.gov")
@less_console_noise_decorator
def test_analyst_can_see_and_edit_requested_domain(self):
"""Tests if an analyst can still see and edit the requested domain field"""
# Create fake creator
_creator = User.objects.create(
username="MrMeoward",
first_name="Meoward",
last_name="Jones",
)
# Create a fake domain request
_domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
p = "userpass"
self.client.login(username="staffuser", password=p)
response = self.client.get(
"/admin/registrar/domainrequest/{}/change/".format(_domain_request.pk),
follow=True,
)
# Filter to get the latest from the DB (rather than direct assignment)
requested_domain = DraftDomain.objects.filter(name=_domain_request.requested_domain.name).get()
# Make sure the page loaded, and that we're on the right page
self.assertEqual(response.status_code, 200)
self.assertContains(response, requested_domain.name)
# Check that the page contains the url we expect
expected_href = reverse("admin:registrar_draftdomain_change", args=[requested_domain.id])
self.assertContains(response, expected_href)
# Navigate to the website to ensure that we can still edit it
response = self.client.get(
"/admin/registrar/draftdomain/{}/change/".format(requested_domain.pk),
follow=True,
)
# Make sure the page loaded, and that we're on the right page
self.assertEqual(response.status_code, 200)
self.assertContains(response, "city.gov")
@less_console_noise_decorator
def test_analyst_can_see_current_websites(self):
"""Tests if an analyst can still see current website field"""
# Create fake creator
_creator = User.objects.create(
username="MrMeoward",
first_name="Meoward",
last_name="Jones",
)
# Create a fake domain request
_domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator)
fake_website = Website.objects.create(website="thisisatest.gov")
_domain_request.current_websites.add(fake_website)
_domain_request.save()
p = "userpass"
self.client.login(username="staffuser", password=p)
response = self.client.get(
"/admin/registrar/domainrequest/{}/change/".format(_domain_request.pk),
follow=True,
)
# Make sure the page loaded, and that we're on the right page
self.assertEqual(response.status_code, 200)
self.assertContains(response, _domain_request.requested_domain.name)
# Test if the page has the current website
self.assertContains(response, "thisisatest.gov")
def test_domain_sortable(self): def test_domain_sortable(self):
"""Tests if the DomainRequest sorts by domain correctly""" """Tests if the DomainRequest sorts by domain correctly"""
with less_console_noise(): with less_console_noise():

View file

@ -37,7 +37,6 @@ class TestGroups(TestCase):
"add_domaininvitation", "add_domaininvitation",
"view_domaininvitation", "view_domaininvitation",
"change_domainrequest", "change_domainrequest",
"change_draftdomain",
"add_federalagency", "add_federalagency",
"change_federalagency", "change_federalagency",
"delete_federalagency", "delete_federalagency",
@ -48,7 +47,6 @@ class TestGroups(TestCase):
"add_verifiedbystaff", "add_verifiedbystaff",
"change_verifiedbystaff", "change_verifiedbystaff",
"delete_verifiedbystaff", "delete_verifiedbystaff",
"change_website",
] ]
# Get the codenames of actual permissions associated with the group # Get the codenames of actual permissions associated with the group