diff --git a/composer.json b/composer.json index 2bc71ca9..6e835011 100644 --- a/composer.json +++ b/composer.json @@ -24,8 +24,8 @@ "ext-soap": "*", "ext-curl": "*", "ass/xmlsecurity": "~1.0", - "symfony/framework-bundle": "~2.6", - "symfony/twig-bundle": "~2.6", + "symfony/framework-bundle": "^3.0 || ^4.0", + "symfony/twig-bundle": "^3.0 || ^4.0", "zendframework/zend-mime": "2.1.*" }, "replace": { diff --git a/src/BeSimple/SoapBundle/Controller/SoapWebServiceController.php b/src/BeSimple/SoapBundle/Controller/SoapWebServiceController.php index 29d08c36..83cefca8 100644 --- a/src/BeSimple/SoapBundle/Controller/SoapWebServiceController.php +++ b/src/BeSimple/SoapBundle/Controller/SoapWebServiceController.php @@ -16,19 +16,20 @@ use BeSimple\SoapBundle\Soap\SoapRequest; use BeSimple\SoapBundle\Soap\SoapResponse; use BeSimple\SoapServer\SoapServerBuilder; -use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\FlattenException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * @author Christian Kerl * @author Francis Besset */ -class SoapWebServiceController extends ContainerAware +class SoapWebServiceController extends AbstractController { /** * @var \SoapServer @@ -58,13 +59,14 @@ class SoapWebServiceController extends ContainerAware /** * @return \BeSimple\SoapBundle\Soap\SoapResponse */ - public function callAction($webservice) + public function callAction(Request $request, $webservice) { $webServiceContext = $this->getWebServiceContext($webservice); $this->serviceBinder = $webServiceContext->getServiceBinder(); - $this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request')); + $this->soapRequest = SoapRequest::createFromHttpRequest($request); + $this->soapServer = $webServiceContext ->getServerBuilder() ->withSoapVersion11() @@ -85,17 +87,16 @@ public function callAction($webservice) /** * @return Symfony\Component\HttpFoundation\Response */ - public function definitionAction($webservice) + public function definitionAction(Request $request, $webservice) { $response = new Response($this->getWebServiceContext($webservice)->getWsdlFileContent( $this->container->get('router')->generate( '_webservice_call', array('webservice' => $webservice), - true + UrlGeneratorInterface::ABSOLUTE_URL ) )); - $request = $this->container->get('request'); $query = $request->query; if ($query->has('wsdl') || $query->has('WSDL')) { $request->setRequestFormat('wsdl'); @@ -121,9 +122,9 @@ public function exceptionAction(Request $request, FlattenException $exception, D throw new \LogicException(sprintf('The parameter "%s" is required in Request::$query parameter bag to generate the SoapFault.', '_besimple_soap_webservice'), null, $e); } - $view = 'TwigBundle:Exception:'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig'; + $view = '@Twig/Exception/'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig'; $code = $exception->getStatusCode(); - $details = $this->container->get('templating')->render($view, array( + $details = $this->container->get('twig')->render($view, array( 'status_code' => $code, 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', 'exception' => $exception, diff --git a/src/BeSimple/SoapBundle/Converter/TypeRepository.php b/src/BeSimple/SoapBundle/Converter/TypeRepository.php index 79b571e1..6a56b44c 100644 --- a/src/BeSimple/SoapBundle/Converter/TypeRepository.php +++ b/src/BeSimple/SoapBundle/Converter/TypeRepository.php @@ -10,7 +10,6 @@ namespace BeSimple\SoapBundle\Converter; -use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition; use BeSimple\SoapBundle\Util\Assert; /** @@ -45,22 +44,4 @@ public function getXmlTypeMapping($phpType) { return isset($this->defaultTypeMap[$phpType]) ? $this->defaultTypeMap[$phpType] : null; } - - public function fixTypeInformation(ServiceDefinition $definition) - { - foreach($definition->getAllTypes() as $type) { - $phpType = $type->getPhpType(); - $xmlType = $type->getXmlType(); - - if (null === $phpType) { - throw new \InvalidArgumentException(); - } - - if (null === $xmlType) { - $xmlType = $this->getXmlTypeMapping($phpType); - } - - $type->setXmlType($xmlType); - } - } } diff --git a/src/BeSimple/SoapBundle/DependencyInjection/BeSimpleSoapExtension.php b/src/BeSimple/SoapBundle/DependencyInjection/BeSimpleSoapExtension.php old mode 100644 new mode 100755 index 4e2b47aa..8b994ff5 --- a/src/BeSimple/SoapBundle/DependencyInjection/BeSimpleSoapExtension.php +++ b/src/BeSimple/SoapBundle/DependencyInjection/BeSimpleSoapExtension.php @@ -17,10 +17,12 @@ use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use BeSimple\SoapCommon\WsSecurityFilterClientServer; +use BeSimple\SoapBundle\Controller\SoapWebServiceController; /** * BeSimpleSoapExtension. @@ -46,10 +48,9 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('converters.xml'); $loader->load('webservice.xml'); - $processor = new Processor(); $configuration = new Configuration(); - $config = $processor->process($configuration->getConfigTree(), $configs); + $config = $this->processConfiguration($configuration, $configs); $this->registerCacheConfiguration($config['cache'], $container, $loader); @@ -83,7 +84,7 @@ private function registerClientConfiguration(array $config, ContainerBuilder $co $loader->load('client.xml'); foreach ($config as $client => $options) { - $definition = new DefinitionDecorator('besimple.soap.client.builder'); + $definition = new ChildDefinition('besimple.soap.client.builder'); $container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition); $definition->replaceArgument(0, $options['wsdl']); @@ -130,7 +131,7 @@ private function registerClientConfiguration(array $config, ContainerBuilder $co private function createClientClassmap($client, array $classmap, ContainerBuilder $container) { - $definition = new DefinitionDecorator('besimple.soap.classmap'); + $definition = new ChildDefinition('besimple.soap.classmap'); $container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition); if (!empty($classmap)) { @@ -144,7 +145,7 @@ private function createClientClassmap($client, array $classmap, ContainerBuilder private function createClient($client, ContainerBuilder $container) { - $definition = new DefinitionDecorator('besimple.soap.client'); + $definition = new ChildDefinition('besimple.soap.client'); $container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition); $definition->setFactory(array( @@ -159,13 +160,19 @@ private function createWebServiceContext(array $config, ContainerBuilder $contai unset($config['binding']); $contextId = 'besimple.soap.context.'.$config['name']; - $definition = new DefinitionDecorator('besimple.soap.context.'.$bindingSuffix); + $definition = new ChildDefinition('besimple.soap.context.'.$bindingSuffix); + $definition->setPublic(true); + $container->setDefinition($contextId, $definition); if (isset($config['cache_type'])) { $config['cache_type'] = $this->getCacheType($config['cache_type']); } + if (isset($config['wsse'])) { + $config['wsse']['password_type'] = $this->getPasswordType($config['wsse']['password_type']); + } + $options = $container ->getDefinition('besimple.soap.context.'.$bindingSuffix) ->getArgument(2); @@ -189,4 +196,15 @@ private function getCacheType($type) return Cache::TYPE_DISK_MEMORY; } } + + private function getPasswordType($type) + { + switch ($type) { + case 'PasswordText': + return WsSecurityFilterClientServer::PASSWORD_TYPE_TEXT; + + case 'PasswordDigest': + return WsSecurityFilterClientServer::PASSWORD_TYPE_DIGEST; + } + } } diff --git a/src/BeSimple/SoapBundle/DependencyInjection/Configuration.php b/src/BeSimple/SoapBundle/DependencyInjection/Configuration.php old mode 100644 new mode 100755 index 9374bff2..2569c370 --- a/src/BeSimple/SoapBundle/DependencyInjection/Configuration.php +++ b/src/BeSimple/SoapBundle/DependencyInjection/Configuration.php @@ -12,6 +12,7 @@ namespace BeSimple\SoapBundle\DependencyInjection; +use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; @@ -21,20 +22,27 @@ * @author Christian Kerl * @author Francis Besset */ -class Configuration +class Configuration implements ConfigurationInterface { private $cacheTypes = array('none', 'disk', 'memory', 'disk_memory'); private $proxyAuth = array('basic', 'ntlm'); + private $passwordTypes = array('PasswordText', 'PasswordDigest'); /** * Generates the configuration tree. * * @return \Symfony\Component\Config\Definition\ArrayNode The config tree */ - public function getConfigTree() + public function getConfigTreeBuilder() { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('be_simple_soap'); + $treeBuilder = new TreeBuilder('be_simple_soap'); + + if (method_exists($treeBuilder, 'getRootNode')) { + $rootNode = $treeBuilder->getRootNode(); + } else { + // BC layer for symfony/config 4.1 and older + $rootNode = $treeBuilder->root('be_simple_soap'); + } $this->addCacheSection($rootNode); $this->addClientSection($rootNode); @@ -43,11 +51,11 @@ public function getConfigTree() $rootNode ->children() - ->scalarNode('exception_controller')->defaultValue('BeSimpleSoapBundle:SoapWebService:exception')->end() + ->scalarNode('exception_controller')->defaultValue('BeSimple\SoapBundle\Controller\SoapWebServiceController::exceptionAction')->end() ->end() ; - return $treeBuilder->buildTree(); + return $treeBuilder; } private function addCacheSection(ArrayNodeDefinition $rootNode) @@ -145,6 +153,19 @@ private function addServicesSection(ArrayNodeDefinition $rootNode) ->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes))) ->end() ->end() + ->arrayNode('wsse') + ->children() + ->scalarNode('password_type') + ->defaultValue($this->passwordTypes[0]) + ->validate() + ->ifNotInArray($this->passwordTypes) + ->thenInvalid(sprintf('The password type has to be either: %s', implode(', ', $this->passwordTypes))) + ->end() + ->end() + ->scalarNode('username')->defaultNull()->end() + ->scalarNode('password')->defaultNull()->end() + ->end() + ->end() ->end() ->end() ->end() diff --git a/src/BeSimple/SoapBundle/EventListener/SoapExceptionListener.php b/src/BeSimple/SoapBundle/EventListener/SoapExceptionListener.php index c31fb393..65b1efde 100644 --- a/src/BeSimple/SoapBundle/EventListener/SoapExceptionListener.php +++ b/src/BeSimple/SoapBundle/EventListener/SoapExceptionListener.php @@ -13,15 +13,15 @@ namespace BeSimple\SoapBundle\EventListener; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; -use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\Event\ExceptionEvent; +use Symfony\Component\HttpKernel\EventListener\ErrorListener; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; /** * @author Francis Besset */ -class SoapExceptionListener extends ExceptionListener +class SoapExceptionListener extends ErrorListener { /** * @var ContainerInterface @@ -44,7 +44,7 @@ public function __construct(ContainerInterface $container, $controller, $logger) $this->container = $container; } - public function onKernelException(GetResponseForExceptionEvent $event) + public function onSoapKernelException(ExceptionEvent $event) { if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { return; @@ -69,7 +69,7 @@ public function onKernelException(GetResponseForExceptionEvent $event) // hack to retrieve the current WebService name in the controller $request->query->set('_besimple_soap_webservice', $webservice); - $exception = $event->getException(); + $exception = $event->getThrowable(); if ($exception instanceof \SoapFault) { $request->query->set('_besimple_soap_fault', $exception); } @@ -77,11 +77,12 @@ public function onKernelException(GetResponseForExceptionEvent $event) parent::onKernelException($event); } - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { - return array( - // Must be called before ExceptionListener of HttpKernel component - KernelEvents::EXCEPTION => array('onKernelException', -64), - ); + return [ + KernelEvents::EXCEPTION => [ + ['onSoapKernelException', -64], + ], + ]; } } diff --git a/src/BeSimple/SoapBundle/Handler/ExceptionHandler.php b/src/BeSimple/SoapBundle/Handler/ExceptionHandler.php index 54c12b42..7fffa87c 100644 --- a/src/BeSimple/SoapBundle/Handler/ExceptionHandler.php +++ b/src/BeSimple/SoapBundle/Handler/ExceptionHandler.php @@ -14,7 +14,7 @@ use BeSimple\SoapServer\Exception\ReceiverSoapFault; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\FlattenException; +use Symfony\Component\ErrorHandler\Exception\FlattenException; /** * @author Francis Besset diff --git a/src/BeSimple/SoapBundle/Resources/config/converters.xml b/src/BeSimple/SoapBundle/Resources/config/converters.xml old mode 100644 new mode 100755 index f3f9c526..4f8f4340 --- a/src/BeSimple/SoapBundle/Resources/config/converters.xml +++ b/src/BeSimple/SoapBundle/Resources/config/converters.xml @@ -8,6 +8,7 @@ BeSimple\SoapCommon\Converter\TypeConverterCollection BeSimple\SoapCommon\Converter\DateTimeTypeConverter BeSimple\SoapCommon\Converter\DateTypeConverter + BeSimple\SoapCommon\Converter\TimeTypeConverter @@ -20,5 +21,9 @@ + + + + diff --git a/src/BeSimple/SoapBundle/Resources/config/routing/webservicecontroller.xml b/src/BeSimple/SoapBundle/Resources/config/routing/webservicecontroller.xml index 98e80e44..d399fca8 100644 --- a/src/BeSimple/SoapBundle/Resources/config/routing/webservicecontroller.xml +++ b/src/BeSimple/SoapBundle/Resources/config/routing/webservicecontroller.xml @@ -4,15 +4,13 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - - BeSimpleSoapBundle:SoapWebService:Call + + BeSimple\SoapBundle\Controller\SoapWebServiceController::CallAction xml - POST - - BeSimpleSoapBundle:SoapWebService:Definition + + BeSimple\SoapBundle\Controller\SoapWebServiceController::DefinitionAction xml - GET diff --git a/src/BeSimple/SoapBundle/Resources/config/webservice.xml b/src/BeSimple/SoapBundle/Resources/config/webservice.xml old mode 100644 new mode 100755 index 343885c4..41315c8b --- a/src/BeSimple/SoapBundle/Resources/config/webservice.xml +++ b/src/BeSimple/SoapBundle/Resources/config/webservice.xml @@ -19,7 +19,15 @@ - + + + + + + + + + @@ -96,6 +104,10 @@ dateTime xsd:dateTime + + time + xsd:time + diff --git a/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralRequestMessageBinder.php b/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralRequestMessageBinder.php old mode 100644 new mode 100755 index 13abf080..beb793aa --- a/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralRequestMessageBinder.php +++ b/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralRequestMessageBinder.php @@ -81,7 +81,14 @@ protected function processType($phpType, $message) $message = $array; } else { - $message = $this->checkComplexType($phpType, $message); + if (is_array($message)) { + foreach ($message as $complexType) { + $array[] = $this->checkComplexType($phpType, $complexType); + } + $message = $array; + } else { + $message = $this->checkComplexType($phpType, $message); + } } } elseif ($isArray) { if (isset($message->item)) { diff --git a/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralResponseMessageBinder.php b/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralResponseMessageBinder.php old mode 100644 new mode 100755 index b6b4361c..125f03c2 --- a/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralResponseMessageBinder.php +++ b/src/BeSimple/SoapBundle/ServiceBinding/RpcLiteralResponseMessageBinder.php @@ -68,7 +68,14 @@ private function processType($phpType, $message) $message = $array; } else { - $message = $this->checkComplexType($phpType, $message); + if (is_array($message)) { + foreach ($message as $complexType) { + $array[] = $this->checkComplexType($phpType, $complexType); + } + $message = $array; + } else { + $message = $this->checkComplexType($phpType, $message); + } } } diff --git a/src/BeSimple/SoapBundle/ServiceBinding/ServiceBinder.php b/src/BeSimple/SoapBundle/ServiceBinding/ServiceBinder.php index 7c78aded..3a3a1136 100644 --- a/src/BeSimple/SoapBundle/ServiceBinding/ServiceBinder.php +++ b/src/BeSimple/SoapBundle/ServiceBinding/ServiceBinder.php @@ -19,7 +19,7 @@ class ServiceBinder { /** - * @var \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition + * @var \BeSimple\SoapBundle\ServiceDefinition\Definition */ private $definition; diff --git a/src/BeSimple/SoapBundle/ServiceDefinition/Annotation/ComplexType.php b/src/BeSimple/SoapBundle/ServiceDefinition/Annotation/ComplexType.php old mode 100644 new mode 100755 index 85072d47..c06bce53 --- a/src/BeSimple/SoapBundle/ServiceDefinition/Annotation/ComplexType.php +++ b/src/BeSimple/SoapBundle/ServiceDefinition/Annotation/ComplexType.php @@ -18,6 +18,8 @@ class ComplexType extends Configuration private $name; private $value; private $isNillable = false; + private $minOccurs; + private $maxOccurs; public function getName() { @@ -49,8 +51,25 @@ public function setNillable($isNillable) $this->isNillable = (bool) $isNillable; } + public function getMinOccurs() + { + return $this->minOccurs; + } + public function setMinOccurs($minOccurs) + { + $this->minOccurs = $minOccurs; + } + public function getMaxOccurs() + { + return $this->maxOccurs; + } + public function setMaxOccurs($maxOccurs) + { + $this->maxOccurs = $maxOccurs; + } + public function getAliasName() { return 'complextype'; } -} \ No newline at end of file +} diff --git a/src/BeSimple/SoapBundle/ServiceDefinition/ComplexType.php b/src/BeSimple/SoapBundle/ServiceDefinition/ComplexType.php old mode 100644 new mode 100755 index d0ae4647..6efb6ad4 --- a/src/BeSimple/SoapBundle/ServiceDefinition/ComplexType.php +++ b/src/BeSimple/SoapBundle/ServiceDefinition/ComplexType.php @@ -20,6 +20,8 @@ class ComplexType private $name; private $value; private $isNillable = false; + private $minOccurs; + private $maxOccurs; public function getName() { @@ -50,4 +52,24 @@ public function setNillable($isNillable) { $this->isNillable = (bool) $isNillable; } + + public function getMinOccurs() + { + return $this->minOccurs; + } + + public function setMinOccurs($minOccurs) + { + $this->minOccurs = $minOccurs; + } + + public function getMaxOccurs() + { + return $this->maxOccurs; + } + + public function setMaxOccurs($maxOccurs) + { + $this->maxOccurs = $maxOccurs; + } } diff --git a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationClassLoader.php b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationClassLoader.php old mode 100644 new mode 100755 index dcd15002..3c090b84 --- a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationClassLoader.php +++ b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationClassLoader.php @@ -50,7 +50,7 @@ public function __construct(Reader $reader, TypeRepository $typeRepository) * @param string $class A class name * @param string $type The resource type * - * @return \BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition A ServiceDefinition instance + * @return \BeSimple\SoapBundle\ServiceDefinition\Definition A Definition instance * * @throws \InvalidArgumentException When route can't be parsed */ @@ -155,7 +155,7 @@ private function loadType($phpType) $loaded = $complexTypeResolver->load($phpType); $complexType = new ComplexType($phpType, isset($loaded['alias']) ? $loaded['alias'] : $phpType); foreach ($loaded['properties'] as $name => $property) { - $complexType->add($name, $this->loadType($property->getValue()), $property->isNillable()); + $complexType->add($name, $this->loadType($property->getValue()), $property->isNillable(), $property->getMinOccurs(), $property->getMaxOccurs()); } $this->typeRepository->addComplexType($complexType); diff --git a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php old mode 100644 new mode 100755 index 1d3e095e..7d030b12 --- a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php +++ b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationComplexTypeLoader.php @@ -58,6 +58,8 @@ public function load($class, $type = null) $propertyComplexType = new ComplexType(); $propertyComplexType->setValue($complexType->getValue()); $propertyComplexType->setNillable($complexType->isNillable()); + $propertyComplexType->setMinOccurs($complexType->getMinOccurs()); + $propertyComplexType->setMaxOccurs($complexType->getMaxOccurs()); $propertyComplexType->setName($property->getName()); $annotations['properties']->add($propertyComplexType); } diff --git a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationFileLoader.php b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationFileLoader.php index b995980d..16b87253 100644 --- a/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationFileLoader.php +++ b/src/BeSimple/SoapBundle/ServiceDefinition/Loader/AnnotationFileLoader.php @@ -12,7 +12,7 @@ namespace BeSimple\SoapBundle\ServiceDefinition\Loader; -use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition; +use BeSimple\SoapBundle\ServiceDefinition\Definition; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\FileLoader; @@ -52,7 +52,7 @@ public function __construct(FileLocator $locator, AnnotationClassLoader $loader, * @param string $file A PHP file path * @param string $type The resource type * - * @return ServiceDefinition A ServiceDefinition instance + * @return Definition A Definition instance * * @throws \InvalidArgumentException When the file does not exist */ diff --git a/src/BeSimple/SoapBundle/WebServiceContext.php b/src/BeSimple/SoapBundle/WebServiceContext.php old mode 100644 new mode 100755 index dce81a76..e341da0e --- a/src/BeSimple/SoapBundle/WebServiceContext.php +++ b/src/BeSimple/SoapBundle/WebServiceContext.php @@ -44,7 +44,7 @@ public function getServiceDefinition() if (null === $this->serviceDefinition) { $cache = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']); if ($cache->isFresh()) { - $this->serviceDefinition = include (string) $cache; + $this->serviceDefinition = include $cache->getPath(); } else { if (!$this->loader->supports($this->options['resource'], $this->options['resource_type'])) { throw new \LogicException(sprintf('Cannot load "%s" (%s)', $this->options['resource'], $this->options['resource_type'])); @@ -71,7 +71,7 @@ public function getWsdlFile($endpoint = null) $file = sprintf ('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint)); $cache = new ConfigCache($file, $this->options['debug']); - if(!$cache->isFresh()) { + if (!$cache->isFresh()) { $definition = $this->getServiceDefinition(); if ($endpoint) { @@ -82,7 +82,7 @@ public function getWsdlFile($endpoint = null) $cache->write($dumper->dump()); } - return (string) $cache; + return $cache->getPath(); } public function getServiceBinder() @@ -111,6 +111,9 @@ public function getServerBuilder() if (null !== $this->options['cache_type']) { $this->serverBuilder->withWsdlCache($this->options['cache_type']); } + if (isset($this->options['wsse'])) { + $this->serverBuilder->withWsse($this->options['wsse']); + } } return $this->serverBuilder; diff --git a/src/BeSimple/SoapClient/WsSecurityFilter.php b/src/BeSimple/SoapClient/WsSecurityFilter.php old mode 100644 new mode 100755 index af51a62f..ce641b2d --- a/src/BeSimple/SoapClient/WsSecurityFilter.php +++ b/src/BeSimple/SoapClient/WsSecurityFilter.php @@ -35,16 +35,6 @@ */ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapRequestFilter, SoapResponseFilter { - /** - * (UT 3.1) Password type: plain text. - */ - const PASSWORD_TYPE_TEXT = 0; - - /** - * (UT 3.1) Password type: digest. - */ - const PASSWORD_TYPE_DIGEST = 1; - /** * (UT 3.1) Password. * diff --git a/src/BeSimple/SoapCommon/Converter/TimeTypeConverter.php b/src/BeSimple/SoapCommon/Converter/TimeTypeConverter.php new file mode 100755 index 00000000..c2699c0a --- /dev/null +++ b/src/BeSimple/SoapCommon/Converter/TimeTypeConverter.php @@ -0,0 +1,47 @@ + + * (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\Converter; + +/** + * @author Francis Besset + */ +class TimeTypeConverter implements TypeConverterInterface +{ + public function getTypeNamespace() + { + return 'http://www.w3.org/2001/XMLSchema'; + } + + public function getTypeName() + { + return 'time'; + } + + public function convertXmlToPhp($data) + { + $doc = new \DOMDocument(); + $doc->loadXML($data); + + if ('' === $doc->textContent) { + return null; + } + + return new \DateTime($doc->textContent); + } + + public function convertPhpToXml($data) + { + return sprintf('<%1$s>%2$s', $this->getTypeName(), $data->format('H:i:s')); + } +} + diff --git a/src/BeSimple/SoapCommon/Definition/Message.php b/src/BeSimple/SoapCommon/Definition/Message.php old mode 100644 new mode 100755 index caa78fe2..bc39cce9 --- a/src/BeSimple/SoapCommon/Definition/Message.php +++ b/src/BeSimple/SoapCommon/Definition/Message.php @@ -48,13 +48,13 @@ public function isEmpty() return 0 === count($this->parts) ? true : false; } - public function add($name, $phpType, $nillable = false) + public function add($name, $phpType, $nillable = false, $minOccurs = null, $maxOccurs = null) { if ($phpType instanceof TypeInterface) { $phpType = $phpType->getPhpType(); } - $this->parts[$name] = new Part($name, $phpType, $nillable); + $this->parts[$name] = new Part($name, $phpType, $nillable, $minOccurs, $maxOccurs); return $this; } diff --git a/src/BeSimple/SoapCommon/Definition/Part.php b/src/BeSimple/SoapCommon/Definition/Part.php old mode 100644 new mode 100755 index 317f0464..19ce1d3f --- a/src/BeSimple/SoapCommon/Definition/Part.php +++ b/src/BeSimple/SoapCommon/Definition/Part.php @@ -20,12 +20,16 @@ class Part protected $name; protected $type; protected $nillable; + protected $minOccurs; + protected $maxOccurs; - public function __construct($name, $type, $nillable = false) + public function __construct($name, $type, $nillable = false, $minOccurs = null, $maxOccurs = null) { $this->name = $name; $this->type = $type; $this->setNillable($nillable); + $this->setMinOccurs($minOccurs); + $this->setMaxOccurs($maxOccurs); } public function getName() @@ -52,4 +56,24 @@ public function setNillable($nillable) { $this->nillable = (boolean) $nillable; } + + public function getMinOccurs() + { + return $this->minOccurs; + } + + public function setMinOccurs($minOccurs) + { + $this->minOccurs = $minOccurs; + } + + public function getMaxOccurs() + { + return $this->maxOccurs; + } + + public function setMaxOccurs($maxOccurs) + { + $this->maxOccurs = $maxOccurs; + } } diff --git a/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php b/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php old mode 100644 new mode 100755 index 15b0d259..1590c808 --- a/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php +++ b/src/BeSimple/SoapCommon/WsSecurityFilterClientServer.php @@ -27,6 +27,16 @@ */ abstract class WsSecurityFilterClientServer { + /** + * (UT 3.1) Password type: plain text. + */ + const PASSWORD_TYPE_TEXT = 0; + + /** + * (UT 3.1) Password type: digest. + */ + const PASSWORD_TYPE_DIGEST = 1; + /** * The date format to be used with {@link \DateTime} */ diff --git a/src/BeSimple/SoapServer/SoapServerBuilder.php b/src/BeSimple/SoapServer/SoapServerBuilder.php old mode 100644 new mode 100755 index 365c812e..ae8e7e82 --- a/src/BeSimple/SoapServer/SoapServerBuilder.php +++ b/src/BeSimple/SoapServer/SoapServerBuilder.php @@ -67,6 +67,21 @@ public function build() $server->setPersistence($this->persistence); } + if (isset($this->options['wsse'])) { + $wssFilter = new WsSecurityFilter(); + $wssFilter->setPasswordType($this->options['wsse']['password_type']); + $wssFilter->setUsernamePasswordCallback(function ($user) { + if ($user == $this->options['wsse']['username']) { + return $this->options['wsse']['password']; + } + + return null; + }); + + $soapKernel = $server->getSoapKernel(); + $soapKernel->registerFilter($wssFilter); + } + if (null !== $this->handlerClass) { $server->setClass($this->handlerClass); } elseif (null !== $this->handlerObject) { @@ -164,6 +179,18 @@ public function withMtomAttachments() return $this; } + /** + * SOAP attachment type MTOM. + * + * @return \BeSimple\SoapServer\SoapServerBuilder + */ + public function withWsse($wsse) + { + $this->options['wsse'] = $wsse; + + return $this; + } + /** * Configures the handler class or object. * @@ -197,4 +224,4 @@ protected function validateOptions() throw new \InvalidArgumentException('The handler has to be configured!'); } } -} \ No newline at end of file +} diff --git a/src/BeSimple/SoapServer/WsSecurityFilter.php b/src/BeSimple/SoapServer/WsSecurityFilter.php old mode 100644 new mode 100755 index b2e86a04..dfe0c05d --- a/src/BeSimple/SoapServer/WsSecurityFilter.php +++ b/src/BeSimple/SoapServer/WsSecurityFilter.php @@ -35,6 +35,8 @@ */ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapRequestFilter, SoapResponseFilter { + private $passwordTypes = array(WsSecurityFilter::PASSWORD_TYPE_TEXT, WsSecurityFilter::PASSWORD_TYPE_DIGEST); + /** * Username/password callback that returns password or null. * @@ -42,6 +44,13 @@ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapReque */ protected $usernamePasswordCallback; + /** + * Password Type (PASSWORD_TYPE_TEXT | PASSWORD_TYPE_DIGEST) + * + * @var int + */ + private $passwordType; + /** * Set username/password callback that returns password or null. * @@ -54,6 +63,22 @@ public function setUsernamePasswordCallback($callback) $this->usernamePasswordCallback = $callback; } + /** + * Set the Securement Password Type + * + * @param callable $callback Username/password callback function + * + * @return void + */ + public function setPasswordType($passwordType) + { + if (!in_array($passwordType, $this->passwordTypes)) { + throw new \Exception(sprintf('The password type has to be either: %s', implode(', ', $this->passwordTypes))); + } + + $this->passwordType = $passwordType; + } + /** * Reset all properties to default values. */ @@ -77,6 +102,11 @@ public function filterRequest(CommonSoapRequest $request) // locate security header $security = $dom->getElementsByTagNameNS(Helper::NS_WSS, 'Security')->item(0); + + if (null === $security && null !== $this->passwordType) { + throw new \SoapFault('wsse:FailedAuthentication', 'No WS-Security header found'); + } + if (null !== $security) { // is security header still valid? @@ -101,7 +131,7 @@ public function filterRequest(CommonSoapRequest $request) $password = call_user_func($this->usernamePasswordCallback, $usernameTokenUsername->textContent); - if ($usernameTokenPassword->getAttribute('Type') == Helper::NAME_WSS_UTP . '#PasswordDigest') { + if ($this->passwordType === WsSecurityFilter::PASSWORD_TYPE_DIGEST && $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)); @@ -174,7 +204,7 @@ public function filterResponse(CommonSoapResponse $response) // create security header $security = $filterHelper->createElement(Helper::NS_WSS, 'Security'); - $filterHelper->addHeaderElement($security, true, $this->actor, $response->getVersion()); + $filterHelper->addHeaderElement($security, false, $this->actor, $response->getVersion()); if (true === $this->addTimestamp || null !== $this->expires) { $timestamp = $filterHelper->createElement(Helper::NS_WSU, 'Timestamp'); diff --git a/src/BeSimple/SoapWsdl/Dumper/Dumper.php b/src/BeSimple/SoapWsdl/Dumper/Dumper.php old mode 100644 new mode 100755 index ef555209..e81fc6ab --- a/src/BeSimple/SoapWsdl/Dumper/Dumper.php +++ b/src/BeSimple/SoapWsdl/Dumper/Dumper.php @@ -236,7 +236,7 @@ protected function addComplexType(ComplexType $type) $complexType = $this->document->createElement(static::XSD_NS.':complexType'); $complexType->setAttribute('name', $type->getXmlType()); - $all = $this->document->createElement(static::XSD_NS.':'.($type instanceof ArrayOfType ? 'sequence' : 'all')); + $all = $this->document->createElement(static::XSD_NS.':sequence'); $complexType->appendChild($all); foreach ($type->all() as $child) { @@ -260,6 +260,13 @@ protected function addComplexType(ComplexType $type) $element->setAttribute('nillable', 'true'); } + if (null !== $child->getMinOccurs() && 1 != $child->getMinOccurs()) { + $element->setAttribute('minOccurs', $child->getMinOccurs()); + } + if (null !== $child->getMaxOccurs() && 1 != $child->getMaxOccurs()) { + $element->setAttribute('maxOccurs', $child->getMaxOccurs()); + } + if ($type instanceof ArrayOfType) { $element->setAttribute('minOccurs', 0); $element->setAttribute('maxOccurs', 'unbounded');