From 64687ea7862f230b90263d98ef0d34602b0fb98b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 4 Jan 2024 10:27:51 -0700 Subject: [PATCH] Some view logic --- src/registrar/config/urls.py | 5 +++ src/registrar/templates/domain_users.html | 6 ++-- src/registrar/views/__init__.py | 1 + src/registrar/views/domain.py | 13 ++++++++ src/registrar/views/utility/mixins.py | 30 ++++++++++++++++++ .../views/utility/permission_views.py | 31 +++++++++++++++++++ 6 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 607bf5f61..416cb7c36 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -138,6 +138,11 @@ urlpatterns = [ views.DomainInvitationDeleteView.as_view(http_method_names=["post"]), name="invitation-delete", ), + path( + "domain//users//delete", + views.DomainDeleteUserView.as_view(http_method_names=["post"]), + name="domain-user-delete", + ), ] # we normally would guard these with `if settings.DEBUG` but tests run with diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index 147c0bb8e..22ef88533 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -58,8 +58,10 @@ aria-describedby="Your DNSSEC records will be deleted from the registry." data-force-action > -
- {% include 'includes/modal.html' with modal_heading="Warning: You are about to remove all DS records on your domain" modal_description="To fully disable DNSSEC: In addition to removing your DS records here you’ll also need to delete the DS records at your DNS host. To avoid causing your domain to appear offline you should wait to delete your DS records at your DNS host until the Time to Live (TTL) expires. This is often less than 24 hours, but confirm with your provider." modal_button=modal_button|safe %} + + {% with heading="Are you sure you want to remove <"|add:permission.user.email|add:">?" %} + {% include 'includes/modal.html' with modal_heading=heading modal_description="<"|add:permission.user.email|add:"> will no longer be able to manage the domain "|add:domain.name|add:"." modal_button=modal_button|safe %} + {% endwith %}
diff --git a/src/registrar/views/__init__.py b/src/registrar/views/__init__.py index c1400d7c0..8785c9076 100644 --- a/src/registrar/views/__init__.py +++ b/src/registrar/views/__init__.py @@ -12,6 +12,7 @@ from .domain import ( DomainUsersView, DomainAddUserView, DomainInvitationDeleteView, + DomainDeleteUserView, ) from .health import * from .index import * diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index aaad016a7..8884f9436 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -33,6 +33,7 @@ from registrar.utility.errors import ( SecurityEmailErrorCodes, ) from registrar.models.utility.contact_error import ContactError +from registrar.views.utility.permission_views import UserDomainRolePermissionView from ..forms import ( ContactForm, @@ -753,3 +754,15 @@ class DomainInvitationDeleteView(DomainInvitationPermissionDeleteView, SuccessMe def get_success_message(self, cleaned_data): return f"Successfully canceled invitation for {self.object.email}." + + +class DomainDeleteUserView(UserDomainRolePermissionView, SuccessMessageMixin): + """Inside of a domain's user management, a form for deleting users. + """ + object: UserDomainRole # workaround for type mismatch in DeleteView + + def get_success_url(self): + return reverse("domain-users", kwargs={"pk": self.object.domain.id}) + + def get_success_message(self, cleaned_data): + return f"Successfully removed manager for {self.object.email}." diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 0cf5970df..af8f66279 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -286,6 +286,36 @@ class DomainApplicationPermission(PermissionsLoginMixin): return True +class UserDomainRolePermission(PermissionsLoginMixin): + + """Permission mixin for UserDomainRole if user + has access, otherwise 403""" + + def has_permission(self): + """Check if this user has access to this domain application. + + The user is in self.request.user and the domain needs to be looked + up from the domain's primary key in self.kwargs["pk"] + """ + domain_pk = self.kwargs["pk"] + user_pk = self.kwargs["user_pk"] + print(f"here is the user: {self.request.user} and kwargs: {domain_pk}") + if not self.request.user.is_authenticated: + return False + print("User was authenticated!") + x = UserDomainRole.objects.filter( + id=user_pk + ).get() + print(x) + # TODO - exclude the creator from this + if not UserDomainRole.objects.filter( + domain__id=domain_pk, domain__permissions__user=self.request.user + ).exists(): + return False + + return True + + class DomainApplicationPermissionWithdraw(PermissionsLoginMixin): """Permission mixin that redirects to withdraw action on domain application diff --git a/src/registrar/views/utility/permission_views.py b/src/registrar/views/utility/permission_views.py index 1798ec79d..5c5ebc494 100644 --- a/src/registrar/views/utility/permission_views.py +++ b/src/registrar/views/utility/permission_views.py @@ -4,6 +4,7 @@ import abc # abstract base class from django.views.generic import DetailView, DeleteView, TemplateView from registrar.models import Domain, DomainApplication, DomainInvitation +from registrar.models.user_domain_role import UserDomainRole from .mixins import ( DomainPermission, @@ -11,6 +12,7 @@ from .mixins import ( DomainApplicationPermissionWithdraw, DomainInvitationPermission, ApplicationWizardPermission, + UserDomainRolePermission, ) import logging @@ -122,3 +124,32 @@ class DomainInvitationPermissionDeleteView(DomainInvitationPermission, DeleteVie model = DomainInvitation object: DomainInvitation # workaround for type mismatch in DeleteView + + +class UserDomainRolePermissionView(UserDomainRolePermission, DetailView, abc.ABC): + + """Abstract base view for UserDomainRole that enforces permissions. + + This abstract view cannot be instantiated. Actual views must specify + `template_name`. + """ + + # DetailView property for what model this is viewing + model = UserDomainRole + # variable name in template context for the model object + context_object_name = "userdomainrole" + + +class UserDomainRolePermissionDeleteView(UserDomainRolePermissionView, DeleteView, abc.ABC): + + """Abstract base view for domain application withdraw function + + This abstract view cannot be instantiated. Actual views must specify + `template_name`. + """ + + # DetailView property for what model this is viewing + model = UserDomainRole + # variable name in template context for the model object + context_object_name = "userdomainrole" +