Fix portfolio transfer

This commit is contained in:
Rachid Mrad 2024-08-30 11:51:24 -04:00
parent 6a196665dd
commit e1e812c5c7
No known key found for this signature in database
4 changed files with 60 additions and 51 deletions

View file

@ -6,7 +6,7 @@ from django.db.models import Q
from django.http import HttpRequest from django.http import HttpRequest
from registrar.models import DomainInformation, UserDomainRole from registrar.models import DomainInformation, UserDomainRole
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices
from .domain_invitation import DomainInvitation from .domain_invitation import DomainInvitation
from .portfolio_invitation import PortfolioInvitation from .portfolio_invitation import PortfolioInvitation
@ -64,32 +64,6 @@ class User(AbstractUser):
# after they login. # after they login.
FIXTURE_USER = "fixture_user", "Created by fixtures" FIXTURE_USER = "fixture_user", "Created by fixtures"
PORTFOLIO_ROLE_PERMISSIONS = {
UserPortfolioRoleChoices.ORGANIZATION_ADMIN: [
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
UserPortfolioPermissionChoices.VIEW_MEMBER,
UserPortfolioPermissionChoices.EDIT_MEMBER,
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
UserPortfolioPermissionChoices.EDIT_REQUESTS,
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
UserPortfolioPermissionChoices.EDIT_PORTFOLIO,
# Domain: field specific permissions
UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION,
UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION,
],
UserPortfolioRoleChoices.ORGANIZATION_ADMIN_READ_ONLY: [
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
UserPortfolioPermissionChoices.VIEW_MEMBER,
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
# Domain: field specific permissions
UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION,
],
UserPortfolioRoleChoices.ORGANIZATION_MEMBER: [
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
],
}
# #### Constants for choice fields #### # #### Constants for choice fields ####
RESTRICTED = "restricted" RESTRICTED = "restricted"
STATUS_CHOICES = ((RESTRICTED, RESTRICTED),) STATUS_CHOICES = ((RESTRICTED, RESTRICTED),)

View file

@ -111,6 +111,18 @@
None None
{% endif %} {% endif %}
</dd> </dd>
<dt>Portfolios:</dt>
<dd>
{% if selected_user_portfolios %}
<ul>
{% for portfolio in selected_user_portfolios %}
<li>{{ portfolio.portfolio }}</li>
{% endfor %}
</ul>
{% else %}
None
{% endif %}
</dd>
</dl> </dl>
{% else %} {% else %}
<p>No user selected yet.</p> <p>No user selected yet.</p>
@ -167,6 +179,18 @@
None None
{% endif %} {% endif %}
</dd> </dd>
<dt>Portfolios:</dt>
<dd>
{% if current_user_portfolios %}
<ul>
{% for portfolio in current_user_portfolios %}
<li>{{ portfolio.portfolio }}</li>
{% endfor %}
</ul>
{% else %}
None
{% endif %}
</dd>
</dl> </dl>
</div> </div>
</div> </div>

View file

@ -45,7 +45,8 @@ from registrar.models import (
from registrar.models.portfolio_invitation import PortfolioInvitation from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.senior_official import SeniorOfficial from registrar.models.senior_official import SeniorOfficial
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
from registrar.models.verified_by_staff import VerifiedByStaff from registrar.models.verified_by_staff import VerifiedByStaff
from .common import ( from .common import (
MockDbForSharedTests, MockDbForSharedTests,
@ -2162,6 +2163,14 @@ class TestTransferUser(WebTest):
) )
domain_request.status = DomainRequest.DomainRequestStatus.APPROVED domain_request.status = DomainRequest.DomainRequestStatus.APPROVED
domain_request.save() domain_request.save()
portfolio1 = Portfolio.objects.create(organization_name="Hotel California", creator=self.user2)
UserPortfolioPermission.objects.create(
user=self.user1, portfolio=portfolio1, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
portfolio2 = Portfolio.objects.create(organization_name="Tokyo Hotel", creator=self.user2)
UserPortfolioPermission.objects.create(
user=self.user2, portfolio=portfolio2, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk])) user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
@ -2171,6 +2180,7 @@ class TestTransferUser(WebTest):
self.assertContains(user_transfer_page, "Road warrior") self.assertContains(user_transfer_page, "Road warrior")
self.assertContains(user_transfer_page, "wasteland.gov") self.assertContains(user_transfer_page, "wasteland.gov")
self.assertContains(user_transfer_page, "citadel.gov") self.assertContains(user_transfer_page, "citadel.gov")
self.assertContains(user_transfer_page, "Hotel California")
select_form = user_transfer_page.forms[0] select_form = user_transfer_page.forms[0]
select_form["selected_user"] = str(self.user2.id) select_form["selected_user"] = str(self.user2.id)
@ -2180,19 +2190,15 @@ class TestTransferUser(WebTest):
self.assertContains(preview_result, "Furiosa") self.assertContains(preview_result, "Furiosa")
self.assertContains(preview_result, "Jabassa") self.assertContains(preview_result, "Jabassa")
self.assertContains(preview_result, "Imperator") self.assertContains(preview_result, "Imperator")
self.assertContains(preview_result, "Tokyo Hotel")
@less_console_noise_decorator @less_console_noise_decorator
def test_transfer_user_transfers_portfolio_roles_and_permissions_and_portfolio_creator(self): def test_transfer_user_transfers_user_portfolio_roles(self):
"""Assert that a portfolio gets copied over """Assert that a portfolio user role gets transferred"""
NOTE: Should be revised for #2644""" portfolio = Portfolio.objects.create(organization_name="Hotel California", creator=self.user2)
portfolio = Portfolio.objects.create(organization_name="Citadel", creator=self.user2) user_portfolio_permission = UserPortfolioPermission.objects.create(
self.user2.portfolio = portfolio user=self.user2, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
self.user2.portfolio_roles = [UserPortfolioRoleChoices.ORGANIZATION_ADMIN] )
self.user2.portfolio_additional_permissions = [
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
UserPortfolioPermissionChoices.EDIT_PORTFOLIO,
]
self.user2.save()
user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk])) user_transfer_page = self.app.get(reverse("transfer_user", args=[self.user1.pk]))
@ -2200,16 +2206,9 @@ class TestTransferUser(WebTest):
submit_form["selected_user"] = self.user2.pk submit_form["selected_user"] = self.user2.pk
submit_form.submit() submit_form.submit()
self.user1.refresh_from_db() user_portfolio_permission.refresh_from_db()
portfolio.refresh_from_db()
self.assertEquals(portfolio.creator, self.user1) self.assertEquals(user_portfolio_permission.user, self.user1)
self.assertEquals(self.user1.portfolio, portfolio)
self.assertEquals(self.user1.portfolio_roles, [UserPortfolioRoleChoices.ORGANIZATION_ADMIN])
self.assertEquals(
self.user1.portfolio_additional_permissions,
[UserPortfolioPermissionChoices.VIEW_PORTFOLIO, UserPortfolioPermissionChoices.EDIT_PORTFOLIO],
)
@less_console_noise_decorator @less_console_noise_decorator
def test_transfer_user_transfers_domain_request_creator_and_investigator(self): def test_transfer_user_transfers_domain_request_creator_and_investigator(self):

