This commit is contained in:
David Kennedy 2024-08-13 12:14:32 -04:00
commit 8dcaa66e14
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
17 changed files with 73 additions and 21 deletions

View file

@ -25,7 +25,7 @@
} }
} }
h3.register-form-review-header { .register-form-review-header {
color: color('primary-dark'); color: color('primary-dark');
margin-top: units(2); margin-top: units(2);
margin-bottom: 0; margin-bottom: 0;

View file

@ -3,6 +3,7 @@ import logging
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.forms import ValidationError
from registrar.models.domain_information import DomainInformation from registrar.models.domain_information import DomainInformation
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
@ -229,6 +230,16 @@ class User(AbstractUser):
def has_contact_info(self): def has_contact_info(self):
return bool(self.title or self.email or self.phone) return bool(self.title or self.email or self.phone)
def clean(self):
"""Extends clean method to perform additional validation, which can raise errors in django admin."""
super().clean()
if self.portfolio is None and self._get_portfolio_permissions():
raise ValidationError("When portfolio roles or additional permissions are assigned, portfolio is required.")
if self.portfolio is not None and not self._get_portfolio_permissions():
raise ValidationError("When portfolio is assigned, portfolio roles or additional permissions are required.")
def _get_portfolio_permissions(self): def _get_portfolio_permissions(self):
""" """
Retrieve the permissions for the user's portfolio roles. Retrieve the permissions for the user's portfolio roles.

View file

@ -54,7 +54,7 @@
{% block domain_content %} {% block domain_content %}
<h1 class="break-word">{{ domain.name }}</h1> <h1 class="break-word">Domain Overview</h1>
{% endblock %} {# domain_content #} {% endblock %} {# domain_content #}
{% endif %} {% endif %}

View file

@ -5,7 +5,7 @@
{% block domain_content %} {% block domain_content %}
{{ block.super }} {{ block.super }}
<div class="margin-top-4 tablet:grid-col-10"> <div class="margin-top-4 tablet:grid-col-10">
<h2 class="text-bold text-primary-dark">{{ domain.name }}</h2>
<div <div
class="usa-summary-box dotgov-status-box padding-bottom-0 margin-top-3 padding-left-2{% if not domain.is_expired %}{% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %} dotgov-status-box--action-need{% endif %}{% endif %}" class="usa-summary-box dotgov-status-box padding-bottom-0 margin-top-3 padding-left-2{% if not domain.is_expired %}{% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %} dotgov-status-box--action-need{% endif %}{% endif %}"
role="region" role="region"
@ -57,13 +57,20 @@
{% include "includes/summary_item.html" with title='DNS name servers' domains='true' value=domain.nameservers list='true' edit_link=url editable=is_editable %} {% include "includes/summary_item.html" with title='DNS name servers' domains='true' value=domain.nameservers list='true' edit_link=url editable=is_editable %}
{% else %} {% else %}
{% if is_editable %} {% if is_editable %}
<h2 class="margin-top-3"> DNS name servers </h2> <h3 class="margin-top-3"> DNS name servers </h3>
<p> No DNS name servers have been added yet. Before your domain can be used well need information about your domain name servers.</p> <p> No DNS name servers have been added yet. Before your domain can be used well need information about your domain name servers.</p>
<a class="usa-button margin-bottom-1" href="{{url}}"> Add DNS name servers </a> <a class="usa-button margin-bottom-1" href="{{url}}"> Add DNS name servers </a>
{% else %} {% else %}
{% include "includes/summary_item.html" with title='DNS name servers' domains='true' value='' edit_link=url editable=is_editable %} {% include "includes/summary_item.html" with title='DNS name servers' domains='true' value='' edit_link=url editable=is_editable %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% url 'domain-dns-dnssec' pk=domain.id as url %}
{% if domain.dnssecdata is not None %}
{% include "includes/summary_item.html" with title='DNSSEC' value='Enabled' edit_link=url editable=is_editable %}
{% else %}
{% include "includes/summary_item.html" with title='DNSSEC' value='Not Enabled' edit_link=url editable=is_editable %}
{% endif %}
{% if portfolio and has_domains_portfolio_permission and request.user.has_view_suborganization %} {% if portfolio and has_domains_portfolio_permission and request.user.has_view_suborganization %}
{% url 'domain-suborganization' pk=domain.id as url %} {% url 'domain-suborganization' pk=domain.id as url %}

View file

@ -47,5 +47,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -30,5 +30,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -31,5 +31,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -32,5 +32,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -38,5 +38,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -26,5 +26,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -49,5 +49,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -77,5 +77,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -38,5 +38,5 @@ The .gov team
Contact us: <https://get.gov/contact/> Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov> Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -60,5 +60,5 @@ The .gov team
Domain management <https://manage.get.gov> Domain management <https://manage.get.gov>
Get.gov <https://get.gov> Get.gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <http://cisa.gov/> The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
{% endautoescape %} {% endautoescape %}

View file

@ -7,7 +7,7 @@
{% if heading_level %} {% if heading_level %}
<{{ heading_level }} <{{ heading_level }}
{% else %} {% else %}
<h2 <h3
{% endif %} {% endif %}
class="summary-item__title class="summary-item__title
font-sans-md font-sans-md
@ -19,10 +19,10 @@
{% if heading_level %} {% if heading_level %}
</{{ heading_level }}> </{{ heading_level }}>
{% else %} {% else %}
</h2> </h3>
{% endif %} {% endif %}
{% if sub_header_text %} {% if sub_header_text %}
<h3 class="register-form-review-header">{{ sub_header_text }}</h3> <h4 class="register-form-review-header">{{ sub_header_text }}</h4>
{% endif %} {% endif %}
{% if address %} {% if address %}
{% include "includes/organization_address.html" with organization=value %} {% include "includes/organization_address.html" with organization=value %}

View file

@ -1,3 +1,4 @@
from django.forms import ValidationError
from django.test import TestCase from django.test import TestCase
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from django.db import transaction from django.db import transaction
@ -1348,6 +1349,7 @@ class TestUser(TestCase):
self.user.phone = None self.user.phone = None
self.assertFalse(self.user.has_contact_info()) self.assertFalse(self.user.has_contact_info())
@less_console_noise_decorator
def test_has_portfolio_permission(self): def test_has_portfolio_permission(self):
""" """
0. Returns False when user does not have a permission 0. Returns False when user does not have a permission
@ -1401,6 +1403,37 @@ class TestUser(TestCase):
Portfolio.objects.all().delete() Portfolio.objects.all().delete()
@less_console_noise_decorator
def test_user_with_portfolio_but_no_roles(self):
# Create an instance of User with a portfolio but no roles or additional permissions
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
self.user.portfolio = portfolio
self.user.portfolio_roles = []
# Test if the ValidationError is raised with the correct message
with self.assertRaises(ValidationError) as cm:
self.user.clean()
self.assertEqual(
cm.exception.message, "When portfolio is assigned, portfolio roles or additional permissions are required."
)
Portfolio.objects.all().delete()
@less_console_noise_decorator
def test_user_with_portfolio_roles_but_no_portfolio(self):
# Create an instance of User with a portfolio role but no portfolio
self.user.portfolio = None
self.user.portfolio_roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
# Test if the ValidationError is raised with the correct message
with self.assertRaises(ValidationError) as cm:
self.user.clean()
self.assertEqual(
cm.exception.message, "When portfolio roles or additional permissions are assigned, portfolio is required."
)
class TestContact(TestCase): class TestContact(TestCase):
@less_console_noise_decorator @less_console_noise_decorator

View file

@ -71,6 +71,7 @@
10038 OUTOFSCOPE http://app:8080/domain_requests/ 10038 OUTOFSCOPE http://app:8080/domain_requests/
10038 OUTOFSCOPE http://app:8080/domains/ 10038 OUTOFSCOPE http://app:8080/domains/
10038 OUTOFSCOPE http://app:8080/organization/ 10038 OUTOFSCOPE http://app:8080/organization/
10038 OUTOFSCOPE http://app:8080/suborganization/
# This URL always returns 404, so include it as well. # This URL always returns 404, so include it as well.
10038 OUTOFSCOPE http://app:8080/todo 10038 OUTOFSCOPE http://app:8080/todo
# OIDC isn't configured in the test environment and DEBUG=True so this gives a 500 without CSP headers # OIDC isn't configured in the test environment and DEBUG=True so this gives a 500 without CSP headers