merge of main

This commit is contained in:
David Kennedy 2023-09-07 07:30:16 -04:00
commit e4922b8543
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
19 changed files with 426 additions and 40 deletions

View file

@ -54,6 +54,7 @@ services:
# command: "python" # command: "python"
command: > command: >
bash -c " python manage.py migrate && bash -c " python manage.py migrate &&
python manage.py load &&
python manage.py runserver 0.0.0.0:8080" python manage.py runserver 0.0.0.0:8080"
db: db:

View file

@ -177,10 +177,13 @@ class DomainAdmin(ListHeaderAdmin):
def response_change(self, request, obj): def response_change(self, request, obj):
PLACE_HOLD = "_place_client_hold" PLACE_HOLD = "_place_client_hold"
REMOVE_HOLD = "_remove_client_hold" REMOVE_HOLD = "_remove_client_hold"
EDIT_DOMAIN = "_edit_domain"
if PLACE_HOLD in request.POST: if PLACE_HOLD in request.POST:
return self.do_place_client_hold(request, obj) return self.do_place_client_hold(request, obj)
elif REMOVE_HOLD in request.POST: elif REMOVE_HOLD in request.POST:
return self.do_remove_client_hold(request, obj) return self.do_remove_client_hold(request, obj)
elif EDIT_DOMAIN in request.POST:
return self.do_edit_domain(request, obj)
return super().response_change(request, obj) return super().response_change(request, obj)
def do_place_client_hold(self, request, obj): def do_place_client_hold(self, request, obj):
@ -217,6 +220,21 @@ class DomainAdmin(ListHeaderAdmin):
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
def do_edit_domain(self, request, obj):
# We want to know, globally, when an edit action occurs
request.session["analyst_action"] = "edit"
# Restricts this action to this domain (pk) only
request.session["analyst_action_location"] = obj.id
return HttpResponseRedirect(reverse("domain", args=(obj.id,)))
def change_view(self, request, object_id):
# If the analyst was recently editing a domain page,
# delete any associated session values
if "analyst_action" in request.session:
del request.session["analyst_action"]
del request.session["analyst_action_location"]
return super().change_view(request, object_id)
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
# Fixes a bug wherein users which are only is_staff # Fixes a bug wherein users which are only is_staff
# can access 'change' when GET, # can access 'change' when GET,

View file

@ -0,0 +1,50 @@
/**
* @file get-gov-admin.js includes custom code for the .gov registrar admin portal.
*
* Constants and helper functions are at the top.
* Event handlers are in the middle.
* Initialization (run-on-load) stuff goes at the bottom.
*/
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Helper functions.
/** Either sets attribute target="_blank" to a given element, or removes it */
function openInNewTab(el, removeAttribute = false){
if(removeAttribute){
el.setAttribute("target", "_blank");
}else{
el.removeAttribute("target", "_blank");
}
};
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Event handlers.
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Initialization code.
/** An IIFE for pages in DjangoAdmin which may need custom JS implementation.
* Currently only appends target="_blank" to the domain_form object,
* but this can be expanded.
*/
(function (){
/*
On mouseover, appends target="_blank" on domain_form under the Domain page.
The reason for this is that the template has a form that contains multiple buttons.
The structure of that template complicates seperating those buttons
out of the form (while maintaining the same position on the page).
However, if we want to open one of those submit actions to a new tab -
such as the manage domain button - we need to dynamically append target.
As there is no built-in django method which handles this, we do it here.
*/
function prepareDjangoAdmin() {
let domainFormElement = document.getElementById("domain_form");
let domainSubmitButton = document.getElementById("manageDomainSubmitButton");
if(domainSubmitButton && domainFormElement){
domainSubmitButton.addEventListener("mouseover", () => openInNewTab(domainFormElement, true));
domainSubmitButton.addEventListener("mouseout", () => openInNewTab(domainFormElement, false));
}
}
prepareDjangoAdmin();
})();

View file

