diff --git a/.travis.yml b/.travis.yml index ae6788a..5150a12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ env: before_script: - composer require symfony/http-foundation:${SYMFONY_VERSION} --no-interaction --prefer-source + - ./src/BeSimple/SoapClient/Tests/bin/phpwebserver.sh + - ./src/BeSimple/SoapClient/Tests/bin/axis.sh script: - phpunit --coverage-text diff --git a/src/BeSimple/SoapClient/Curl.php b/src/BeSimple/SoapClient/Curl.php index 54e40cf..699e56c 100644 --- a/src/BeSimple/SoapClient/Curl.php +++ b/src/BeSimple/SoapClient/Curl.php @@ -128,7 +128,7 @@ class Curl curl_setopt($this->ch, CURLOPT_HTTPHEADER, $requestHeaders); } - $this->response = $this->execManualRedirect($this->followLocationMaxRedirects); + $this->response = $this->execManualRedirect(); return ($this->response === false) ? false : true; } diff --git a/src/BeSimple/SoapClient/SoapClient.php b/src/BeSimple/SoapClient/SoapClient.php index d0d99e8..5476488 100644 --- a/src/BeSimple/SoapClient/SoapClient.php +++ b/src/BeSimple/SoapClient/SoapClient.php @@ -15,6 +15,7 @@ namespace BeSimple\SoapClient; use BeSimple\SoapCommon\Helper; use BeSimple\SoapCommon\Converter\MtomTypeConverter; use BeSimple\SoapCommon\Converter\SwaTypeConverter; +use BeSimple\SoapCommon\SoapMessage; /** * Extended SoapClient that uses a a cURL wrapper for all underlying HTTP @@ -40,6 +41,15 @@ class SoapClient extends \SoapClient */ protected $tracingEnabled = false; + /** + * Work around missing header/php://input access in PHP cli webserver by + * setting headers additionally as GET parameters and SOAP request body + * explicitly as POST variable. + * + * @var boolean + */ + private $cliWebserverWorkaround = false; + /** * cURL instance. * @@ -98,6 +108,10 @@ class SoapClient extends \SoapClient if (isset($options['soap_version'])) { $this->soapVersion = $options['soap_version']; } + // activate cli webserver workaround + if (isset($options['cli_webserver_workaround'])) { + $this->cliWebserverWorkaround = $options['cli_webserver_workaround']; + } $this->curl = new Curl($options); $wsdlFile = $this->loadWsdl($wsdl, $options); // TODO $wsdlHandler = new WsdlHandler($wsdlFile, $this->soapVersion); @@ -124,14 +138,45 @@ class SoapClient extends \SoapClient private function __doHttpRequest(SoapRequest $soapRequest) { // HTTP headers - $headers = array( - 'Content-Type:' . $soapRequest->getContentType(), - 'SOAPAction: "' . $soapRequest->getAction() . '"', - ); + $soapVersion = $soapRequest->getVersion(); + $soapAction = $soapRequest->getAction(); + if (SOAP_1_1 == $soapVersion) { + $headers = array( + 'Content-Type:' . $soapRequest->getContentType(), + 'SOAPAction: "' . $soapAction . '"', + ); + } else { + $headers = array( + 'Content-Type:' . $soapRequest->getContentType() . '; action="' . $soapAction . '"', + ); + } + + $location = $soapRequest->getLocation(); + $content = $soapRequest->getContent(); + /* + * Work around missing header/php://input access in PHP cli webserver by + * setting headers additionally as GET parameters and SOAP request body + * explicitly as POST variable + */ + if ($this->cliWebserverWorkaround === true) { + if (strpos($location, '?') === false) { + $location .= '?'; + } else { + $location .= '&'; + } + $location .= SoapMessage::CONTENT_TYPE_HEADER.'='.urlencode($soapRequest->getContentType()); + $location .= '&'; + $location .= SoapMessage::SOAP_ACTION_HEADER.'='.urlencode($soapRequest->getAction()); + + $content = http_build_query(array('request' => $content)); + + $headers = array(); + } + // execute HTTP request with cURL $responseSuccessfull = $this->curl->exec( - $soapRequest->getLocation(), - $soapRequest->getContent(), + $location, + $content, $headers ); // tracing enabled: store last request header and body diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/AttachmentRequest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/AttachmentRequest.php new file mode 100644 index 0000000..e4f0236 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/AttachmentRequest.php @@ -0,0 +1,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA.wsdl b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/SwA.wsdl similarity index 98% rename from src/BeSimple/SoapClient/Tests/AxisInterop/SwA.wsdl rename to src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/SwA.wsdl index a4de7e0..d63fe2d 100644 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA.wsdl +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/SwA.wsdl @@ -1,162 +1,162 @@ - - - BeSimpleSwaService - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + BeSimpleSwaService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEnc.wsdl b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/WsSecuritySigEnc.wsdl similarity index 100% rename from src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEnc.wsdl rename to src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/WsSecuritySigEnc.wsdl diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPass.wsdl b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/WsSecurityUserPass.wsdl similarity index 100% rename from src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPass.wsdl rename to src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/WsSecurityUserPass.wsdl diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/addBook.php b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/addBook.php new file mode 100644 index 0000000..7a171b0 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/Fixtures/addBook.php @@ -0,0 +1,11 @@ + SOAP_1_1, - 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 - 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders - 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM, - 'cache_wsdl' => WSDL_CACHE_NONE, - 'classmap' => array( - 'base64Binary' => 'base64Binary', - 'AttachmentRequest' => 'AttachmentRequest', - ), -); - -/* - * Deploy "axis_services/sample-mtom.aar" to Apache Axis2 to get this - * example to work. - * - * Apache Axis2 MTOM example. - * - */ -$sc = new BeSimpleSoapClient('MTOM.wsdl', $options); - -//var_dump($sc->__getFunctions()); -//var_dump($sc->__getTypes()); - -try { - $b64 = new base64Binary(); - $b64->_ = 'This is a test. :)'; - $b64->contentType = 'text/plain'; - - $attachment = new AttachmentRequest(); - $attachment->fileName = 'test123.txt'; - $attachment->binaryData = $b64; - - var_dump($sc->attachment($attachment)); - -} catch (Exception $e) { - var_dump($e); -} - -// var_dump( -// $sc->__getLastRequestHeaders(), -// $sc->__getLastRequest(), -// $sc->__getLastResponseHeaders(), -// $sc->__getLastResponse() -// ); \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/MtomAxisInteropTest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/MtomAxisInteropTest.php new file mode 100644 index 0000000..27d9e20 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/MtomAxisInteropTest.php @@ -0,0 +1,51 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'base64Binary' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\base64Binary', + 'AttachmentRequest' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\AttachmentRequest', + ), + ); + + public function testAttachment() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/MTOM.wsdl', $this->options); + + $b64 = new base64Binary(); + $b64->_ = 'This is a test. :)'; + $b64->contentType = 'text/plain'; + + $attachment = new AttachmentRequest(); + $attachment->fileName = 'test123.txt'; + $attachment->binaryData = $b64; + + $this->assertEquals('File saved succesfully.', $sc->attachment($attachment)); + +// $fileCreatedByServer = __DIR__.'/'.$attachment->fileName; +// $this->assertEquals($b64->_, file_get_contents($fileCreatedByServer)); +// unlink($fileCreatedByServer); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA.php b/src/BeSimple/SoapClient/Tests/AxisInterop/SwA.php deleted file mode 100644 index eaf1bc6..0000000 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA.php +++ /dev/null @@ -1,84 +0,0 @@ - SOAP_1_1, - 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 - 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders - 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA, - 'cache_wsdl' => WSDL_CACHE_NONE, -); - -/* - * Deploy "axis_services/besimple-swa.aar" to Apache Axis2 to get this - * example to work. - * - * Run ant to rebuild aar. - * - * Example based on: - * http://axis.apache.org/axis2/java/core/docs/mtom-guide.html#a3 - * http://wso2.org/library/1675 - * - * Doesn't work directly with ?wsdl served by Apache Axis! - * - */ - -$sc = new BeSimpleSoapClient('SwA.wsdl', $options); - -//var_dump($sc->__getFunctions()); -//var_dump($sc->__getTypes()); - -try { - $file = new stdClass(); - $file->name = 'upload.txt'; - $file->data = 'This is a test text!'; - $result = $sc->uploadFile($file); - - var_dump( - $result->return - ); - - $file = new stdClass(); - $file->name = 'upload.txt'; - $result = $sc->downloadFile($file); - - var_dump( - $result->data - ); - - $file = new stdClass(); - $file->name = 'image.jpg'; // source: http://www.freeimageslive.com/galleries/light/pics/swirl3768.jpg - $file->data = file_get_contents('image.jpg'); - $result = $sc->uploadFile($file); - - var_dump( - $result->return - ); - - $crc32 = crc32($file->data); - - $file = new stdClass(); - $file->name = 'image.jpg'; - $result = $sc->downloadFile($file); - - file_put_contents('image2.jpg', $result->data); - - - var_dump( - crc32($result->data) === $crc32 - ); - -} catch (Exception $e) { - var_dump($e); -} - -// var_dump( -// $sc->__getLastRequestHeaders(), -// $sc->__getLastRequest(), -// $sc->__getLastResponseHeaders(), -// $sc->__getLastResponse() -// ); \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/build.xml b/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/build.xml index f5261ed..836bda8 100644 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/build.xml +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/build.xml @@ -1,38 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/resources/META-INF/services.xml b/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/resources/META-INF/services.xml index 8b49f87..8bac2eb 100644 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/resources/META-INF/services.xml +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/SwA/resources/META-INF/services.xml @@ -1,15 +1,15 @@ - - + + BeSimple test service for SwA. - true - besimple.service.BeSimpleSwaService - - urn:uploadFile - - - - urn:downloadFile - - - - + true + besimple.service.BeSimpleSwaService + + urn:uploadFile + + + + urn:downloadFile + + + + diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/SwaAxisInteropTest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/SwaAxisInteropTest.php new file mode 100644 index 0000000..4b9921f --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/SwaAxisInteropTest.php @@ -0,0 +1,77 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'downloadFile' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\downloadFile', + 'downloadFileResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\downloadFileResponse', + 'uploadFile' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\uploadFile', + 'uploadFileResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\uploadFileResponse', + ), + ); + + public function testUploadDownloadText() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/SwA.wsdl', $this->options); + + $upload = new uploadFile(); + $upload->name = 'upload.txt'; + $upload->data = 'This is a test. :)'; + $result = $sc->uploadFile($upload); + + $this->assertEquals('File saved succesfully.', $result->return); + + $download = new downloadFile(); + $download->name = 'upload.txt'; + $result = $sc->downloadFile($download); + + $this->assertEquals($upload->data, $result->data); + } + + public function testUploadDownloadImage() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/SwA.wsdl', $this->options); + + $upload = new uploadFile(); + $upload->name = 'image.jpg'; + $upload->data = file_get_contents(__DIR__.'/Fixtures/image.jpg'); // source: http://www.freeimageslive.com/galleries/light/pics/swirl3768.jpg; + $result = $sc->uploadFile($upload); + + $this->assertEquals('File saved succesfully.', $result->return); + + $download = new downloadFile(); + $download->name = 'image.jpg'; + $result = $sc->downloadFile($download); + + $this->assertEquals($upload->data, $result->data); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/TestCase.php b/src/BeSimple/SoapClient/Tests/AxisInterop/TestCase.php new file mode 100644 index 0000000..eb57435 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/TestCase.php @@ -0,0 +1,23 @@ +markTestSkipped( + 'The Axis server is not started on port 8080.' + ); + } + + curl_close($ch); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressing.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressing.php deleted file mode 100644 index dacabcb..0000000 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressing.php +++ /dev/null @@ -1,73 +0,0 @@ - SOAP_1_2, - 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 - 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders -); - -/* - * Deploy "axis_services/version2.aar" to Apache Axis2 to get this example to - * work. - * - * To rebuild the "axis_services/version2.aar" the following steps need to be - * done to build a working Apache Axis2 version service with SOAP session - * enabled. - * - * 1) Go to $AXIS_HOME/samples/version and edit the following files: - * - * resources/META-INF/services.xml: - * - * ... - * - * - * build.xml: - * replace version.aar with version2.aar - * - * 2) Run ant build.xml in "$AXIS_HOME/samples/version" - * - */ - -$sc = new BeSimpleSoapClient('http://localhost:8080/axis2/services/Version2?wsdl', $options); -$soapKernel = $sc->getSoapKernel(); -$wsaFilter = new BeSimpleWsAddressingFilter(); -$soapKernel->registerFilter($wsaFilter); - -//var_dump($sc->__getFunctions()); -//var_dump($sc->__getTypes()); - -try { - $wsaFilter->setReplyTo(BeSimpleWsAddressingFilter::ENDPOINT_REFERENCE_ANONYMOUS); - $wsaFilter->setMessageId(); - - var_dump($sc->getVersion()); - - $soapSessionId1 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId'); - echo 'ID1: ' .$soapSessionId1 . PHP_EOL; - - $wsaFilter->addReferenceParameter('http://ws.apache.org/namespaces/axis2', 'axis2', 'ServiceGroupId', $soapSessionId1); - - var_dump($sc->getVersion()); - - $soapSessionId2 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId'); - echo 'ID2: ' . $soapSessionId2 . PHP_EOL; - - if ($soapSessionId1 == $soapSessionId2) { - echo PHP_EOL; - echo 'SOAP session worked :)'; - } -} catch (Exception $e) { - var_dump($e); -} - -// var_dump( -// $sc->__getLastRequestHeaders(), -// $sc->__getLastRequest(), -// $sc->__getLastResponseHeaders(), -// $sc->__getLastResponse() -// ); \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressingAxisInteropTest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressingAxisInteropTest.php new file mode 100644 index 0000000..cf7308a --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/WsAddressingAxisInteropTest.php @@ -0,0 +1,59 @@ + + * ... + * + * + * build.xml: + * replace version.aar with version2.aar + * + * 2) Run ant build.xml in "$AXIS_HOME/samples/version" + * + */ + +use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient; +use BeSimple\SoapClient\WsAddressingFilter as BeSimpleWsAddressingFilter; + +use BeSimple\SoapClient\Tests\AxisInterop\TestCase; + +class WsAddressingAxisInteropTest extends TestCase +{ + private $options = array( + 'soap_version' => SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + ); + + public function testSession() + { + $sc = new BeSimpleSoapClient('http://localhost:8080/axis2/services/Version2?wsdl', $this->options); + $soapKernel = $sc->getSoapKernel(); + $wsaFilter = new BeSimpleWsAddressingFilter(); + $soapKernel->registerFilter($wsaFilter); + + $wsaFilter->setReplyTo(BeSimpleWsAddressingFilter::ENDPOINT_REFERENCE_ANONYMOUS); + $wsaFilter->setMessageId(); + + $version = $sc->getVersion(); + + $soapSessionId1 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId'); + + $wsaFilter->addReferenceParameter('http://ws.apache.org/namespaces/axis2', 'axis2', 'ServiceGroupId', $soapSessionId1); + + $version = $sc->getVersion(); + + $soapSessionId2 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId'); + + $this->assertEquals($soapSessionId1, $soapSessionId2); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEnc.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEnc.php deleted file mode 100644 index d6a31fb..0000000 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEnc.php +++ /dev/null @@ -1,116 +0,0 @@ -'; - -$options = array( - 'soap_version' => SOAP_1_2, - 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 - 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders -); - -/* - * Deploy "axis_services/library-signencr.aar" to Apache Axis2 to get this - * example to work. - * - * Links: - * http://www.dcc.uchile.cl/~pcamacho/tutorial/web/xmlsec/xmlsec.html - * http://www.aleksey.com/xmlsec/xmldsig-verifier.html - * - * Using code from axis example: - * http://www.ibm.com/developerworks/java/library/j-jws5/index.html - * - * Download key tool to export private key - * http://couchpotato.net/pkeytool/ - * - * keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer - * openssl x509 -out servercert.pem -outform pem -in servercert.pem -inform der - * - * keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer - * openssl x509 -out clientcert.pem -outform pem -in clientcert.pem -inform der - * java -jar pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem - * - * C:\Program Files\Java\jre6\bin\keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer - * C:\xampp\apache\bin\openssl x509 -out servercert.pem -outform pem -in servercert.cer -inform der - * - * C:\Program Files\Java\jre6\bin\keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer - * C:\xampp\apache\bin\openssl x509 -out clientcert.pem -outform pem -in clientcert.cer -inform der - * java -jar C:\axis2\pkeytool\pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem - * - * build.properties: - * server-policy=hash-policy-server.xml - * - * allows both text and digest! - */ - -class getBook {} -class getBookResponse {} -class getBooksByType {} -class getBooksByTypeResponse {} -class addBook {} -class addBookResponse {} -class BookInformation {} - -$options['classmap'] = array( - 'getBook' => 'getBook', - 'getBookResponse' => 'getBookResponse', - 'getBooksByType' => 'getBooksByType', - 'getBooksByTypeResponse' => 'getBooksByTypeResponse', - 'addBook' => 'addBook', - 'addBookResponse' => 'addBookResponse', - 'BookInformation' => 'BookInformation', -); - -$sc = new BeSimpleSoapClient('WsSecuritySigEnc.wsdl', $options); - -$wssFilter = new BeSimpleWsSecurityFilter(); -// user key for signature and encryption -$securityKeyUser = new BeSimpleWsSecurityKey(); -$securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, 'clientkey.pem', true); -$securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, 'clientcert.pem', true); -$wssFilter->setUserSecurityKeyObject($securityKeyUser); -// service key for encryption -$securityKeyService = new BeSimpleWsSecurityKey(); -$securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC); -$securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, 'servercert.pem', true); -$wssFilter->setServiceSecurityKeyObject($securityKeyService); -// TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1 -$wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN); -$wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1); - -$soapKernel = $sc->getSoapKernel(); -$soapKernel->registerFilter($wssFilter); - -//var_dump($sc->__getFunctions()); -//var_dump($sc->__getTypes()); - -try { - $gb = new getBook(); - $gb->isbn = '0061020052'; - var_dump($sc->getBook($gb)); - - $ab = new addBook(); - $ab->isbn = '0445203498'; - $ab->title = 'The Dragon Never Sleeps'; - $ab->author = 'Cook, Glen'; - $ab->type = 'scifi'; - var_dump($sc->addBook($ab)); - - // getBooksByType("scifi"); -} catch (Exception $e) { - var_dump($e); -} - -//var_dump( -// $sc->__getLastRequestHeaders(), -// $sc->__getLastRequest(), -// $sc->__getLastResponseHeaders(), -// $sc->__getLastResponse() -//); diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEncAxisInteropTest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEncAxisInteropTest.php new file mode 100644 index 0000000..6e4f0d9 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecuritySigEncAxisInteropTest.php @@ -0,0 +1,106 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\BookInformation', + ), + ); + + public function testSigEnc() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecuritySigEnc.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(); + // user key for signature and encryption + $securityKeyUser = new BeSimpleWsSecurityKey(); + $securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientkey.pem', true); + $securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientcert.pem', true); + $wssFilter->setUserSecurityKeyObject($securityKeyUser); + // service key for encryption + $securityKeyService = new BeSimpleWsSecurityKey(); + $securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC); + $securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, __DIR__.'/Fixtures/servercert.pem', true); + $wssFilter->setServiceSecurityKeyObject($securityKeyService); + // TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1 + $wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN); + $wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\AxisInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPass.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPass.php deleted file mode 100644 index 231f1e5..0000000 --- a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPass.php +++ /dev/null @@ -1,79 +0,0 @@ - SOAP_1_2, - 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 - 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders -); - -/* - * Deploy "axis_services/library-username-digest.aar" to Apache Axis2 to get - * this example to work. - * - * Using code from axis example: - * http://www.ibm.com/developerworks/java/library/j-jws4/index.html - * - * build.properties: - * server-policy=hash-policy-server.xml - * - * allows both text and digest! - */ - -class getBook {} -class getBookResponse {} -class getBooksByType {} -class getBooksByTypeResponse {} -class addBook {} -class addBookResponse {} -class BookInformation {} - -$options['classmap'] = array( - 'getBook' => 'getBook', - 'getBookResponse' => 'getBookResponse', - 'getBooksByType' => 'getBooksByType', - 'getBooksByTypeResponse' => 'getBooksByTypeResponse', - 'addBook' => 'addBook', - 'addBookResponse' => 'addBookResponse', - 'BookInformation' => 'BookInformation', -); - -$sc = new BeSimpleSoapClient('WsSecurityUserPass.wsdl', $options); - -$wssFilter = new BeSimpleWsSecurityFilter(true, 600); -$wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_TEXT); -//$wssFilter->addUserData( 'libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST ); - -$soapKernel = $sc->getSoapKernel(); -$soapKernel->registerFilter($wssFilter); - -//var_dump($sc->__getFunctions()); -//var_dump($sc->__getTypes()); - -try { - $gb = new getBook(); - $gb->isbn = '0061020052'; - var_dump($sc->getBook($gb)); - - $ab = new addBook(); - $ab->isbn = '0445203498'; - $ab->title = 'The Dragon Never Sleeps'; - $ab->author = 'Cook, Glen'; - $ab->type = 'scifi'; - var_dump($sc->addBook($ab)); - - // getBooksByType("scifi"); -} catch (Exception $e) { - var_dump($e); -} - -//var_dump( -// $sc->__getLastRequestHeaders(), -// $sc->__getLastRequest(), -// $sc->__getLastResponseHeaders(), -// $sc->__getLastResponse() -//); diff --git a/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPassAxisInteropTest.php b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPassAxisInteropTest.php new file mode 100644 index 0000000..6ab2824 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/AxisInterop/WsSecurityUserPassAxisInteropTest.php @@ -0,0 +1,96 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\AxisInterop\Fixtures\BookInformation', + ), + ); + + public function testUserPassText() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(true, 600); + $wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_TEXT); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\AxisInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } + + public function testUserPassDigest() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(true, 600); + $wssFilter->addUserData( 'libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST ); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\AxisInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/AttachmentRequest.php b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/AttachmentRequest.php new file mode 100644 index 0000000..8be17dc --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/AttachmentRequest.php @@ -0,0 +1,7 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/SwA.wsdl b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/SwA.wsdl new file mode 100644 index 0000000..46b14b7 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/SwA.wsdl @@ -0,0 +1,162 @@ + + + BeSimpleSwaService + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecuritySigEnc.wsdl b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecuritySigEnc.wsdl new file mode 100644 index 0000000..d329f1f --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecuritySigEnc.wsdl @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecurityUserPass.wsdl b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecurityUserPass.wsdl new file mode 100644 index 0000000..7d4b33f --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/WsSecurityUserPass.wsdl @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/addBook.php b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/addBook.php new file mode 100644 index 0000000..8d22133 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/Fixtures/addBook.php @@ -0,0 +1,11 @@ + SOAP_1_1, @@ -28,12 +15,14 @@ $options = array( 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM, 'cache_wsdl' => WSDL_CACHE_NONE, 'classmap' => array( - 'base64Binary' => 'base64Binary', - 'AttachmentRequest' => 'AttachmentRequest', + 'base64Binary' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\base64Binary', + 'AttachmentRequest' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\AttachmentRequest', ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. + 'connection_timeout' => 1, ); -$sc = new BeSimpleSoapClient('MTOM.wsdl', $options); +$sc = new BeSimpleSoapClient('Fixtures/MTOM.wsdl', $options); //var_dump($sc->__getFunctions()); //var_dump($sc->__getTypes()); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/MTOMServer.php b/src/BeSimple/SoapClient/Tests/ServerInterop/MTOMServer.php index 5deee38..f86bda5 100644 --- a/src/BeSimple/SoapClient/Tests/ServerInterop/MTOMServer.php +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/MTOMServer.php @@ -1,25 +1,11 @@ SOAP_1_1, @@ -27,8 +13,8 @@ $options = array( 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM, 'cache_wsdl' => WSDL_CACHE_NONE, 'classmap' => array( - 'base64Binary' => 'base64Binary', - 'AttachmentRequest' => 'AttachmentRequest', + 'base64Binary' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\base64Binary', + 'AttachmentRequest' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\AttachmentRequest', ), ); @@ -38,16 +24,12 @@ class Mtom { $b64 = $attachment->binaryData; - file_put_contents('test.txt', var_export(array( - $attachment->fileName, - $b64->_, - $b64->contentType - ), true)); + file_put_contents(__DIR__.'/'.$attachment->fileName, $b64->_); - return 'done'; + return 'File saved succesfully.'; } } -$ss = new BeSimpleSoapServer('MTOM.wsdl', $options); +$ss = new BeSimpleSoapServer(__DIR__.'/Fixtures/MTOM.wsdl', $options); $ss->setClass('Mtom'); $ss->handle(); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/MtomServerInteropTest.php b/src/BeSimple/SoapClient/Tests/ServerInterop/MtomServerInteropTest.php new file mode 100644 index 0000000..7277bed --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/MtomServerInteropTest.php @@ -0,0 +1,44 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'base64Binary' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\base64Binary', + 'AttachmentRequest' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\AttachmentRequest', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. + ); + + public function testAttachment() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/MTOM.wsdl', $this->options); + + $b64 = new base64Binary(); + $b64->_ = 'This is a test. :)'; + $b64->contentType = 'text/plain'; + + $attachment = new AttachmentRequest(); + $attachment->fileName = 'test123.txt'; + $attachment->binaryData = $b64; + + $this->assertEquals('File saved succesfully.', $sc->attachment($attachment)); + + $fileCreatedByServer = __DIR__.'/'.$attachment->fileName; + $this->assertEquals($b64->_, file_get_contents($fileCreatedByServer)); + unlink($fileCreatedByServer); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/SwAServer.php b/src/BeSimple/SoapClient/Tests/ServerInterop/SwAServer.php new file mode 100644 index 0000000..337a0a0 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/SwAServer.php @@ -0,0 +1,49 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'downloadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFile', + 'downloadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFileResponse', + 'uploadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFile', + 'uploadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFileResponse', + ), +); + +class SwA +{ + public function uploadFile(uploadFile $uploadFile) + { + file_put_contents(__DIR__.'/'.$uploadFile->name, $uploadFile->data); + + $ufr = new uploadFileResponse(); + $ufr->return = 'File saved succesfully.'; + + return $ufr; + } + + public function downloadFile(downloadFile $downloadFile) + { + $dfr = new downloadFileResponse(); + $dfr->data = file_get_contents(__DIR__.'/'.$downloadFile->name); + + return $dfr; + } +} + +$ss = new BeSimpleSoapServer(__DIR__.'/Fixtures/SwA.wsdl', $options); +$ss->setClass('SwA'); +$ss->handle(); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/SwaClient.php b/src/BeSimple/SoapClient/Tests/ServerInterop/SwaClient.php new file mode 100644 index 0000000..054fefe --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/SwaClient.php @@ -0,0 +1,53 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders + 'classmap' => array( + 'downloadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFile', + 'downloadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFileResponse', + 'uploadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFile', + 'uploadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFileResponse', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. +); + +$sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/SwA.wsdl', $options); + +try { + + $upload = new uploadFile(); + $upload->name = 'upload.txt'; + $upload->data = 'This is a test. :)'; + $result = $sc->uploadFile($upload); + + var_dump($result); + + $download = new downloadFile(); + $download->name = 'upload.txt'; + var_dump($sc->downloadFile($download)); +} catch (Exception $e) { + var_dump($e); +} + +// var_dump( +// $sc->__getLastRequestHeaders(), +// $sc->__getLastRequest(), +// $sc->__getLastResponseHeaders(), +// $sc->__getLastResponse() +// ); \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/SwaServerInteropTest.php b/src/BeSimple/SoapClient/Tests/ServerInterop/SwaServerInteropTest.php new file mode 100644 index 0000000..90f7fa0 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/SwaServerInteropTest.php @@ -0,0 +1,64 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA, + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'downloadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFile', + 'downloadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\downloadFileResponse', + 'uploadFile' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFile', + 'uploadFileResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\uploadFileResponse', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. + ); + + public function testUploadDownloadText() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/SwA.wsdl', $this->options); + + $upload = new uploadFile(); + $upload->name = 'upload.txt'; + $upload->data = 'This is a test. :)'; + $result = $sc->uploadFile($upload); + + $this->assertEquals('File saved succesfully.', $result->return); + + $download = new downloadFile(); + $download->name = 'upload.txt'; + $result = $sc->downloadFile($download); + + $this->assertEquals($upload->data, $result->data); + } + + public function testUploadDownloadImage() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/SwA.wsdl', $this->options); + + $upload = new uploadFile(); + $upload->name = 'image.jpg'; + $upload->data = file_get_contents(__DIR__.'/Fixtures/image.jpg'); // source: http://www.freeimageslive.com/galleries/light/pics/swirl3768.jpg; + $result = $sc->uploadFile($upload); + + $this->assertEquals('File saved succesfully.', $result->return); + + $download = new downloadFile(); + $download->name = 'image.jpg'; + $result = $sc->downloadFile($download); + + $this->assertEquals($upload->data, $result->data); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/TestCase.php b/src/BeSimple/SoapClient/Tests/ServerInterop/TestCase.php new file mode 100644 index 0000000..b0f1f40 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/TestCase.php @@ -0,0 +1,29 @@ +markTestSkipped( + 'The PHP cli webserver is not available with PHP 5.3.' + ); + } + + $ch = curl_init('http://localhost:8081/'); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_NOBODY, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + if (curl_exec($ch) === false) { + $this->markTestSkipped( + 'The PHP webserver is not started on port 8081.' + ); + } + + curl_close($ch); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServer.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServer.php new file mode 100644 index 0000000..c8b01c6 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServer.php @@ -0,0 +1,82 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), +); + +class WsSecuritySigEncServer +{ + public function getBook(getBook $gb) + { + $bi = new BookInformation(); + $bi->isbn = $gb->isbn; + $bi->title = 'title'; + $bi->author = 'author'; + $bi->type = 'scifi'; + + $br = new getBookResponse(); + $br->getBookReturn = $bi; + + return $br; + } + + public function addBook(addBook $ab) + { + $abr = new addBookResponse(); + $abr->addBookReturn = true; + + return $abr; + } +} + +$ss = new BeSimpleSoapServer(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $options); + +$wssFilter = new BeSimpleWsSecurityFilter(); + +// user key for signature and encryption +$securityKeyUser = new BeSimpleWsSecurityKey(); +$securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/serverkey.pem', true); +$securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/servercert.pem', true); +$wssFilter->setUserSecurityKeyObject($securityKeyUser); +// service key for encryption +$securityKeyService = new BeSimpleWsSecurityKey(); +$securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC); +$securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, __DIR__.'/Fixtures/clientcert.pem', true); +$wssFilter->setServiceSecurityKeyObject($securityKeyService); +// TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1 +$wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN); +$wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1); + +$soapKernel = $ss->getSoapKernel(); +$soapKernel->registerFilter($wssFilter); + +$ss->setClass('WsSecuritySigEncServer'); +$ss->handle(); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerClient.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerClient.php new file mode 100644 index 0000000..6c7fe1d --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerClient.php @@ -0,0 +1,83 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. +); + +$sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecuritySigEnc.wsdl', $options); + +//var_dump($sc->__getFunctions()); +//var_dump($sc->__getTypes()); + +try { + $wssFilter = new BeSimpleWsSecurityFilter(); + // user key for signature and encryption + $securityKeyUser = new BeSimpleWsSecurityKey(); + $securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientkey.pem', true); + $securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientcert.pem', true); + $wssFilter->setUserSecurityKeyObject($securityKeyUser); + // service key for encryption + $securityKeyService = new BeSimpleWsSecurityKey(); + $securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC); + $securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, __DIR__.'/Fixtures/servercert.pem', true); + $wssFilter->setServiceSecurityKeyObject($securityKeyService); + // TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1 + $wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN); + $wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + var_dump($result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + var_dump($sc->addBook($ab)); + +} catch (Exception $e) { + var_dump($e); +} + +// var_dump( +// $sc->__getLastRequestHeaders(), +// $sc->__getLastRequest(), +// $sc->__getLastResponseHeaders(), +// $sc->__getLastResponse() +// ); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerInteropTest.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerInteropTest.php new file mode 100644 index 0000000..7cbbef4 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecuritySigEncServerInteropTest.php @@ -0,0 +1,73 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. + ); + + public function testSigEnc() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecuritySigEnc.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(); + // user key for signature and encryption + $securityKeyUser = new BeSimpleWsSecurityKey(); + $securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientkey.pem', true); + $securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, __DIR__.'/Fixtures/clientcert.pem', true); + $wssFilter->setUserSecurityKeyObject($securityKeyUser); + // service key for encryption + $securityKeyService = new BeSimpleWsSecurityKey(); + $securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC); + $securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, __DIR__.'/Fixtures/servercert.pem', true); + $wssFilter->setServiceSecurityKeyObject($securityKeyService); + // TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1 + $wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN); + $wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServer.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServer.php new file mode 100644 index 0000000..8a2e2dc --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServer.php @@ -0,0 +1,78 @@ + SOAP_1_1, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'cache_wsdl' => WSDL_CACHE_NONE, + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), +); + +class Auth +{ + public static function usernamePasswordCallback($user) + { + if ($user == 'libuser') { + return 'books'; + } + + return null; + } +} + +class WsSecurityUserPassServer +{ + public function getBook(getBook $gb) + { + $bi = new BookInformation(); + $bi->isbn = $gb->isbn; + $bi->title = 'title'; + $bi->author = 'author'; + $bi->type = 'scifi'; + + $br = new getBookResponse(); + $br->getBookReturn = $bi; + + return $br; + } + + public function addBook(addBook $ab) + { + $abr = new addBookResponse(); + $abr->addBookReturn = true; + + return $abr; + } +} + +$ss = new BeSimpleSoapServer(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $options); + +$wssFilter = new BeSimpleWsSecurityFilter(); +$wssFilter->setUsernamePasswordCallback(array('Auth', 'usernamePasswordCallback')); + +$soapKernel = $ss->getSoapKernel(); +$soapKernel->registerFilter($wssFilter); + +$ss->setClass('WsSecurityUserPassServer'); +$ss->handle(); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerClient.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerClient.php new file mode 100644 index 0000000..b69b3d9 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerClient.php @@ -0,0 +1,68 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. +); + +$sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $options); + +//var_dump($sc->__getFunctions()); +//var_dump($sc->__getTypes()); + +try { + $wssFilter = new BeSimpleWsSecurityFilter(true, 600); + $wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + var_dump($result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + var_dump($sc->addBook($ab)); + +} catch (Exception $e) { + var_dump($e); +} + +// var_dump( +// $sc->__getLastRequestHeaders(), +// $sc->__getLastRequest(), +// $sc->__getLastResponseHeaders(), +// $sc->__getLastResponse() +// ); diff --git a/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerInteropTest.php b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerInteropTest.php new file mode 100644 index 0000000..4030507 --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/ServerInterop/WsSecurityUserPassServerInteropTest.php @@ -0,0 +1,86 @@ + SOAP_1_2, + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1 + 'classmap' => array( + 'getBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBook', + 'getBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBookResponse', + 'getBooksByType' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByType', + 'getBooksByTypeResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\getBooksByTypeResponse', + 'addBook' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBook', + 'addBookResponse' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\addBookResponse', + 'BookInformation' => 'BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', + ), + 'cli_webserver_workaround' => true, // Work around missing header access in PHP cli webserver by setting headers additionally as GET parameters. + ); + + public function testUserPassText() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(true, 600); + $wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_TEXT); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } + + public function testUserPassDigest() + { + $sc = new BeSimpleSoapClient(__DIR__.'/Fixtures/WsSecurityUserPass.wsdl', $this->options); + + $wssFilter = new BeSimpleWsSecurityFilter(true, 600); + $wssFilter->addUserData( 'libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST ); + + $soapKernel = $sc->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + + $gb = new getBook(); + $gb->isbn = '0061020052'; + $result = $sc->getBook($gb); + $this->assertInstanceOf('BeSimple\SoapClient\Tests\ServerInterop\Fixtures\BookInformation', $result->getBookReturn); + + $ab = new addBook(); + $ab->isbn = '0445203498'; + $ab->title = 'The Dragon Never Sleeps'; + $ab->author = 'Cook, Glen'; + $ab->type = 'scifi'; + + $this->assertTrue((bool) $sc->addBook($ab)); + + // getBooksByType("scifi"); + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/bin/axis.sh b/src/BeSimple/SoapClient/Tests/bin/axis.sh new file mode 100755 index 0000000..d143ebd --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/bin/axis.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR + +VERSION_AXIS=1.5.1 +ZIP_AXIS=axis2-$VERSION_AXIS-bin.zip +if [[ "$VERSION_AXIS" > "1.5.1" ]]; then + PATH_AXIS=http://archive.apache.org/dist/axis/axis2/java/core/$VERSION_AXIS/$ZIP_AXIS +else + PATH_AXIS=http://archive.apache.org/dist/ws/axis2/${VERSION_AXIS//./_}/$ZIP_AXIS +fi + +if [ ! -f "$DIR/$ZIP_AXIS" ]; then + curl -O -s $PATH_AXIS +fi + +VERSION_RAMPART=1.5 +ZIP_RAMPART=rampart-dist-$VERSION_RAMPART-bin.zip +PATH_RAMPART=http://archive.apache.org/dist/axis/axis2/java/rampart/$VERSION_RAMPART/$ZIP_RAMPART + +if [ ! -f "$DIR/$ZIP_RAMPART" ]; then + curl -O -s $PATH_RAMPART +fi + +unzip -qq "$DIR/$ZIP_AXIS" + +AXIS_DIR=$DIR/axis2-$VERSION_AXIS + +unzip -qq -j "$DIR/$ZIP_RAMPART" '*/lib/*.jar' -d $AXIS_DIR/lib +unzip -qq -j "$DIR/$ZIP_RAMPART" '*/modules/*.mar' -d $AXIS_DIR/repository/modules + +cp -r $DIR/../AxisInterop/axis_services/* $AXIS_DIR/repository/services + +$AXIS_DIR/bin/axis2server.sh& + +echo "Waiting until Axis is ready on port 8080" +while [[ -z `curl -s 'http://localhost:8080/axis2/services/' ` ]] +do + echo -n "." + sleep 2s +done + +echo "Axis is up" \ No newline at end of file diff --git a/src/BeSimple/SoapClient/Tests/bin/phpwebserver.sh b/src/BeSimple/SoapClient/Tests/bin/phpwebserver.sh new file mode 100755 index 0000000..0d07b8e --- /dev/null +++ b/src/BeSimple/SoapClient/Tests/bin/phpwebserver.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +PHPWEBSERVEROK="$( php -r "if (version_compare(phpversion(), '5.4', '<')) { echo 'nok'; } else { echo 'ok'; }" )" + +if [ "$PHPWEBSERVEROK" != "ok" ]; then + echo "No PHP webserver available before version 5.4..." + exit +fi + +DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR + +php -S localhost:8081 -t "$DIR/.."& + +echo "Waiting until PHP webserver is ready on port 8081" +while [[ -z `curl -s 'http://localhost:8081' ` ]] +do + echo -n "." + sleep 2s +done + +echo "PHP webserver is up" \ No newline at end of file diff --git a/src/BeSimple/SoapClient/WsSecurityFilter.php b/src/BeSimple/SoapClient/WsSecurityFilter.php index cb3fc7c..b66aae3 100644 --- a/src/BeSimple/SoapClient/WsSecurityFilter.php +++ b/src/BeSimple/SoapClient/WsSecurityFilter.php @@ -15,6 +15,7 @@ namespace BeSimple\SoapClient; use ass\XmlSecurity\DSig as XmlSecurityDSig; use ass\XmlSecurity\Enc as XmlSecurityEnc; use ass\XmlSecurity\Key as XmlSecurityKey; +use ass\XmlSecurity\Pem as XmlSecurityPem; use BeSimple\SoapCommon\FilterHelper; use BeSimple\SoapCommon\Helper; @@ -23,6 +24,7 @@ use BeSimple\SoapCommon\SoapRequestFilter; use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse; use BeSimple\SoapCommon\SoapResponseFilter; use BeSimple\SoapCommon\WsSecurityKey; +use BeSimple\SoapCommon\WsSecurityFilterClientServer; /** * This plugin implements a subset of the following standards: @@ -35,13 +37,8 @@ use BeSimple\SoapCommon\WsSecurityKey; * * @author Andreas Schamberger */ -class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter +class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapRequestFilter, SoapResponseFilter { - /* - * The date format to be used with {@link \DateTime} - */ - const DATETIME_FORMAT = 'Y-m-d\TH:i:s.000\Z'; - /** * (UT 3.1) Password type: plain text. */ @@ -52,49 +49,6 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter */ const PASSWORD_TYPE_DIGEST = 1; - /** - * (X509 3.2.1) Reference to a Subject Key Identifier - */ - const TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER = 0; - - /** - * (X509 3.2.1) Reference to a Security Token - */ - const TOKEN_REFERENCE_SECURITY_TOKEN = 1; - - /** - * (SMS_1.1 7.3) Key Identifiers - */ - const TOKEN_REFERENCE_THUMBPRINT_SHA1 = 2; - - /** - * Actor. - * - * @var string - */ - protected $actor; - - /** - * (SMS 10) Add security timestamp. - * - * @var boolean - */ - protected $addTimestamp; - - /** - * Encrypt the signature? - * - * @var boolean - */ - protected $encryptSignature; - - /** - * (SMS 10) Security timestamp expires time in seconds. - * - * @var int - */ - protected $expires; - /** * (UT 3.1) Password. * @@ -109,34 +63,6 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter */ protected $passwordType; - /** - * Sign all headers. - * - * @var boolean - */ - protected $signAllHeaders; - - /** - * (X509 3.2) Token reference type for encryption. - * - * @var int - */ - protected $tokenReferenceEncryption = null; - - /** - * (X509 3.2) Token reference type for signature. - * - * @var int - */ - protected $tokenReferenceSignature = null; - - /** - * Service WsSecurityKey. - * - * @var \BeSimple\SoapCommon\WsSecurityKey - */ - protected $serviceSecurityKey; - /** * (UT 3.1) Username. * @@ -151,20 +77,6 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter */ protected $userSecurityKey; - /** - * Constructor. - * - * @param boolean $addTimestamp (SMS 10) Add security timestamp. - * @param int $expires (SMS 10) Security timestamp expires time in seconds. - * @param string $actor SOAP actor - */ - public function __construct($addTimestamp = true, $expires = 300, $actor = null) - { - $this->addTimestamp = $addTimestamp; - $this->expires = $expires; - $this->actor = $actor; - } - /** * Add user data. * @@ -186,70 +98,10 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter */ public function resetFilter() { - $this->actor = null; - $this->addTimestamp = null; - $this->encryptSignature = null; - $this->expires = null; - $this->password = null; - $this->passwordType = null; - $this->serviceSecurityKey = null; - $this->signAllHeaders = null; - $this->tokenReferenceEncryption = null; - $this->tokenReferenceSignature = null; - $this->username = null; - $this->userSecurityKey = null; - } - - /** - * Get service security key. - * - * @param \BeSimple\SoapCommon\WsSecurityKey $serviceSecurityKey Service security key - * - * @return void - */ - public function setServiceSecurityKeyObject(WsSecurityKey $serviceSecurityKey) - { - $this->serviceSecurityKey = $serviceSecurityKey; - } - - /** - * Get user security key. - * - * @param \BeSimple\SoapCommon\WsSecurityKey $userSecurityKey User security key - * - * @return void - */ - public function setUserSecurityKeyObject(WsSecurityKey $userSecurityKey) - { - $this->userSecurityKey = $userSecurityKey; - } - - /** - * Set security options. - * - * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1 - * @param boolean $encryptSignature Encrypt signature - * - * @return void - */ - public function setSecurityOptionsEncryption($tokenReference, $encryptSignature = false) - { - $this->tokenReferenceEncryption = $tokenReference; - $this->encryptSignature = $encryptSignature; - } - - /** - * Set security options. - * - * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1 - * @param boolean $signAllHeaders Sign all headers? - * - * @return void - */ - public function setSecurityOptionsSignature($tokenReference, $signAllHeaders = false) - { - $this->tokenReferenceSignature = $tokenReference; - $this->signAllHeaders = $signAllHeaders; + parent::resetFilter(); + $this->password = null; + $this->passwordType = null; + $this->username = null; } /** @@ -390,10 +242,8 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter $security = $dom->getElementsByTagNameNS(Helper::NS_WSS, 'Security')->item(0); if (null !== $security) { // add SecurityTokenReference resolver for KeyInfo - if (null !== $this->serviceSecurityKey) { - $keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver'); - XmlSecurityDSig::addKeyInfoResolver(Helper::NS_WSS, 'SecurityTokenReference', $keyResolver); - } + $keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver'); + XmlSecurityDSig::addKeyInfoResolver(Helper::NS_WSS, 'SecurityTokenReference', $keyResolver); // do we have a reference list in header $referenceList = XmlSecurityEnc::locateReferenceList($security); // get a list of encrypted nodes @@ -420,162 +270,8 @@ class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid'); } } + + $security->parentNode->removeChild($security); } } - - /** - * Adds the configured KeyInfo to the parentNode. - * - * @param FilterHelper $filterHelper Filter helper object - * @param int $tokenReference Token reference type - * @param string $guid Unique ID - * @param \ass\XmlSecurity\Key $xmlSecurityKey XML security key - * - * @return \DOMElement - */ - protected function createKeyInfo(FilterHelper $filterHelper, $tokenReference, $guid, XmlSecurityKey $xmlSecurityKey = null) - { - $keyInfo = $filterHelper->createElement(XmlSecurityDSig::NS_XMLDSIG, 'KeyInfo'); - $securityTokenReference = $filterHelper->createElement(Helper::NS_WSS, 'SecurityTokenReference'); - $keyInfo->appendChild($securityTokenReference); - // security token - if (self::TOKEN_REFERENCE_SECURITY_TOKEN === $tokenReference) { - $reference = $filterHelper->createElement(Helper::NS_WSS, 'Reference'); - $filterHelper->setAttribute($reference, null, 'URI', '#' . $guid); - if (null !== $xmlSecurityKey) { - $filterHelper->setAttribute($reference, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3'); - } - $securityTokenReference->appendChild($reference); - // subject key identifier - } elseif (self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER === $tokenReference && null !== $xmlSecurityKey) { - $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier'); - $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary'); - $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_X509 . '#509SubjectKeyIdentifier'); - $securityTokenReference->appendChild($keyIdentifier); - $certificate = $xmlSecurityKey->getX509SubjectKeyIdentifier(); - $dataNode = new \DOMText($certificate); - $keyIdentifier->appendChild($dataNode); - // thumbprint sha1 - } elseif (self::TOKEN_REFERENCE_THUMBPRINT_SHA1 === $tokenReference && null !== $xmlSecurityKey) { - $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier'); - $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary'); - $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_SMS_1_1 . '#ThumbprintSHA1'); - $securityTokenReference->appendChild($keyIdentifier); - $thumbprintSha1 = base64_encode(sha1(base64_decode($xmlSecurityKey->getX509Certificate(true)), true)); - $dataNode = new \DOMText($thumbprintSha1); - $keyIdentifier->appendChild($dataNode); - } - - return $keyInfo; - } - - /** - * Create a list of \DOMNodes that should be encrypted. - * - * @param \DOMDocument $dom DOMDocument to query - * @param \DOMElement $security Security element - * - * @return \DOMNodeList - */ - protected function createNodeListForEncryption(\DOMDocument $dom, \DOMElement $security) - { - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('SOAP-ENV', $dom->documentElement->namespaceURI); - $xpath->registerNamespace('ds', XmlSecurityDSig::NS_XMLDSIG); - if ($this->encryptSignature === true) { - $query = '//ds:Signature | //SOAP-ENV:Body'; - } else { - $query = '//SOAP-ENV:Body'; - } - - return $xpath->query($query); - } - - /** - * Create a list of \DOMNodes that should be signed. - * - * @param \DOMDocument $dom DOMDocument to query - * @param \DOMElement $security Security element - * - * @return array(\DOMNode) - */ - protected function createNodeListForSigning(\DOMDocument $dom, \DOMElement $security) - { - $nodes = array(); - $body = $dom->getElementsByTagNameNS($dom->documentElement->namespaceURI, 'Body')->item(0); - if (null !== $body) { - $nodes[] = $body; - } - foreach ($security->childNodes as $node) { - if (XML_ELEMENT_NODE === $node->nodeType) { - $nodes[] = $node; - } - } - if ($this->signAllHeaders) { - foreach ($security->parentNode->childNodes as $node) { - if (XML_ELEMENT_NODE === $node->nodeType && - Helper::NS_WSS !== $node->namespaceURI) { - $nodes[] = $node; - } - } - } - - return $nodes; - } - - /** - * Gets the referenced node for the given URI. - * - * @param \DOMElement $node Node - * @param string $uri URI - * - * @return \DOMElement - */ - protected function getReferenceNodeForUri(\DOMElement $node, $uri) - { - $url = parse_url($uri); - $referenceId = $url['fragment']; - $query = '//*[@'.Helper::PFX_WSU.':Id="'.$referenceId.'" or @Id="'.$referenceId.'"]'; - $xpath = new \DOMXPath($node->ownerDocument); - $xpath->registerNamespace(Helper::PFX_WSU, Helper::NS_WSU); - - return $xpath->query($query)->item(0); - } - - /** - * Tries to resolve a key from the given \DOMElement. - * - * @param \DOMElement $node Node where to resolve the key - * @param string $algorithm XML security key algorithm - * - * @return \ass\XmlSecurity\Key|null - */ - public function keyInfoSecurityTokenReferenceResolver(\DOMElement $node, $algorithm) - { - foreach ($node->childNodes as $key) { - if (Helper::NS_WSS === $key->namespaceURI) { - switch ($key->localName) { - case 'KeyIdentifier': - - return $this->serviceSecurityKey->getPublicKey(); - case 'Reference': - $uri = $key->getAttribute('URI'); - $referencedNode = $this->getReferenceNodeForUri($node, $uri); - - if (XmlSecurityEnc::NS_XMLENC === $referencedNode->namespaceURI - && 'EncryptedKey' == $referencedNode->localName) { - $key = XmlSecurityEnc::decryptEncryptedKey($referencedNode, $this->userSecurityKey->getPrivateKey()); - - return XmlSecurityKey::factory($algorithm, $key, XmlSecurityKey::TYPE_PRIVATE); - } else { - //$valueType = $key->getAttribute('ValueType'); - - return $this->serviceSecurityKey->getPublicKey(); - } - } - } - } - - return null; - } } \ No newline at end of file diff --git a/src/BeSimple/SoapClient/WsdlDownloader.php b/src/BeSimple/SoapClient/WsdlDownloader.php index 6e403ce..97a7d67 100644 --- a/src/BeSimple/SoapClient/WsdlDownloader.php +++ b/src/BeSimple/SoapClient/WsdlDownloader.php @@ -74,11 +74,8 @@ class WsdlDownloader // get current WSDL caching config $this->cacheEnabled = $cacheWsdl === Cache::TYPE_NONE ? Cache::DISABLED : Cache::ENABLED == Cache::isEnabled(); - - if ($this->cacheEnabled) { - $this->cacheDir = Cache::getDirectory(); - $this->cacheTtl = Cache::getLifetime(); - } + $this->cacheDir = Cache::getDirectory(); + $this->cacheTtl = Cache::getLifetime(); } /** diff --git a/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php index 40d6372..138b973 100644 --- a/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php +++ b/src/BeSimple/SoapCommon/Converter/SwaTypeConverter.php @@ -83,7 +83,7 @@ class SwaTypeConverter implements TypeConverterInterface, SoapKernelAwareInterfa $this->soapKernel->addAttachment($part); - return sprintf('<%s href="%s"/>', $this->getTypeName(), $contentId); + return sprintf('<%s href="%s"/>', $this->getTypeName(), 'cid:' . $contentId); } /** diff --git a/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php b/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php new file mode 100644 index 0000000..00231cc --- /dev/null +++ b/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php @@ -0,0 +1,358 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapCommon; + +use ass\XmlSecurity\DSig as XmlSecurityDSig; +use ass\XmlSecurity\Enc as XmlSecurityEnc; +use ass\XmlSecurity\Key as XmlSecurityKey; +use ass\XmlSecurity\Pem as XmlSecurityPem; + +use BeSimple\SoapCommon\FilterHelper; +use BeSimple\SoapCommon\Helper; +use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest; +use BeSimple\SoapCommon\SoapRequestFilter; +use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse; +use BeSimple\SoapCommon\SoapResponseFilter; +use BeSimple\SoapCommon\WsSecurityKey; + +/** + * WS-Security common code for client & server. + * + * @author Andreas Schamberger + */ +abstract class WsSecurityFilterClientServer +{ + /** + * The date format to be used with {@link \DateTime} + */ + const DATETIME_FORMAT = 'Y-m-d\TH:i:s.000\Z'; + + /** + * (X509 3.2.1) Reference to a Subject Key Identifier + */ + const TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER = 0; + + /** + * (X509 3.2.1) Reference to a Security Token + */ + const TOKEN_REFERENCE_SECURITY_TOKEN = 1; + + /** + * (SMS_1.1 7.3) Key Identifiers + */ + const TOKEN_REFERENCE_THUMBPRINT_SHA1 = 2; + + /** + * Actor. + * + * @var string + */ + protected $actor; + + /** + * (SMS 10) Add security timestamp. + * + * @var boolean + */ + protected $addTimestamp; + + /** + * Encrypt the signature? + * + * @var boolean + */ + protected $encryptSignature; + + /** + * (SMS 10) Security timestamp expires time in seconds. + * + * @var int + */ + protected $expires; + + /** + * Sign all headers. + * + * @var boolean + */ + protected $signAllHeaders; + + /** + * (X509 3.2) Token reference type for encryption. + * + * @var int + */ + protected $tokenReferenceEncryption = null; + + /** + * (X509 3.2) Token reference type for signature. + * + * @var int + */ + protected $tokenReferenceSignature = null; + + /** + * Service WsSecurityKey. + * + * @var \BeSimple\SoapCommon\WsSecurityKey + */ + protected $serviceSecurityKey; + + /** + * User WsSecurityKey. + * + * @var \BeSimple\SoapCommon\WsSecurityKey + */ + protected $userSecurityKey; + + /** + * Constructor. + * + * @param boolean $addTimestamp (SMS 10) Add security timestamp. + * @param int $expires (SMS 10) Security timestamp expires time in seconds. + * @param string $actor SOAP actor + */ + public function __construct($addTimestamp = true, $expires = 300, $actor = null) + { + $this->addTimestamp = $addTimestamp; + $this->expires = $expires; + $this->actor = $actor; + } + + /** + * Reset all properties to default values. + */ + public function resetFilter() + { + $this->actor = null; + $this->addTimestamp = null; + $this->encryptSignature = null; + $this->expires = null; + $this->serviceSecurityKey = null; + $this->signAllHeaders = null; + $this->tokenReferenceEncryption = null; + $this->tokenReferenceSignature = null; + $this->userSecurityKey = null; + } + + /** + * Set service security key. + * + * @param \BeSimple\SoapCommon\WsSecurityKey $serviceSecurityKey Service security key + * + * @return void + */ + public function setServiceSecurityKeyObject(WsSecurityKey $serviceSecurityKey) + { + $this->serviceSecurityKey = $serviceSecurityKey; + } + + /** + * Set user security key. + * + * @param \BeSimple\SoapCommon\WsSecurityKey $userSecurityKey User security key + * + * @return void + */ + public function setUserSecurityKeyObject(WsSecurityKey $userSecurityKey) + { + $this->userSecurityKey = $userSecurityKey; + } + + /** + * Set security options. + * + * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1 + * @param boolean $encryptSignature Encrypt signature + * + * @return void + */ + public function setSecurityOptionsEncryption($tokenReference, $encryptSignature = false) + { + $this->tokenReferenceEncryption = $tokenReference; + $this->encryptSignature = $encryptSignature; + } + + /** + * Set security options. + * + * @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1 + * @param boolean $signAllHeaders Sign all headers? + * + * @return void + */ + public function setSecurityOptionsSignature($tokenReference, $signAllHeaders = false) + { + $this->tokenReferenceSignature = $tokenReference; + $this->signAllHeaders = $signAllHeaders; + } + + /** + * Adds the configured KeyInfo to the parentNode. + * + * @param FilterHelper $filterHelper Filter helper object + * @param int $tokenReference Token reference type + * @param string $guid Unique ID + * @param \ass\XmlSecurity\Key $xmlSecurityKey XML security key + * + * @return \DOMElement + */ + protected function createKeyInfo(FilterHelper $filterHelper, $tokenReference, $guid, XmlSecurityKey $xmlSecurityKey = null) + { + $keyInfo = $filterHelper->createElement(XmlSecurityDSig::NS_XMLDSIG, 'KeyInfo'); + $securityTokenReference = $filterHelper->createElement(Helper::NS_WSS, 'SecurityTokenReference'); + $keyInfo->appendChild($securityTokenReference); + // security token + if (self::TOKEN_REFERENCE_SECURITY_TOKEN === $tokenReference) { + $reference = $filterHelper->createElement(Helper::NS_WSS, 'Reference'); + $filterHelper->setAttribute($reference, null, 'URI', '#' . $guid); + if (null !== $xmlSecurityKey) { + $filterHelper->setAttribute($reference, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3'); + } + $securityTokenReference->appendChild($reference); + // subject key identifier + } elseif (self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER === $tokenReference && null !== $xmlSecurityKey) { + $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier'); + $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary'); + $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_X509 . '#509SubjectKeyIdentifier'); + $securityTokenReference->appendChild($keyIdentifier); + $certificate = $xmlSecurityKey->getX509SubjectKeyIdentifier(); + $dataNode = new \DOMText($certificate); + $keyIdentifier->appendChild($dataNode); + // thumbprint sha1 + } elseif (self::TOKEN_REFERENCE_THUMBPRINT_SHA1 === $tokenReference && null !== $xmlSecurityKey) { + $keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier'); + $filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary'); + $filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_SMS_1_1 . '#ThumbprintSHA1'); + $securityTokenReference->appendChild($keyIdentifier); + $thumbprintSha1 = base64_encode(sha1(base64_decode($xmlSecurityKey->getX509Certificate(true)), true)); + $dataNode = new \DOMText($thumbprintSha1); + $keyIdentifier->appendChild($dataNode); + } + + return $keyInfo; + } + + /** + * Create a list of \DOMNodes that should be encrypted. + * + * @param \DOMDocument $dom DOMDocument to query + * @param \DOMElement $security Security element + * + * @return \DOMNodeList + */ + protected function createNodeListForEncryption(\DOMDocument $dom, \DOMElement $security) + { + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace('SOAP-ENV', $dom->documentElement->namespaceURI); + $xpath->registerNamespace('ds', XmlSecurityDSig::NS_XMLDSIG); + if ($this->encryptSignature === true) { + $query = '//ds:Signature | //SOAP-ENV:Body'; + } else { + $query = '//SOAP-ENV:Body'; + } + + return $xpath->query($query); + } + + /** + * Create a list of \DOMNodes that should be signed. + * + * @param \DOMDocument $dom DOMDocument to query + * @param \DOMElement $security Security element + * + * @return array(\DOMNode) + */ + protected function createNodeListForSigning(\DOMDocument $dom, \DOMElement $security) + { + $nodes = array(); + $body = $dom->getElementsByTagNameNS($dom->documentElement->namespaceURI, 'Body')->item(0); + if (null !== $body) { + $nodes[] = $body; + } + foreach ($security->childNodes as $node) { + if (XML_ELEMENT_NODE === $node->nodeType) { + $nodes[] = $node; + } + } + if ($this->signAllHeaders) { + foreach ($security->parentNode->childNodes as $node) { + if (XML_ELEMENT_NODE === $node->nodeType && + Helper::NS_WSS !== $node->namespaceURI) { + $nodes[] = $node; + } + } + } + + return $nodes; + } + + /** + * Gets the referenced node for the given URI. + * + * @param \DOMElement $node Node + * @param string $uri URI + * + * @return \DOMElement + */ + protected function getReferenceNodeForUri(\DOMElement $node, $uri) + { + $url = parse_url($uri); + $referenceId = $url['fragment']; + $query = '//*[@'.Helper::PFX_WSU.':Id="'.$referenceId.'" or @Id="'.$referenceId.'"]'; + $xpath = new \DOMXPath($node->ownerDocument); + $xpath->registerNamespace(Helper::PFX_WSU, Helper::NS_WSU); + + return $xpath->query($query)->item(0); + } + + + /** + * Tries to resolve a key from the given \DOMElement. + * + * @param \DOMElement $node Node where to resolve the key + * @param string $algorithm XML security key algorithm + * + * @return \ass\XmlSecurity\Key|null + */ + public function keyInfoSecurityTokenReferenceResolver(\DOMElement $node, $algorithm) + { + foreach ($node->childNodes as $key) { + if (Helper::NS_WSS === $key->namespaceURI) { + switch ($key->localName) { + case 'KeyIdentifier': + + return $this->serviceSecurityKey->getPublicKey(); + case 'Reference': + $uri = $key->getAttribute('URI'); + $referencedNode = $this->getReferenceNodeForUri($node, $uri); + + if (XmlSecurityEnc::NS_XMLENC === $referencedNode->namespaceURI + && 'EncryptedKey' == $referencedNode->localName) { + $key = XmlSecurityEnc::decryptEncryptedKey($referencedNode, $this->userSecurityKey->getPrivateKey()); + + return XmlSecurityKey::factory($algorithm, $key, false, XmlSecurityKey::TYPE_PRIVATE); + } elseif (Helper::NS_WSS === $referencedNode->namespaceURI + && 'BinarySecurityToken' == $referencedNode->localName) { + + $key = XmlSecurityPem::formatKeyInPemFormat($referencedNode->textContent); + + return XmlSecurityKey::factory(XmlSecurityKey::RSA_SHA1, $key, false, XmlSecurityKey::TYPE_PUBLIC); + } + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/BeSimple/SoapServer/SoapRequest.php b/src/BeSimple/SoapServer/SoapRequest.php index 0964771..4f6e53c 100644 --- a/src/BeSimple/SoapServer/SoapRequest.php +++ b/src/BeSimple/SoapServer/SoapRequest.php @@ -32,10 +32,21 @@ class SoapRequest extends CommonSoapRequest */ public static function create($content, $version) { - $content = is_null($content) ? file_get_contents("php://input") : $content; $location = self::getCurrentUrl(); - $action = isset($_SERVER[SoapMessage::SOAP_ACTION_HEADER]) ? $_SERVER[SoapMessage::SOAP_ACTION_HEADER] : null; - $contentType = $_SERVER[SoapMessage::CONTENT_TYPE_HEADER]; + /* + * Work around missing header/php://input access in PHP cli webserver by + * setting headers additionally as GET parameters and SOAP request body + * explicitly as POST variable + */ + if (php_sapi_name() == "cli-server") { + $content = is_null($content) ? $_POST['request'] : $content; + $action = $_GET[SoapMessage::SOAP_ACTION_HEADER]; + $contentType = $_GET[SoapMessage::CONTENT_TYPE_HEADER]; + } else { + $content = is_null($content) ? file_get_contents("php://input") : $content; + $action = isset($_SERVER[SoapMessage::SOAP_ACTION_HEADER]) ? $_SERVER[SoapMessage::SOAP_ACTION_HEADER] : null]; + $contentType = $_SERVER[SoapMessage::CONTENT_TYPE_HEADER]; + } $request = new SoapRequest(); // $content is if unmodified from SoapClient not a php string type! diff --git a/src/BeSimple/SoapServer/SoapServer.php b/src/BeSimple/SoapServer/SoapServer.php index 045dcd6..9667df6 100644 --- a/src/BeSimple/SoapServer/SoapServer.php +++ b/src/BeSimple/SoapServer/SoapServer.php @@ -70,7 +70,12 @@ class SoapServer extends \SoapServer $soapRequest = SoapRequest::create($request, $this->soapVersion); // handle actual SOAP request - $soapResponse = $this->handle2($soapRequest); + try { + $soapResponse = $this->handle2($soapRequest); + } catch (\SoapFault $fault) { + // issue an error to the client + $this->fault($fault->faultcode, $fault->faultstring); + } // send SOAP response to client $soapResponse->send(); diff --git a/src/BeSimple/SoapServer/WsSecurityFilter.php b/src/BeSimple/SoapServer/WsSecurityFilter.php new file mode 100644 index 0000000..6e412b2 --- /dev/null +++ b/src/BeSimple/SoapServer/WsSecurityFilter.php @@ -0,0 +1,244 @@ + + * (c) Francis Besset + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace BeSimple\SoapServer; + +use ass\XmlSecurity\DSig as XmlSecurityDSig; +use ass\XmlSecurity\Enc as XmlSecurityEnc; +use ass\XmlSecurity\Key as XmlSecurityKey; +use ass\XmlSecurity\Pem as XmlSecurityPem; + +use BeSimple\SoapCommon\FilterHelper; +use BeSimple\SoapCommon\Helper; +use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest; +use BeSimple\SoapCommon\SoapRequestFilter; +use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse; +use BeSimple\SoapCommon\SoapResponseFilter; +use BeSimple\SoapCommon\WsSecurityKey; +use BeSimple\SoapCommon\WsSecurityFilterClientServer; + +/** + * This plugin implements a subset of the following standards: + * * Web Services Security: SOAP Message Security 1.0 (WS-Security 2004) + * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf + * * Web Services Security UsernameToken Profile 1.0 + * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf + * * Web Services Security X.509 Certificate Token Profile + * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf + * + * @author Andreas Schamberger + */ +class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapRequestFilter, SoapResponseFilter +{ + /** + * Username/password callback that returns password or null. + * + * @var callable + */ + protected $usernamePasswordCallback; + + /** + * Set username/password callback that returns password or null. + * + * @param callable $callback Username/password callback function + * + * @return void + */ + public function setUsernamePasswordCallback($callback) + { + $this->usernamePasswordCallback = $callback; + } + + /** + * Reset all properties to default values. + */ + public function resetFilter() + { + parent::resetFilter(); + $this->usernamePasswordCallback = null; + } + + /** + * Modify the given request XML. + * + * @param \BeSimple\SoapCommon\SoapRequest $request SOAP request + * + * @return void + */ + public function filterRequest(CommonSoapRequest $request) + { + // get \DOMDocument from SOAP request + $dom = $request->getContentDocument(); + + // locate security header + $security = $dom->getElementsByTagNameNS(Helper::NS_WSS, 'Security')->item(0); + if (null !== $security) { + + // is security header still valid? + $query = '//'.Helper::PFX_WSU.':Timestamp/'.Helper::PFX_WSU.':Expires'; + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace(Helper::PFX_WSU, Helper::NS_WSU); + $expires = $xpath->query($query, $security)->item(0); + + if (null !== $expires) { + $expiresDatetime = \DateTime::createFromFormat(self::DATETIME_FORMAT, $expires->textContent, new \DateTimeZone('UTC')); + $currentDatetime = new \DateTime('now', new \DateTimeZone('UTC')); + + if ($currentDatetime > $expiresDatetime) { + throw new \SoapFault('wsu:MessageExpired', 'Security semantics are expired'); + } + } + + $usernameToken = $security->getElementsByTagNameNS(Helper::NS_WSS, 'UsernameToken')->item(0); + if (null !== $usernameToken) { + $usernameTokenUsername = $usernameToken->getElementsByTagNameNS(Helper::NS_WSS, 'Username')->item(0); + $usernameTokenPassword = $usernameToken->getElementsByTagNameNS(Helper::NS_WSS, 'Password')->item(0); + + $password = call_user_func($this->usernamePasswordCallback, $usernameTokenUsername->textContent); + + if ($usernameTokenPassword->getAttribute('Type') == Helper::NAME_WSS_UTP . '#PasswordDigest') { + $nonce = $usernameToken->getElementsByTagNameNS(Helper::NS_WSS, 'Nonce')->item(0); + $created = $usernameToken->getElementsByTagNameNS(Helper::NS_WSU, 'Created')->item(0); + $password = base64_encode(sha1(base64_decode($nonce->textContent) . $created->textContent . $password, true)); + } + + if (null === $password || $usernameTokenPassword->textContent != $password) { + throw new \SoapFault('wsse:FailedAuthentication', 'The security token could not be authenticated or authorized'); + } + } + + // add SecurityTokenReference resolver for KeyInfo + $keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver'); + XmlSecurityDSig::addKeyInfoResolver(Helper::NS_WSS, 'SecurityTokenReference', $keyResolver); + + // do we have a reference list in header + $referenceList = XmlSecurityEnc::locateReferenceList($security); + // get a list of encrypted nodes + $encryptedNodes = XmlSecurityEnc::locateEncryptedData($dom, $referenceList); + // decrypt them + if (null !== $encryptedNodes) { + foreach ($encryptedNodes as $encryptedNode) { + XmlSecurityEnc::decryptNode($encryptedNode); + } + } + + // locate signature node + $signature = XmlSecurityDSig::locateSignature($security); + if (null !== $signature) { + // verify references + $options = array( + 'id_ns_prefix' => Helper::PFX_WSU, + 'id_prefix_ns' => Helper::NS_WSU, + ); + if (XmlSecurityDSig::verifyReferences($signature, $options) !== true) { + throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid'); + } + // verify signature + if (XmlSecurityDSig::verifyDocumentSignature($signature) !== true) { + throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid'); + } + } + + $security->parentNode->removeChild($security); + } + } + + /** + * Modify the given request XML. + * + * @param \BeSimple\SoapCommon\SoapResponse $response SOAP response + * + * @return void + */ + public function filterResponse(CommonSoapResponse $response) + { + // get \DOMDocument from SOAP response + $dom = $response->getContentDocument(); + + // create FilterHelper + $filterHelper = new FilterHelper($dom); + + // add the neccessary namespaces + $filterHelper->addNamespace(Helper::PFX_WSS, Helper::NS_WSS); + $filterHelper->addNamespace(Helper::PFX_WSU, Helper::NS_WSU); + $filterHelper->registerNamespace(XmlSecurityDSig::PFX_XMLDSIG, XmlSecurityDSig::NS_XMLDSIG); + + // init timestamp + $dt = new \DateTime('now', new \DateTimeZone('UTC')); + $createdTimestamp = $dt->format(self::DATETIME_FORMAT); + + // create security header + $security = $filterHelper->createElement(Helper::NS_WSS, 'Security'); + $filterHelper->addHeaderElement($security, true, $this->actor, $response->getVersion()); + + if (true === $this->addTimestamp || null !== $this->expires) { + $timestamp = $filterHelper->createElement(Helper::NS_WSU, 'Timestamp'); + $created = $filterHelper->createElement(Helper::NS_WSU, 'Created', $createdTimestamp); + $timestamp->appendChild($created); + if (null !== $this->expires) { + $dt->modify('+' . $this->expires . ' seconds'); + $expiresTimestamp = $dt->format(self::DATETIME_FORMAT); + $expires = $filterHelper->createElement(Helper::NS_WSU, 'Expires', $expiresTimestamp); + $timestamp->appendChild($expires); + } + $security->appendChild($timestamp); + } + + if (null !== $this->userSecurityKey && $this->userSecurityKey->hasKeys()) { + $guid = 'CertId-' . Helper::generateUUID(); + // add token references + $keyInfo = null; + if (null !== $this->tokenReferenceSignature) { + $keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceSignature, $guid, $this->userSecurityKey->getPublicKey()); + } + $nodes = $this->createNodeListForSigning($dom, $security); + $signature = XmlSecurityDSig::createSignature($this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N, $security, null, $keyInfo); + $options = array( + 'id_ns_prefix' => Helper::PFX_WSU, + 'id_prefix_ns' => Helper::NS_WSU, + ); + foreach ($nodes as $node) { + XmlSecurityDSig::addNodeToSignature($signature, $node, XmlSecurityDSig::SHA1, XmlSecurityDSig::EXC_C14N, $options); + } + XmlSecurityDSig::signDocument($signature, $this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N); + + $publicCertificate = $this->userSecurityKey->getPublicKey()->getX509Certificate(true); + $binarySecurityToken = $filterHelper->createElement(Helper::NS_WSS, 'BinarySecurityToken', $publicCertificate); + $filterHelper->setAttribute($binarySecurityToken, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary'); + $filterHelper->setAttribute($binarySecurityToken, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3'); + $filterHelper->setAttribute($binarySecurityToken, Helper::NS_WSU, 'Id', $guid); + $security->insertBefore($binarySecurityToken, $signature); + + // encrypt soap document + if (null !== $this->serviceSecurityKey && $this->serviceSecurityKey->hasKeys()) { + $guid = 'EncKey-' . Helper::generateUUID(); + // add token references + $keyInfo = null; + if (null !== $this->tokenReferenceEncryption) { + $keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceEncryption, $guid, $this->serviceSecurityKey->getPublicKey()); + } + $encryptedKey = XmlSecurityEnc::createEncryptedKey($guid, $this->serviceSecurityKey->getPrivateKey(), $this->serviceSecurityKey->getPublicKey(), $security, $signature, $keyInfo); + $referenceList = XmlSecurityEnc::createReferenceList($encryptedKey); + // token reference to encrypted key + $keyInfo = $this->createKeyInfo($filterHelper, self::TOKEN_REFERENCE_SECURITY_TOKEN, $guid); + $nodes = $this->createNodeListForEncryption($dom, $security); + foreach ($nodes as $node) { + $type = XmlSecurityEnc::ELEMENT; + if ($node->localName == 'Body') { + $type = XmlSecurityEnc::CONTENT; + } + XmlSecurityEnc::encryptNode($node, $type, $this->serviceSecurityKey->getPrivateKey(), $referenceList, $keyInfo); + } + } + } + } +} \ No newline at end of file