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

1337 email owner as system admin group (un)assigned #3253

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ compliance/opencontrols/
compliance/exports/
tdrs-backend/tdpservice/static/*
*gunicorn.log
*.log

# don't ignore requirements.txt
!requirements.txt
Expand Down Expand Up @@ -115,3 +116,4 @@ cypress.env.json

# DB seeds
tdrs-backend/*.pg
tdrs-backend/django.log
1 change: 1 addition & 0 deletions tdrs-backend/tdpservice/email/email_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ class EmailType(Enum):
ACCOUNT_DEACTIVATED_ADMIN = 'account-deactivated-admin.html'
UPCOMING_SUBMISSION_DEADLINE = 'upcoming-submission-deadline.html'
STUCK_FILE_LIST = 'stuck-file-list.html'
SYSTEM_ADMIN_ROLE_CHANGED = 'system-admin-role-changed.html'
34 changes: 34 additions & 0 deletions tdrs-backend/tdpservice/email/helpers/admin_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,37 @@ def email_admin_deactivated_user(user):
text_message=text_message,
logger_context=logger_context
)

def email_system_owner_system_admin_role_change(user, action):
raftmsohani marked this conversation as resolved.
Show resolved Hide resolved
"""Send an email to the System Owner when a user is assigned or removed from the System Admin role."""
from tdpservice.users.models import User
from tdpservice.email.email_enums import EmailType
from tdpservice.email.email import automated_email, log
from tdpservice.email.tasks import get_system_owner_email
Copy link

Choose a reason for hiding this comment

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

is there a reason for the imports in the function? i see it in other functions in this file, so just curious

Copy link
Author

Choose a reason for hiding this comment

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

I think originally I got circular error, but will check back again because I changed things around

recipient_email = get_system_owner_email()
logger_context = {
'user_id': user.id,
'object_id': user.id,
'object_repr': user.username,
'content_type': User,
}

template_path = EmailType.SYSTEM_ADMIN_ROLE_CHANGED.value
text_message = 'A user has been assigned or removed from the System Admin role.'
subject = 'TDP User Role Change: System Admin'
context = {
'user': user,
'action': action,
}

log(f"Preparing email to System Owner for System Admin role change for user {user.username}",
logger_context=logger_context)

automated_email(
email_path=template_path,
recipient_email=recipient_email,
subject=subject,
email_context=context,
text_message=text_message,
logger_context=logger_context
)
8 changes: 8 additions & 0 deletions tdrs-backend/tdpservice/email/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ def get_ofa_admin_user_emails():
groups__in=Group.objects.filter(name__in=('OFA Admin', 'OFA System Admin'))
).values_list('email', flat=True).distinct()

def get_system_owner_email():
"""Return the email of the System Owner."""
try:
user_email = User.objects.filter(groups__name='System Owner').values_list('email', flat=True).distinct()
raftmsohani marked this conversation as resolved.
Show resolved Hide resolved
except User.DoesNotExist:
user_email = [None]
return user_email

def get_num_access_requests():
"""Return the number of users requesting access."""
return User.objects.filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends 'base.html' %}
{% block content %}
<!-- Body copy -->
<p style="color: #000000;">

<p>The following Admin User account for the TANF Data Portal (TDP) has been {{ action }}.</p>
Copy link

@jtimpe jtimpe Nov 12, 2024

Choose a reason for hiding this comment

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

This reads a little bit weird with "added" and "removed" as the action. Could we do "assigned" and "unassigned"? Or change the sentence a little.

"The System Admin role has been (assigned/unassigned) for a user of the TANF Data Portal (TDP)"

just a suggestion. sounds a little like the account has been deleted otherwise.


<p>Account Information:</p>
<ul>
<li>Name: {{ user.first_name }}</li>
<li>Last name: {{ user.last_name }}</li>
<li>Email: {{ user.email }}</li>
</ul>

<p>Thank you,</p>
TDP Team
</p>
{% endblock %}
4 changes: 4 additions & 0 deletions tdrs-backend/tdpservice/users/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ class UsersConfig(AppConfig):

name = "tdpservice.users"
verbose_name = "Users"

def ready(self):
"""Import signals."""
import tdpservice.users.signals # noqa
30 changes: 30 additions & 0 deletions tdrs-backend/tdpservice/users/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Signals for the users app."""
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from tdpservice.users.models import User
from django.contrib.auth.models import Group
from tdpservice.email.helpers.admin_notifications import email_system_owner_system_admin_role_change

import logging
logger = logging.getLogger()

@receiver(m2m_changed, sender=User.groups.through)
def user_group_changed(sender, instance, action, pk_set, **kwargs):
"""Send an email to the System Owner when a user is assigned or removed from the System Admin role."""
ACTIONS = {
'PRE_REMOVE': 'pre_remove',
'PRE_ADD': 'pre_add',
'PRE_CLEAR': 'pre_clear'
Copy link

Choose a reason for hiding this comment

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

enum?

}
if pk_set:
ADMIN_GROUP_PK = Group.objects.get(name="OFA System Admin").pk
group_change_list = [pk for pk in pk_set]
if ADMIN_GROUP_PK in group_change_list and action == ACTIONS['PRE_ADD']:
# EMAIL ADMIN GROUP ADDED to OFA ADMIN
email_system_owner_system_admin_role_change(instance, "added")
elif ADMIN_GROUP_PK in group_change_list and action == ACTIONS['PRE_REMOVE']:
# EMAIL ADMIN GROUP REMOVED from OFA ADMIN
email_system_owner_system_admin_role_change(instance, "removed")
elif pk_set is None and action == ACTIONS['PRE_CLEAR']:
# EMAIL ADMIN GROUP REMOVED from OFA ADMIN
email_system_owner_system_admin_role_change(instance, "removed")
37 changes: 37 additions & 0 deletions tdrs-backend/tdpservice/users/test/test_signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Test signals."""
import pytest
from unittest.mock import patch
from tdpservice.users.models import User
from tdpservice.users.test.factories import AdminUserFactory
from django.contrib.auth.models import Group
import logging
import django


logger = logging.getLogger(__name__)


@pytest.mark.django_db
def test_my_signal_receiver(mocker):
"""Test my_signal_receiver."""
with patch("django.db.models.signals.m2m_changed.send") as mock_receiver:
instance = AdminUserFactory.create()
instance.groups.add(Group.objects.get(name="OFA System Admin"))

mock_receiver.assert_called_with(
sender=User.groups.through,
instance=instance,
action="post_add",
pk_set={Group.objects.get(name="OFA System Admin").pk},
reverse=False,
using="default",
model=django.contrib.auth.models.Group,
)
mock_receiver.call_count = 2 # pre_save and post_save

with patch(
"tdpservice.users.signals.email_system_owner_system_admin_role_change"
) as mock_email_system_owner_system_admin_role_change:
instance = AdminUserFactory.create()
instance.groups.add(Group.objects.get(name="OFA System Admin"))
mock_email_system_owner_system_admin_role_change.assert_called_once()
Loading