View file

@ -11,7 +11,9 @@ from django.contrib.admin import site
from django.contrib import messages from django.contrib import messages
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.verified_by_staff import VerifiedByStaff from registrar.models.verified_by_staff import VerifiedByStaff
from typing import Any, List
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -26,9 +28,12 @@ class TransferUserView(View):
(DomainRequest, "investigator"), (DomainRequest, "investigator"),
(UserDomainRole, "user"), (UserDomainRole, "user"),
(VerifiedByStaff, "requestor"), (VerifiedByStaff, "requestor"),
(UserPortfolioPermission, "user"),
] ]
USER_FIELDS = ["portfolio", "portfolio_roles", "portfolio_additional_permissions"] # Future-proofing in case joined fields get added on the user model side
# This was tested in the first portfolio model iteration and works
USER_FIELDS: List[Any] = []
def get(self, request, user_id): def get(self, request, user_id):
"""current_user referes to the 'source' user where the button that redirects to this view was clicked. """current_user referes to the 'source' user where the button that redirects to this view was clicked.
@ -51,6 +56,7 @@ class TransferUserView(View):
**admin_context, # Include the admin context **admin_context, # Include the admin context
"current_user_domains": self.get_domains(current_user), "current_user_domains": self.get_domains(current_user),
"current_user_domain_requests": self.get_domain_requests(current_user), "current_user_domain_requests": self.get_domain_requests(current_user),
"current_user_portfolios": self.get_portfolios(current_user),
} }
selected_user_id = request.GET.get("selected_user") selected_user_id = request.GET.get("selected_user")
@ -59,6 +65,7 @@ class TransferUserView(View):
context["selected_user"] = selected_user context["selected_user"] = selected_user
context["selected_user_domains"] = self.get_domains(selected_user) context["selected_user_domains"] = self.get_domains(selected_user)
context["selected_user_domain_requests"] = self.get_domain_requests(selected_user) context["selected_user_domain_requests"] = self.get_domain_requests(selected_user)
context["selected_user_portfolios"] = self.get_portfolios(selected_user)
return render(request, "admin/transfer_user.html", context) return render(request, "admin/transfer_user.html", context)
@ -121,8 +128,6 @@ class TransferUserView(View):
""" """
Transfers portfolio fields from the selected_user to the current_user. Transfers portfolio fields from the selected_user to the current_user.
Logs the changes for each transferred field. Logs the changes for each transferred field.
NOTE: This will be refactored in #2644
""" """
for field in cls.USER_FIELDS: for field in cls.USER_FIELDS:
field_value = getattr(selected_user, field, None) field_value = getattr(selected_user, field, None)
@ -158,3 +163,10 @@ class TransferUserView(View):
domain_requests = DomainRequest.objects.filter(creator=user) domain_requests = DomainRequest.objects.filter(creator=user)
return domain_requests return domain_requests
@classmethod
def get_portfolios(cls, user):
"""Get portfolios"""
portfolios = UserPortfolioPermission.objects.filter(user=user)
return portfolios