Net-SPID

 view release on metacpan or  search on metacpan

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

    my $classref = $_[0]->xpath->findvalue('/samlp:Response/saml:Assertion/saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef')->value
        or return undef;
    $classref =~ /SpidL(\d)$/ or return undef;
    return $1;
});

has 'attributes' => (is => 'lazy', builder => sub {
    return {
        map { $_->getAttribute('Name') => $_->findnodes("*[local-name()='AttributeValue']")->[0]->string_value }
            $_[0]->xpath->findnodes("/samlp:Response/saml:Assertion/saml:AttributeStatement/saml:Attribute"),
    }
});

use Carp;
use Crypt::OpenSSL::RSA;
use DateTime;
use DateTime::Format::XSD;
use Mojo::XMLSig;

sub validate {
    my ($self, %args) = @_;
    
    $self->SUPER::validate(%args) or return 0;
    
    my $xpath = $self->xpath;
    
    # TODO: validate IssueInstant
    
    croak "Missing 'in_response_to' argument for validate()"
        if !defined $args{in_response_to};
    
    croak sprintf "Invalid InResponseTo: '%s' (expected: '%s')",
        $self->InResponseTo, $args{in_response_to}
        if $self->InResponseTo ne $args{in_response_to};
    
    croak sprintf "Invalid Destination: '%s'", $self->Destination
        if !grep { $_ eq $self->Destination } @{$self->_spid->sp_assertionconsumerservice};
    
    if ($self->success) {
        # We expect to have an <Assertion> element
        
        croak sprintf "Response/Issuer (%s) does not match Assertion/Issuer (%s)",
            $self->Issuer, $self->Assertion_Issuer
            if $self->Issuer ne $self->Assertion_Issuer;
    
        croak sprintf "Invalid Audience: '%s' (expected: '%s')",
            $self->Assertion_Audience, $self->_spid->sp_entityid
            if $self->Assertion_Audience ne $self->_spid->sp_entityid;
    
        croak sprintf "Invalid InResponseTo: '%s' (expected: '%s')",
            $self->Assertion_InResponseTo, $args{in_response_to}
            if $self->Assertion_InResponseTo ne $args{in_response_to};
    
        # this validates all the signatures in the given XML, and requires that at least one exists
        my $pubkey = Crypt::OpenSSL::RSA->new_public_key($self->_idp->cert->pubkey);
        Mojo::XMLSig::verify($self->xml, $pubkey)
            or croak "Signature verification failed";
    
        # SPID regulations require that Assertion is signed, while Response can be not signed
        croak "Response/Assertion is not signed"
            if $xpath->findnodes('/samlp:Response/saml:Assertion/dsig:Signature')->size == 0;
    
        my $now = DateTime->now;
    
        # exact match is ok
        croak sprintf "Invalid NotBefore: '%s' (now: '%s')",
            $self->NotBefore->iso8601, $now->iso8601
            if DateTime->compare($now, $self->NotBefore) < 0;
    
        # exact match is *not* ok
        croak sprintf "Invalid NotOnOrAfter: '%s' (now: '%s')",
            $self->NotOnOrAfter->iso8601, $now->iso8601
            if DateTime->compare($now, $self->NotOnOrAfter) > -1;
    
        # exact match is *not* ok
        croak sprintf "Invalid SubjectConfirmationData/NotOnOrAfter: '%s' (now: '%s')",
            $self->SubjectConfirmationData_NotOnOrAfter->iso8601, $now->iso8601
            if DateTime->compare($now, $self->SubjectConfirmationData_NotOnOrAfter) > -1;
    
        croak "Invalid SubjectConfirmationData/\@Recipient'"
            if !grep { $_ eq $self->Assertion_Recipient } @{$self->_spid->sp_assertionconsumerservice};
    
        croak "Mismatch between Destination and SubjectConfirmationData/\@Recipient"
            if $self->Destination ne $self->Assertion_Recipient;
    } else {
        # Authentication failed, so we expect no <Assertion> element.
    }
    
    return 1;
}

sub success {
    my ($self) = @_;
    
    return $self->StatusCode eq 'urn:oasis:names:tc:SAML:2.0:status:Success';
}

sub spid_session {
    my ($self) = @_;
    
    return undef if !$self->success;
    
    return Net::SPID::Session->new(
        idp_id          => $self->Issuer,
        nameid          => $self->NameID,
        session_index   => $self->SessionIndex,
        attributes      => $self->attributes,
        level           => $self->spid_level,
        assertion_xml   => $self->xml,
    );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME



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