Net-SAML2
view release on metacpan or search on metacpan
lib/Net/SAML2/Object/Response.pm view on Meta::CPAN
has assertions => (
is => 'ro',
isa => 'XML::LibXML::NodeList',
required => 0,
predicate => 'has_assertions',
);
has 'cacert' => (
isa => 'Str',
is => 'ro',
required => 0);
has 'insecure_trust_embedded_cert' => (
isa => 'Bool',
is => 'ro',
default => 0,
);
has 'require_signed_response' => (
isa => 'Bool',
is => 'ro',
default => 0,
);
# BUILDARGS
around BUILDARGS => sub {
my $orig = shift;
my $self = shift;
my %params = @_;
unless ($params{cacert}
|| $params{insecure_trust_embedded_cert}) {
croak(
"Net::SAML2::Object::Response->new() requires 'cacert' "
. "on the object to verify SAML response signatures. "
. "To explicitly disable signature verification (test/dev only) "
. ", pass insecure_trust_embedded_cert => 1 to new()."
);
}
return $self->$orig(%params);
};
sub new_from_xml {
my $self = shift;
my %args = @_;
my $xml = no_comments($args{xml});
my $destination = delete $args{destination};
my $cacert = delete $args{cacert};
my $insecure_trust_embedded_cert = delete $args{insecure_trust_embedded_cert};
# The default may change in the future
my $require_signed_response = delete $args{require_signed_response} // 0;
my $xpath = XML::LibXML::XPathContext->new($xml);
$xpath->registerNs('saml', URN_ASSERTION);
$xpath->registerNs('samlp', URN_PROTOCOL);
$xpath->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
my $actual_destination = $xpath->findvalue(
'/samlp:Response/@Destination | /samlp:ArtifactResponse/@Destination');
if (defined $destination && $destination ne $actual_destination) {
croak(sprintf("Response Destination (%s) does not match expected value (%s)",
$actual_destination,
$destination)
);
}
my $response = $xpath->findnodes('/samlp:Response|/samlp:ArtifactResponse');
croak("Unable to parse response") unless $response->size;
$response = $response->get_node(1);
# require_signed_response only applies to front-channel Responses.
#
# ArtifactResponse is retrieved by the SP over a mutually authenticated
# TLS back-channel (the SP initiates the connection to the IdP's known
# artifact resolution endpoint), so transport already authenticates it
# and it is exempt from this check.
my $is_artifact = $response->nodePath eq '/samlp:ArtifactResponse';
if ($require_signed_response && !$is_artifact) {
my $signature = $xpath->findnodes('/samlp:Response/dsig:Signature', $response);
# FIXME: should check that a Response-level Signature cryptographically
# validates and it actually covers this Response - Assertion verification
# happens downstream.
if ($signature->size != 1) {
croak "Net::SAML2::Object::Response requires that the Response "
. "include exactly one Signature. Pass `require_signed_response` = 0 "
. "to allow unsigned Responses";
}
}
my $code_path = 'samlp:Status/samlp:StatusCode';
if ($is_artifact) {
$code_path = "samlp:Response/$code_path";
}
my $status = $xpath->findnodes($code_path, $response);
croak("Unable to parse status from response") unless $status->size;
my $status_node = $status->get_node(1);
$status = $status_node->getAttribute('Value');
my $substatus = $xpath->findvalue('samlp:StatusCode/@Value', $status_node);
my $nodes = $xpath->findnodes('//saml:EncryptedAssertion|//saml:Assertion', $response);
return $self->new(
dom => $xml,
status => $status,
$substatus ? ( substatus => $substatus) : (),
issuer => $xpath->findvalue('saml:Issuer', $response),
id => $response->getAttribute('ID'),
in_response_to => $response->getAttribute('InResponseTo'),
$nodes->size ? (assertions => $nodes) : (),
$cacert ? (cacert => $cacert) : (),
$insecure_trust_embedded_cert ? (insecure_trust_embedded_cert => $insecure_trust_embedded_cert) : (),
require_signed_response => $require_signed_response,
);
}
sub to_string {
my $self = shift;
return $self->_dom->toString;
}
sub to_assertion {
my $self = shift;
my %args = @_;
if (!$self->has_assertions) {
croak("There are no assertions found in the response object");
}
# Propagate the trust configuration from the Response so the Assertion
# inherits the same trust anchor. Without this the caller would have to
# supply cacert (or insecure_trust_embedded_cert) a second time, and a
# Response built with a trust anchor could silently produce an Assertion
# built with none. Caller-supplied %args still override these defaults.
return Net::SAML2::Protocol::Assertion->new_from_xml(
( run in 0.443 second using v1.01-cache-2.11-cpan-5fbc6bb55f2 )