phpweb-servicessoapwsdlsoapserver

Entity in the header of a SOAP message is interpreted as an operation


I need to implement a gateway in php that receives calls in SOAP.

The WSDL of the web service is not very complicated. Next I expose an extract of the same one.

<portType name="DeliveryNotification">
    <operation name="notifyDelivery" parameterOrder="parameters platformHeader">
      <input message="tns:notifyDelivery"/>
      <output message="tns:notifyDeliveryResponse"/>
    </operation>
  </portType>
  <binding name="DeliveryNotificationPortBinding" type="tns:DeliveryNotification">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="notifyDelivery">
      <soap:operation soapAction="notifyDelivery"/>
      <input>
        <soap:body use="literal" parts="parameters"/>
        <soap:header message="tns:notifyDelivery" part="platformHeader" use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="DeliveryNotificationService">
    <port name="DeliveryNotificationPortBinding" binding="tns:DeliveryNotificationPortBinding">
      <soap:address location="http://localhost:8888/soapTest/outbound-test.php"/>
    </port>
  </service>

The script that processes the service execution request is very simple. I use a separate class to handle this operation and the following:

$server = new Gateway\Debug\DebugSoapServer(OUTBOUND_API_WSDL, array(
    'soap_version' => SOAP_1_2
));

$server->setObject(new Gateway\Handlers\OutboundHandlerDecorator(new Gateway\Handlers\OutboundHandler()));

try {
    $server->handle();
} catch (Exception $e) {
    $server->fault('Sender', $e->getMessage());
}

OutboundHandlerDecorator would act as a proxy to validate access and then delegate to the final handler to process the response.

next I expose its definition:

class OutboundHandlerDecorator
{
    private $decorated = null;

    public function __construct(OutboundHandler $decorated)
    {
        $this->decorated = $decorated;
    }

    public function __call($method, $params)
    {
        global $methodCalled;

        $methodCalled = $method;

        if (method_exists($this->decorated, $method)) {
            return call_user_func_array(
                array($this->decorated, $method), $params);
        } else {
            throw new \BadMethodCallException();
        }
    }

}

My problem is the following:

I created the following SOAP message to simulate the call to the notifyDelivery service, using the SOAPUI tool. Next I expose the result:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://test.com/">
   <soapenv:Header>
      <ws:platformHeader>
         <platformCredentials>
            <password>myPassword</password>
            <userId>myUser</userId>
         </platformCredentials>
         <platformFlags>
            <retry>1</retry>
         </platformFlags>
      </ws:platformHeader>
   </soapenv:Header>
   <soapenv:Body>
      <ws:notifyDelivery>
         <simId>1234567891234567789</simId>
         <simIdType>ICCID</simIdType>
      </ws:notifyDelivery>
   </soapenv:Body>
</soapenv:Envelope>

As a result I get a failed SOAP message, because the BadMethodCallException exception is thrown. Since first of all it tries to find and execute a method called "platformHeader" and for this there is no function defined in the class "OutboundHandler".

If I do not throw this exception, the notifyDelivery method of the "OutboundHandler" class will be executed correctly.

Therefore, my question is: Why does SoapServer identify the header as an operation? From SOAPUI I am configuring the SOAPAction HTTP header to "notifyDelivery". I do not understand this operation.

How could I access the header data independently? In the variable $ params I only have the data of the body of the message.

Thank you very much in advance.

regards


Solution

  • As you can see in this answer, In the "OutboundHandler" handler in addition to a method for each service defined in the WSDL there should also be a method for each message header and define the access control logic there. You should create a method called "platformHeader" and if the credentials are not correct throw a SoapFault exception. At the moment I will implement it in this way.

    class OutboundHandler
    {
        /**
         * Validate User Credentials
         * @param $params
         */
        public function platformHeader( $params ){
    
            if(!($params->platformCredentials->password == "myPassword" && $params->platformCredentials->userId == "myUser"))
                throw new \SoapFault("1", "Incorrect user, request rejected.");
        }
    
        public function notifyDelivery($params) {
    
            // do something.
    
        }
    
    }