Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2729: Update Add a Domain Manager page - [ES] #2857

Merged
merged 24 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
caad00d
Block users invited to other orgs from being domain managers
erinysong Sep 26, 2024
42de7f2
Add domain manager breadcrumb nav
erinysong Sep 26, 2024
8b61eb1
Update add domain manager page content
erinysong Sep 26, 2024
b0fe698
Add error code for outside org members being added
erinysong Sep 26, 2024
d710696
Fix linting. Revert to original email sending logic
erinysong Sep 26, 2024
d66ff33
Readd outside org member error handling
erinysong Sep 26, 2024
3d1781c
Fix linting
erinysong Sep 26, 2024
117900c
Revert to try else catch
erinysong Sep 30, 2024
2059c9f
Merge branch 'main' of https://github.com/cisagov/manage.get.gov into…
erinysong Oct 8, 2024
09944e4
Fix form valid logic
erinysong Oct 8, 2024
467b7a9
Readd try block
erinysong Oct 8, 2024
17b5f36
Fix indent
erinysong Oct 8, 2024
e396534
Fix indent
erinysong Oct 8, 2024
9425d4c
Isolate user domain role create
erinysong Oct 8, 2024
2f2c4e1
Simplify try catch
erinysong Oct 8, 2024
6a01e56
Debug email bug
erinysong Oct 8, 2024
889c0a2
Secure portfolio check on requestor org
erinysong Oct 8, 2024
aec1a4f
Reverse email send check
erinysong Oct 9, 2024
76fc713
Re-revert email_success check
erinysong Oct 9, 2024
dd29cbf
Add domain manager after email sending error
erinysong Oct 9, 2024
4393b5a
Add domain manager page content updates
erinysong Oct 15, 2024
961a289
Update domain manager page content
erinysong Oct 15, 2024
3cb341d
Add content updates
erinysong Oct 16, 2024
62b3cbf
Resolve merge conflict in fixtures
erinysong Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/registrar/templates/domain_add_user.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@
{% block title %}Add a domain manager | {% endblock %}

{% block domain_content %}
{% block breadcrumb %}
{% url 'domain-users' pk=domain.id as url %}
<nav class="usa-breadcrumb padding-top-0" aria-label="Domain manager breadcrumb">
<ol class="usa-breadcrumb__list">
<li class="usa-breadcrumb__list-item">
<a href="{{ url }}" class="usa-breadcrumb__link"><span>Domain managers</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Add a domain manager</span>
</li>
</ol>
</nav>
{% endblock breadcrumb %}
<h1>Add a domain manager</h1>

<p>You can add another user to help manage your domain. They will need to sign
in to the .gov registrar with their Login.gov account.
<p>You can add another user to help manage your domain. If they aren't an organization member they will
need to sign in to the .gov registrar with their Login.gov account.
</p>

<form class="usa-form usa-form--large" method="post" novalidate>
Expand Down
9 changes: 9 additions & 0 deletions src/registrar/utility/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ class InvalidDomainError(ValueError):
pass


class OutsideOrgMemberError(ValueError):
Copy link
Contributor Author

@erinysong erinysong Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can delete this error class and all references to it once we allow users to be members of multiple orgs as per the ticket ACs

"""
Error raised when an org member tries adding a user from a different .gov org.
To be deleted when users can be members of multiple orgs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love the comment here too! great!

"""

pass


class ActionNotAllowed(Exception):
"""User accessed an action that is not
allowed by the current state"""
Expand Down
65 changes: 49 additions & 16 deletions src/registrar/views/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
DomainRequest,
DomainInformation,
DomainInvitation,
PortfolioInvitation,
User,
UserDomainRole,
UserPortfolioPermission,
PublicContact,
)
from registrar.utility.enums import DefaultEmail
Expand All @@ -35,9 +37,11 @@
DsDataErrorCodes,
SecurityEmailError,
SecurityEmailErrorCodes,
OutsideOrgMemberError,
)
from registrar.models.utility.contact_error import ContactError
from registrar.views.utility.permission_views import UserDomainRolePermissionDeleteView
from registrar.utility.waffle import flag_is_active_for_user

