Net-SPID

 view release on metacpan or  search on metacpan

lib/Net/SPID/SAML/IdP.pm  view on Meta::CPAN

package Net::SPID::SAML::IdP;
$Net::SPID::SAML::IdP::VERSION = '0.15';
use Moo;

has '_spid' => (is => 'ro', required => 1, weak_ref => 1);  # Net::SPID::SAML

has 'xml'           => (is => 'ro', required => 1);
has 'entityID'      => (is => 'ro', required => 1);
has 'cert'          => (is => 'ro', required => 1);
has 'sso_urls'      => (is => 'ro', default => sub { {} });
has 'sloreq_urls'   => (is => 'ro', default => sub { {} });
has 'slores_urls'   => (is => 'ro', default => sub { {} });

use Carp;
use Crypt::OpenSSL::X509;
use Mojo::XMLSig;
use XML::XPath;

sub new_from_xml {
    my ($class, %args) = @_;

    my $xpath = XML::XPath->new(xml => $args{xml});
    $xpath->set_namespace('md', 'urn:oasis:names:tc:SAML:2.0:metadata');
    $xpath->set_namespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
    
    if ($xpath->findnodes('/md:EntityDescriptor/dsig:Signature')->size > 0) {
        # TODO: validate certificate against a known CA
        Mojo::XMLSig::verify($args{xml})
            or croak "Signature verification failed";
    }
    
    $args{entityID} = $xpath->findvalue('/md:EntityDescriptor/@entityID')->value;
    
    $args{sso_urls} //= {};
    for my $sso ($xpath->findnodes('/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService')){
        my $binding = $sso->getAttribute('Binding');
        $args{sso_urls}{$binding} = $sso->getAttribute('Location');
    }

    $args{sloreq_urls} //= {};
    $args{slores_urls} //= {};
    for my $slo ($xpath->findnodes('/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService')) {
        my $binding = $slo->getAttribute('Binding');
        $args{sloreq_urls}{$binding} = $slo->getAttribute('Location');
        $args{slores_urls}{$binding} = $slo->getAttribute('Location') // $slo->getAttribute('ResponseLocation');
    }
    
    for my $certnode ($xpath->findnodes('/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor[@use="signing"]/ds:KeyInfo/ds:X509Data/ds:X509Certificate/text()')) {
        my $cert = $certnode->getValue;
        
        # rewrap the base64 data from the metadata; it may not
        # be wrapped at 64 characters as PEM requires
        $cert =~ s/\s//g;
        $cert = join "\n", $cert =~ /.{1,64}/gs;
        
        # form a PEM certificate
        $args{cert} = Crypt::OpenSSL::X509->new_from_string(
            "-----BEGIN CERTIFICATE-----\n$cert\n-----END CERTIFICATE-----\n",
            Crypt::OpenSSL::X509::FORMAT_PEM,
        );
    }

    return $class->new(%args);
}

sub authnrequest {
    my ($self, %args) = @_;
    
    return Net::SPID::SAML::Out::AuthnRequest->new(
        _spid       => $self->_spid,
        _idp        => $self,
        %args,
    );
}

sub logoutrequest {
    my ($self, %args) = @_;
    
    return Net::SPID::SAML::Out::LogoutRequest->new(
        _spid       => $self->_spid,
        _idp        => $self,
        %args,
    );
}

sub logoutresponse {



( run in 0.842 second using v1.01-cache-2.11-cpan-71847e10f99 )