diff --git a/FEATURES/101989 b/FEATURES/101989
new file mode 100644
index 0000000000000000000000000000000000000000..204911bea37ff2dd51350a4e70a8656ebc2ba321
--- /dev/null
+++ b/FEATURES/101989
@@ -0,0 +1,10 @@
+        '101989' =>
+            ['Label' => $this->_('SSO avec les portails Archimed Syracuse pour l'accès aux ressources numériques'),
+             'Desc' => $this->_('Connecteur de bibliothèque numérique pour accès par exemple aux ressources de la médiathèque départementale si elle utilise le système Syracuse'),
+             'Image' => '',
+             'Video' => '',
+             'Category' => $this->_('Ressources numériques'),
+             'Right' => function($feature_description, $user) {return true;},
+             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php/Syracuse',
+             'Test' => '',
+             'Date' => '2020-02-06'],
\ No newline at end of file
diff --git a/VERSIONS_WIP/101989 b/VERSIONS_WIP/101989
new file mode 100644
index 0000000000000000000000000000000000000000..1186e568f7d7d54de80683e8e980f9c6e53f080c
--- /dev/null
+++ b/VERSIONS_WIP/101989
@@ -0,0 +1 @@
+ - ticket #101989 : SSO avec les portails Archimed Syracuse pour l'accès aux ressources numériques
\ No newline at end of file
diff --git a/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php b/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php
index 032902a7ca826a0c03f1497ef02abf3843a8d920..5eedb6b03225607f628a2a7e05dd3e7dc891d0a5 100644
--- a/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php
+++ b/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php
@@ -22,30 +22,46 @@
 
 class ZendAfi_Controller_Action_Helper_CasValidResponse extends Zend_Controller_Action_Helper_Abstract {
 
-
   public function direct($user, $ticket, $attributes = []) {
     $this->getActionController()->getHelper('ViewRenderer')->setNoRender();
     $this->getResponse()->setHeader('Content-Type', 'application/xml;charset=utf-8');
 
-    $this->getResponse()->setBody("<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
-    <cas:authenticationSuccess>
-        <cas:user>".$user->getId()."</cas:user>
-            <cas:proxyGrantingTicket>".$ticket."
-        </cas:proxyGrantingTicket>".
-                                  $this->renderAttributes($attributes).
-    "</cas:authenticationSuccess>
-   </cas:serviceResponse>");
+    $this->getResponse()
+         ->setBody("<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>\n"
+                   . "  <cas:authenticationSuccess>\n"
+                   . "    <cas:user>".$user->getId()."</cas:user>\n"
+                   . "    <cas:proxyGrantingTicket>".$ticket."</cas:proxyGrantingTicket>\n"
+                   . $this->renderAttributes($attributes)
+                   . "  </cas:authenticationSuccess>\n"
+                   . "</cas:serviceResponse>");
   }
 
+
   public function renderAttributes($attributes) {
     if (!$attributes)
       return '';
-    $xml = '';
-    foreach($attributes as $key => $value) {
-      $xml .= '<cas:'.$key.'>'.$value.'</cas:'.$key.'>';
-    }
-    return '<cas:attributes>'.$xml.'</cas:attributes>';
-  }
 
+    $cas_attributes = [];
+    $push_attribute = function($name, $value) use (&$cas_attributes) {
+      $cas_attributes []= sprintf('<cas:%1$s>%2$s</cas:%1$s>',
+                                  $name,
+                                  $value);
+    };
+
+    array_walk($attributes,
+               function($value_or_array, $name) use ($push_attribute)
+               {
+                 if (!is_array($value_or_array))
+                   return $push_attribute($name, $value_or_array);
+
+                 foreach($value_or_array as $value)
+                   $push_attribute($name, $value);
+               });
+
+    return
+      "    <cas:attributes>\n      "
+      . implode("\n      ",  $cas_attributes)
+      . "\n    </cas:attributes>\n";
+  }
 }
 ?>
\ No newline at end of file
diff --git a/library/digital_resources/Skilleos/tests/SkilleosTest.php b/library/digital_resources/Skilleos/tests/SkilleosTest.php
index 61f9e4f7c52d04407234c3b227bb3d22447a477d..5ecc6de6552eac9415a85b1260a07fb0efb8f9a6 100644
--- a/library/digital_resources/Skilleos/tests/SkilleosTest.php
+++ b/library/digital_resources/Skilleos/tests/SkilleosTest.php
@@ -93,17 +93,30 @@ class SkilleosModulesControllerUserWithGroupWithRightTest
   }
 
 
