Création d’un Web-Service SOAP avec Identification par WSSE

Publié le par maxime

Je vous donne déjà la base de ce tuto, c’est-à-dire la création d’un service-web SOAP et l’identification par WSSE.

Ensuite nous allons voir ensemble comment faire communiquer ces deux éléments distincts ensemble.

Création d’une identification par WSSE dans Symfony2:

http://symfony.com/fr/do/current/cookbook/security/custom_authentication_provider.html

Création d’un web-service en SOAP dans Symfony2.

http://symfony.com/fr/doc/current/cookbook/web_services/php_soap_extension.html

Nous allons maintenant voir comment faire le mix entre ces deux solutions, pour pouvoir s’authentifier en WSSE grâce au SoapClient.
Tout d’abord, on modifie le fichier WsseListener pour que l’on puisse récupérer l’identification WSSE envoyée par le client SOAP. Ainsi, on vérifie que l’identification est bonne ou non. SI l’identification WSSE est correcte, le client peut récupérer les informations du Web-Service. Sinon on retourne une erreur 403.

Voici le code du WsseListener que nous avons modifié afin de pouvoir récupérer le header WSSE.

On vérifie que le WSSE est bien dans le header de la requête, s’il est présent on récupère le XML et on passe les informations émises par le header SOAP au security afin de créer le token d’identification.

// SOAP request
if(!$request->headers->has('x-wsse') && $request->headers->has('user-agent')
&& substr($request->headers->get('user-agent'), 0, 8 ) == 'PHP-SOAP') {

 $decodedRequest=preg_replace('/(<\/?)([-\w]+):([^>]*>)/','$1$3',$request->getContent());
  $xmlRequest = simplexml_load_string($decodedRequest);

    if (isset($xmlRequest->Header->Security->UsernameToken->Username)
    && isset($xmlRequest->Header->Security->UsernameToken->Password)
    && isset($xmlRequest->Header->Security->UsernameToken->Nonce)
    && isset($xmlRequest->Header->Security->UsernameToken->Created))
    {
      $request->headers->set('x-wsse', sprintf(<<<EOF
      UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"
      EOF
      , (string)$xmlRequest->Header->Security->UsernameToken->Username
      ,(string)$xmlRequest->Header->Security->UsernameToken->Password
      , (string)$xmlRequest->Header->Security->UsernameToken->Nonce
      , (string)$xmlRequest->Header->Security->UsernameToken->Created));
    }
}

Et si le WSSE n’est pas présent dans le header, on modifie la ligne qui est dans la doc Symfony …

$wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
  if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
    return;
  }

… par le code suivant afin d’avoir une erreur 403 en cas de non présence du WSSE.

if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
  $response = new Response();
  $response->setStatusCode(403);
  $response->setContent('Invalid or missing WSSE.');
  $event->setResponse($response);
  return;
}

Ensuite l’on crée une classe qui étend de la classe \SoapClient dans laquelle surcharge la fonction __soapcall afin de pouvoir envoyer les identifiants WSSE depuis SOAP.

Comme nous avons surchargé la class SoapClient, on surcharge le constructeur afin de pouvoir passer le username et le password du client, en plus des informations du constructeur de base.

public function __construct($wsdl, $username, $password, array $options = null)
{
  parent::SoapClient($wsdl, $options);
  $this->username = $username;
  $this->password = $password;
}

Voici la surcharge la fonction __soapCall introduisant le nœud de sécurité WSSE :

public function __soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null)
{
  $timestamp = date(DATE_ATOM);
  $nonce = mt_rand();
  $xml = sprintf(<<<EOF
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsse:UsernameToken>
    <wsse:Username>%s</wsse:Username>
   <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%s</wsse:Password>
    <wsse:Nonce>%s</wsse:Nonce>
   <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">%s</wsu:Created>
    </wsse:UsernameToken>
    </wsse:Security>
    EOF
    , $this->username
  , base64_encode(sha1(base64_decode($nonce).$timestamp.$this->password, true))
    , $nonce
    , $timestamp
  );
  $input_headers = new \SoapHeader('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd','Security',new \SoapVar($xml, XSD_ANYXML),true);

return parent::__soapCall($function_name, $arguments, $options, $input_headers, $output_headers);}

Voici un exemple d’utilisation :

Utilisation du constructeur de la classe où l’on passe le nom d’utilisateur et le mot de passe du client.

$soap = new SoapClient(‘test.wsdl', $user->getUsername(), $ user ->getPassword(), array('cache_wsdl' => WSDL_CACHE_NONE));

Puis on appelle notre fonction ‘test’ qui est présente dans le WSDL avec son paramètre.

$soap->__soapCall(‘test’, ‘test’);

Cette entrée a été publiée dans Symfony 2 API. Vous pouvez la mettre en favoris avec ce permalien.



Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*


eight + = 15

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>