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

Refactor: rename EligibilityVerifier eligibility fields #2272

Merged
merged 9 commits into from
Aug 6, 2024
22 changes: 11 additions & 11 deletions benefits/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,26 @@ class SortableEligibilityVerifierAdmin(SortableAdminMixin, admin.ModelAdmin): #
def get_exclude(self, request, obj=None):
if not request.user.is_superuser:
return [
"api_auth_header",
"api_auth_key_secret_name",
"public_key",
"jwe_cek_enc",
"jwe_encryption_alg",
"jws_signing_alg",
"form_class",
"eligibility_api_auth_header",
"eligibility_api_auth_key_secret_name",
"eligibility_api_public_key",
"eligibility_api_jwe_cek_enc",
"eligibility_api_jwe_encryption_alg",
"eligibility_api_jws_signing_alg",
"eligibility_form_class",
]
else:
return super().get_exclude(request, obj)

def get_readonly_fields(self, request, obj=None):
if not request.user.is_superuser:
return [
"api_url",
"claims_provider",
"selection_label_template",
"start_template",
"unverified_template",
"eligibility_api_url",
"eligibility_start_template",
"eligibility_unverified_template",
"help_template",
"selection_label_template",
]
else:
return super().get_readonly_fields(request, obj)
Expand Down
68 changes: 68 additions & 0 deletions benefits/core/migrations/0018_rename_eligibility_api_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Generated by Django 5.0.7 on 2024-08-01 21:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0017_refactor_authprovider_claimsprovider"),
]