@ -432,3 +432,21 @@ abbr[title] {
height: units('mobile'); height: units('mobile');
} }
} }
// Fixes some font size disparities with the Figma
// for usa-alert alert elements
.usa-alert {
.usa-alert__heading.larger-font-sizing {
font-size: units(3);
}
}
// The icon was off center for some reason
// Fixes that issue
@media (min-width: 64em){
.usa-alert--warning{
.usa-alert__body::before {
left: 1rem !important;
}
}
}

View file

@ -63,7 +63,7 @@ class UserFixture:
"last_name": "Adkinson", "last_name": "Adkinson",
}, },
{ {
"username": "bb21f687-c773-4df3-9243-111cfd4c0be4", "username": "2bf518c2-485a-4c42-ab1a-f5a8b0a08484",
"first_name": "Paul", "first_name": "Paul",
"last_name": "Kuykendall", "last_name": "Kuykendall",
}, },
@ -117,6 +117,12 @@ class UserFixture:
"first_name": "David-Analyst", "first_name": "David-Analyst",
"last_name": "Kennedy-Analyst", "last_name": "Kennedy-Analyst",
}, },
{
"username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47",
"first_name": "Gaby-Analyst",
"last_name": "DiSarli-Analyst",
"email": "gaby@truss.works",
},
] ]
STAFF_PERMISSIONS = [ STAFF_PERMISSIONS = [

View file

@ -2,6 +2,7 @@ import logging
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from auditlog.context import disable_auditlog # type: ignore from auditlog.context import disable_auditlog # type: ignore
from django.conf import settings
from registrar.fixtures import UserFixture, DomainApplicationFixture, DomainFixture from registrar.fixtures import UserFixture, DomainApplicationFixture, DomainFixture
@ -12,8 +13,11 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
# django-auditlog has some bugs with fixtures # django-auditlog has some bugs with fixtures
# https://github.com/jazzband/django-auditlog/issues/17 # https://github.com/jazzband/django-auditlog/issues/17
with disable_auditlog(): if settings.DEBUG:
UserFixture.load() with disable_auditlog():
DomainApplicationFixture.load() UserFixture.load()
DomainFixture.load() DomainApplicationFixture.load()
logger.info("All fixtures loaded.") DomainFixture.load()
logger.info("All fixtures loaded.")
else:
logger.warn("Refusing to load fixture data in a non DEBUG env")

View file

@ -348,6 +348,9 @@ class Domain(TimeStampedModel, DomainHelper):
help_text="Very basic info about the lifecycle of this domain object", help_text="Very basic info about the lifecycle of this domain object",
) )
def isActive(self):
return self.state == Domain.State.CREATED
# ForeignKey on UserDomainRole creates a "permissions" member for # ForeignKey on UserDomainRole creates a "permissions" member for
# all of the user-roles that are in place for this domain # all of the user-roles that are in place for this domain

View file

@ -1,8 +1,6 @@
import logging import logging
from django.conf import settings from django.db.models.signals import post_save
from django.core.management import call_command
from django.db.models.signals import post_save, post_migrate
from django.dispatch import receiver from django.dispatch import receiver
from .models import User, Contact from .models import User, Contact
@ -55,13 +53,3 @@ def handle_profile(sender, instance, **kwargs):
"There are multiple Contacts with the same email address." "There are multiple Contacts with the same email address."
f" Picking #{contacts[0].id} for User #{instance.id}." f" Picking #{contacts[0].id} for User #{instance.id}."
) )
@receiver(post_migrate)
def handle_loaddata(**kwargs):
"""Attempt to load test fixtures when in DEBUG mode."""
if settings.DEBUG:
try:
call_command("load")
except Exception as e:
logger.warning(e)

View file

