diff --git a/backend/api/test_serializers.py b/backend/api/test_serializers.py index 2ed985e3de..489ef933f0 100644 --- a/backend/api/test_serializers.py +++ b/backend/api/test_serializers.py @@ -2,6 +2,8 @@ from unittest import TestCase from unittest.mock import patch +from django.utils import timezone as django_timezone +from datetime import timedelta from django.test import SimpleTestCase from model_bakery import baker @@ -110,6 +112,25 @@ def test_waived_uei_payload(self): mock_get.return_value.json.return_value = json.loads(missing_uei_results) self.assertTrue(UEISerializer(data=valid).is_valid()) + def test_expired_waived_uei_payload(self): + """ + A UEI with an expired validation waiver should not pass. + """ + yesterday = django_timezone.now() - timedelta(days=1) + expired = {"auditee_uei": "SUPERC00LUE1", "expiration": yesterday} + + baker.make( + UeiValidationWaiver, + uei=expired["auditee_uei"], + expiration=expired["expiration"], + ) + + # Invalid due to the waiver being expired. Mock the SAM call as though the entity doesnt exist. + with patch("api.uei.SESSION.get") as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.json.return_value = json.loads(missing_uei_results) + self.assertFalse(UEISerializer(data=expired).is_valid()) + def test_quirky_uei_payload(self): """ It turns out that some entries can be missing fields that we thought would diff --git a/backend/api/uei.py b/backend/api/uei.py index 837264bf1c..2d2c0e765d 100644 --- a/backend/api/uei.py +++ b/backend/api/uei.py @@ -1,3 +1,4 @@ +from django.utils import timezone as django_timezone import logging import requests import ssl @@ -161,13 +162,10 @@ def get_uei_info_from_sam_gov(uei: str) -> dict: if results["valid"] and (not results.get("errors")): return results - # To honor expiration dates: - # from datetime import date - # today = date.today() - # waiver = UeiValidationWaiver.objects.filter(uei=uei, expiration__gte=today).first() - # 3. Check for a waiver. - waiver = UeiValidationWaiver.objects.filter(uei=uei).first() + waiver = UeiValidationWaiver.objects.filter( + uei=uei, expiration__gte=django_timezone.now() + ).first() if not waiver: return {"valid": False, "errors": ["UEI was not found in SAM.gov"]} diff --git a/backend/audit/admin.py b/backend/audit/admin.py index 7469adb253..d8b19da38d 100644 --- a/backend/audit/admin.py +++ b/backend/audit/admin.py @@ -228,8 +228,10 @@ class UeiValidationWaiverAdmin(admin.ModelAdmin): "id", "uei", "timestamp", + "expiration", "approver_email", "requester_email", + "justification", ) search_fields = ( "id", diff --git a/backend/audit/migrations/0011_ueivalidationwaiver_expiration_and_more.py b/backend/audit/migrations/0011_ueivalidationwaiver_expiration_and_more.py new file mode 100644 index 0000000000..ab18c11316 --- /dev/null +++ b/backend/audit/migrations/0011_ueivalidationwaiver_expiration_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.0.4 on 2024-07-23 15:54 + +import audit.models.models +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("audit", "0010_alter_ueivalidationwaiver_uei"), + ] + + operations = [ + migrations.AddField( + model_name="ueivalidationwaiver", + name="expiration", + field=models.DateTimeField( + default=audit.models.models.one_month_from_today, + verbose_name="When the waiver will expire", + ), + ), + migrations.AddField( + model_name="ueivalidationwaiver", + name="timestamp", + field=models.DateTimeField( + default=django.utils.timezone.now, + verbose_name="When the waiver was created", + ), + ), + ] diff --git a/backend/audit/models/models.py b/backend/audit/models/models.py index c79bca1a76..6b240f6fab 100644 --- a/backend/audit/models/models.py +++ b/backend/audit/models/models.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import datetime, timezone, timedelta from itertools import chain import json import logging @@ -71,6 +71,10 @@ def generate_sac_report_id(end_date=None, source="GSAFAC", count=None): return report_id +def one_month_from_today(): + return django_timezone.now() + timedelta(days=30) + + class SingleAuditChecklistManager(models.Manager): """Manager for SAC""" @@ -704,16 +708,13 @@ def __str__(self): # Not unique, in the case that one UEI needs to be waived several times with different expiration dates. uei = models.TextField("UEI") - timestamp = ( - models.DateTimeField( - "When the waiver was created", - default=django_timezone.now, - ), + timestamp = models.DateTimeField( + "When the waiver was created", + default=django_timezone.now, ) - expiration = ( - models.DateTimeField( - "When the waiver expires", - ), + expiration = models.DateTimeField( + "When the waiver will expire", + default=one_month_from_today, ) approver_email = models.TextField( "Email address of FAC staff member approving the waiver",