operations = [
migrations.RenameField(
model_name="eligibilityverifier",
old_name="api_auth_header",
new_name="eligibility_api_auth_header",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="api_auth_key_secret_name",
new_name="eligibility_api_auth_key_secret_name",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="jwe_cek_enc",
new_name="eligibility_api_jwe_cek_enc",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="jwe_encryption_alg",
new_name="eligibility_api_jwe_encryption_alg",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="jws_signing_alg",
new_name="eligibility_api_jws_signing_alg",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="public_key",
new_name="eligibility_api_public_key",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="api_url",
new_name="eligibility_api_url",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="form_class",
new_name="eligibility_form_class",
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="start_template",
new_name="eligibility_start_template",
),
migrations.AlterField(
model_name="eligibilityverifier",
name="eligibility_start_template",
field=models.TextField(default="eligibility/start.html"),
),
migrations.RenameField(
model_name="eligibilityverifier",
old_name="unverified_template",
new_name="eligibility_unverified_template",
),
]
51 changes: 13 additions & 38 deletions benefits/core/migrations/local_fixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,10 @@
"name": "(CST) oauth claims via Login.gov",
"display_order": 1,
"active": true,
"api_url": null,
"api_auth_header": null,
"api_auth_key_secret_name": null,
"eligibility_type": 1,
"public_key": null,
"jwe_cek_enc": null,
"jwe_encryption_alg": null,
"jws_signing_alg": null,
"claims_provider": 1,
"selection_label_template": "eligibility/includes/selection-label--senior.html",
"start_template": "eligibility/start--senior.html",
"form_class": null
"eligibility_start_template": "eligibility/start--senior.html"
}
},
{
Expand All @@ -141,18 +133,10 @@
"name": "(CST) VA.gov - veteran",
"display_order": 3,
"active": true,
"api_url": null,
"api_auth_header": null,
"api_auth_key_secret_name": null,
"eligibility_type": 2,
"public_key": null,
"jwe_cek_enc": null,
"jwe_encryption_alg": null,
"jws_signing_alg": null,
"claims_provider": 2,
"selection_label_template": "eligibility/includes/selection-label--veteran.html",
"start_template": "eligibility/start--veteran.html",
"form_class": null
"eligibility_start_template": "eligibility/start--veteran.html"
}
},
{
Expand All @@ -162,19 +146,18 @@
"name": "(CST) eligibility server verifier",
"display_order": 4,
"active": true,
"api_url": "http://server:8000/verify",
"api_auth_header": "X-Server-API-Key",
"api_auth_key_secret_name": "agency-card-verifier-api-auth-key",
"eligibility_api_url": "http://server:8000/verify",
"eligibility_api_auth_header": "X-Server-API-Key",
"eligibility_api_auth_key_secret_name": "agency-card-verifier-api-auth-key",
"eligibility_type": 3,
"public_key": 1,
"jwe_cek_enc": "A256CBC-HS512",
"jwe_encryption_alg": "RSA-OAEP",
"jws_signing_alg": "RS256",
"claims_provider": null,
"eligibility_api_public_key": 1,
"eligibility_api_jwe_cek_enc": "A256CBC-HS512",
"eligibility_api_jwe_encryption_alg": "RSA-OAEP",
"eligibility_api_jws_signing_alg": "RS256",
"selection_label_template": "eligibility/includes/selection-label--cst-agency-card.html",
"start_template": "eligibility/start--cst-agency-card.html",
"form_class": "benefits.eligibility.forms.CSTAgencyCard",
"unverified_template": "eligibility/unverified--cst-agency-card.html",
"eligibility_start_template": "eligibility/start--cst-agency-card.html",
"eligibility_form_class": "benefits.eligibility.forms.CSTAgencyCard",
"eligibility_unverified_template": "eligibility/unverified--cst-agency-card.html",
"help_template": "core/includes/help--cst-agency-card.html"
}
},
Expand All @@ -185,18 +168,10 @@
"name": "(CST) CalFresh oauth claims via Login.gov",
"display_order": 2,
"active": true,
"api_url": null,
"api_auth_header": null,
"api_auth_key_secret_name": null,
"eligibility_type": 4,
"public_key": null,
"jwe_cek_enc": null,
"jwe_encryption_alg": null,
"jws_signing_alg": null,
"claims_provider": 3,
"selection_label_template": "eligibility/includes/selection-label--calfresh.html",
"start_template": "eligibility/start--calfresh.html",
"form_class": null,
"eligibility_start_template": "eligibility/start--calfresh.html",
"help_template": "core/includes/help--calfresh.html"
}
},
Expand Down
36 changes: 18 additions & 18 deletions benefits/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,24 +175,24 @@ class EligibilityVerifier(models.Model):
name = models.TextField()
display_order = models.PositiveSmallIntegerField(default=0, blank=False, null=False)
active = models.BooleanField(default=False)
api_url = models.TextField(null=True, blank=True)
api_auth_header = models.TextField(null=True, blank=True)
api_auth_key_secret_name = SecretNameField(null=True, blank=True)
eligibility_api_url = models.TextField(null=True, blank=True)
eligibility_api_auth_header = models.TextField(null=True, blank=True)
eligibility_api_auth_key_secret_name = SecretNameField(null=True, blank=True)
eligibility_type = models.ForeignKey(EligibilityType, on_delete=models.PROTECT)
# public key is used to encrypt requests targeted at this Verifier and to verify signed responses from this verifier
public_key = models.ForeignKey(PemData, related_name="+", on_delete=models.PROTECT, null=True, blank=True)
eligibility_api_public_key = models.ForeignKey(PemData, related_name="+", on_delete=models.PROTECT, null=True, blank=True)
# The JWE-compatible Content Encryption Key (CEK) key-length and mode
jwe_cek_enc = models.TextField(null=True, blank=True)
eligibility_api_jwe_cek_enc = models.TextField(null=True, blank=True)
# The JWE-compatible encryption algorithm
jwe_encryption_alg = models.TextField(null=True, blank=True)
eligibility_api_jwe_encryption_alg = models.TextField(null=True, blank=True)
# The JWS-compatible signing algorithm
jws_signing_alg = models.TextField(null=True, blank=True)
eligibility_api_jws_signing_alg = models.TextField(null=True, blank=True)
claims_provider = models.ForeignKey(ClaimsProvider, on_delete=models.PROTECT, null=True, blank=True)
selection_label_template = models.TextField()
start_template = models.TextField(null=True, blank=True)
eligibility_start_template = models.TextField(default="eligibility/start.html")
# reference to a form class used by this Verifier, e.g. benefits.app.forms.FormClass
form_class = models.TextField(null=True, blank=True)
unverified_template = models.TextField(default="eligibility/unverified.html")
eligibility_form_class = models.TextField(null=True, blank=True)
eligibility_unverified_template = models.TextField(default="eligibility/unverified.html")
help_template = models.TextField(null=True, blank=True)

class Meta:
Expand All @@ -202,29 +202,29 @@ def __str__(self):
return self.name

@property
def api_auth_key(self):
if self.api_auth_key_secret_name is not None:
return get_secret_by_name(self.api_auth_key_secret_name)
def eligibility_api_auth_key(self):
if self.eligibility_api_auth_key_secret_name is not None:
return get_secret_by_name(self.eligibility_api_auth_key_secret_name)
else:
return None

@property
def public_key_data(self):
def eligibility_api_public_key_data(self):
"""This Verifier's public key as a string."""
return self.public_key.data
return self.eligibility_api_public_key.data

@property
def uses_claims_verification(self):
"""True if this Verifier verifies via the claims provider. False otherwise."""
return self.claims_provider is not None and self.claims_provider.supports_claims_verification

def form_instance(self, *args, **kwargs):
def eligibility_form_instance(self, *args, **kwargs):
"""Return an instance of this verifier's form, or None."""
if not bool(self.form_class):
if not bool(self.eligibility_form_class):
return None