@ -0,0 +1,12 @@
{% extends "admin/change_form.html" %}
{% comment %} Replace the Django ul markup with a div. We'll edit the child markup accordingly in change_form_object_tools {% endcomment %}
{% block object-tools %}
{% if change and not is_popup %}
<div class="object-tools">
{% block object-tools-items %}
{{ block.super }}
{% endblock %}
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,20 @@
{% load i18n admin_urls %}
{% comment %} Replace li with p for more semantic HTML if we have a single child {% endcomment %}
{% block object-tools-items %}
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
{% if has_absolute_url %}
<ul>
<li>
<a href="{% add_preserved_filters history_url %}" class="historylink">{% translate "History" %}</a>
</li>
<li>
<a href="{{ absolute_url }}" class="viewsitelink">{% translate "View on site" %}</a>
</li>
</ul>
{% else %}
<p class="margin-0 padding-0">
<a href="{% add_preserved_filters history_url %}" class="historylink">{% translate "History" %}</a>
</p>
{% endif %}
{% endblock %}

View file

@ -24,3 +24,12 @@
{% endif %} {% endif %}
</h2> </h2>
{% endblock %} {% endblock %}
{% comment %} Replace the Django ul markup with a div. We'll replace the li with a p in change_list_object_tools {% endcomment %}
{% block object-tools %}
<div class="object-tools">
{% block object-tools-items %}
{{ block.super }}
{% endblock %}
</div>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% load i18n admin_urls %}
{% comment %} Replace li with p for more semantic HTML {% endcomment %}
{% block object-tools-items %}
{% if has_add_permission %}
<p class="margin-0 padding-0">
{% url cl.opts|admin_urlname:'add' as add_url %}
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
{% blocktranslate with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktranslate %}
</a>
</p>
{% endif %}
{% endblock %}

View file

@ -1,4 +1,10 @@
{% extends 'admin/change_form.html' %} {% extends 'admin/change_form.html' %}
{% load i18n static %}
{% block extrahead %}
{{ block.super }}
<script type="application/javascript" src="{% static 'js/get-gov-admin.js' %}" defer></script>
{% endblock %}
{% block field_sets %} {% block field_sets %}
<div class="submit-row"> <div class="submit-row">
@ -7,6 +13,7 @@
{% elif original.state == original.State.ONHOLD %} {% elif original.state == original.State.ONHOLD %}
<input type="submit" value="Remove hold" name="_remove_client_hold"> <input type="submit" value="Remove hold" name="_remove_client_hold">
{% endif %} {% endif %}
<input id="manageDomainSubmitButton" type="submit" value="Manage Domain" name="_edit_domain">
</div> </div>
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}

View file

