diff --git a/README.md b/README.md
index 1f4970e..8f4fe7a 100644
--- a/README.md
+++ b/README.md
@@ -33,17 +33,13 @@ Installation
The recommended way to install goetas-webservices / soap-client is using [Composer](https://getcomposer.org/):
-Add this packages to your `composer.json` file.
```
-{
- "require": {
- "goetas-webservices/soap-client": "^0.2",
- },
- "require-dev": {
- "goetas-webservices/wsdl2php": "^0.3",
- },
-}
+composer require goetas-webservices/soap-client
+composer require goetas-webservices/wsdl2php --dev
+
+# to use WS Security
+composer require ass/xmlsecurity
```
More dependencies might be needed depending on your PSR-7 and HTTP client preferred implementation.
@@ -65,7 +61,7 @@ Here is an example:
# config.yml
soap_client:
- alternative_endpoints:
+ alternative_endpoints: # optional
MyServiceName:
MySoapPortName: http://localhost:8080/service
@@ -75,7 +71,7 @@ soap_client:
'TestNs/MyApp': soap/src
destinations_jms:
'TestNs/MyApp': soap/metadata
- aliases:
+ aliases: # optional
'http://www.example.org/test/':
MyCustomXSDType: 'MyCustomMappedPHPType'
diff --git a/composer.json b/composer.json
index 42934b7..5e47dbb 100644
--- a/composer.json
+++ b/composer.json
@@ -23,12 +23,20 @@
"require-dev": {
"phpunit/phpunit": "^4.8|^5.0",
- "goetas-webservices/wsdl2php": "^0.3",
+ "goetas-webservices/wsdl2php": "^0.4",
+ "goetas-webservices/xsd2php": "^0.2.3",
+
+ "jms/serializer": "dev-xml-namespaces-improvements as 1.5.0",
+
"goetas-webservices/wsdl-reader": "^v0.3.1",
+ "ass/xmlsecurity" : "^1.0",
"php-http/guzzle6-adapter": "^1.0",
"php-http/message": "^1.0"
},
+ "suggest": {
+ "ass/xmlsecurity" : "Required for WS Security features"
+ },
"autoload": {
"psr-4": {
"GoetasWebservices\\SoapServices\\SoapClient\\": "src"
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ab62156..44dfb05 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -22,12 +22,14 @@
-
-
- src
-
-
-
+
+
+ src
+
+ src/WssWsSecurity/XmlSign
+
+
+
diff --git a/src/Arguments/Headers/Handler/HeaderHandler.php b/src/Arguments/Headers/Handler/HeaderHandler.php
index 72d3efa..ed66589 100644
--- a/src/Arguments/Headers/Handler/HeaderHandler.php
+++ b/src/Arguments/Headers/Handler/HeaderHandler.php
@@ -9,13 +9,9 @@
use JMS\Serializer\SerializationContext;
use JMS\Serializer\XmlDeserializationVisitor;
use JMS\Serializer\XmlSerializationVisitor;
-use Symfony\Component\DependencyInjection\SimpleXMLElement;
class HeaderHandler implements SubscribingHandlerInterface
{
- const SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
- const SOAP_12 = 'http://www.w3.org/2003/05/soap-envelope';
-
protected $headerData = [];
public static function getSubscribingMethods()
@@ -67,7 +63,7 @@ public function serializeHeader(XmlSerializationVisitor $visitor, HeaderPlacehol
$metadata = new StaticPropertyMetadata($classMetadata->name, $classMetadata->xmlRootName, $header->getData());
$metadata->xmlNamespace = $classMetadata->xmlRootNamespace;
- $metadata->serializedName = $classMetadata->xmlRootName;
+ $metadata->serializedName = $classMetadata->xmlRootName ?: 'header';
$visitor->visitProperty($metadata, $header->getData(), $context);
@@ -88,12 +84,7 @@ private function handleOptions(XmlSerializationVisitor $visitor, $header)
foreach ($options as $option => $value) {
if (in_array($option, ['mustUnderstand', 'required', 'role', 'actor'])) {
- if ($currentNode->ownerDocument->documentElement->namespaceURI === self::SOAP_12) {
- $envelopeNS = self::SOAP_12;
- } else {
- $envelopeNS = self::SOAP;
- }
- $this->setAttributeOnNode($currentNode->lastChild, $option, $value, $envelopeNS);
+ $this->setAttributeOnNode($currentNode->lastChild, $option, $value, $currentNode->ownerDocument->documentElement->namespaceURI);
}
}
}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 1c7483b..1969d6a 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -83,7 +83,12 @@ public function getConfigTreeBuilder()
->prototype('scalar')->end()
->end()
-
+ ->scalarNode('any_element')
+ ->defaultValue(false)
+ ->end()
+ ->scalarNode('any_attribute')
+ ->defaultValue(false)
+ ->end()
diff --git a/src/DependencyInjection/SoapClientExtension.php b/src/DependencyInjection/SoapClientExtension.php
index 3b01419..74afc9c 100644
--- a/src/DependencyInjection/SoapClientExtension.php
+++ b/src/DependencyInjection/SoapClientExtension.php
@@ -42,7 +42,11 @@ public function load(array $configs, ContainerBuilder $container)
foreach (['php', 'jms'] as $type) {
+
$converter = $container->getDefinition('goetas_webservices.xsd2php.converter.' . $type);
+
+ $converterWsdl = $container->getDefinition('goetas_webservices.wsdl2php.converter.' . $type);
+
foreach ($config['namespaces'] as $xml => $php) {
$converter->addMethodCall('addNamespace', [$xml, self::sanitizePhp($php)]);
}
@@ -53,6 +57,11 @@ public function load(array $configs, ContainerBuilder $container)
}
}
+ $schemaReader = $container->getDefinition('goetas_webservices.xsd2php.schema_reader');
+ foreach ($config['known_locations'] as $namespace => $location) {
+ $schemaReader->addMethodCall('addKnownSchemaLocation', [$namespace, $location]);
+ }
+
$definition = $container->getDefinition('goetas_webservices.xsd2php.naming_convention.' . $config['naming_strategy']);
$container->setDefinition('goetas_webservices.xsd2php.naming_convention', $definition);
diff --git a/src/WssWsSecurity/Exception/ClientException.php b/src/WssWsSecurity/Exception/ClientException.php
new file mode 100644
index 0000000..747653f
--- /dev/null
+++ b/src/WssWsSecurity/Exception/ClientException.php
@@ -0,0 +1,9 @@
+password;
+ }
+
+ /**
+ * @param string $password
+ * @param int $passwordType
+ */
+ public function setPassword($password, $passwordType = self::PASSWORD_TYPE_DIGEST)
+ {
+ $this->password = $password;
+ $this->passwordType = $passwordType;
+ }
+
+ public function isPasswordDigest()
+ {
+ return $this->passwordType === self::PASSWORD_TYPE_DIGEST;
+ }
+
+ public function isPasswordPlain()
+ {
+ return $this->passwordType === self::PASSWORD_TYPE_TEXT;
+ }
+
+ /**
+ * @return string
+ */
+ public function getUsername()
+ {
+ return $this->username;
+ }
+
+ /**
+ * @param string $username
+ */
+ public function setUsername($username)
+ {
+ $this->username = $username;
+ }
+}
diff --git a/src/WssWsSecurity/SecurityKeyPair.php b/src/WssWsSecurity/SecurityKeyPair.php
new file mode 100644
index 0000000..31e43ea
--- /dev/null
+++ b/src/WssWsSecurity/SecurityKeyPair.php
@@ -0,0 +1,101 @@
+privateKey = XmlSecurityKey::factory($encryptionType, $key, $keyIsFile, XmlSecurityKey::TYPE_PRIVATE, $passphrase);
+ }
+
+ /**
+ * Add public key.
+ *
+ * @param string $encryptionType Encryption type
+ * @param string $key Public key
+ * @param boolean $keyIsFile Given key parameter is path to key file
+ *
+ * @return void
+ */
+ public function setPublicKey($encryptionType, $key = null, $keyIsFile = true)
+ {
+ $this->publicKey = XmlSecurityKey::factory($encryptionType, $key, $keyIsFile, XmlSecurityKey::TYPE_PUBLIC);
+ }
+
+ /**
+ * Get private key.
+ *
+ * @return \ass\XmlSecurity\Key
+ */
+ public function getPrivateKey()
+ {
+ return $this->privateKey;
+ }
+
+ /**
+ * Get public key.
+ *
+ * @return \ass\XmlSecurity\Key
+ */
+ public function getPublicKey()
+ {
+ return $this->publicKey;
+ }
+
+ /**
+ * Has private and public key?
+ *
+ * @return boolean
+ */
+ public function hasKeys()
+ {
+ return null !== $this->privateKey && null !== $this->publicKey;
+ }
+
+ /**
+ * Has private key?
+ *
+ * @return boolean
+ */
+ public function hasPrivateKey()
+ {
+ return null !== $this->privateKey;
+ }
+
+ /**
+ * Has public key?
+ *
+ * @return boolean
+ */
+ public function hasPublicKey()
+ {
+ return null !== $this->publicKey;
+ }
+}
diff --git a/src/WssWsSecurity/Serializer/AbstractWsSecurityFilter.php b/src/WssWsSecurity/Serializer/AbstractWsSecurityFilter.php
new file mode 100644
index 0000000..09b1e02
--- /dev/null
+++ b/src/WssWsSecurity/Serializer/AbstractWsSecurityFilter.php
@@ -0,0 +1,55 @@
+serviceSecurityKey = $serviceSecurityKey;
+ }
+
+ /**
+ * Set user security key.
+ *
+ * @param SecurityKeyPair $userSecurityKey User security key
+ *
+ * @return void
+ */
+ public function setUserSecurityKeyObject(SecurityKeyPair $userSecurityKey = null)
+ {
+ $this->userSecurityKey = $userSecurityKey;
+ }
+}
diff --git a/src/WssWsSecurity/Serializer/WsSecurityFilterRequest.php b/src/WssWsSecurity/Serializer/WsSecurityFilterRequest.php
new file mode 100644
index 0000000..00f3952
--- /dev/null
+++ b/src/WssWsSecurity/Serializer/WsSecurityFilterRequest.php
@@ -0,0 +1,459 @@
+addTimestamp = $addTimestamp;
+ $this->expires = $expires;
+ }
+
+ /**
+ * @param \DateTime $initialTimestamp
+ */
+ public function __construct(\DateTime $initialTimestamp = null)
+ {
+ $this->initialTimestamp = $initialTimestamp;
+ }
+
+ /**
+ * 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 \DOMDocument $dom
+ * @param int $tokenReference Token reference type
+ * @param string $guid Unique ID
+ * @param XmlSecurityKey $xmlSecurityKey XML security key
+ *
+ * @return \DOMElement
+ */
+ private function createKeyInfo(\DOMDocument $dom, $tokenReference, $guid, XmlSecurityKey $xmlSecurityKey = null)
+ {
+ $keyInfo = $dom->createElementNS(XmlSecurityDSig::NS_XMLDSIG, 'KeyInfo');
+ $securityTokenReference = $dom->createElementNS(self::NS_WSS, 'SecurityTokenReference');
+ $keyInfo->appendChild($securityTokenReference);
+ // security token
+ if (self::TOKEN_REFERENCE_SECURITY_TOKEN === $tokenReference) {
+ $reference = $dom->createElementNS(self::NS_WSS, 'Reference');
+ $reference->setAttribute('URI', '#' . $guid);
+ if (null !== $xmlSecurityKey) {
+ $reference->setAttribute('ValueType', self::NAME_WSS_X509 . '#X509v3');
+ }
+ $securityTokenReference->appendChild($reference);
+ // subject key identifier
+ } elseif (self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER === $tokenReference && null !== $xmlSecurityKey) {
+ $keyIdentifier = $dom->createElementNS(self::NS_WSS, 'KeyIdentifier');
+ $keyIdentifier->setAttribute('EncodingType', self::NAME_WSS_SMS . '#Base64Binary');
+ $keyIdentifier->setAttribute('ValueType', self::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 = $dom->createElementNS(self::NS_WSS, 'KeyIdentifier');
+ $keyIdentifier->setAttribute('EncodingType', self::NAME_WSS_SMS . '#Base64Binary');
+ $keyIdentifier->setAttribute('ValueType', self::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
+ *
+ * @return \DOMNodeList
+ */
+ private function createNodeListForEncryption(\DOMDocument $dom)
+ {
+ $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)
+ */
+ private 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 &&
+ self::NS_WSS !== $node->namespaceURI
+ ) {
+ $nodes[] = $node;
+ }
+ }
+ }
+ return $nodes;
+ }
+
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \DOMElement $currentNode,
+ * @param Security $securityData
+ *
+ * @return \DOMElement
+ */
+ public function filterDom(\DOMElement $currentNode, Security $securityData)
+ {
+ $dom = $currentNode->ownerDocument;
+ $root = $dom->documentElement;
+
+ $namespaces = array(
+ 'ws' => self::NS_WSS,
+ 'wsu' => self::NS_WSU,
+ XmlSecurityDSig::PFX_XMLDSIG => XmlSecurityDSig::NS_XMLDSIG,
+ XmlSecurityEnc::PFX_XMLENC => XmlSecurityEnc::NS_XMLENC,
+ );
+
+ foreach ($namespaces as $prefix => $ns) {
+ $root->setAttributeNS(
+ 'http://www.w3.org/2000/xmlns/', // xmlns namespace URI
+ 'xmlns:'.$prefix,
+ $ns
+ );
+ }
+
+ $security = $dom->createElementNS(self::NS_WSS, $root->lookupPrefix(self::NS_WSS).':Security');
+ $currentNode->parentNode->replaceChild($security, $currentNode);
+
+ // init timestamp
+ $dt = $this->initialTimestamp ?: new \DateTime('now', new \DateTimeZone('UTC'));
+
+ if (true === $this->addTimestamp || null !== $this->expires) {
+ $this->handleTimestamp($security, $dt);
+ }
+
+ if (null !== $securityData->getUsername()) {
+ $this->handleUsername($security, $dt, $securityData);
+ }
+
+ if (null !== $this->userSecurityKey && $this->userSecurityKey->hasKeys()) {
+ $signature = $this->handleSignature($security);
+
+ // encrypt soap document
+ if (null !== $this->serviceSecurityKey && $this->serviceSecurityKey->hasKeys()) {
+ $this->handleEncryption($security, $signature);
+ }
+ }
+ return $security;
+ }
+
+ /**
+ * Generate a pseudo-random version 4 UUID.
+ *
+ * @see http://de.php.net/manual/en/function.uniqid.php#94959
+ *
+ * @return string
+ */
+ private static function generateUUID()
+ {
+ return sprintf(
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+ // 32 bits for "time_low"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+ // 16 bits for "time_mid"
+ mt_rand(0, 0xffff),
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand(0, 0x0fff) | 0x4000,
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand(0, 0x3fff) | 0x8000,
+ // 48 bits for "node"
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
+ );
+ }
+
+ /**
+ * @param \DOMElement $security
+ * @param \DateTime $dt
+ */
+ private function handleTimestamp(\DOMElement $security, \DateTime $dt)
+ {
+ $dom = $security->ownerDocument;
+ $timestamp = $dom->createElementNS(self::NS_WSU, 'Timestamp');
+ $created = $dom->createElementNS(self::NS_WSU, 'Created', $dt->format(self::DATETIME_FORMAT));
+ $timestamp->appendChild($created);
+ if (null !== $this->expires) {
+ $dt = clone $dt;
+ $dt->modify('+' . $this->expires . ' seconds');
+ $expiresTimestamp = $dt->format(self::DATETIME_FORMAT);
+ $expires = $dom->createElementNS(self::NS_WSU, 'Expires', $expiresTimestamp);
+ $timestamp->appendChild($expires);
+ }
+ $security->appendChild($timestamp);
+ }
+
+ /**
+ * @param \DOMElement $security
+ * @param $dt
+ * @param Security $securityData
+ */
+ private function handleUsername(\DOMElement $security, $dt, Security $securityData)
+ {
+ $dom = $security->ownerDocument;
+
+ $usernameToken = $dom->createElementNS(self::NS_WSS, 'UsernameToken');
+ $security->appendChild($usernameToken);
+
+ $username = $dom->createElementNS(self::NS_WSS, 'Username', $securityData->getUsername());
+ $usernameToken->appendChild($username);
+
+ if (null !== $securityData->getPassword()
+ && (null === $this->userSecurityKey
+ || (null !== $this->userSecurityKey && !$this->userSecurityKey->hasPrivateKey()))
+ ) {
+
+ if ($securityData->isPasswordDigest()) {
+ $nonce = mt_rand();
+ $password = base64_encode(sha1($nonce . $dt->format(self::DATETIME_FORMAT) . $securityData->getPassword(), true));
+ $passwordType = self::NAME_WSS_UTP . '#PasswordDigest';
+ } else {
+ $password = $securityData->getPassword();
+ $passwordType = self::NAME_WSS_UTP . '#PasswordText';
+ }
+
+ $password = $dom->createElementNS(self::NS_WSS, 'Password', $password);
+ $password->setAttribute('Type', $passwordType);
+ $usernameToken->appendChild($password);
+ if ($securityData->isPasswordDigest()) {
+ $nonce = $dom->createElementNS(self::NS_WSS, 'Nonce', base64_encode($nonce));
+ $usernameToken->appendChild($nonce);
+
+ $created = $dom->createElementNS(self::NS_WSU, 'Created', $dt->format(self::DATETIME_FORMAT));
+ $usernameToken->appendChild($created);
+ }
+ }
+ }
+
+ /**
+ * @param \DOMElement $security
+ * @return \DOMElement
+ */
+ private function handleSignature(\DOMElement $security)
+ {
+ $dom = $security->ownerDocument;
+
+ // this is fundamental for the signature
+ // formatting the dom adds nodes that are not signed
+ $dom->formatOutput = false;
+ $dom->preserveWhiteSpace = true;
+
+ $guid = 'CertId-' . self::generateUUID();
+ // add token references
+ $keyInfo = null;
+ if (null !== $this->tokenReferenceSignature) {
+ $keyInfo = $this->createKeyInfo($dom, $this->tokenReferenceSignature, $guid, $this->userSecurityKey->getPublicKey());
+ }
+ $nodes = $this->createNodeListForSigning($dom, $security);
+
+
+ $signature = XmlSecurityDSig::createSignature($this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N, $security, null, $keyInfo);
+
+ if ((!$prefix = $security->lookupPrefix(self::NS_WSU)) && (!$prefix = $security->ownerDocument->lookupPrefix(self::NS_WSU))) {
+ $prefix = 'ns-'. substr(sha1(self::NS_WSU), 0, 8);
+ }
+
+ $options = array(
+ 'id_ns_prefix' => $prefix,
+ 'id_prefix_ns' => self::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 = $dom->createElementNS(self::NS_WSS, 'BinarySecurityToken', $publicCertificate);
+ $binarySecurityToken->setAttribute('EncodingType', self::NAME_WSS_SMS . '#Base64Binary');
+ $binarySecurityToken->setAttribute('ValueType', self::NAME_WSS_X509 . '#X509v3');
+
+ $security->insertBefore($binarySecurityToken, $signature);
+
+ $binarySecurityToken->setAttributeNs(self::NS_WSU, $prefix.':Id', $guid);
+
+ return $signature;
+ }
+
+ /**
+ * @param \DOMElement $security
+ * @param \DOMElement $signature
+ */
+ private function handleEncryption(\DOMElement $security, \DOMElement $signature)
+ {
+ $dom = $security->ownerDocument;
+ $guid = 'EncKey-' . self::generateUUID();
+ // add token references
+ $keyInfo = null;
+ if (null !== $this->tokenReferenceEncryption) {
+ $keyInfo = $this->createKeyInfo($dom, $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($dom, self::TOKEN_REFERENCE_SECURITY_TOKEN, $guid);
+ $nodes = $this->createNodeListForEncryption($dom);
+ foreach ($nodes as $node) {
+ $type = XmlSecurityEnc::ELEMENT;
+ if ($node->localName == 'Body') {
+ $type = XmlSecurityEnc::CONTENT;
+ }
+ XmlSecurityEnc::encryptNode($node, $type, $this->serviceSecurityKey->getPrivateKey(), $referenceList, $keyInfo);
+ }
+ }
+}
diff --git a/src/WssWsSecurity/Serializer/WsSecurityFilterResponse.php b/src/WssWsSecurity/Serializer/WsSecurityFilterResponse.php
new file mode 100644
index 0000000..61992bc
--- /dev/null
+++ b/src/WssWsSecurity/Serializer/WsSecurityFilterResponse.php
@@ -0,0 +1,121 @@
+ownerDocument);
+ $xpath->registerNamespace('wsu', self::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 (self::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 (self::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;
+ }
+
+
+ /**
+ * Modify the given request XML.
+ *
+ * @param \DOMDocument $dom
+ *
+ * @return void
+ */
+ public function filterDom(\DOMDocument $dom)
+ {
+ // locate security header
+ $security = $dom->getElementsByTagNameNS(self::NS_WSS, 'Security')->item(0);
+ if (null !== $security) {
+ // add SecurityTokenReference resolver for KeyInfo
+ $keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver');
+ XmlSecurityDSig::addKeyInfoResolver(self::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' => 'wsu', // used only for the xpath prefix
+ 'id_prefix_ns' => self::NS_WSU
+ );
+ if (XmlSecurityDSig::verifyReferences($signature, $options) !== true) {
+ throw new ClientException('The node signature or decryption was invalid');
+ }
+ // verify signature
+ if (XmlSecurityDSig::verifyDocumentSignature($signature) !== true) {
+ throw new ClientException('The document signature or decryption was invalid');
+ }
+ }
+
+ $security->parentNode->removeChild($security);
+ }
+ }
+}
diff --git a/src/WssWsSecurity/Serializer/WssSecurityHeaderEventListener.php b/src/WssWsSecurity/Serializer/WssSecurityHeaderEventListener.php
new file mode 100644
index 0000000..0eceadc
--- /dev/null
+++ b/src/WssWsSecurity/Serializer/WssSecurityHeaderEventListener.php
@@ -0,0 +1,48 @@
+ 'serializer.pre_deserialize',
+ 'method' => 'onPreDeserializeEvent',
+ 'class' => HeaderPlaceholder::class,
+ 'format' => 'xml'
+ ),
+ array(
+ 'event' => 'serializer.pre_deserialize',
+ 'method' => 'onPreDeserializeEvent',
+ 'class' => 'Ex\SoapEnvelope12\Messages\RequestHeaderInput',
+ 'format' => 'xml'
+ ),
+ );
+ }
+
+ public function __construct(WsSecurityFilterResponse $filter)
+ {
+ $this->filter = $filter;
+ }
+
+ public function onPreDeserializeEvent(PreDeserializeEvent $event)
+ {
+ $data = $event->getData();
+
+ $envelope = dom_import_simplexml($data);
+ $this->filter->filterDom($envelope->ownerDocument);
+echo $envelope->ownerDocument->saveXML();
+ $newData = simplexml_import_dom($envelope);
+ $event->setData($newData);
+ }
+}
diff --git a/src/WssWsSecurity/Serializer/WssSecurityHeaderHandler.php b/src/WssWsSecurity/Serializer/WssSecurityHeaderHandler.php
new file mode 100644
index 0000000..a52c60f
--- /dev/null
+++ b/src/WssWsSecurity/Serializer/WssSecurityHeaderHandler.php
@@ -0,0 +1,42 @@
+ GraphNavigator::DIRECTION_SERIALIZATION,
+ 'format' => 'xml',
+ 'type' => Security::class,
+ 'method' => 'serializeHeader'
+ ),
+ );
+ }
+
+ public function __construct(WsSecurityFilterRequest $filter)
+ {
+ $this->filter = $filter;
+ }
+
+ public function serializeHeader(XmlSerializationVisitor $visitor, Security $data, array $type, SerializationContext $context)
+ {
+ $currentNode = $visitor->getCurrentNode();
+ $securityHeader = $this->filter->filterDom($currentNode, $data);
+ $visitor->revertCurrentNode();
+ $visitor->setCurrentNode($securityHeader);
+ }
+}
diff --git a/tests/Client/BuildClientTest.php b/tests/Client/BuildClientTest.php
index 3216764..aab1d47 100644
--- a/tests/Client/BuildClientTest.php
+++ b/tests/Client/BuildClientTest.php
@@ -18,16 +18,20 @@ class BuildClientTest extends \PHPUnit_Framework_TestCase
*/
protected $factory;
+ protected static $namespaces = [
+ 'http://www.example.org/test/' => "Ex",
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Secext',
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Utility',
+ 'http://www.w3.org/2000/09/xmldsig#' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\XmlSign',
+ ];
+
public function setUp()
{
- $namespaces = [
- 'http://www.example.org/test/' => "Ex"
- ];
- $generator = new Generator($namespaces);
+ $generator = new Generator(self::$namespaces);
$serializer = $generator->buildSerializer();
$naming = new ShortNamingStrategy();
- $metadataGenerator = new MetadataGenerator($naming, $namespaces);
+ $metadataGenerator = new MetadataGenerator($naming, self::$namespaces);
$dispatcher = new EventDispatcher();
$wsdlReader = new DefinitionsReader(null, $dispatcher);
diff --git a/tests/Client/Client12RequestResponsesTest.php b/tests/Client/Client12RequestResponsesTest.php
index 4c84a5f..0f93d94 100644
--- a/tests/Client/Client12RequestResponsesTest.php
+++ b/tests/Client/Client12RequestResponsesTest.php
@@ -28,7 +28,10 @@
class Client12RequestResponsesTest extends \PHPUnit_Framework_TestCase
{
protected static $namespaces = [
- 'http://www.example.org/test/' => "Ex"
+ 'http://www.example.org/test/' => "Ex",
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Secext',
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Utility',
+ 'http://www.w3.org/2000/09/xmldsig#' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\XmlSign',
];
/**
* @var Generator
@@ -102,7 +105,7 @@ public function testGetSimple()
{
$httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
-
+
@@ -124,7 +127,7 @@ public function testGetSimpleUnwrapped()
{
$httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
-
+
@@ -155,7 +158,7 @@ public function testHeaders()
{
$httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
-
+
@@ -174,12 +177,12 @@ public function testHeaders()
$client->getSimple("foo", new MustUnderstandHeader($mp));
$this->assertXmlStringEqualsXmlString(
'
-
+
-
+
@@ -189,12 +192,12 @@ public function testHeaders()
$this->assertXmlStringEqualsXmlString(
'
-
+
-
+
@@ -250,7 +253,7 @@ public function testNoInput()
$this->responseMock->append(
new Response(200, ['Content-Type' => 'application/soap+xml'], '
-
+
@@ -292,7 +295,7 @@ public function testReturnMultiParam()
200,
['Content-Type' => 'application/soap+xml'],
'
-
+
@@ -313,7 +316,7 @@ public function testReturnMultiParam()
$this->assertXmlStringEqualsXmlString(
'
-
+
@@ -329,7 +332,7 @@ public function testMultiParamRequest()
200,
['Content-Type' => 'application/soap+xml'],
'
-
+
@@ -345,7 +348,7 @@ public function testMultiParamRequest()
$this->assertXmlStringEqualsXmlString(
'
-
+
diff --git a/tests/Client/ClientRequestResponsesTest.php b/tests/Client/ClientRequestResponsesTest.php
index 87576a9..3490b28 100644
--- a/tests/Client/ClientRequestResponsesTest.php
+++ b/tests/Client/ClientRequestResponsesTest.php
@@ -28,7 +28,10 @@
class ClientRequestResponsesTest extends \PHPUnit_Framework_TestCase
{
protected static $namespaces = [
- 'http://www.example.org/test/' => "Ex"
+ 'http://www.example.org/test/' => "Ex",
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Secext',
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Utility',
+ 'http://www.w3.org/2000/09/xmldsig#' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\XmlSign',
];
/**
* @var Generator
@@ -102,7 +105,7 @@ public function testGetSimple()
{
$httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
-
+
@@ -124,7 +127,7 @@ public function testGetSimpleUnwrapped()
{
$httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
-
+
@@ -155,7 +158,7 @@ public function testHeaders()
{
$httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
-
+
@@ -174,12 +177,12 @@ public function testHeaders()
$client->getSimple("foo", new MustUnderstandHeader($mp));
$this->assertXmlStringEqualsXmlString(
'
-
+
-
+
@@ -189,12 +192,12 @@ public function testHeaders()
$this->assertXmlStringEqualsXmlString(
'
-
+
-
+
@@ -222,7 +225,7 @@ public function testNoInput()
$this->responseMock->append(
new Response(200, ['Content-Type' => 'text/xml'], '
-
+
@@ -264,7 +267,7 @@ public function testReturnMultiParam()
200,
['Content-Type' => 'text/xml'],
'
-
+
@@ -285,7 +288,7 @@ public function testReturnMultiParam()
$this->assertXmlStringEqualsXmlString(
'
-
+
@@ -301,7 +304,7 @@ public function testMultiParamRequest()
200,
['Content-Type' => 'text/xml'],
'
-
+
@@ -317,7 +320,7 @@ public function testMultiParamRequest()
$this->assertXmlStringEqualsXmlString(
'
-
+
diff --git a/tests/Fixtures/client_private_key.pem b/tests/Fixtures/client_private_key.pem
new file mode 100644
index 0000000..ac19d1f
--- /dev/null
+++ b/tests/Fixtures/client_private_key.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMsT5SynGYPCrpIP
+1LkVZvEZwI9YoUQOHmnj4AN0SklWsZTOwBsJGJoi9MKm+QNAuLMFeYezn7a7nyEK
+7Awls7yvig7xRn2yj5KskroAdyTvwB6s8yx+1BfkwOCZCOpOiYBNxpsRezjafYD8
+2SgwPywL3OYjgRUBraAbUPXt1KfrAgMBAAECgYABCb3/J2+C8+jOiFQvCgP3sYkB
+cpOIdIYFRovrmJmUnGPV/eSPftFoYEtd/1qAgMEw8RM49VsYpQbgNV0Vhs/PYcQB
+Jlpo/Lhv58/BiOVYei2bi3lQsIM2Y+LppF06JqcbpGn7neWILfYQiMKF4Zjxrj2F
+v3JWJfTE2RzRUqQx+QJBAPU6MXbf+4eUl4VJkFAWG2qleDZsGNfutBxzKMLv2HWn
+ciTLo+VXMfSxVNVECH8sjbwWYEAY0MiJZk1ZpVtk900CQQDT/7Dh5qFmQxmH1L40
+rGfgvaHQzsjk3pYkia84A0dudthExvpSPGwoGDY5HWwEgHKczWjfhZ1olXlSbKpk
+9DAXAkEApVrDBdRMSATDEuYiwE3X2NaQs6m6KshTfKeOQbv2qobpKbSC5F8iWUvF
+1zRTwmUpgT1ZU38oMUCs0dVz8aeoNQJBAKISrXDWt/eNPtx4SX3NfJD1iNsw66cF
+gHWoiTtiTl7mHsrd8Aukw+8XK4UYuDbs2DKGWzHfXYrSE3FvQAl0IbsCQQDPj/Ua
+KvpKA+HNpaHUyPrVG2d/tBJgcvh7Q3EF+2LuBxS1gW/4VulhNwhbbm8Wi1xz3mCC
+rmejXgOSRBlBAjDW
+-----END PRIVATE KEY-----
diff --git a/tests/Fixtures/client_public_key.pem b/tests/Fixtures/client_public_key.pem
new file mode 100644
index 0000000..bdc043c
--- /dev/null
+++ b/tests/Fixtures/client_public_key.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLE+UspxmDwq6SD9S5FWbxGcCP
+WKFEDh5p4+ADdEpJVrGUzsAbCRiaIvTCpvkDQLizBXmHs5+2u58hCuwMJbO8r4oO
+8UZ9so+SrJK6AHck78AerPMsftQX5MDgmQjqTomATcabEXs42n2A/NkoMD8sC9zm
+I4EVAa2gG1D17dSn6wIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tests/Fixtures/config.yml b/tests/Fixtures/config.yml
index 3d06155..8377ae0 100644
--- a/tests/Fixtures/config.yml
+++ b/tests/Fixtures/config.yml
@@ -8,11 +8,13 @@ soap_client:
'http://www.example.org/test/': 'TestNs'
destinations_php:
'TestNs': soap/src
+
destinations_jms:
'TestNs\SoapEnvelope12': soap/metadata/soap-env-12
'TestNs\SoapEnvelope': soap/metadata/soap-env-11
'TestNs\SoapParts': soap/metadata/soap-parts
'TestNs': soap/metadata
+
aliases:
'http://www.example.org/test/':
responseHeaderMessagesResponse: 'HeaderResponse'
diff --git a/tests/Fixtures/server_private_key.pem b/tests/Fixtures/server_private_key.pem
new file mode 100644
index 0000000..ee0af3c
--- /dev/null
+++ b/tests/Fixtures/server_private_key.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMOlvDM/vmkXG4m7
+TdXdY4f6yS4FsXEDRPfxuX2eM3wl1pNd6ZYVmCvby3xf1SXyV+b+RaY5IYHmT9rn
+zXkJMQHTH5Kk2kxmUic9oDvYJak3FygW4bejnVsZ+U3z8S1LEyMuBEOvqgUJ11aA
+3GPXVZI66so37mOEhW0/I50uHnddAgMBAAECgYEAhozhjGFHQyDIKIWu9ujFfYvk
+dYkmyfEUqmwMRC7be4LOOhT5AuWg/HCxVbzWar1q5Ip0PefGen545rRKI/ZFQ6U+
+Fp0dH/4KIjVJP3YWSXB4GcJ/BtBj5op1o0T6rthIkvBLZULtu991+dbmnsOgsJ/x
+Cgm68Eu8NngQ+mucqsECQQD6PrIRoVy6T5mYfID9V/aE4GEbDKPEKx7eaWa6prUj
+BLXiATTR1txlV7VjiBzIQ+o/abtn+MQ9culIEbXwM5zxAkEAyCWZeP0Bt1LcQjFR
+Vjfo4Nwyu/Z32Tpz/wNZakwuFbDWZ5vbN5LKDbs+gYyFqV97Kgqr10Z8tiEJs/4+
+Bx3xLQJACK3d/TCMh8W0/Q3sZ10Cps8lbwu8LlSUiIA9WOHpTGKgcEs8ar65/CXT
+m7Uf0m5QlIx1PIDrRXpTzvUWS1Nu8QJBAKxC4EKnz9BO+s/lzpGccU0HeIsaaLCI
+hMmZwl2gz5FPsFlgZV8BcfI7lGK/5VKPoVvf72LLgg7nhIhsbEqH1MkCQFcRuk/k
+VHZQYJ2lxcqT4Q8BJiXppoplJjxM1xWnjYHGwDh6+T/yGgWIgbtWg3lv9q8z3sHH
+qnNxabaU4Dz9wt4=
+-----END PRIVATE KEY-----
diff --git a/tests/Fixtures/server_public_key.pem b/tests/Fixtures/server_public_key.pem
new file mode 100644
index 0000000..c87dbb0
--- /dev/null
+++ b/tests/Fixtures/server_public_key.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDpbwzP75pFxuJu03V3WOH+sku
+BbFxA0T38bl9njN8JdaTXemWFZgr28t8X9Ul8lfm/kWmOSGB5k/a5815CTEB0x+S
+pNpMZlInPaA72CWpNxcoFuG3o51bGflN8/EtSxMjLgRDr6oFCddWgNxj11WSOurK
+N+5jhIVtPyOdLh53XQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tests/Fixtures/test.wsdl b/tests/Fixtures/test.wsdl
index 84cf392..933a8eb 100644
--- a/tests/Fixtures/test.wsdl
+++ b/tests/Fixtures/test.wsdl
@@ -2,7 +2,8 @@
@@ -199,7 +200,7 @@
-
+
@@ -345,123 +346,141 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -582,6 +601,7 @@
+
diff --git a/tests/Serializer/WssSecurityTest.php b/tests/Serializer/WssSecurityTest.php
new file mode 100644
index 0000000..7f224b2
--- /dev/null
+++ b/tests/Serializer/WssSecurityTest.php
@@ -0,0 +1,437 @@
+ "Ex",
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Secext',
+ 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\Utility',
+ 'http://www.w3.org/2000/09/xmldsig#' => 'GoetasWebservices\SoapServices\SoapClient\WssWsSecurity\XmlSign',
+ ];
+
+
+ public static function setUpBeforeClass()
+ {
+ self::$generator = new Generator(self::$namespaces);
+ self::$generator->generate([__DIR__ . '/../Fixtures/test.wsdl']);
+ self::$generator->registerAutoloader();
+ }
+
+ public static function tearDownAfterClass()
+ {
+ self::$generator->unRegisterAutoloader();
+ //self::$generator->cleanDirectories();
+ }
+
+ public function setUp()
+ {
+
+ $generator = self::$generator;
+ $ref = new \ReflectionClass(Fault12::class);
+
+ $this->requestFilter = new WsSecurityFilterRequest();
+ $this->responseFilter = new WsSecurityFilterResponse();
+
+ $keypair1 = new SecurityKeyPair();
+ $keypair1->setPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, __DIR__.'/../Fixtures/client_private_key.pem');
+ $keypair1->setPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, __DIR__.'/../Fixtures/client_public_key.pem');
+
+ $this->requestFilter->setUserSecurityKeyObject($keypair1);
+
+ $keypair2 = new SecurityKeyPair();
+ $keypair2->setPrivateKey(\ass\XmlSecurity\Key::TRIPLEDES_CBC);
+ $keypair2->setPublicKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/server_public_key.pem');
+
+ $this->requestFilter->setServiceSecurityKeyObject($keypair2);
+
+ $keypair3 = new SecurityKeyPair();
+ $keypair3->setPrivateKey(\ass\XmlSecurity\Key::RSA_SHA1, __DIR__.'/../Fixtures/server_private_key.pem');
+ $keypair3->setPublicKey(\ass\XmlSecurity\Key::RSA_SHA1, __DIR__.'/../Fixtures/client_public_key.pem');
+
+
+ $this->responseFilter->setUserSecurityKeyObject($keypair3);
+
+ $this->headerHandler = new HeaderHandler();
+ $this->serializer = $generator->buildSerializer(function (HandlerRegistryInterface $h) {
+ $h->registerSubscribingHandler($this->headerHandler);
+
+ $sechandler = new WssSecurityHeaderHandler($this->requestFilter);
+ $h->registerSubscribingHandler($sechandler);
+ }, [
+ 'GoetasWebservices\SoapServices\SoapClient\Envelope\SoapEnvelope12' => dirname($ref->getFileName()) . '/../../../Resources/metadata/jms12',
+ ], function(EventDispatcherInterface $d) {
+ $d->addSubscriber(new WssSecurityHeaderEventListener($this->responseFilter));
+ });
+
+ }
+
+ public function testSerializeSecurity()
+ {
+ $headerPlaceholder = new HeaderPlaceholder();
+
+ $this->requestFilter->setSecurityOptionsEncryption(WsSecurityFilterRequest::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER);
+
+ $security = new Security();
+ $security->setUsername('foo');
+ $security->setPassword('pass');
+
+ $auth = new \Ex\AuthHeader();
+ $auth->setUser('bar');
+
+ $this->headerHandler->addHeaderData($headerPlaceholder, new MustUnderstandHeader($security));
+ $this->headerHandler->addHeaderData($headerPlaceholder, new MustUnderstandHeader($auth));
+
+ $env = new RequestHeaderInput();
+ $env->setHeader($headerPlaceholder);
+
+ $body = new \Ex\SoapParts\RequestHeaderInput();
+ $p = new \Ex\RequestHeader();
+ $p->setIn("sss");
+ $body->setParameters($p);
+ $env->setBody($body);
+
+ $xml = $this->serializer->serialize($env, 'xml');
+
+ $expectedString = '
+
+
+
+
+
+
+
+
+
+
+ aPfDzwzxuVaqRXvKfnBIKxqyPgdyZvBLm0JyNXRyZDn7HNHoX7cuAtT5tHne9lQP4/9DRXyhA/1HdxY755dv8IS9PMcNpGGEdhwy2UgTko0zDynpwUcvrslxukFehskvhhyFAxIShmbFJzRZjbq/OYeggiXWhcg+/tezDuyNbHSdnWcSq8w9+oO5UTog4Uj47DWY+2r6iP1/Ln7boMi8wn2xABIxkBFFU32z51tngtMIzvWyoFFDf9xc3jDgz5Vd9t4jUbm9akH68m2TAngGahGOEzIa3JudVLEkDLJXT8UCZkuHmP85r2QGEMz/8cMJOE8cjDKtAlbMy1lL7kbzSFA2yYZBEkyXM+F1y1E/A7YS+/mrQ2VqdDldEQNtfSiaV/VPEonrGJkFrSWlYeEUWF2Cg8rEerDrmYZX3vLyVxrtjNHSA5JVaQOpwWO0BGajIuEiCgoHRbA1zPmtpoZh9u39Hd/F8bg9ZBDCOky1L83f0IW5LHUjYOv0FxqFXftdKvnF7/aDbiZnhX3330p/Iw2guOWpwJTHyOilfMXo0sC3jY/hB36jIznYb+TtAMKB3lki47jVj2DREYQDh/nsH1HhbpgFe+JhtFvV3ewZGjc=
+
+
+
+
+
+
+ 2016-12-21T14:04:53.000Z
+ 2016-12-21T14:09:53.000Z
+
+
+ foo
+
+
+
+
+
+
+
+
+
+
+ og8QzG1KkeF3W302te9V5leP4Mnvrb+qJEld055MFCYUFN6znwR0dbp/P3YRqV0efBIkF6MA5Gj4TX6TkyIvHjELTJNyQDpfrLWXZBhzP+6AdrCQzpWZ0p3sYUJXDVoYXEKhfPYmdpReMoK/H4cmcfdZj2B/ZcMmL/ZCQNxOAv4=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ji4+Hoi++5jtopiElK3EFL/AcTM=
+
+
+
+
+
+
+ H4BPAIlGlXwRxZcXxgJTgBMxQ5Y=
+
+
+
+
+
+
+ Unz40wju5ExcAkTC5PMMJaRxZV8=
+
+
+ kRjlhN3nNNHciRBaD89V89NDZyiKrHV1WcgmQ7mdKyBgXeRMhitnYIt0YsPqwA0DA86doFNiMqfkI4OqcCltUL4VQt3MiMn+6st5R/tQgGoExZ/VE5QqzWjZiJEk8nLcDmqVmOFS8HSsp3cct2rj4PhxBo1IxSo2ZJo0psBg3G4=
+
+
+
+
+
+
+
+';
+
+ $this->assertXmlStringEqualsXmlString($this->cleanXML($expectedString), $this->cleanXML($xml));
+ }
+
+ public function testDeSerializeSecurity()
+ {
+
+ $headerPlaceholder = new HeaderPlaceholder();
+
+ $this->requestFilter->setSecurityOptionsEncryption(WsSecurityFilterRequest::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER);
+ $this->requestFilter->setSecurityOptionsSignature(WsSecurityFilterRequest::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER);
+
+ $keypair1 = new SecurityKeyPair();
+ $keypair1->setPrivateKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/client_private_key.pem');
+ $keypair1->setPublicKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/client_public_key.pem');
+
+ $keypair2 = new SecurityKeyPair();
+ $keypair2->setPrivateKey(\ass\XmlSecurity\Key::TRIPLEDES_CBC, str_repeat("1", 24));
+ $keypair2->setPublicKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/server_public_key.pem');
+
+ $keypair3 = new SecurityKeyPair();
+ $keypair3->setPrivateKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/server_private_key.pem');
+
+ $keypair4 = new SecurityKeyPair();
+ $keypair4->setPublicKey(\ass\XmlSecurity\Key::RSA_1_5, __DIR__.'/../Fixtures/client_public_key.pem', true);
+
+ $this->requestFilter->setUserSecurityKeyObject($keypair1);
+ $this->requestFilter->setServiceSecurityKeyObject($keypair2);
+
+ $this->responseFilter->setServiceSecurityKeyObject($keypair4);
+ $this->responseFilter->setUserSecurityKeyObject($keypair3);
+
+ $security = new Security();
+ $security->setUsername('foo');
+
+ $this->headerHandler->addHeaderData($headerPlaceholder, new MustUnderstandHeader($security));
+
+
+ $env = new RequestHeaderInput();
+ $env->setHeader($headerPlaceholder);
+
+ $body = new \Ex\SoapParts\RequestHeaderInput();
+ $p = new \Ex\RequestHeader();
+ $p->setIn("sss");
+ $body->setParameters($p);
+ $env->setBody($body);
+
+ $xmlString = $this->serializer->serialize($env, 'xml');
+
+ /**
+ * @var $object RequestHeaderInput
+ */
+ $object = $this->serializer->deserialize($xmlString, RequestHeaderInput::class, 'xml');
+
+ $this->assertEquals("sss", $object->getBody()->getParameters()->getIn());
+ $this->assertInstanceOf('Ex\RequestHeader', $object->getBody()->getParameters());
+ }
+
+ public function testSerializeSecurityWithTokenTypes()
+ {
+ $headerPlaceholder = new HeaderPlaceholder();
+
+ $this->requestFilter->setSecurityOptionsEncryption(WsSecurityFilterRequest::TOKEN_REFERENCE_SECURITY_TOKEN);
+ $this->requestFilter->setSecurityOptionsSignature(WsSecurityFilterRequest::TOKEN_REFERENCE_SECURITY_TOKEN);
+
+ $security = new Security();
+ $security->setUsername('foo');
+ $security->setPassword('pass');
+
+ $this->headerHandler->addHeaderData($headerPlaceholder, new MustUnderstandHeader($security));
+
+ $env = new RequestHeaderInput();
+ $env->setHeader($headerPlaceholder);
+
+ $body = new \Ex\SoapParts\RequestHeaderInput();
+ $p = new \Ex\RequestHeader();
+ $p->setIn("sss");
+ $body->setParameters($p);
+ $env->setBody($body);
+
+ $xml = $this->serializer->serialize($env, 'xml');
+
+ $expectedString = '
+
+
+
+
+
+
+
+
+
+
+ RuqNIV9tlKbV/XBoB0vxN5DjvCuVtJArIgSKcOQ8LoS7CEuD3mbmHsbDwAf3nEWQ7zRrQTw2C3XC+J5xNrMpyaAmiJuSea4TdhhSJ0uMFgtKY9OpSVEAEvFklx7kaOJTxTg+M1DMopAljldbRFVlghmTR0g2PIPaXhJfawO6HbjIeAJ8BfYblDnzoHv6CzpbaRJCKcbi1PjlLSaub1kQtjdONWv5VfLh1jB7M+EgWKGVbjLG6Xq1cColAo24NaD1TI+deaNKT4/Rw3w3zJKnxbEIQ01xgCtiB6AfR6G9851FXqR0OdcqHhVe0vfnjT+NGDEwazgLX6XcnwFDkge7zJ/vqKHWC1Jyjl2ym/P3JTSjje/ozFPNnQjlMVSAK0c5wA57+HsA3OCo9fHWtQoaRw7cboMOg0Qg6IoUi2+g0OhWgC0EKVnUqQzFGqDEXD9S3gIsv7Q+jCpwFEcKvkYe287VgeKmePOqbBLuA5LcMRYDMoOGtY0CUpe0A1klC2cGIYn2EMpGcTK4+ckUp5Hz02JgK6VnaI54f8EaVIV2hhrnnJSes+3/94v3erytbY6pROvM6TzFUDc5dOD+ZU07MfIPZAhV0h0dXmpObiyctpw=
+
+
+
+
+
+
+ 2016-12-21T14:00:49.000Z
+ 2016-12-21T14:05:49.000Z
+
+
+ foo
+
+
+
+
+
+
+
+
+
+
+ lWbYQaXg8hhPxjyMlBMCt0dsxt3NWFZWIlNL3lq6s1qsvQ+UN8Gn1jJYivPkLTtiGKatTtwQijS8iBcOmyp73/27EkGwLqovbhnkz+ZmWdyIcFtdD2qpcb2HD10cppo0a8QPmwEtXIYPM5GlH8kLXm4u9kn9OJA5FreCFASCwT0=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RHWAhMuWzw2uUF5cBc6ObCBpC9I=
+
+
+
+
+
+
+ CaIRD27prYA1YCyY4fUV5L9rj7Y=
+
+
+
+
+
+
+ Mbbxwz6mcQhxMbiw8RIKHqyXeHs=
+
+
+ F8aOS40ypNHMHnsWDbkWb27NAEezKV7RmodKFuXjmkRB70YKoGUMIZSyvIlaj/E6GQ7a27gQyv6Kjzb8XTemQw+I8kgMGXiECVplbhuH3OayOKvMXPo8g4y0+7P5uf7h0OhFoq9U2Pbya3HDqsxVm5AUdl5AAm9SZkNLoC43tuc=
+
+
+
+
+
+
+
+
+
+';
+
+ $this->assertXmlStringEqualsXmlString($this->cleanXML($expectedString), $this->cleanXML($xml));
+ }
+
+ public function testSerializeSecurityWithNoCerts()
+ {
+ $headerPlaceholder = new HeaderPlaceholder();
+
+ $this->requestFilter->setServiceSecurityKeyObject(null);
+ $this->requestFilter->setUserSecurityKeyObject(null);
+
+ $security = new Security();
+ $security->setUsername('foo');
+ $security->setPassword('pass');
+
+ $this->headerHandler->addHeaderData($headerPlaceholder, new MustUnderstandHeader($security));
+
+ $env = new RequestHeaderInput();
+ $env->setHeader($headerPlaceholder);
+
+ $body = new \Ex\SoapParts\RequestHeaderInput();
+ $p = new \Ex\RequestHeader();
+ $p->setIn("sss");
+ $body->setParameters($p);
+ $env->setBody($body);
+
+ $xml = $this->serializer->serialize($env, 'xml');
+
+ $expectedString = '
+
+
+
+
+
+
+
+
+
+ 2016-12-21T09:51:41.000Z
+ 2016-12-21T09:56:41.000Z
+
+
+ foo
+ E+rwHP8u/HO0gP/9gHweD4hm/Lk=
+ MTg3NzQxNjUzMw==
+ 2016-12-21T09:51:41.000Z
+
+
+
+ ';
+
+ $this->assertXmlStringEqualsXmlString($this->cleanXML($expectedString), $this->cleanXML($xml));
+ }
+
+ private function cleanXML($xml)
+ {
+ $xml = preg_replace('~(Id|EncKey|Cert|\#)[a-f0-9\-]+~', 'X', $xml);
+ $xml = preg_replace('~\d{4}\-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*?Z~', 'X', $xml);
+
+ $tags = ['ds:SignatureValue', 'ds:DigestValue', 'xenc:CipherValue', 'ws:BinarySecurityToken', 'ws:Nonce', 'ws:Password'];
+ foreach ($tags as $tag) {
+ $xml = preg_replace("~(<$tag.*?>)(.+?)(<\\/$tag>)~", '\1abc\3', $xml);
+ }
+ return $xml;
+ }
+}
diff --git a/tests/example.php b/tests/example.php
index d7809b9..51f78da 100644
--- a/tests/example.php
+++ b/tests/example.php
@@ -26,7 +26,7 @@
$responseMock = new MockHandler();
$httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
-
+