-  /** @test */
-  public function validateWithValidUserTicketShouldAnswerSuccessAndLibraryName() {
-
-    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
-    $this->dispatch('/Skilleos_Plugin/auth/servicevalidate/service/skilleos/ticket/' . $ticket,
-                    true);
-    $this->assertContains('<cas:user>666</cas:user>',$this->_response->getBody());
-    $this->assertContains('<cas:sn>jumper</cas:sn><cas:mail>jolly@jumper.com</cas:mail><cas:givenName>jolly</cas:givenName><cas:bibId>Tombouctou</cas:bibId>',$this->_response->getBody());
 
+  public function expectedCasTags() {
+    return [
+            ['user', 666],
+            ['sn', 'jumper'],
+            ['givenName', 'jolly'],
+            ['mail', 'jolly@jumper.com'],
+            ['bibId', 'Tombouctou'],
+    ];
   }
 
+
+    /**
+   * @dataProvider expectedCasTags
+   * @test
+   */
+  public function validateWithValidUserTicketShouldAnswerSuccessAndAttributes($tag, $value) {
+    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
+    $this->dispatch('/Skilleos_Plugin/auth/servicevalidate/service/skilleos/ticket/' . $ticket);
+    $this->assertContains('<cas:' . $tag .'>'
+                          . (is_callable($value) ? $value() : $value)
+                          . '</cas:' . $tag . '>',
+                          $this->_response->getBody());
+  }
 }
 
 
