File Coverage

blib/lib/Net/SAML2/Role/VerifyXML.pm
Criterion Covered Total %
statement 48 51 94.1
branch 15 18 83.3
condition 3 3 100.0
subroutine 11 12 91.6
pod 1 1 100.0
total 78 85 91.7


line stmt bran cond sub pod time code
1             package Net::SAML2::Role::VerifyXML;
2 25     25   17469 use Moose::Role;
  25         10268  
  25         271  
3             our $VERSION = '0.73'; # VERSION
4              
5 25     25   152277 use Net::SAML2::XML::Sig;
  25         78  
  25         182  
6 25     25   1342 use Crypt::OpenSSL::Verify;
  25         107  
  25         706  
7 25     25   153 use Crypt::OpenSSL::X509;
  25         74  
  25         1119  
8 25     25   179 use Carp qw(croak);
  25         86  
  25         1354  
9 25     25   184 use List::Util qw(none);
  25         88  
  25         1665  
10 25     25   207 use Try::Tiny;
  25         85  
  25         12476  
11              
12             # ABSTRACT: A role to verify the SAML response XML
13              
14              
15              
16              
17             sub verify_xml {
18 10     10 1 21 my $self = shift;
19 10         22 my $xml = shift;
20 10         38 my %args = @_;
21              
22 10         26 my $cacert = delete $args{cacert};
23 10         22 my $anchors = delete $args{anchors};
24              
25 10         358 my $x = Net::SAML2::XML::Sig->new({
26             x509 => 1,
27             exclusive => 1,
28             %args,
29             });
30              
31 10 100       757 croak("XML signature check failed") unless $x->verify($xml);
32              
33 9 100 100     23292 if (!$anchors && !$cacert) {
34 1         37 return 1;
35             }
36              
37 8 50       58 my $cert = $x->signer_cert
38             or die "Certificate not provided in SAML Response, cannot validate\n";
39              
40 8 100       147 if ($cacert) {
41 4         1716 my $ca = Crypt::OpenSSL::Verify->new($cacert, { strict_certs => 0 });
42 4     4   602 try { $ca->verify($cert) }
43             catch {
44 0     0   0 croak("Could not verify CA certificate: $_");
45 4         81 };
46             }
47              
48 8 100       1229 return 1 if !$anchors;
49              
50 4 50       13 if (ref $anchors ne 'HASH') {
51 0         0 croak("Unable to verify anchor trust");
52             }
53              
54 4         13 my ($key) = keys %$anchors;
55 4 50   7   20 if (none { $key eq $_ } qw(subject issuer issuer_hash)) {
  7         16  
56 0         0 croak("Unable to verify anchor trust, requires subject, issuer or issuer_hash");
57             }
58              
59 4         28 my $got = $cert->$key;
60 4         74 my $want = $anchors->{$key};
61 4 100       10 if (!ref $want) {
62 2         5 $want = [ $want ];
63             }
64              
65 4 100   4   17 if (none { $_ eq $got } @$want) {
  4         11  
66 1         19 croak("Could not verify trust anchors of certificate!");
67             }
68 3         16 return 1;
69              
70             }
71              
72              
73             1;
74              
75             __END__
76              
77             =pod
78              
79             =encoding UTF-8
80              
81             =head1 NAME
82              
83             Net::SAML2::Role::VerifyXML - A role to verify the SAML response XML
84              
85             =head1 VERSION
86              
87             version 0.73
88              
89             =head1 SYNOPSIS
90              
91             use Net::SAML2::Some::Module;
92              
93             use Moose;
94             with 'Net::SAML2::Role::VerifyXML';
95              
96             sub do_something_with_xml {
97             my $self = shift;
98             my $xml = shift;
99              
100             $self->verify_xml($xml,
101             # Most of these options are passed to Net::SAML2::XML::Sig, except for the
102             # cacert
103             # Most options are optional
104             cacert => $self->cacert,
105             cert_text => $self->cert,
106             no_xml_declaration => 1,
107             );
108             }
109              
110             =head1 DESCRIPTION
111              
112             =head1 METHODS
113              
114             =head2 verify_xml($xml, %args)
115              
116             $self->verify_xml($xml,
117             # Most of these options are passed to Net::SAML2::XML::Sig, except for the
118             # cacert
119             # Most options are optional
120             cert_text => $self->cert,
121             no_xml_declaration => 1,
122              
123             # Used for a trust model, if lacking, everything is trusted
124             cacert => $self->cacert,
125             # or check specific certificates based on subject/issuer or issuer hash
126             anchors => {
127             # one of the following is allowed
128             subject => ["subject a", "subject b"],
129             issuer => ["Issuer A", "Issuer B"],
130             issuer_hash => ["Issuer A hash", "Issuer B hash"],
131             },
132             );
133              
134             =head1 AUTHORS
135              
136             =over 4
137              
138             =item *
139              
140             Chris Andrews <chrisa@cpan.org>
141              
142             =item *
143              
144             Timothy Legge <timlegge@gmail.com>
145              
146             =back
147              
148             =head1 COPYRIGHT AND LICENSE
149              
150             This software is copyright (c) 2023 by Venda Ltd, see the CONTRIBUTORS file for others.
151              
152             This is free software; you can redistribute it and/or modify it under
153             the same terms as the Perl 5 programming language system itself.
154              
155             =cut