Merge pull request #1167 from cisagov/rh/1031-staff-enable-editing

Ticket #1031: Update Staff permissions for contacts, websites, addresses, domain information and domain application
This commit is contained in:
Rebecca H 2023-10-20 15:18:53 -07:00 committed by GitHub
commit faf03334a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 11 deletions

View file

@ -294,6 +294,26 @@ class ContactAdmin(ListHeaderAdmin):
contact.admin_order_field = "first_name" # type: ignore contact.admin_order_field = "first_name" # type: ignore
# Read only that we'll leverage for CISA Analysts
analyst_readonly_fields = [
"user",
]
def get_readonly_fields(self, request, obj=None):
"""Set the read-only state on form elements.
We have 1 conditions that determine which fields are read-only:
admin user permissions.
"""
readonly_fields = list(self.readonly_fields)
if request.user.has_perm("registrar.full_access_permission"):
return readonly_fields
# Return restrictive Read-only fields for analysts and
# users who might not belong to groups
readonly_fields.extend([field for field in self.analyst_readonly_fields])
return readonly_fields # Read-only fields for analysts
class WebsiteAdmin(ListHeaderAdmin): class WebsiteAdmin(ListHeaderAdmin):
"""Custom website admin class.""" """Custom website admin class."""
@ -420,9 +440,6 @@ class DomainInformationAdmin(ListHeaderAdmin):
"creator", "creator",
"type_of_work", "type_of_work",
"more_organization_information", "more_organization_information",
"address_line1",
"address_line2",
"zipcode",
"domain", "domain",
"submitter", "submitter",
"no_other_contacts_rationale", "no_other_contacts_rationale",
@ -557,9 +574,6 @@ class DomainApplicationAdmin(ListHeaderAdmin):
analyst_readonly_fields = [ analyst_readonly_fields = [
"creator", "creator",
"about_your_organization", "about_your_organization",
"address_line1",
"address_line2",
"zipcode",
"requested_domain", "requested_domain",
"alternative_domains", "alternative_domains",
"purpose", "purpose",

View file

@ -0,0 +1,37 @@
# This migration creates the create_full_access_group and create_cisa_analyst_group groups
# It is dependent on 0035 (which populates ContentType and Permissions)
# 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", "0041_alter_domainapplication_organization_type_and_more"),
]
operations = [
migrations.RunPython(
create_groups,
reverse_code=migrations.RunPython.noop,
atomic=True,
),
]

View file

@ -24,7 +24,7 @@ class UserGroup(Group):
{ {
"app_label": "registrar", "app_label": "registrar",
"model": "contact", "model": "contact",
"permissions": ["view_contact"], "permissions": ["change_contact"],
}, },
{ {
"app_label": "registrar", "app_label": "registrar",
@ -56,6 +56,11 @@ 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"],
},
] ]
# Avoid error: You can't execute queries until the end # Avoid error: You can't execute queries until the end

View file

@ -11,6 +11,7 @@ from registrar.admin import (
ListHeaderAdmin, ListHeaderAdmin,
MyUserAdmin, MyUserAdmin,
AuditedAdmin, AuditedAdmin,
ContactAdmin,
) )
from registrar.models import ( from registrar.models import (
Domain, Domain,
@ -660,9 +661,6 @@ class TestDomainApplicationAdmin(MockEppLib):
expected_fields = [ expected_fields = [
"creator", "creator",
"about_your_organization", "about_your_organization",
"address_line1",
"address_line2",
"zipcode",
"requested_domain", "requested_domain",
"alternative_domains", "alternative_domains",
"purpose", "purpose",
@ -1353,3 +1351,38 @@ class DomainSessionVariableTest(TestCase):
{"_edit_domain": "true"}, {"_edit_domain": "true"},
follow=True, follow=True,
) )
class ContactAdminTest(TestCase):
def setUp(self):
self.site = AdminSite()
self.factory = RequestFactory()
self.client = Client(HTTP_HOST="localhost:8080")
self.admin = ContactAdmin(model=get_user_model(), admin_site=None)
self.superuser = create_superuser()
self.staffuser = create_user()
def test_readonly_when_restricted_staffuser(self):
request = self.factory.get("/")
request.user = self.staffuser
readonly_fields = self.admin.get_readonly_fields(request)
expected_fields = [
"user",
]
self.assertEqual(readonly_fields, expected_fields)
def test_readonly_when_restricted_superuser(self):
request = self.factory.get("/")
request.user = self.superuser
readonly_fields = self.admin.get_readonly_fields(request)
expected_fields = []
self.assertEqual(readonly_fields, expected_fields)
def tearDown(self):
User.objects.all().delete()

View file

@ -36,7 +36,7 @@ class TestGroups(TestCase):
# Define the expected permission codenames # Define the expected permission codenames
expected_permissions = [ expected_permissions = [
"view_logentry", "view_logentry",
"view_contact", "change_contact",
"view_domain", "view_domain",
"change_domainapplication", "change_domainapplication",
"change_domaininformation", "change_domaininformation",
@ -45,6 +45,7 @@ class TestGroups(TestCase):
"change_draftdomain", "change_draftdomain",
"analyst_access_permission", "analyst_access_permission",
"change_user", "change_user",
"change_website",
] ]
# Get the codenames of actual permissions associated with the group # Get the codenames of actual permissions associated with the group