diff --git a/passbook/providers/saml/api.py b/passbook/providers/saml/api.py index f2023eae7..2a95287df 100644 --- a/passbook/providers/saml/api.py +++ b/passbook/providers/saml/api.py @@ -25,6 +25,7 @@ class SAMLProviderSerializer(ModelSerializer): "signature_algorithm", "signing_kp", "require_signing", + "verification_kp", ] diff --git a/passbook/providers/saml/forms.py b/passbook/providers/saml/forms.py index e1247575a..dff79a24e 100644 --- a/passbook/providers/saml/forms.py +++ b/passbook/providers/saml/forms.py @@ -34,11 +34,12 @@ class SAMLProviderForm(forms.ModelForm): "assertion_valid_not_before", "assertion_valid_not_on_or_after", "session_valid_not_on_or_after", - "property_mappings", "digest_algorithm", "require_signing", "signature_algorithm", "signing_kp", + "verification_kp", + "property_mappings", ] widgets = { "name": forms.TextInput(), diff --git a/passbook/providers/saml/migrations/0007_samlprovider_verification_kp.py b/passbook/providers/saml/migrations/0007_samlprovider_verification_kp.py new file mode 100644 index 000000000..9e87edb14 --- /dev/null +++ b/passbook/providers/saml/migrations/0007_samlprovider_verification_kp.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1.3 on 2020-11-08 21:22 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_crypto", "0002_create_self_signed_kp"), + ("passbook_providers_saml", "0006_remove_samlprovider_name"), + ] + + operations = [ + migrations.AddField( + model_name="samlprovider", + name="verification_kp", + field=models.ForeignKey( + default=None, + help_text="If selected, incoming assertion's Signatures will be validated.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="passbook_crypto.certificatekeypair", + verbose_name="Verification Keypair", + ), + ), + ] diff --git a/passbook/providers/saml/models.py b/passbook/providers/saml/models.py index 5e5ad8b6a..e0ff57286 100644 --- a/passbook/providers/saml/models.py +++ b/passbook/providers/saml/models.py @@ -87,6 +87,15 @@ class SAMLProvider(Provider): default="rsa-sha256", ) + verification_kp = models.ForeignKey( + CertificateKeyPair, + default=None, + null=True, + help_text=_("If selected, incoming assertion's Signatures will be validated."), + on_delete=models.SET_NULL, + verbose_name=_("Verification Keypair"), + related_name="+", + ) signing_kp = models.ForeignKey( CertificateKeyPair, default=None, diff --git a/passbook/providers/saml/processors/request_parser.py b/passbook/providers/saml/processors/request_parser.py index a4454d40c..c7df85544 100644 --- a/passbook/providers/saml/processors/request_parser.py +++ b/passbook/providers/saml/processors/request_parser.py @@ -69,10 +69,11 @@ class AuthNRequestParser: """Validate and parse raw request with enveloped signautre.""" decoded_xml = decode_base64_and_inflate(saml_request) - if self.provider.signing_kp: + if self.provider.verification_kp: try: XMLVerifier().verify( - decoded_xml, x509_cert=self.provider.signing_kp.certificate_data + decoded_xml, + x509_cert=self.provider.verification_kp.certificate_data, ) except InvalidSignature as exc: raise CannotHandleAssertion("Failed to verify signature") from exc @@ -98,7 +99,11 @@ class AuthNRequestParser: querystring += f"RelayState={quote_plus(relay_state)}&" querystring += f"SigAlg={sig_alg}" - public_key = self.provider.signing_kp.private_key.public_key() + if not self.provider.verification_kp: + raise CannotHandleAssertion( + "Provider does not have a Validation Certificate configured." + ) + public_key = self.provider.verification_kp.private_key.public_key() try: public_key.verify( b64decode(signature),