Skip to content

Commit

Permalink
refactor: move verify logic back to eligibility app
Browse files Browse the repository at this point in the history
  • Loading branch information
angela-tran committed Jun 16, 2022
1 parent 314abac commit 808f673
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 52 deletions.
39 changes: 0 additions & 39 deletions benefits/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
"""
import logging

from django.conf import settings
from django.db import models
from django.urls import reverse
from eligibility_api.client import Client


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -110,43 +108,6 @@ def by_id(id):
logger.debug(f"Get {EligibilityVerifier.__name__} by id: {id}")
return EligibilityVerifier.objects.get(id=id)

def get_verified_types(self, form=None, agency=None, oauth_claim=None):
if form is not None and agency is not None:
return self._get_api_verified_types(form, agency)
elif oauth_claim is not None and self.requires_authentication and self.auth_claim == oauth_claim:
# TODO: refactor verifier to hold single eligibility_type
return list(map(lambda t: t.name, self.eligibility_types.all()))
else:
return []

def _get_api_verified_types(self, form, agency):
sub, name = form.cleaned_data.get("sub"), form.cleaned_data.get("name")

client = Client(
verify_url=self.api_url,
headers={self.api_auth_header: self.api_auth_key},
issuer=settings.ALLOWED_HOSTS[0],
agency=agency.agency_id,
jws_signing_alg=agency.jws_signing_alg,
client_private_key=agency.private_key_data,
jwe_encryption_alg=self.jwe_encryption_alg,
jwe_cek_enc=self.jwe_cek_enc,
server_public_key=self.public_key_data,
)

# get the eligibility type names to check
types = list(map(lambda t: t.name, agency.types_to_verify(self)))

response = client.verify(sub, name, types)

if response.error and any(response.error):
form.add_api_errors(response.error)
return None
elif any(response.eligibility):
return list(response.eligibility)
else:
return []


class PaymentProcessor(models.Model):
"""An entity that processes payments for transit agencies."""
Expand Down
40 changes: 40 additions & 0 deletions benefits/eligibility/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.conf import settings

from eligibility_api.client import Client


def eligibility_from_api(verifier, form, agency):
sub, name = form.cleaned_data.get("sub"), form.cleaned_data.get("name")

client = Client(
verify_url=verifier.api_url,
headers={verifier.api_auth_header: verifier.api_auth_key},
issuer=settings.ALLOWED_HOSTS[0],
agency=agency.agency_id,
jws_signing_alg=agency.jws_signing_alg,
client_private_key=agency.private_key_data,
jwe_encryption_alg=verifier.jwe_encryption_alg,
jwe_cek_enc=verifier.jwe_cek_enc,
server_public_key=verifier.public_key_data,
)

# get the eligibility type names to check
types = list(map(lambda t: t.name, agency.types_to_verify(verifier)))

response = client.verify(sub, name, types)

if response.error and any(response.error):
form.add_api_errors(response.error)
return None
elif any(response.eligibility):
return list(response.eligibility)
else:
return []


def eligibility_from_oauth(verifier, oauth_claim):
if verifier.requires_authentication and verifier.auth_claim == oauth_claim:
# TODO: refactor verifier to hold single eligibility_type
return list(map(lambda t: t.name, verifier.eligibility_types.all()))
else:
return []
6 changes: 3 additions & 3 deletions benefits/eligibility/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from benefits.core.middleware import AgencySessionRequired, LoginRequired, RateLimit, VerifierSessionRequired
from benefits.core.models import EligibilityVerifier
from benefits.core.views import ROUTE_HELP, TEMPLATE_PAGE
from . import analytics, forms
from . import analytics, forms, verify


ROUTE_INDEX = "eligibility:index"
Expand Down Expand Up @@ -195,7 +195,7 @@ def confirm(request):
return TemplateResponse(request, TEMPLATE_CONFIRM, page.context_dict())

# form is valid, make Eligibility Verification request to get the verified types
verified_types = verifier.get_verified_types(form=form, agency=session.agency(request))
verified_types = verify.eligibility_from_api(verifier, form, session.agency(request))

# form was not valid, allow for correction/resubmission
if verified_types is None:
Expand All @@ -216,7 +216,7 @@ def confirm(request):

# GET from an unverified user, see if verifier can get verified types and if not, present the form
else:
verified_types = verifier.get_verified_types(session.oauth_claim(request))
verified_types = verify.eligibility_from_oauth(verifier, session.oauth_claim(request))
if verified_types:
return verified(request, verified_types)
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from benefits.eligibility.forms import EligibilityVerificationForm
from benefits.eligibility.verify import eligibility_from_api


@pytest.fixture
Expand All @@ -10,40 +11,40 @@ def form(mocker):

@pytest.fixture
def mock_api_client_verify(mocker):
return mocker.patch("benefits.core.models.Client.verify")
return mocker.patch("benefits.eligibility.verify.Client.verify")


@pytest.mark.django_db
def test_get_verified_types_error(mocker, first_agency, first_verifier, mock_api_client_verify, form):
def test_eligibility_from_api_error(mocker, first_agency, first_verifier, mock_api_client_verify, form):
api_errors = {"name": "Name error"}
api_response = mocker.Mock(error=api_errors)
mock_api_client_verify.return_value = api_response

response = first_verifier.get_verified_types(form, first_agency)
response = eligibility_from_api(first_verifier, form, first_agency)

assert response is None
form.add_api_errors.assert_called_once_with(api_errors)


@pytest.mark.django_db
def test_get_verified_types_verified_types(mocker, first_agency, first_verifier, mock_api_client_verify, form):
def test_eligibility_from_api_verified_types(mocker, first_agency, first_verifier, mock_api_client_verify, form):
verified_types = ["type1", "type2"]
api_response = mocker.Mock(eligibility=verified_types, error=None)
mock_api_client_verify.return_value = api_response

response = first_verifier.get_verified_types(form, first_agency)
response = eligibility_from_api(first_verifier, form, first_agency)

assert response == verified_types
form.add_api_errors.assert_not_called()


@pytest.mark.django_db
def test_get_verified_types_no_verified_types(mocker, first_agency, first_verifier, mock_api_client_verify, form):
def test_eligibility_from_api_no_verified_types(mocker, first_agency, first_verifier, mock_api_client_verify, form):
verified_types = []
api_response = mocker.Mock(eligibility=verified_types, error=None)
mock_api_client_verify.return_value = api_response

response = first_verifier.get_verified_types(form, first_agency)
response = eligibility_from_api(first_verifier, form, first_agency)

assert response == verified_types
form.add_api_errors.assert_not_called()
6 changes: 3 additions & 3 deletions tests/pytest/eligibility/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def test_confirm_post_recaptcha_fail(mocker, client, invalid_form_data):
@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_eligibility_auth_request")
def test_confirm_post_valid_form_eligibility_error(mocker, client, form_data, mocked_analytics_module):
mocker.patch("benefits.core.models.EligibilityVerifier.get_verified_types", return_value=None)
mocker.patch("benefits.eligibility.verify.eligibility_from_api", return_value=None)

path = reverse(ROUTE_CONFIRM)
response = client.post(path, form_data)
Expand All @@ -243,7 +243,7 @@ def test_confirm_post_valid_form_eligibility_error(mocker, client, form_data, mo
@pytest.mark.django_db
@pytest.mark.usefixtures("mocked_eligibility_auth_request")
def test_confirm_post_valid_form_eligibility_unverified(mocker, client, form_data, mocked_analytics_module):
mocker.patch("benefits.core.models.EligibilityVerifier.get_verified_types", return_value=[])
mocker.patch("benefits.eligibility.verify.eligibility_from_api", return_value=[])

path = reverse(ROUTE_CONFIRM)
response = client.post(path, form_data)
Expand All @@ -261,7 +261,7 @@ def test_confirm_post_valid_form_eligibility_verified(
# mocked_session_eligibility is a fixture that mocks benefits.core.session.eligibility(request)
# call it here, passing a None request, to get the return value from the mock
eligibility = mocked_session_eligibility(None)
mocker.patch("benefits.core.models.EligibilityVerifier.get_verified_types", return_value=[eligibility])
mocker.patch("benefits.eligibility.verify.eligibility_from_api", return_value=[eligibility])

path = reverse(ROUTE_CONFIRM)
response = client.post(path, form_data)
Expand Down

0 comments on commit 808f673

Please sign in to comment.