diff --git a/library/digital_resources/Syracuse/Config.php b/library/digital_resources/Syracuse/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..6965a2b07215a7b9f66174f6acebf29fdc2528f8
--- /dev/null
+++ b/library/digital_resources/Syracuse/Config.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Syracuse_Config extends Class_DigitalResource_Config {
+  protected function _getConfig() {
+    return ['Introduction' => $this->_('Connexion avec une médiathèque départementale utilisant Syracuse pour l\'accès aux ressources numériques'),
+            'AdminVars' => ['SSO_URL' => Class_AdminVar_Meta::newDefault($this->_('URL du serveur CAS Syracuse'))
+                            ->bePrivate()],
+            'PermissionLabel' => $this->_('Bibliothèque numérique: accéder aux ressources Syracuse'),
+            'SsoAction' => true,
+            'Harvesting' => false,
+
+            'HelpLink' => 'http://wiki.bokeh-library-portal.org/index.php/Syracuse',
+            'Url' => 'https://www.archimed.fr/module-solutions-pour-bibliotheque/bibliotheque-numerique/',
+            'Icon' => 'https://www.archimed.fr/wp-content/themes/wp-insita-bibdoc/images/logo.png',
+
+            'MenuLabel' => $this->_('Lien vers Syracuse'),
+            'ModuleMenu' => $this->withNameSpace('ModuleMenu'),
+            'NotAllowedMessage' => $this->_('Vous devez être abonné pour accéder à cette ressource.'),
+    ];
+  }
+
+
+  public function getSsoUrl($user) {
+    return $this->getAdminVar('SSO_URL') . '&ticket='.(new Class_CasTicket())->getTicketForUser($user);
+  }
+
+
+  public function isEnabled() {
+    return '' != $this->getAdminVar('SSO_URL');
+  }
+
+
+  public function renderCustomDiagOn($view) {
+    return (new Syracuse_View_Helper_Dashboard)
+      ->setView($view)
+      ->dashboard();
+  }
+
+
+  public function getSsoValidateUrl() {
+    return Class_Url::absolute(['controller' => 'auth',
+                                'action' => 'serviceValidate']);
+  }
+
+
+  public function validateUrlFor($user) {
+    return $this->getSsoValidateUrl()
+      . '?'
+      . http_build_query(['service' => 'bokeh-test',
+                          'ticket' => (new Class_CasTicket())->getTicketForUser($user)]);
+  }
+
+}
+?>
\ No newline at end of file
diff --git a/library/digital_resources/Syracuse/ModuleMenu.php b/library/digital_resources/Syracuse/ModuleMenu.php
new file mode 100644
index 0000000000000000000000000000000000000000..76f469744bcba4c2465f74e3ff80d6672ee36a5a
--- /dev/null
+++ b/library/digital_resources/Syracuse/ModuleMenu.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Syracuse_ModuleMenu extends Class_DigitalResource_ModuleMenu {}
diff --git a/library/digital_resources/Syracuse/View/Helper/Dashboard.php b/library/digital_resources/Syracuse/View/Helper/Dashboard.php
new file mode 100644
index 0000000000000000000000000000000000000000..d92d22abe6b4d62a37928e2143d4c4c77e591350
--- /dev/null
+++ b/library/digital_resources/Syracuse/View/Helper/Dashboard.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Syracuse_View_Helper_Dashboard extends ZendAfi_View_Helper_BaseHelper {
+  public function dashboard() {
+    return
+      $this->_tag('h3', $this->_('Informations à transmettre à Archimed'))
+      .
+      $this->_tag('h4', $this->_('Configuration du serveur CAS'))
+      .
+      $this->_tag('dl',
+                  $this->_tag('dt', $this->_('URL d\'authentification'))
+                  .
+                  $this->_tag('dd',
+                              $this->_tag('pre',
+                                          Class_Url::absolute(['module' => 'opac',
+                                                               'controller' => 'cas-server',
+                                                               'action' => 'login'])))
+                  .
+                  $this->_tag('dt', $this->_('URL de validation'))
+                  .
+                  $this->_tag('dd',
+                              $this->_tag('pre',
+                                          Class_Url::absolute(['controller' => 'auth',
+                                                               'action' => 'serviceValidate']))));
+  }
+}
diff --git a/library/digital_resources/Syracuse/controllers/AuthController.php b/library/digital_resources/Syracuse/controllers/AuthController.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7933eb2590464c269ed40411c9128b7321a498e
--- /dev/null
+++ b/library/digital_resources/Syracuse/controllers/AuthController.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Syracuse_Plugin_AuthController extends Class_DigitalResource_Controller {
+  public function servicevalidateAction() {
+    $service = $this->_request->getParam('service');
+    $ticket = $this->_request->getParam('ticket');
+    if (strlen($ticket)<1 || strlen($service)<1) {
+      return  $this->_helper->casFailureResponse('INVALID_REQUEST');
+    }
+
+    if (!$user = (new Class_CasTicket())->userForTicket($ticket))
+      return  $this->_helper->casFailureResponse('INVALID_TICKET',$ticket);
+
+    $attributes = ['lastname' => $user->getNom(),
+                   'firstname' => $user->getPrenom(),
+                   'mail' => $user->getMail(),
+                   'expire_at' => $user->getDateFin(),
+                   'card_number' => $user->getIdabon(),
+                   'site_code' => $user->getLibraryId(),
+                   'site_label' => $user->getLibelleBib(),
+                   'birth_date' => $user->getNaissance(),
+                   'postal_code' => $user->getCodePostal(),
+                   'city' => $user->getVille(),
+                   'affiliation' => array_map(
+                                              function($group) {
+                                                return $group->getLibelle();
+                                              },
+                                              $user->getUserGroups())
+                  ];
+
+    return $this->_helper->casValidResponse($user, $ticket, $attributes);
+  }
+}
+?>
\ No newline at end of file
diff --git a/library/digital_resources/Syracuse/controllers/IndexController.php b/library/digital_resources/Syracuse/controllers/IndexController.php
new file mode 100644
index 0000000000000000000000000000000000000000..99a70f0c8697129b6e83e5f59b649e0a602ee47b
--- /dev/null
+++ b/library/digital_resources/Syracuse/controllers/IndexController.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Syracuse_Plugin_IndexController extends Class_DigitalResource_Controller {}
\ No newline at end of file
diff --git a/library/digital_resources/Syracuse/tests/SyracuseTest.php b/library/digital_resources/Syracuse/tests/SyracuseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..507ffcdb0a5afb58641d85dd32497779d05d3493
--- /dev/null
+++ b/library/digital_resources/Syracuse/tests/SyracuseTest.php
@@ -0,0 +1,245 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+abstract class SyracuseModulesControllerTestCase extends AbstractControllerTestCase {
+  protected
+    $_storm_default_to_volatile = true,
+    $_user;
+
+  public function setUp() {
+    parent::setUp();
+    Storm_Cache::beVolatile();
+    Class_AdminVar::set('Syracuse_SSO_URL',
+                        'https://mediatheque.departementale.fr/default.aspx?forceLogon=CG_CAS_BDP');
+
+    $this->fixture('Class_Bib',
+                   ['id' => 34,
+                    'libelle' => 'Gresivaudan']);
+
+    $this->_user = $this->fixture('Class_Users',
+                                  ['id' => 10,
+                                   'nom' => 'Pouce',
+                                   'prenom' => 'Tom',
+                                   'date_fin' => '2020-02-12',
+                                   'naissance' => '1978-08-02',
+                                   'code_postal' => '38123',
+                                   'ville' => 'Laval',
+                                   'idabon' => '123XPE',
+                                   'id_site' => 34,
+                                   'mail' => 'tom@pouce.fr',
+                                   'login' => 'Tom',
+                                   'password' => 'pwd']);
+    $this->_user
+      ->addUserGroup($this->fixture('Class_UserGroup',
+                                    ['id' => 1,
+                                     'libelle' => 'supremes']))
+      ->addUserGroup($this->fixture('Class_UserGroup',
+                                    ['id' => 2,
+                                     'libelle' => 'temptations']))
+      ->assertSave();
+
+    ZendAfi_Auth::getInstance()->logUser($this->_user);
+  }
+}
+
+
+
+
+class SyracuseModulesControllerTest extends SyracuseModulesControllerTestCase {
+
+  /** @test */
+  public function userWithoutSIGBAccountShouldNotHaveAccessToSyracuse() {
+    $this->dispatch('/opac/modules/syracuse');
+    $this->assertFlashMessengerContentContains('Vous devez être abonné pour accéder à cette ressource.');
+  }
+
+
+  /** @test */
+  public function userWithoutRightsShouldBeRedirectedToSyracuse() {
+    $this->_user
+      ->beAbonneSIGB()
+      ->setDateFin('2099-02-12')
+      ->setIdabon('34245')
+      ->setBib($this->fixture('Class_Bib',
+                              ['id' => 2,
+                               'libelle' => 'Chambe']))
+      ->assertSave();
+    $this->dispatch('/opac/modules/syracuse');
+    $this->assertFlashMessengerContentContains('Vous devez être abonné pour accéder à cette ressource.');
+  }
+
+
+
+  /** @test */
+  public function userGroupPermissionsShouldContainsSyracuse() {
+    $this->assertContains('Bibliothèque numérique: accéder aux ressources Syracuse',
+                          Class_DigitalResource::getInstance()->getPermissionsLabel());
+  }
+
+
+  /** @test */
+  public function validateAuthWithoutTicketShouldAnswerError() {
+    $this->dispatch('/Syracuse_Plugin/auth/servicevalidate');
+    $this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">',
+                          $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function validateAuthWithValidTicketButNoServiceShouldAnswerErrorINVAID_REQUEST() {
+    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
+    $this->dispatch('/Syracuse_Plugin/auth/servicevalidate/ticket/' . $ticket);
+    $this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">',
+                          $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function validateAuthWithInvalidTicketShouldAnswerError() {
+    $this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/blabla/ticket/666');
+    $this->assertContains('<cas:authenticationFailure code="INVALID_TICKET"> Ticket 666 not recognized</cas:authenticationFailure>',
+                          $this->_response->getBody());
+  }
+
+
+  public function expectedCasTags() {
+    return [
+            ['user', 10],
+            ['proxyGrantingTicket', function() { return (new Class_CasTicket())->getTicketForCurrentUser(); }],
+            ['lastname', 'Pouce'],
+            ['firstname', 'Tom'],
+            ['mail', 'tom@pouce.fr'],
+            ['expire_at', '2020-02-12'],
+            ['card_number', '123XPE'],
+            ['site_code',  34],
+            ['site_label', 'Gresivaudan'],
+            ['birth_date', '1978-08-02'],
+            ['affiliation', 'supremes'],
+            ['affiliation', 'temptations'],
+            ['postal_code', '38123'],
+            ['city', 'Laval']
+    ];
+  }
+
+
+  /**
+   * @dataProvider expectedCasTags
+   * @test
+   */
+  public function validateWithValidUserTicketShouldAnswerSuccessAndAttributes($tag, $value) {
+    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
+    $this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/syracuse/ticket/' . $ticket);
+    $this->assertContains('<cas:' . $tag .'>'
+                          . (is_callable($value) ? $value() : $value)
+                          . '</cas:' . $tag . '>',
+                          $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function validateWithValidUserTicketShouldContainsFifteenCasAttributes() {
+    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
+    $this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/syracuse/ticket/' . $ticket);
+
+    preg_match_all('/<cas:[^ >]+>/',
+                   $this->_response->getBody(),
+                   $matches);
+    $attributes = array_diff($matches[0], ['<cas:authenticationSuccess>']);
+    $this->assertCount(15, $attributes);
+
+  }
+
+
+  /** @test */
+  public function modulesMenuShouldBeAvailable() {
+    $menu = new Class_Systeme_ModulesMenu;
+    $this->assertEquals('Syracuse', $menu->getFonction('Syracuse')->getType());
+  }
+
+
+  /** @test */
+  public function dashboardShouldContainsUrlCasServiceValidate() {
+    $this->dispatch('/Syracuse_Plugin');
+    $this->assertXPathContentContains('//dl/dd/pre',
+                                      'http://localhost' . Class_Url::baseUrl() . '/Syracuse_Plugin/auth/serviceValidate',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function dashboardShouldContainsUrlCasLogin() {
+    $this->dispatch('/Syracuse_Plugin');
+    $this->assertXPathContentContains('//dl/dd/pre',
+                                      'http://localhost' . Class_Url::baseUrl() . '/cas-server/login',
+                                      $this->_response->getBody());
+  }
+}
+
+
+
+
+class SyracuseModulesControllerWithRightsTest extends SyracuseModulesControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+    $group = $this->fixture('Class_UserGroup',
+                            ['id' => 3,
+                             'libelle' => 'multimedia']);
+
+    $this->fixture('Class_Permission',
+                   ['id' => 1,
+                    'code' => 'Syracuse'])
+         ->permitTo($group,  new Class_Entity());
+
+    $this->_user->beAbonneSIGB()
+                ->setDateFin('2099-02-12')
+                ->setIdabon('34245')
+                ->setBib($this->fixture('Class_Bib',
+                                        ['id' => 2,
+                                         'libelle' => 'Gresivaudan']))
+                ->addUserGroup($group)
+                ->assertSave();
+  }
+
+
+  /** @test */
+  public function userWithRightsShouldBeRedirectedToSyracuseWithCasTicket() {
+    $this->dispatch('/opac/modules/syracuse');
+
+    $ticket = (new Class_CasTicket())->getTicketForCurrentUser();
+    $this->assertXpathContentContains('//script', 'document.location.href="https://mediatheque.departementale.fr/default.aspx?forceLogon=CG_CAS_BDP&ticket=' . $ticket . '";');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsUrlSsoTitle() {
+    $this->dispatch('/Syracuse_Plugin');
+    $this->assertXPathContentContains('//h4', 'URL SSO générée par /modules/syracuse pour l\'utilisateur "Syracuse_test_user"');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsSsoUrlForServiceValidate() {
+    $this->dispatch('/Syracuse_Plugin');
+    $ticket = (new Class_CasTicket())->getTicketForUser(Class_Users::findFirstBy(['login' => 'Syracuse_test_user']));
+    $this->assertContains('<pre>http://localhost' . Class_Url::baseUrl() . '/Syracuse_Plugin/auth/serviceValidate?service=bokeh-test&ticket=' . $ticket . '</pre>',
+                          $this->_response->getBody());
+  }
+}
\ No newline at end of file