@ -5,12 +5,14 @@
{% block content %} {% block content %}
<div class="grid-container"> <div class="grid-container">
<div class="grid-row"> <div class="grid-row">
<p class="font-body-md margin-top-0 margin-bottom-2 {% if not is_analyst_or_superuser or not analyst_action or analyst_action_location != domain.pk %}
<p class="font-body-md margin-top-0 margin-bottom-2
text-primary-darker text-semibold" text-primary-darker text-semibold"
> >
<span class="usa-sr-only"> Domain name:</span> {{ domain.name }} <span class="usa-sr-only"> Domain name:</span> {{ domain.name }}
</p> </p>
{% endif %}
</div> </div>
<div class="grid-row grid-gap"> <div class="grid-row grid-gap">
<div class="tablet:grid-col-3"> <div class="tablet:grid-col-3">
@ -20,15 +22,26 @@
<div class="tablet:grid-col-9"> <div class="tablet:grid-col-9">
<main id="main-content" class="grid-container"> <main id="main-content" class="grid-container">
<a href="{% url 'home' %}" class="breadcrumb__back"> {% if is_analyst_or_superuser and analyst_action == 'edit' and analyst_action_location == domain.pk %}
<div class="usa-alert usa-alert--warning margin-bottom-2">
<div class="usa-alert__body">
<h4 class="usa-alert__heading larger-font-sizing">Attention!</h4>
<p class="usa-alert__text ">
You are making changes to a registrants domain. When finished making changes, close this tab and inform the registrant of your updates.
</p>
</div>
</div>
{% else %}
<a href="{% url 'home' %}" class="breadcrumb__back">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{% static 'img/sprite.svg' %}#arrow_back"></use> <use xlink:href="{% static 'img/sprite.svg' %}#arrow_back"></use>
</svg> </svg>
<p class="margin-left-05 margin-top-0 margin-bottom-0 line-height-sans-1"> <p class="margin-left-05 margin-top-0 margin-bottom-0 line-height-sans-1">
Back to manage your domains Back to manage your domains
</p> </p>
</a> </a>
{% endif %}
{# messages block is under the back breadcrumb link #} {# messages block is under the back breadcrumb link #}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}

View file

@ -526,3 +526,11 @@ def multiple_unalphabetical_domain_objects(
application = mock.create_full_dummy_domain_object(domain_type, object_name) application = mock.create_full_dummy_domain_object(domain_type, object_name)
applications.append(application) applications.append(application)
return applications return applications
def generic_domain_object(domain_type, object_name):
"""Returns a generic domain object of
domain_type 'application', 'information', or 'invitation'"""
mock = AuditedAdminMockData()
application = mock.create_full_dummy_domain_object(domain_type, object_name)
return application

View file

@ -1,5 +1,6 @@
from django.test import TestCase, RequestFactory, Client from django.test import TestCase, RequestFactory, Client
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from django.urls import reverse
from registrar.admin import ( from registrar.admin import (
DomainAdmin, DomainAdmin,
@ -14,16 +15,18 @@ from registrar.models import (
DomainInformation, DomainInformation,
User, User,
DomainInvitation, DomainInvitation,
Domain,
) )
from .common import ( from .common import (
completed_application, completed_application,
generic_domain_object,
mock_user, mock_user,
create_superuser, create_superuser,
create_user, create_user,
create_ready_domain, create_ready_domain,
multiple_unalphabetical_domain_objects, multiple_unalphabetical_domain_objects,
) )
from django.contrib.sessions.backends.db import SessionStore
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from unittest.mock import patch from unittest.mock import patch
@ -826,3 +829,129 @@ class AuditedAdminTest(TestCase):
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
DomainApplication.objects.all().delete() DomainApplication.objects.all().delete()
DomainInvitation.objects.all().delete() DomainInvitation.objects.all().delete()
class DomainSessionVariableTest(TestCase):
"""Test cases for session variables in Django Admin"""
def setUp(self):
self.factory = RequestFactory()
self.admin = DomainAdmin(Domain, None)
self.client = Client(HTTP_HOST="localhost:8080")
def test_session_vars_set_correctly(self):
"""Checks if session variables are being set correctly"""
p = "adminpass"
self.client.login(username="superuser", password=p)
dummy_domain_information = generic_domain_object("information", "session")
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
self.populate_session_values(request, dummy_domain_information.domain)
self.assertEqual(request.session["analyst_action"], "edit")
self.assertEqual(
request.session["analyst_action_location"],
dummy_domain_information.domain.pk,
)
def test_session_vars_set_correctly_hardcoded_domain(self):
"""Checks if session variables are being set correctly"""
p = "adminpass"
self.client.login(username="superuser", password=p)
dummy_domain_information: Domain = generic_domain_object(
"information", "session"
)
dummy_domain_information.domain.pk = 1
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
self.populate_session_values(request, dummy_domain_information.domain)
self.assertEqual(request.session["analyst_action"], "edit")
self.assertEqual(request.session["analyst_action_location"], 1)
def test_session_variables_reset_correctly(self):
"""Checks if incorrect session variables get overridden"""
p = "adminpass"
self.client.login(username="superuser", password=p)
dummy_domain_information = generic_domain_object("information", "session")
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
self.populate_session_values(
request, dummy_domain_information.domain, preload_bad_data=True
)
self.assertEqual(request.session["analyst_action"], "edit")
self.assertEqual(
request.session["analyst_action_location"],
dummy_domain_information.domain.pk,
)
def test_session_variables_retain_information(self):
"""Checks to see if session variables retain old information"""
p = "adminpass"
self.client.login(username="superuser", password=p)
dummy_domain_information_list = multiple_unalphabetical_domain_objects(
"information"
)
for item in dummy_domain_information_list:
request = self.get_factory_post_edit_domain(item.domain.pk)
self.populate_session_values(request, item.domain)
self.assertEqual(request.session["analyst_action"], "edit")
self.assertEqual(request.session["analyst_action_location"], item.domain.pk)
def test_session_variables_concurrent_requests(self):
"""Simulates two requests at once"""
p = "adminpass"
self.client.login(username="superuser", password=p)
info_first = generic_domain_object("information", "session")
info_second = generic_domain_object("information", "session2")
request_first = self.get_factory_post_edit_domain(info_first.domain.pk)
request_second = self.get_factory_post_edit_domain(info_second.domain.pk)
self.populate_session_values(request_first, info_first.domain, True)
self.populate_session_values(request_second, info_second.domain, True)
# Check if anything got nulled out
self.assertNotEqual(request_first.session["analyst_action"], None)
self.assertNotEqual(request_second.session["analyst_action"], None)
self.assertNotEqual(request_first.session["analyst_action_location"], None)
self.assertNotEqual(request_second.session["analyst_action_location"], None)
# Check if they are both the same action 'type'
self.assertEqual(request_first.session["analyst_action"], "edit")
self.assertEqual(request_second.session["analyst_action"], "edit")
# Check their locations, and ensure they aren't the same across both
self.assertNotEqual(
request_first.session["analyst_action_location"],
request_second.session["analyst_action_location"],
)
def populate_session_values(self, request, domain_object, preload_bad_data=False):
"""Boilerplate for creating mock sessions"""
request.user = self.client
request.session = SessionStore()
request.session.create()
if preload_bad_data:
request.session["analyst_action"] = "invalid"
request.session["analyst_action_location"] = "bad location"
self.admin.response_change(request, domain_object)
def get_factory_post_edit_domain(self, primary_key):
"""Posts to registrar domain change
with the edit domain button 'clicked',
then returns the factory object"""
return self.factory.post(
reverse("admin:registrar_domain_change", args=(primary_key,)),
{"_edit_domain": "true"},
follow=True,
)

View file

@ -79,6 +79,7 @@ class DomainOrgNameAddressView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, "The organization name and mailing address has been updated." self.request, "The organization name and mailing address has been updated."
) )
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -121,6 +122,7 @@ class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, "The authorizing official for this domain has been updated." self.request, "The authorizing official for this domain has been updated."
) )
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -187,6 +189,7 @@ class DomainNameserversView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, "The name servers for this domain have been updated." self.request, "The name servers for this domain have been updated."
) )
# superclass has the redirect # superclass has the redirect
return super().form_valid(formset) return super().form_valid(formset)
@ -227,6 +230,7 @@ class DomainYourContactInformationView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, "Your contact information for this domain has been updated." self.request, "Your contact information for this domain has been updated."
) )
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -272,6 +276,7 @@ class DomainSecurityEmailView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, "The security email for this domain have been updated." self.request, "The security email for this domain have been updated."
) )
# superclass has the redirect # superclass has the redirect
return redirect(self.get_success_url()) return redirect(self.get_success_url())
@ -347,6 +352,7 @@ class DomainAddUserView(DomainPermissionView, FormMixin):
messages.success( messages.success(
self.request, f"Invited {email_address} to this domain." self.request, f"Invited {email_address} to this domain."
) )
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def form_valid(self, form): def form_valid(self, form):
@ -368,6 +374,7 @@ class DomainAddUserView(DomainPermissionView, FormMixin):
pass pass
messages.success(self.request, f"Added user {requested_email}.") messages.success(self.request, f"Added user {requested_email}.")
return redirect(self.get_success_url()) return redirect(self.get_success_url())

