providers/saml: optionally verify SAML Signature
This commit is contained in:
parent
75bb59a22a
commit
fff05e35ac
|
@ -25,6 +25,7 @@ class SAMLProviderSerializer(ModelSerializer):
|
|||
"digest_algorithm",
|
||||
"signature_algorithm",
|
||||
"signing_kp",
|
||||
"require_signing",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class SAMLProviderForm(forms.ModelForm):
|
|||
"session_valid_not_on_or_after",
|
||||
"property_mappings",
|
||||
"digest_algorithm",
|
||||
"require_signing",
|
||||
"signature_algorithm",
|
||||
"signing_kp",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.0.3 on 2020-05-06 15:51
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_crypto", "0001_initial"),
|
||||
("passbook_providers_saml", "0008_auto_20200305_1606"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="require_signing",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Singing Keypair`.",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="samlprovider",
|
||||
name="issuer",
|
||||
field=models.TextField(help_text="Also known as EntityID"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="samlprovider",
|
||||
name="signing_kp",
|
||||
field=models.ForeignKey(
|
||||
default=None,
|
||||
help_text="Singing is enabled upon selection of a Key Pair.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="passbook_crypto.CertificateKeyPair",
|
||||
verbose_name="Signing Keypair",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -25,7 +25,7 @@ class SAMLProvider(Provider):
|
|||
|
||||
acs_url = models.URLField(verbose_name=_("ACS URL"))
|
||||
audience = models.TextField(default="")
|
||||
issuer = models.TextField()
|
||||
issuer = models.TextField(help_text=_("Also known as EntityID"))
|
||||
|
||||
assertion_valid_not_before = models.TextField(
|
||||
default="minutes=-5",
|
||||
|
@ -81,6 +81,15 @@ class SAMLProvider(Provider):
|
|||
null=True,
|
||||
help_text=_("Singing is enabled upon selection of a Key Pair."),
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Signing Keypair"),
|
||||
)
|
||||
|
||||
require_signing = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_(
|
||||
"Require Requests to be signed by an X509 Certificate. "
|
||||
"Must match the Certificate selected in `Singing Keypair`."
|
||||
),
|
||||
)
|
||||
|
||||
form = "passbook.providers.saml.forms.SAMLProviderForm"
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
"""Basic SAML Processor"""
|
||||
from typing import TYPE_CHECKING, Dict, List, Union
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from defusedxml import ElementTree
|
||||
from django.http import HttpRequest
|
||||
from signxml import XMLVerifier
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.exceptions import PropertyMappingExpressionException
|
||||
|
@ -146,6 +148,15 @@ class Processor:
|
|||
"""Parses various parameters from _request_xml into _request_params."""
|
||||
decoded_xml = decode_base64_and_inflate(self._saml_request)
|
||||
|
||||
if self._remote.require_signing and self._remote.signing_kp:
|
||||
self._logger.debug("Verifying Request signature")
|
||||
try:
|
||||
XMLVerifier().verify(
|
||||
decoded_xml, x509_cert=self._remote.signing_kp.certificate_data
|
||||
)
|
||||
except InvalidSignature as exc:
|
||||
raise CannotHandleAssertion("Failed to verify signature") from exc
|
||||
|
||||
root = ElementTree.fromstring(decoded_xml)
|
||||
|
||||
params = {}
|
||||
|
|
Reference in a new issue