Skip to content

Commit

Permalink
Feat: automatically associate user to agency (#2325)
Browse files Browse the repository at this point in the history
  • Loading branch information
angela-tran authored Aug 28, 2024
2 parents 9139b06 + 04d3c19 commit 1e2ff89
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 16 deletions.
5 changes: 5 additions & 0 deletions benefits/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ def index(self, request, extra_context=None):
else:
agency = TransitAgency.for_user(request.user)
session.update(request, agency=agency)

if agency is not None:
has_permission_for_in_person = agency.customer_service_group in request.user.groups.all()
context.update({"has_permission_for_in_person": has_permission_for_in_person})

return TemplateResponse(request, "admin/agency-dashboard-index.html", context)
11 changes: 10 additions & 1 deletion benefits/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def pre_login_user(user, request):
logger.debug(f"Running pre-login callback for user: {user.username}")
add_google_sso_userinfo(user, request)
add_staff_user_to_group(user, request)
add_transit_agency_staff_user_to_group(user, request)


def add_google_sso_userinfo(user, request):
Expand All @@ -150,4 +151,12 @@ def add_google_sso_userinfo(user, request):
def add_staff_user_to_group(user, request):
if user.email in settings.GOOGLE_SSO_STAFF_LIST:
staff_group = Group.objects.get(name=settings.STAFF_GROUP_NAME)
user.groups.add(staff_group)
staff_group.user_set.add(user)


def add_transit_agency_staff_user_to_group(user, request):
user_sso_domain = user.email.split("@")[1]
if user_sso_domain:
agency = models.TransitAgency.objects.filter(sso_domain=user_sso_domain).first()
if agency is not None and agency.staff_group:
agency.staff_group.user_set.add(user)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 5.0.7 on 2024-08-27 16:02

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("core", "0023_transitagency_staff_group"),
]

operations = [
migrations.AddField(
model_name="transitagency",
name="sso_domain",
field=models.TextField(
blank=True,
default="",
help_text="The email domain of users to automatically add to this agency's staff group upon login.",
null=True,
),
),
migrations.AlterField(
model_name="transitagency",
name="staff_group",
field=models.OneToOneField(
blank=True,
default=None,
help_text="The group of users associated with this TransitAgency.",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="transit_agency",
to="auth.group",
),
),
migrations.AddField(
model_name="transitagency",
name="customer_service_group",
field=models.OneToOneField(
blank=True,
default=None,
help_text="The group of users who are allowed to do in-person eligibility verification and enrollment.",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="auth.group",
),
),
]
28 changes: 22 additions & 6 deletions benefits/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,23 @@ class TransitAgency(models.Model):
null=True,
blank=True,
default=None,
help_text="The group of users who can access this TransitAgency.",
help_text="The group of users associated with this TransitAgency.",
related_name="transit_agency",
)
sso_domain = models.TextField(
null=True,
blank=True,
default="",
help_text="The email domain of users to automatically add to this agency's staff group upon login.",
)
customer_service_group = models.OneToOneField(
Group,
on_delete=models.PROTECT,
null=True,
blank=True,
default=None,
help_text="The group of users who are allowed to do in-person eligibility verification and enrollment.",
related_name="+",
)

def __str__(self):
Expand Down Expand Up @@ -397,9 +413,9 @@ def all_active():

@staticmethod
def for_user(user: User):
group = user.groups.first()
for group in user.groups.all():
if hasattr(group, "transit_agency"):
return group.transit_agency # this is looking at the TransitAgency's staff_group