View file

@ -2,7 +2,16 @@
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from registrar.models import UserDomainRole, DomainApplication, DomainInvitation from registrar.models import (
DomainApplication,
DomainInvitation,
DomainInformation,
UserDomainRole,
)
import logging
logger = logging.getLogger(__name__)
class PermissionsLoginMixin(PermissionRequiredMixin): class PermissionsLoginMixin(PermissionRequiredMixin):
@ -25,27 +34,80 @@ class DomainPermission(PermissionsLoginMixin):
up from the domain's primary key in self.kwargs["pk"] up from the domain's primary key in self.kwargs["pk"]
""" """
# ticket 806
# if self.request.user is staff or admin and
# domain.application__status = 'approved' or 'rejected' or 'action needed'
# return True
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
return False return False
# user needs to have a role on the domain
if not UserDomainRole.objects.filter(
user=self.request.user, domain__id=self.kwargs["pk"]
).exists():
return False
# The user has an ineligible flag
if self.request.user.is_restricted(): if self.request.user.is_restricted():
return False return False
pk = self.kwargs["pk"]
# If pk is none then something went very wrong...
if pk is None:
raise ValueError("Primary key is None")
if self.can_access_other_user_domains(pk):
return True
# user needs to have a role on the domain
if not UserDomainRole.objects.filter(
user=self.request.user, domain__id=pk
).exists():
return False
# if we need to check more about the nature of role, do it here. # if we need to check more about the nature of role, do it here.
return True return True
def can_access_other_user_domains(self, pk):
"""Checks to see if an authorized user (staff or superuser)
can access a domain that they did not create or was invited to.
"""
# Check if the user is permissioned...
user_is_analyst_or_superuser = (
self.request.user.is_staff or self.request.user.is_superuser
)
if not user_is_analyst_or_superuser:
return False
# Check if the user is attempting a valid edit action.
# In other words, if the analyst/admin did not click
# the 'Manage Domain' button in /admin,
# then they cannot access this page.
session = self.request.session
can_do_action = (
"analyst_action" in session
and "analyst_action_location" in session
and session["analyst_action_location"] == pk
)
if not can_do_action:
return False
# Analysts may manage domains, when they are in these statuses:
valid_domain_statuses = [
DomainApplication.APPROVED,
DomainApplication.IN_REVIEW,
DomainApplication.REJECTED,
DomainApplication.ACTION_NEEDED,
# Edge case - some domains do not have
# a status or DomainInformation... aka a status of 'None'.
# It is necessary to access those to correct errors.
None,
]
requested_domain = None
if DomainInformation.objects.filter(id=pk).exists():
requested_domain = DomainInformation.objects.get(id=pk)
if requested_domain.domain_application.status not in valid_domain_statuses:
return False
# Valid session keys exist,
# the user is permissioned,
# and it is in a valid status
return True
class DomainApplicationPermission(PermissionsLoginMixin): class DomainApplicationPermission(PermissionsLoginMixin):

