mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 09:37:03 +02:00
unit tests, add cisa_analyst permission in the cisa_analysts_group for better grannular hasPerm testing in admin.py
This commit is contained in:
parent
cd14eb2584
commit
155baa0200
11 changed files with 142 additions and 64 deletions
|
@ -9,13 +9,8 @@ our `user_group` model and run in a migration.
|
|||
|
||||
## Editing group permissions through code
|
||||
|
||||
We can edit and deploy new group permissions by
|
||||
editing `user_group` then:
|
||||
We can edit and deploy new group permissions by:
|
||||
|
||||
- Duplicating migration `0036_create_groups`
|
||||
and running migrations (RECOMMENDED METHOD), or
|
||||
|
||||
- Fake the previous migration to run an existing create groups migration:
|
||||
- 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
|
||||
1. editing `user_group` then:
|
||||
2. Duplicating migration `0036_create_groups`
|
||||
and running migrations
|
|
@ -205,7 +205,7 @@ class MyUserAdmin(BaseUserAdmin):
|
|||
# which is equivalent to superuser. The other group we use to manage
|
||||
# perms is cisa_analysts_group. cisa_analysts_group will never contain
|
||||
# full_access_permission
|
||||
if request.user.has_perm('registrar.full_access_permission'):
|
||||
if request.user.has_perm("registrar.full_access_permission"):
|
||||
# Use the default list display for all access users
|
||||
return super().get_list_display(request)
|
||||
|
||||
|
@ -220,17 +220,23 @@ class MyUserAdmin(BaseUserAdmin):
|
|||
)
|
||||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
if request.user.has_perm('registrar.full_access_permission'):
|
||||
if request.user.has_perm("registrar.full_access_permission"):
|
||||
# Show all fields for all access users
|
||||
return super().get_fieldsets(request, obj)
|
||||
|
||||
elif request.user.has_perm("registrar.analyst_access_permission"):
|
||||
# show analyst_fieldsets for analysts
|
||||
return self.analyst_fieldsets
|
||||
else:
|
||||
# any admin user should belong to either full_access_group
|
||||
# or cisa_analyst_group
|
||||
return []
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
if request.user.has_perm('registrar.full_access_permission'):
|
||||
if request.user.has_perm("registrar.full_access_permission"):
|
||||
return () # No read-only fields for all access users
|
||||
return self.analyst_readonly_fields # Read-only fields for analysts
|
||||
# Return restrictive Read-only fields for analysts and
|
||||
# users who might not belong to groups
|
||||
return self.analyst_readonly_fields
|
||||
|
||||
|
||||
class HostIPInline(admin.StackedInline):
|
||||
|
@ -409,11 +415,12 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
|||
|
||||
readonly_fields = list(self.readonly_fields)
|
||||
|
||||
if request.user.has_perm('registrar.full_access_permission'):
|
||||
if request.user.has_perm("registrar.full_access_permission"):
|
||||
return readonly_fields
|
||||
else:
|
||||
# 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
|
||||
return readonly_fields # Read-only fields for analysts
|
||||
|
||||
|
||||
class DomainApplicationAdminForm(forms.ModelForm):
|
||||
|
@ -627,9 +634,10 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
["current_websites", "other_contacts", "alternative_domains"]
|
||||
)
|
||||
|
||||
if request.user.has_perm('registrar.full_access_permission'):
|
||||
if request.user.has_perm("registrar.full_access_permission"):
|
||||
return readonly_fields
|
||||
else:
|
||||
# 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
|
||||
|
||||
|
@ -702,7 +710,7 @@ class DomainAdmin(ListHeaderAdmin):
|
|||
search_fields = ["name"]
|
||||
search_help_text = "Search by domain name."
|
||||
change_form_template = "django/admin/domain_change_form.html"
|
||||
readonly_fields = ["state"]
|
||||
# readonly_fields = ["state"]
|
||||
|
||||
def response_change(self, request, obj):
|
||||
# Create dictionary of action functions
|
||||
|
|
|
@ -10,9 +10,6 @@ from registrar.models import (
|
|||
Website,
|
||||
)
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
fake = Faker()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -153,4 +153,3 @@ class UserFixture:
|
|||
def load(cls):
|
||||
cls.load_users(cls, cls.ADMINS, "full_access_group")
|
||||
cls.load_users(cls, cls.STAFF, "cisa_analysts_group")
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ class Migration(migrations.Migration):
|
|||
name="user",
|
||||
options={
|
||||
"permissions": [
|
||||
("analyst_access_permission", "Analyst Access Permission"),
|
||||
("full_access_permission", "Full Access Permission"),
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# From mithuntnt's answer on:
|
||||
# https://stackoverflow.com/questions/26464838/getting-model-contenttype-in-migration-django-1-7
|
||||
# The problem is that ContentType and Permission objects are not already created
|
||||
# while we're still running migrations, so we'll go ahead and speen up that process
|
||||
# while we're still running migrations, so we'll go ahead and speed up that process
|
||||
# a bit before we attempt to create groups which require Permissions and ContentType.
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_all_contenttypes(**kwargs):
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.management import create_contenttypes
|
||||
|
@ -14,6 +15,7 @@ def create_all_contenttypes(**kwargs):
|
|||
for app_config in apps.get_app_configs():
|
||||
create_contenttypes(app_config, **kwargs)
|
||||
|
||||
|
||||
def create_all_permissions(**kwargs):
|
||||
from django.contrib.auth.management import create_permissions
|
||||
from django.apps import apps
|
||||
|
@ -21,20 +23,21 @@ def create_all_permissions(**kwargs):
|
|||
for app_config in apps.get_app_configs():
|
||||
create_permissions(app_config, **kwargs)
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
create_all_contenttypes()
|
||||
create_all_permissions()
|
||||
|
||||
|
||||
def backward(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
("registrar", "0034_alter_user_options"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, backward)
|
||||
]
|
||||
operations = [migrations.RunPython(forward, backward)]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# 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:
|
||||
# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions
|
||||
|
@ -10,13 +11,20 @@
|
|||
from django.db import migrations
|
||||
from registrar.models import UserGroup
|
||||
|
||||
def create_groups():
|
||||
UserGroup.create_cisa_analyst_group()
|
||||
UserGroup.create_full_access_group()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0035_contenttypes_permissions"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(UserGroup.create_cisa_analyst_group, reverse_code=migrations.RunPython.noop, atomic=True),
|
||||
migrations.RunPython(UserGroup.create_full_access_group, reverse_code=migrations.RunPython.noop, atomic=True),
|
||||
migrations.RunPython(
|
||||
create_groups, # noqa
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
atomic=True,
|
||||
),
|
||||
]
|
||||
|
||||
|
|
|
@ -675,7 +675,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
max_length=21,
|
||||
choices=State.choices,
|
||||
default=State.UNKNOWN,
|
||||
protected=True, # cannot change state directly, particularly in Django admin
|
||||
protected=False, # cannot change state directly, particularly in Django admin
|
||||
help_text="Very basic info about the lifecycle of this domain object",
|
||||
)
|
||||
|
||||
|
|
|
@ -84,5 +84,6 @@ class User(AbstractUser):
|
|||
|
||||
class Meta:
|
||||
permissions = [
|
||||
("analyst_access_permission", "Analyst Access Permission"),
|
||||
("full_access_permission", "Full Access Permission"),
|
||||
]
|
||||
|
|
|
@ -3,13 +3,14 @@ import logging
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class UserGroup(Group):
|
||||
|
||||
class UserGroup(Group):
|
||||
class Meta:
|
||||
verbose_name = "User group"
|
||||
verbose_name_plural = "User groups"
|
||||
|
||||
def create_cisa_analyst_group(apps, schema_editor):
|
||||
"""This method gets run from a data migration."""
|
||||
|
||||
# Hard to pass self to these methods as the calls from migrations
|
||||
# are only expecting apps and schema_editor, so we'll just define
|
||||
|
@ -20,7 +21,11 @@ class UserGroup(Group):
|
|||
"model": "logentry",
|
||||
"permissions": ["view_logentry"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "contact", "permissions": ["view_contact"]},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "contact",
|
||||
"permissions": ["view_contact"],
|
||||
},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "domaininformation",
|
||||
|
@ -31,13 +36,21 @@ class UserGroup(Group):
|
|||
"model": "domainapplication",
|
||||
"permissions": ["change_domainapplication"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "domain", "permissions": ["view_domain"]},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "domain",
|
||||
"permissions": ["view_domain"],
|
||||
},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "draftdomain",
|
||||
"permissions": ["change_draftdomain"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "user", "permissions": ["change_user"]},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "user",
|
||||
"permissions": ["analyst_access_permission", "change_user"],
|
||||
},
|
||||
]
|
||||
|
||||
# Avoid error: You can't execute queries until the end
|
||||
|
@ -77,9 +90,7 @@ class UserGroup(Group):
|
|||
cisa_analysts_group.permissions.add(*permissions)
|
||||
|
||||
# Convert the permissions QuerySet to a list of codenames
|
||||
permission_list = list(
|
||||
permissions.values_list("codename", flat=True)
|
||||
)
|
||||
permission_list = list(permissions.values_list("codename", flat=True))
|
||||
|
||||
logger.debug(
|
||||
app_label
|
||||
|
@ -92,11 +103,15 @@ class UserGroup(Group):
|
|||
)
|
||||
|
||||
cisa_analysts_group.save()
|
||||
logger.debug("CISA Analyt permissions added to group " + cisa_analysts_group.name)
|
||||
logger.debug(
|
||||
"CISA Analyt permissions added to group " + cisa_analysts_group.name
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating analyst permissions group: {e}")
|
||||
|
||||
def create_full_access_group(apps, schema_editor):
|
||||
"""This method gets run from a data migration."""
|
||||
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
UserGroup = apps.get_model("registrar", "UserGroup")
|
||||
|
||||
|
|
51
src/registrar/tests/test_migrations.py
Normal file
51
src/registrar/tests/test_migrations.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from registrar.models import (
|
||||
UserGroup,
|
||||
)
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestGroups(TestCase):
|
||||
def test_groups_created(self):
|
||||
"""The test enviroment contains data that was created in migration,
|
||||
so we are able to test groups and permissions.
|
||||
|
||||
- Test cisa_analysts_group and full_access_group created
|
||||
- Test permissions on full_access_group
|
||||
"""
|
||||
|
||||
# Get the UserGroup objects
|
||||
cisa_analysts_group = UserGroup.objects.get(name="cisa_analysts_group")
|
||||
full_access_group = UserGroup.objects.get(name="full_access_group")
|
||||
|
||||
# Assert that the cisa_analysts_group exists in the database
|
||||
self.assertQuerysetEqual(
|
||||
UserGroup.objects.filter(name="cisa_analysts_group"), [cisa_analysts_group]
|
||||
)
|
||||
|
||||
# Assert that the full_access_group exists in the database
|
||||
self.assertQuerysetEqual(
|
||||
UserGroup.objects.filter(name="full_access_group"), [full_access_group]
|
||||
)
|
||||
|
||||
# Test permissions for cisa_analysts)group
|
||||
# Define the expected permission codenames
|
||||
expected_permissions = [
|
||||
"view_logentry",
|
||||
"view_contact",
|
||||
"view_domain",
|
||||
"change_domainapplication",
|
||||
"change_domaininformation",
|
||||
"change_draftdomain",
|
||||
"analyst_access_permission",
|
||||
"change_user",
|
||||
]
|
||||
|
||||
# Get the codenames of actual permissions associated with the group
|
||||
actual_permissions = [p.codename for p in cisa_analysts_group.permissions.all()]
|
||||
|
||||
# Assert that the actual permissions match the expected permissions
|
||||
self.assertListEqual(actual_permissions, expected_permissions)
|
Loading…
Add table
Add a link
Reference in a new issue