Net-SAML2
view release on metacpan or search on metacpan
lib/Net/SAML2/Protocol/Assertion.pm view on Meta::CPAN
package Net::SAML2::Protocol::Assertion;
use Moose;
our $VERSION = '0.85'; # VERSION
use MooseX::Types::DateTime qw/ DateTime /;
use MooseX::Types::Common::String qw/ NonEmptySimpleStr /;
use DateTime;
use DateTime::HiRes;
use DateTime::Format::XSD;
use Net::SAML2::XML::Util qw/ no_comments /;
use Net::SAML2::XML::Sig;
use XML::Enc;
use XML::LibXML::XPathContext;
use List::Util qw(first);
use URN::OASIS::SAML2 qw(STATUS_SUCCESS);
use Carp qw(croak);
with 'Net::SAML2::Role::ProtocolMessage';
# ABSTRACT: SAML2 assertion object
has 'attributes' => (isa => 'HashRef[ArrayRef]', is => 'ro', required => 1);
has 'audience' => (isa => NonEmptySimpleStr, is => 'ro', required => 1);
has 'not_after' => (isa => DateTime, is => 'ro', required => 1);
has 'not_before' => (isa => DateTime, is => 'ro', required => 1);
has 'session' => (isa => 'Str', is => 'ro', required => 1);
has 'in_response_to' => (isa => 'Str', is => 'ro', required => 1);
has 'response_status' => (isa => 'Str', is => 'ro', required => 1);
has 'response_substatus' => (isa => 'Str', is => 'ro');
has 'xpath' => (isa => 'XML::LibXML::XPathContext', is => 'ro', required => 1);
has 'nameid_object' => (
isa => 'XML::LibXML::Element',
is => 'ro',
required => 0,
init_arg => 'nameid',
predicate => 'has_nameid',
);
has 'authnstatement_object' => (
isa => 'XML::LibXML::Element',
is => 'ro',
required => 0,
init_arg => 'authnstatement',
predicate => 'has_authnstatement',
);
sub _verify_encrypted_assertion {
my $self = shift;
my $xml = shift;
my $cacert = shift;
my $key_file = shift;
my $key_name = shift;
my $xpath = XML::LibXML::XPathContext->new($xml);
$xpath->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
$xpath->registerNs('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
$xpath->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
$xpath->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
return $xml unless $xpath->exists('//saml:EncryptedAssertion');
croak "Encrypted Assertions require key_file" if !defined $key_file;
$xml = $self->_decrypt(
$xml,
key_file => $key_file,
key_name => $key_name,
);
$xpath->setContextNode($xml);
my $assert_nodes = $xpath->findnodes('//saml:Assertion');
return $xml unless $assert_nodes->size;
my $assert = $assert_nodes->get_node(1);
return $xml unless $xpath->exists('dsig:Signature', $assert);
my $xml_opts->{ no_xml_declaration } = 1;
my $x = Net::SAML2::XML::Sig->new($xml_opts);
my $ret = $x->verify($assert->toString());
die "Decrypted Assertion signature check failed" unless $ret;
return $xml unless $cacert;
my $cert = $x->signer_cert;
die "Certificate not provided in SAML Response, cannot validate" unless $cert;
my $ca = Crypt::OpenSSL::Verify->new($cacert, { strict_certs => 0 });
die "Unable to verify signer cert with cacert: " . $cert->subject
unless $ca->verify($cert);
return $xml;
}
sub new_from_xml {
my($class, %args) = @_;
my $key_file = $args{key_file};
my $cacert = delete $args{cacert};
my $xpath = XML::LibXML::XPathContext->new();
$xpath->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
$xpath->registerNs('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
$xpath->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
$xpath->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
my $xml = no_comments($args{xml});
$xpath->setContextNode($xml);
$xml = $class->_verify_encrypted_assertion(
$xml,
$cacert,
$key_file,
$args{key_name},
);
my $dec = $class->_decrypt(
$xml,
key_file => $key_file,
key_name => $args{key_name}
);
$xpath->setContextNode($dec);
my $attributes = {};
for my $node ($xpath->findnodes('//saml:Assertion/saml:AttributeStatement/saml:Attribute/saml:AttributeValue/..'))
{
my @values = $xpath->findnodes("saml:AttributeValue", $node);
$attributes->{$node->getAttribute('Name')} = [map $_->string_value, @values];
}
my $xpath_base = '//samlp:Response/saml:Assertion/saml:Conditions/';
my $not_before;
if (my $value = $xpath->findvalue($xpath_base . '@NotBefore')) {
$not_before = DateTime::Format::XSD->parse_datetime($value);
}
elsif (my $global = $xpath->findvalue('//saml:Conditions/@NotBefore')) {
$not_before = DateTime::Format::XSD->parse_datetime($global);
}
else {
$not_before = DateTime::HiRes->now();
}
my $not_after;
if (my $value = $xpath->findvalue($xpath_base . '@NotOnOrAfter')) {
$not_after = DateTime::Format::XSD->parse_datetime($value);
}
elsif (my $global = $xpath->findvalue('//saml:Conditions/@NotOnOrAfter')) {
$not_after = DateTime::Format::XSD->parse_datetime($global);
}
else {
$not_after = DateTime->from_epoch(epoch => time() + 1000);
}
my $nameid;
if (my $node = $xpath->findnodes('/samlp:Response/saml:Assertion/saml:Subject/saml:NameID')) {
$nameid = $node->get_node(1);
}
elsif (my $global = $xpath->findnodes('//saml:Subject/saml:NameID')) {
$nameid = $global->get_node(1);
}
my $authnstatement;
if (my $node = $xpath->findnodes('/samlp:Response/saml:Assertion/saml:AuthnStatement')) {
( run in 1.487 second using v1.01-cache-2.11-cpan-ceb78f64989 )