From 619a751abe0b0538cc1a8ee67f4cdd6fdd142e7c Mon Sep 17 00:00:00 2001 From: Chris Andrews Date: Nov 12 2010 16:04:32 +0000 Subject: Roundtrip testing for SOAP binding. --- diff --git a/lib/Net/SAML2/Binding/SOAP.pm b/lib/Net/SAML2/Binding/SOAP.pm index b2321da..8f17398 100644 --- a/lib/Net/SAML2/Binding/SOAP.pm +++ b/lib/Net/SAML2/Binding/SOAP.pm @@ -54,16 +54,40 @@ sub new { return $self; } -=head2 request($req) +=head2 request($message) -Submit the request to the IdP's service. +Submit the message to the IdP's service. Returns the Response, or dies if there was an error. =cut sub request { - my ($self, $request) = @_; + my ($self, $message) = @_; + my $request = $self->create_request($message); + + my $soap_action = 'http://www.oasis-open.org/committees/security'; + + my $req = POST $self->{url}; + $req->header('SOAPAction' => $soap_action); + $req->header('Content-Type' => 'text/xml'); + $req->header('Content-Length' => length $request); + $req->content($request); + + my $ua = $self->{ua} || LWP::UserAgent->new; + my $res = $ua->request($req); + + return $self->handle_response($res->content); +} + +=head2 create_request( $message ) + +Signs the given message, and returns it as a SOAP request. + +=cut + +sub create_request { + my ($self, $message) = @_; # sign the request my $sig = XML::Sig->new({ @@ -71,32 +95,20 @@ sub request { key => $self->{key}, cert => $self->{cert} }); - my $signed_req = $sig->sign($request); + my $signed_req = $sig->sign($message); # test verify my $ret = $sig->verify($signed_req); die "failed to sign" unless $ret; - my $soap_req = <<"SOAP"; + my $request = <<"SOAP"; $signed_req SOAP - - my $soap_action = 'http://www.oasis-open.org/committees/security'; - - my $req = POST $self->{url}; - $req->header('SOAPAction' => $soap_action); - $req->header('Content-Type' => 'text/xml'); - $req->header('Content-Length' => length $soap_req); - $req->content($soap_req); - - my $ua = $self->{ua} || LWP::UserAgent->new; - my $res = $ua->request($req); - - return $self->handle_response($res->content); + return $request; } =head2 handle_response( $response ) @@ -134,7 +146,7 @@ Accepts a string containing the complete SOAP request. sub handle_request { my ($self, $request) = @_; - + my $parser = XML::XPath->new( xml => $request ); $parser->set_namespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/'); $parser->set_namespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol'); @@ -160,7 +172,7 @@ Signs and SOAP-wraps the given message. =cut sub create_response { - my ($self, $message) = @_; + my ($self, $response) = @_; # sign the response my $sig = XML::Sig->new({ @@ -168,7 +180,7 @@ sub create_response { key => $self->{key}, cert => $self->{cert} }); - my $signed_res = $sig->sign($message); + my $signed_res = $sig->sign($response); # test verify my $ret = $sig->verify($signed_res); diff --git a/lib/Net/SAML2/IdP.pm b/lib/Net/SAML2/IdP.pm index 0d7254f..a454ac7 100644 --- a/lib/Net/SAML2/IdP.pm +++ b/lib/Net/SAML2/IdP.pm @@ -151,4 +151,42 @@ sub entityID { return $self->{entityID}; } +=head2 metadata + +Returns IdP metadata for this instance + +=cut + +sub metadata { + my ($self) = @_; + + return <<"METADATA"; + + + + + + + +$self->{cert} + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + + + +METADATA +} + 1; diff --git a/lib/Net/SAML2/Protocol/LogoutRequest.pm b/lib/Net/SAML2/Protocol/LogoutRequest.pm index 0c7d058..bd19855 100644 --- a/lib/Net/SAML2/Protocol/LogoutRequest.pm +++ b/lib/Net/SAML2/Protocol/LogoutRequest.pm @@ -57,10 +57,10 @@ sub new_from_xml { $xpath->set_namespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol'); $self->{id} = $xpath->findvalue('/samlp:LogoutRequest/@ID')->value; - $self->{destination} = $xpath->findvalue('/samlp:LogoutRequest/@Destination')->value; $self->{session} = $xpath->findvalue('/samlp:LogoutRequest/samlp:SessionIndex')->value; $self->{issuer} = $xpath->findvalue('/samlp:LogoutRequest/saml:Issuer')->value; $self->{nameid} = $xpath->findvalue('/samlp:LogoutRequest/saml:NameID')->value; + $self->{destination} = $xpath->findvalue('/samlp:LogoutRequest/saml:NameID/@NameQualifier')->value; return $self; } diff --git a/t/04-response.t b/t/04-response.t index 0398911..1b9dec4 100644 --- a/t/04-response.t +++ b/t/04-response.t @@ -81,6 +81,6 @@ my $assertion = Net::SAML2::Protocol::Assertion->new( xml => $xml, ); ok($assertion); -diag Dumper { assertion => $assertion }; +#diag Dumper { assertion => $assertion }; done_testing; diff --git a/t/05-soap-binding.t b/t/05-soap-binding.t new file mode 100644 index 0000000..547614d --- /dev/null +++ b/t/05-soap-binding.t @@ -0,0 +1,55 @@ +use Test::More; +use strict; +use warnings; +use Net::SAML2; +use MIME::Base64; +use Data::Dumper; +use File::Slurp; +use LWP::UserAgent; + +my $sp = Net::SAML2::SP->new( + id => 'http://localhost:3000', + url => 'http://localhost:3000', + cert => 't/sign-nopw-cert.pem', + cacert => 't/cacert.pem', + org_name => 'Test', + org_display_name => 'Test', + org_contact => 'test@example.com', +); +ok($sp); + +my $metadata = read_file('t/idp-metadata.xml'); +ok($metadata); +my $idp = Net::SAML2::IdP->new($metadata); +ok($idp); +my $slo_url = $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:SOAP'); +ok($slo_url); +my $idp_cert = $idp->cert('signing'); +ok($idp_cert); + +my $nameid = 'user-to-log-out'; +my $session = 'session-to-log-out'; + +my $request = $sp->logout_request( + $idp->entityID, $nameid, $session, +)->as_xml; +ok($request); + +my $ua = LWP::UserAgent->new; # not used +my $soap = $sp->soap_binding($ua, $slo_url, $idp_cert); +ok($soap); + +my $soap_req = $soap->create_request($request); +ok($soap_req); + +my ($subject, $xml) = $soap->handle_request($soap_req); +ok($subject); +ok($xml); + +my $soaped_request = Net::SAML2::Protocol::LogoutRequest->new_from_xml( + xml => $xml +); +ok($soaped_request); +ok($request eq $soaped_request->as_xml); + +done_testing; diff --git a/t/idp-metadata.xml b/t/idp-metadata.xml new file mode 100644 index 0000000..df68039 --- /dev/null +++ b/t/idp-metadata.xml @@ -0,0 +1,51 @@ + + + + + + + +MIIDFTCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQUFADA3MQswCQYDVQQGEwJVUzEO +MAwGA1UECgwFbG9jYWwxCzAJBgNVBAsMAmN0MQswCQYDVQQDDAJDQTAeFw0xMDEw +MDYxMjM4MTRaFw0xMTEwMDYxMjM4MTRaMFcxCzAJBgNVBAYTAlVTMQ4wDAYDVQQK +DAVsb2NhbDELMAkGA1UECwwCY3QxDTALBgNVBAMMBHNhbWwxHDAaBgkqhkiG9w0B +CQEWDXNhbWxAY3QubG9jYWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMhu +pJZpvu1m6ys+IrWrm3pK+onwRAYCyrgQ0RyK2cHbVLFbjBqTjKnt+PiVbnZPZUTs +tkV9oijZGQvaMy9ingJursICUQzmOfYRDm4s9gFJJOHUGYnItRhp4uj3EoWWyX8I +6Mr+g3/vNgNFvD5S9L7Hk1mSw8SnPlblZAWlFUwXAgMBAAGjgY8wgYwwDAYDVR0T +AQH/BAIwADAxBglghkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVyYXRlZCBD +ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUGy/iPd7PVObrF+lK4+ZShcbStLYwCwYDVR0P +BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDANBgkqhkiG9w0B +AQUFAAOCAQEAYoYq3Rc6jC7f8DnKxDHntHxH91F5mfp8Y3j7ALcRG/mrzkMhvxU2 +O2qmh4aHzZBoY1EU9VjrVgyPJPAjFQVC+OjIE46Gavh5wobzYmVGeFLOa9NhPv50 +h3EOw1eCda3VwcvStWw1OhT8cpEGqgJJVAcjwcm4VBtWjodxRn3E4zBr/xxzR1HU +ISvnu1/xomsSS+aenG5toWmhoJIKFbfhQkpnBlgGD5+12Cxn2jHpgv15262ZZIJS +WPp/0bQqdAAUzkJZPpUGUN1sTXPJexYT6na7XvLd6mvO1g+WDk6aZnW/zcT3T9tL +Iavyic/p4gZtXckweq+VTn9CdZp6ZTQtVw== + + + + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + + + + + + + + + diff --git a/t/sign-nopw-cert.pem b/t/sign-nopw-cert.pem index d8fa75b..72d3799 100644 --- a/t/sign-nopw-cert.pem +++ b/t/sign-nopw-cert.pem @@ -1,34 +1,34 @@ ------BEGIN CERTIFICATE----- -MIIDKjCCApOgAwIBAgIEZ4SpojANBgkqhkiG9w0BAQQFADCBqjEMMAoGA1UEAxMD -ZGV2MT4wPAYDVQQLFDVTU08gRGVwdCBaWElEIEF1dG8tQ2VydCBodHRwOi8vZGV2 -L2NnaS1iaW4venhpZGhsby5wbDErMCkGA1UEChQiVW5zcGVjaWZpZWQgT1JHX05B -TUUgY29uZiB2YXJpYWJsZTEPMA0GA1UEBxQGTGlzYm9hMQ8wDQYDVQQIFAZMaXNi -b2ExCzAJBgNVBAYUAlBUMB4XDTcwMDEwMTAwMDAwMFoXDTM4MDExOTAzMTQwN1ow -gaoxDDAKBgNVBAMTA2RldjE+MDwGA1UECxQ1U1NPIERlcHQgWlhJRCBBdXRvLUNl -cnQgaHR0cDovL2Rldi9jZ2ktYmluL3p4aWRobG8ucGwxKzApBgNVBAoUIlVuc3Bl -Y2lmaWVkIE9SR19OQU1FIGNvbmYgdmFyaWFibGUxDzANBgNVBAcUBkxpc2JvYTEP -MA0GA1UECBQGTGlzYm9hMQswCQYDVQQGFAJQVDCBnzANBgkqhkiG9w0BAQEFAAOB -jQAwgYkCgYEAw1UsriZr4p3wcSl6xoJ/45FL1loKjRBMcmCt49hUnSEk79isADK6 -Wb/OOzDOwe3Fsu4O3vXB5iPwWmc6pZrrM1gfml8oT5xEbaDLrE3/SWiEuPcpED+L -nOiOLfLYKTO5MyXHrcNhGsqLrGFkmEAACECiR/l7+Co0BfNrMOgwiAMCAwEAAaNb -MFkwDwYDVR0TBAgwBgEB/wIBAzARBglghkgBhvhCAQEEBAMCAPcwCwYDVR0PBAQD -AgH+MCYGCWCGSAGG+EIBDQQZFhdBdXRvLUNlcnQsIHNlZSB6eGlkLm9yZzANBgkq -hkiG9w0BAQQFAAOBgQB6ItQUkq4Ehd6j9JVBkX7hD8zDk7cahTU6u3edZmRr3CAa -yYKMcLPSuy2sNzV4EmpB+MU6vd98VFrkNV1g3UXgjj7IXmaOudMIUtJEd7oLWc8n -RUDLIiCOatwFV5KeI+B0MjtSyPWFbSz6zg2MSpxKPZcMAeMdIRsIKZ+v3AQHuw== ------END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDDVSyuJmvinfBxKXrGgn/jkUvWWgqNEExyYK3j2FSdISTv2KwA -MrpZv847MM7B7cWy7g7e9cHmI/BaZzqlmuszWB+aXyhPnERtoMusTf9JaIS49ykQ -P4uc6I4t8tgpM7kzJcetw2EayousYWSYQAAIQKJH+Xv4KjQF82sw6DCIAwIDAQAB -AoGBAKA1I8hSSvo7gBHQa5qVPj9v1Dui7jR0wb6t/PF7ZSrbHGh71dbzxFQINSXn -Ci2UzynKhvfnqXkQ0PnU4Q0i2vR5LEpYgVXoFVZMsEF3OHpn0CrpT3ZLnTLKu+HD -llQy6GD87BIN3sNLrl6lWYXIWmqG2BFrc4iLr5MXZ3UTCI2BAkEA9nb8gMwj5i00 -2Z13/VycHV04aUMmhXp9qVnEnbUjQzfrAmoffZ6Kp4ZDBD4cdSGt9la9vLPZwcDH -quRf0oNDKwJBAMrjxUe6yGnfmieBp3VbAFonX/iRv/3erx3CKdvmNxVw6v6JL6ig -wxDqT41FXlTefctUm1P2KesPO/bJvRuhwokCQHX1QKhMC6pwXGkCipkCJ92N7h/C -+b7MfzP8OLPaK8RSEPIY5HV02ZuQEKcJKwmMUvwhS7HyyfeHUoihwJK6K/0CQQCF -EFlEsAXdGh9aJbiUSTiz5/sTZVPLxo6HL96QMC6US1hz07mEGNuWbnX6KzKw6rMJ -9dVhfaMEXRtwweLbCn4xAkB6qnB9EtworFnFhpJYNdAeIMnOURo3dzq+qlRk/CVj -7uYnih/0DXJPG9/8hQjUAe12htA43Tvarhlv1+iiJoNT +MIICXgIBAAKBgQDIbqSWab7tZusrPiK1q5t6SvqJ8EQGAsq4ENEcitnB21SxW4wa +k4yp7fj4lW52T2VE7LZFfaIo2RkL2jMvYp4Cbq7CAlEM5jn2EQ5uLPYBSSTh1BmJ +yLUYaeLo9xKFlsl/COjK/oN/7zYDRbw+UvS+x5NZksPEpz5W5WQFpRVMFwIDAQAB +AoGBAIEdlXU3xcDVLeV7b4ysccoEBwVY+9kLb1/HpY3HfKO/EP33xXdRWaoWHO62 +BKS+XLRb8McvOYkq4VGbTKHX24T78kyhNVhAmjwr67wEaC+Hn2UePiczG8tq9xOt +JzEgitHvoEONO/wAePUl6lfpg/WPaBZVzKan4/UB1BhIINiRAkEA/UKgAeDWQUS1 +bBieGftuJWnr0KKJO8gHgsxuG4b1nr+4PjHGtUWT3ytBGTqMhvaphHOMGdn97x5z +a11QLuT0KwJBAMqZt3n7B0ZJnnlf2zTTVJlcmrUjN2hkUy/pHnsW9Fmd2csBN8t1 +PYt1Q8aEZmaeiS5T4TqPQOzQoTaQ8ypJtcUCQFNeyVmU8DMQMUDiYblf4YSDd9N4 +jgCV+pL+RQjexqB5sGcY77bgrqr6jDWH5pbPQEKP3wDrmAJNXeVsFGc7i30CQQCI +tRynBIouFXMHt3S0unz1VxVVcMPsfvXa+FSG/kA3UForr1C0yxGSTrhC9gga6pLq +XdM2rn9CzKxbo4dRW5d5AkEAgIOgX9GpuSdahBvyPcXxRzISs0lTVZn0VbdMAu4d +LrcRD1q0xzby/7HzISQZuw4LCk5Z6FbMqAp/6iIv5A7j/g== -----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDFTCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQUFADA3MQswCQYDVQQGEwJVUzEO +MAwGA1UECgwFbG9jYWwxCzAJBgNVBAsMAmN0MQswCQYDVQQDDAJDQTAeFw0xMDEw +MDYxMjM4MTRaFw0xMTEwMDYxMjM4MTRaMFcxCzAJBgNVBAYTAlVTMQ4wDAYDVQQK +DAVsb2NhbDELMAkGA1UECwwCY3QxDTALBgNVBAMMBHNhbWwxHDAaBgkqhkiG9w0B +CQEWDXNhbWxAY3QubG9jYWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMhu +pJZpvu1m6ys+IrWrm3pK+onwRAYCyrgQ0RyK2cHbVLFbjBqTjKnt+PiVbnZPZUTs +tkV9oijZGQvaMy9ingJursICUQzmOfYRDm4s9gFJJOHUGYnItRhp4uj3EoWWyX8I +6Mr+g3/vNgNFvD5S9L7Hk1mSw8SnPlblZAWlFUwXAgMBAAGjgY8wgYwwDAYDVR0T +AQH/BAIwADAxBglghkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVyYXRlZCBD +ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUGy/iPd7PVObrF+lK4+ZShcbStLYwCwYDVR0P +BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDANBgkqhkiG9w0B +AQUFAAOCAQEAYoYq3Rc6jC7f8DnKxDHntHxH91F5mfp8Y3j7ALcRG/mrzkMhvxU2 +O2qmh4aHzZBoY1EU9VjrVgyPJPAjFQVC+OjIE46Gavh5wobzYmVGeFLOa9NhPv50 +h3EOw1eCda3VwcvStWw1OhT8cpEGqgJJVAcjwcm4VBtWjodxRn3E4zBr/xxzR1HU +ISvnu1/xomsSS+aenG5toWmhoJIKFbfhQkpnBlgGD5+12Cxn2jHpgv15262ZZIJS +WPp/0bQqdAAUzkJZPpUGUN1sTXPJexYT6na7XvLd6mvO1g+WDk6aZnW/zcT3T9tL +Iavyic/p4gZtXckweq+VTn9CdZp6ZTQtVw== +-----END CERTIFICATE-----