# inspired by https://stackoverflow.com/a/30941292
module_name, class_name = self.form_class.rsplit(".", 1)
module_name, class_name = self.eligibility_form_class.rsplit(".", 1)
FormClass = getattr(importlib.import_module(module_name), class_name)

return FormClass(*args, **kwargs)
Expand Down
10 changes: 5 additions & 5 deletions benefits/eligibility/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ 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},
verify_url=verifier.eligibility_api_url,
headers={verifier.eligibility_api_auth_header: verifier.eligibility_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,
jwe_encryption_alg=verifier.eligibility_api_jwe_encryption_alg,
jwe_cek_enc=verifier.eligibility_api_jwe_cek_enc,
server_public_key=verifier.eligibility_api_public_key_data,
timeout=settings.REQUESTS_TIMEOUT,
)

Expand Down
10 changes: 4 additions & 6 deletions benefits/eligibility/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
ROUTE_UNVERIFIED = "eligibility:unverified"
ROUTE_ENROLLMENT = "enrollment:index"

TEMPLATE_START = "eligibility/start.html"
TEMPLATE_CONFIRM = "eligibility/confirm.html"


Expand Down Expand Up @@ -78,9 +77,8 @@ def start(request):
session.update(request, eligibility_types=[], origin=reverse(ROUTE_START))

verifier = session.verifier(request)
template = verifier.start_template or TEMPLATE_START

return TemplateResponse(request, template)
return TemplateResponse(request, verifier.eligibility_start_template)


@decorator_from_middleware(AgencySessionRequired)
Expand Down Expand Up @@ -111,7 +109,7 @@ def confirm(request):
else:
return redirect(unverified_view)

form = verifier.form_instance()
form = verifier.eligibility_form_instance()

# GET/POST for Eligibility API verification
context = {"form": form}
Expand All @@ -124,7 +122,7 @@ def confirm(request):
elif request.method == "POST":
analytics.started_eligibility(request, types_to_verify)

form = verifier.form_instance(data=request.POST)
form = verifier.eligibility_form_instance(data=request.POST)
# form was not valid, allow for correction/resubmission
if not form.is_valid():
if recaptcha.has_error(form):
Expand Down Expand Up @@ -171,4 +169,4 @@ def unverified(request):

analytics.returned_fail(request, types_to_verify)

return TemplateResponse(request, verifier.unverified_template)
return TemplateResponse(request, verifier.eligibility_unverified_template)
2 changes: 1 addition & 1 deletion benefits/oauth/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def process_request(self, request):
if verifier.uses_claims_verification:
# all good, the chosen verifier is configured correctly
return None
elif not (verifier.api_url or verifier.form_class):
elif not (verifier.eligibility_api_url or verifier.eligibility_form_class):
# the chosen verifier doesn't have Eligibility API config OR claims provider config
# this is likely a misconfiguration on the backend, not a user error
message = f"Verifier with no API or IDP config: {verifier.name} (id={verifier.id})"
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/transit-agency.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Note that a `TransitAgency` model requires:
- HTML templates for various buttons, text and other user interface elements of the flow, including:
- `index_template`: _Required for agencies_ - Text for agency direct entry page
- `eligibility_index_template`: _Required for agencies_ - Text for Eligibility Index page
- `selection_label_template`: _Required for verifiers_ - Text and optional modals for the radio button form on the Eligibility Index page
- `eligibility_start_template`: _Required for verifiers_ - Text and optional custom styles for call to action button on the Eligibility Start page
- `enrollment_success_template`: _Required for agencies_ - Text for Enrollment Success page
- `help_template`: _Required for agencies_ - Agency-specific help questions and answers
- `selection_label_template`: _Required for verifiers_ - Text and optional modals for the radio button form on the Eligibility Index page
- `start_template`: _Required for verifiers_ - Text and optional custom styles for call to action button on the Eligibility Start page
- `sign_out_button_template`: _Required for claims providers_ - Sign out link button, used on any page after sign in
- `sign_out_link_template`: _Required for claims providers_ - Sign out link text, used on any page after sign in

Expand Down
8 changes: 4 additions & 4 deletions tests/pytest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ def model_EligibilityVerifier(model_PemData, model_EligibilityType):
verifier = EligibilityVerifier.objects.create(
name="Test Verifier",
active=True,
api_url="https://example.com/verify",
api_auth_header="X-API-AUTH",
api_auth_key_secret_name="secret-key",
eligibility_api_url="https://example.com/verify",
eligibility_api_auth_header="X-API-AUTH",
eligibility_api_auth_key_secret_name="secret-key",
eligibility_type=model_EligibilityType,
public_key=model_PemData,
eligibility_api_public_key=model_PemData,
selection_label_template="eligibility/includes/selection-label.html",
)

Expand Down
Loading
Loading