View file

@ -3,7 +3,6 @@
import abc # abstract base class import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView from django.views.generic import DetailView, DeleteView, TemplateView
from registrar.models import Domain, DomainApplication, DomainInvitation from registrar.models import Domain, DomainApplication, DomainInvitation
from .mixins import ( from .mixins import (
@ -12,6 +11,9 @@ from .mixins import (
DomainInvitationPermission, DomainInvitationPermission,
ApplicationWizardPermission, ApplicationWizardPermission,
) )
import logging
logger = logging.getLogger(__name__)
class DomainPermissionView(DomainPermission, DetailView, abc.ABC): class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
@ -27,6 +29,22 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
# variable name in template context for the model object # variable name in template context for the model object
context_object_name = "domain" context_object_name = "domain"
# Adds context information for user permissions
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
context["is_analyst_or_superuser"] = user.is_staff or user.is_superuser
# Stored in a variable for the linter
action = "analyst_action"
action_location = "analyst_action_location"
# Flag to see if an analyst is attempting to make edits
if action in self.request.session:
context[action] = self.request.session[action]
if action_location in self.request.session:
context[action_location] = self.request.session[action_location]
return context
# Abstract property enforces NotImplementedError on an attribute. # Abstract property enforces NotImplementedError on an attribute.
@property @property
@abc.abstractmethod @abc.abstractmethod