if group is not None and hasattr(group, "transitagency"):
return group.transitagency
else:
return None
# the loop above returns the first match found. Return None if no match was found.
return None
18 changes: 10 additions & 8 deletions benefits/templates/admin/agency-dashboard-index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@
<div class="row">
<div class="col-lg-12">
<h1 class="pt-0 pb-3 text-left">{{ agency.long_name }}</h1>
<div class="border border-3 p-3">
<h2 class="pt-0 pb-2 text-left">In-person enrollment</h2>
<p>Verify transit benefit eligibility using agency criteria and complete a rider’s open-loop card enrollment.</p>
{% url routes.IN_PERSON_ELIGIBILITY as url_eligibility %}
<div class="row pt-4">
<div class="col-lg-2">
<a href="{{ url_eligibility }}" class="btn btn-lg btn-primary d-block">New enrollment</a>
{% if has_permission_for_in_person %}
<div class="border border-3 p-3">
<h2 class="pt-0 pb-2 text-left">In-person enrollment</h2>
<p>Verify transit benefit eligibility using agency criteria and complete a rider’s open-loop card enrollment.</p>
{% url routes.IN_PERSON_ELIGIBILITY as url_eligibility %}
<div class="row pt-4">
<div class="col-lg-2">
<a href="{{ url_eligibility }}" class="btn btn-lg btn-primary d-block">New enrollment</a>
</div>
</div>
</div>
</div>
{% endif %}
</div>
</div>

Expand Down
25 changes: 25 additions & 0 deletions tests/pytest/core/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from django.conf import settings
from django.contrib.auth.models import User, Group

import benefits.core.admin
from benefits.core.admin import GOOGLE_USER_INFO_URL, pre_login_user

Expand Down Expand Up @@ -94,3 +95,27 @@ def test_pre_login_user_does_not_add_transit_staff_to_group(mocker, settings):
staff_group = Group.objects.get(name=settings.STAFF_GROUP_NAME)
assert staff_group.user_set.count() == 0
assert agency_user.groups.count() == 0


@pytest.mark.django_db
def test_pre_login_user_add_transit_staff_to_transit_staff_group(mocker, settings, model_TransitAgency):
mocked_request = mocker.Mock()
mocked_request.session.get.return_value = None

transit_agency_staff_group = Group.objects.create(name="CST Staff")
model_TransitAgency.pk = None
model_TransitAgency.staff_group = transit_agency_staff_group
model_TransitAgency.sso_domain = "cst.org"
model_TransitAgency.save()

settings.GOOGLE_SSO_STAFF_LIST = ["*"]
settings.GOOGLE_SSO_ALLOWABLE_DOMAINS = ["cst.org"]

# simulate what `django_google_sso` does for us (sets is_staff to True)
agency_user = User.objects.create_user(username="agency_user", email="[email protected]", is_staff=True)

pre_login_user(agency_user, mocked_request)

# assert that a transit agency user gets added to their TransitAgency's staff group based on SSO domain
assert agency_user.groups.count() == 1
assert agency_user.groups.first() == transit_agency_staff_group
30 changes: 29 additions & 1 deletion tests/pytest/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,40 @@ def model_SuperUser(model_User):


@pytest.mark.django_db
def test_admin_override_agency_user(app_request, model_AgencyUser):
def test_admin_override_agency_user_customer_service(app_request, model_AgencyUser, model_TransitAgency):
app_request.user = model_AgencyUser

# set up TransitAgency with staff_group and customer_service_group
model_TransitAgency.pk = None
model_TransitAgency.staff_group = Group.objects.get(name="CST")
customer_service_group = Group.objects.create(name="CST Customer Service")
model_TransitAgency.customer_service_group = customer_service_group
model_TransitAgency.save()

# add the user to the customer service group
model_AgencyUser.groups.add(customer_service_group)

response = BenefitsAdminSite().index(app_request)

assert response.status_code == 200
assert response.template_name == "admin/agency-dashboard-index.html"
assert response.context_data["has_permission_for_in_person"] is True


@pytest.mark.django_db
def test_admin_override_agency_user_not_customer_service(app_request, model_AgencyUser, model_TransitAgency):
app_request.user = model_AgencyUser

# set up TransitAgency with only a staff_group
model_TransitAgency.pk = None
model_TransitAgency.staff_group = Group.objects.get(name="CST")
model_TransitAgency.save()

response = BenefitsAdminSite().index(app_request)

assert response.status_code == 200
assert response.template_name == "admin/agency-dashboard-index.html"
assert response.context_data["has_permission_for_in_person"] is False


@pytest.mark.django_db
Expand Down

0 comments on commit 1e2ff89

Please sign in to comment.