from ..forms import (
SeniorOfficialContactForm,
Expand Down Expand Up @@ -778,7 +782,18 @@ def _domain_abs_url(self):
"""Get an absolute URL for this domain."""
return self.request.build_absolute_uri(reverse("domain", kwargs={"pk": self.object.id}))

def _send_domain_invitation_email(self, email: str, requestor: User, add_success=True):
def _is_member_of_different_org(self, email, requestor, requested_user):
"""Verifies if an email belongs to a different organization as a member or invited member."""
# Check if user is a already member of a different organization than the requestor's org
requestor_org = UserPortfolioPermission.objects.get(user=requestor).portfolio
existing_org_permission = UserPortfolioPermission.objects.filter(user=requested_user).first()
existing_org_invitation = PortfolioInvitation.objects.filter(email=email).first()

return (existing_org_permission and existing_org_permission.portfolio != requestor_org) or (
existing_org_invitation and existing_org_invitation.portfolio != requestor_org
)

def _send_domain_invitation_email(self, email: str, requestor: User, requested_user=None, add_success=True):
"""Performs the sending of the domain invitation email,
does not make a domain information object
email: string- email to send to
Expand All @@ -803,6 +818,17 @@ def _send_domain_invitation_email(self, email: str, requestor: User, add_success
)
return None

# Check is user is a member or invited member of a different org from this domain's org
if flag_is_active_for_user(requestor, "organization_feature") and self._is_member_of_different_org(
email, requestor, requested_user
):
add_success = False
messages.error(
self.request,
"That email is already a member of another .gov organization.",
)
raise OutsideOrgMemberError

# Check to see if an invite has already been sent
try:
invite = DomainInvitation.objects.get(email=email, domain=self.object)
Expand Down Expand Up @@ -831,6 +857,8 @@ def _send_domain_invitation_email(self, email: str, requestor: User, add_success
"requestor_email": requestor_email,
},
)
if add_success:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this into the try statement since we shouldn't be sending emails if we do run into Exceptions that aren't EmailSendingError - feel free to let me know if there was a reason we did this previously though!

messages.success(self.request, f"{email} has been invited to this domain.")
except EmailSendingError as exc:
logger.warn(
"Could not sent email invitation to %s for domain %s",
Expand All @@ -839,9 +867,6 @@ def _send_domain_invitation_email(self, email: str, requestor: User, add_success
exc_info=True,
)
raise EmailSendingError("Could not send email invitation.") from exc
else:
if add_success:
messages.success(self.request, f"{email} has been invited to this domain.")

def _make_invitation(self, email_address: str, requestor: User):
"""Make a Domain invitation for this email and redirect with a message."""
Expand All @@ -868,32 +893,40 @@ def form_valid(self, form):
else:
# if user already exists then just send an email
try:
self._send_domain_invitation_email(requested_email, requestor, add_success=False)
self._send_domain_invitation_email(
requested_email, requestor, requested_user=requested_user, add_success=False
)
except EmailSendingError:
logger.warn(
"Could not send email invitation (EmailSendingError)",
self.object,
exc_info=True,
)
messages.warning(self.request, "Could not send email invitation.")
except OutsideOrgMemberError:
logger.warn(
"Could not send email. Can not invite member of a .gov organization to a different organization.",
self.object,
exc_info=True,
)
except Exception:
logger.warn(
"Could not send email invitation (Other Exception)",
self.object,
exc_info=True,
)
messages.warning(self.request, "Could not send email invitation.")

try:
UserDomainRole.objects.create(
user=requested_user,
domain=self.object,
role=UserDomainRole.Roles.MANAGER,
)
except IntegrityError:
messages.warning(self.request, f"{requested_email} is already a manager for this domain")
else:
messages.success(self.request, f"Added user {requested_email}.")
else:
try:
UserDomainRole.objects.create(
user=requested_user,
domain=self.object,
role=UserDomainRole.Roles.MANAGER,
)
except IntegrityError:
messages.warning(self.request, f"{requested_email} is already a manager for this domain")
else:
messages.success(self.request, f"Added user {requested_email}.")
return redirect(self.get_success_url())


Expand Down
Loading