Cas.php 8.87 KB
Newer Older
Bertrand Gauthier's avatar
Bertrand Gauthier committed
1
<?php
2

Bertrand Gauthier's avatar
Bertrand Gauthier committed
3 4
namespace UnicaenAuth\Authentication\Adapter;

5
use Exception;
6
use phpCAS;
7
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
8
use UnicaenAuth\Options\ModuleOptions;
9
use UnicaenAuth\Service\User;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
10 11
use Zend\Authentication\Exception\UnexpectedValueException;
use Zend\Authentication\Result as AuthenticationResult;
12
use Zend\EventManager\Event;
13
use Zend\EventManager\EventInterface;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
14 15 16
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
17 18
use Zend\Router\RouteInterface;
use Zend\Router\RouteStackInterface;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
19 20
use ZfcUser\Authentication\Adapter\AbstractAdapter;
use ZfcUser\Authentication\Adapter\ChainableAdapter;
21
use Zend\Authentication\Storage\Session;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
22 23 24 25 26 27

/**
 * CAS authentication adpater
 *
 * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
 */
28
class Cas extends AbstractAdapter implements EventManagerAwareInterface
Bertrand Gauthier's avatar
Bertrand Gauthier committed
29
{
Bertrand Gauthier's avatar
Bertrand Gauthier committed
30 31 32 33
    /**
     * @var EventManager
     */
    protected $eventManager;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
34

Bertrand Gauthier's avatar
Bertrand Gauthier committed
35 36 37 38
    /**
     * @var ModuleOptions
     */
    protected $options;
39 40 41 42 43 44 45 46 47 48

    /**
     * @var array
     */
    protected $casOptions;

    /**
     * @var phpCAS
     */
    protected $casClient;
49

50 51 52 53 54
    /**
     * @var LdapPeopleMapper
     */
    protected $ldapPeopleMapper;

55 56 57 58 59 60 61 62 63 64 65 66 67 68
    /**
     * @var User
     */
    private $userService;

    /**
     * @param User $userService
     */
    public function setUserService(User $userService)
    {
        $this->userService = $userService;
    }

    /**
69
     * @var RouteInterface
70 71 72 73
     */
    private $router;

    /**
74
     * @param RouteInterface $router
75
     */
76
    public function setRouter(RouteInterface $router)
77 78 79 80
    {
        $this->router = $router;
    }

81 82 83 84 85 86 87 88 89
    public function getStorage()
    {
        if (null === $this->storage) {
            $this->setStorage(new Session());
        }

        return $this->storage;
    }
    
Bertrand Gauthier's avatar
Bertrand Gauthier committed
90
    /**
91
     * Réalise l'authentification.
92
     *
93
     * @param EventInterface $e
Bertrand Gauthier's avatar
Bertrand Gauthier committed
94 95 96
     * @throws UnexpectedValueException
     * @see ChainableAdapter
     */
97
    public function authenticate(EventInterface $e)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
98
    {
99 100 101 102 103
        // NB: Dans la version 3.0.0 de zf-commons/zfc-user, cette méthode prend un EventInterface.
        // Mais dans la branche 3.x, c'est un AdapterChainEvent !
        // Si un jour c'est un AdapterChainEvent qui est attendu, plus besoin de faire $e->getTarget().
        $e = $e->getTarget();

104 105 106 107 108 109 110 111
//        if ($e->getIdentity()) {
//            return;
//        }
	/* DS : modification liée à une boucle infinie lors de l'authentification CAS */
	if ($this->isSatisfied()) {
            $storage = $this->getStorage()->read();
            $e->setIdentity($storage['identity'])
                    ->setCode(AuthenticationResult::SUCCESS)
112
                    ->setMessages(['Authentication successful.']);
113 114
            return;
        }
115

Bertrand Gauthier's avatar
Bertrand Gauthier committed
116 117 118 119
        $config = $this->getOptions()->getCas();
        if (!$config) {
            return; // NB: l'authentification CAS est désactivée ssi le tableau des options est vide
        }
120

Bertrand Gauthier's avatar
Bertrand Gauthier committed
121 122
        error_reporting($oldErrorReporting = error_reporting() & ~E_NOTICE);

123
        $this->getCasClient()->forceAuthentication();
Bertrand Gauthier's avatar
Bertrand Gauthier committed
124 125 126 127

        // at this step, the user has been authenticated by the CAS server
        // and the user's login name can be read with phpCAS::getUser().

128
        $identity = $this->getCasClient(false)->getUser();
129

Bertrand Gauthier's avatar
Bertrand Gauthier committed
130
        error_reporting($oldErrorReporting);
131

Bertrand Gauthier's avatar
Bertrand Gauthier committed
132 133 134 135 136 137
        $e->setIdentity($identity);
        $this->setSatisfied(true);
        $storage = $this->getStorage()->read();
        $storage['identity'] = $e->getIdentity();
        $this->getStorage()->write($storage);
        $e->setCode(AuthenticationResult::SUCCESS)
138 139
          ->setMessages(['Authentication successful.']);

140 141 142 143
        // recherche de l'individu dans l'annuaire LDAP (il existe forcément puisque l'auth CAS a réussi)
        $ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($identity);

        /* @var $userService User */
144
        $this->userService->userAuthenticated($ldapPeople);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
145
    }
146

147
    /**
148
     *
149
     * @param Event $e
150 151
     * @see ChainableAdapter
     */
152
    public function logout(Event $e)
153 154 155 156
    {
        if (!$this->getOptions()->getCas()) {
            return; // NB: l'authentification CAS est désactivée ssi le tableau des options est vide
        }
157

158
        $returnUrl = $this->router->getRequestUri()->setPath($this->router->getBaseUrl())->toString();
159
        $this->getCasClient()->logoutWithRedirectService($returnUrl);
160
    }
161

162
    /**
163
     * Retourne le client CAS.
164
     *
165 166 167
     * @param boolean $initClient
     * @return phpCAS
     * @throws Exception
168
     */
169
    public function getCasClient($initClient = true)
170
    {
171 172
        if (null === $this->casClient) {
            $this->casClient = new phpCAS();
173
        }
174

175 176 177
        if (!$initClient) {
            return $this->casClient;
        }
178

179 180 181 182 183 184 185
        if (null === $this->casOptions) {
            $config = $this->getOptions()->getCas();
            if (!isset($config['connection']['default']['params']) || !$config['connection']['default']['params']) {
                throw new Exception("Les paramètres de connexion au serveur CAS sont invalides.");
            }
            $this->casOptions = $config['connection']['default']['params'];
        }
186

187
        $options = $this->casOptions;
188

189
        if (array_key_exists('debug', $options) && (bool) $options['debug']) {
190
            $this->casClient->setDebug();
191
        }
192

193
        // initialize phpCAS
194
        $this->casClient->client($options['version'], $options['hostname'], $options['port'], $options['uri'], true);
195
        // no SSL validation for the CAS server
196
        $this->casClient->setNoCasServerValidation();
197

198 199
        return $this->casClient;
    }
200

201 202
    /**
     * Spécifie le client CAS.
203
     *
204 205 206 207 208 209 210
     * @param phpCAS $casClient
     * @return self
     */
    public function setCasClient(phpCAS $casClient)
    {
        $this->casClient = $casClient;
        return $this;
211
    }
212

Bertrand Gauthier's avatar
Bertrand Gauthier committed
213 214 215 216 217 218 219 220 221 222 223 224 225
    /**
     * @param ModuleOptions $options
     */
    public function setOptions(ModuleOptions $options)
    {
        $this->options = $options;
    }

    /**
     * @return ModuleOptions
     */
    public function getOptions()
    {
226 227 228 229 230 231
//        if (!$this->options instanceof ModuleOptions) {
//            $options = array_merge(
//                    $this->serviceLocator->get('zfcuser_module_options')->toArray(),
//                    $this->serviceLocator->get('unicaen-auth_module_options')->toArray());
//            $this->setOptions(new ModuleOptions($options));
//        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
232 233 234
        return $this->options;
    }

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    /**
     * get ldap people mapper
     *
     * @return LdapPeopleMapper
     */
    public function getLdapPeopleMapper()
    {
        return $this->ldapPeopleMapper;
    }

    /**
     * set ldap people mapper
     *
     * @param LdapPeopleMapper $mapper
     * @return self
     */
    public function setLdapPeopleMapper(LdapPeopleMapper $mapper)
    {
        $this->ldapPeopleMapper = $mapper;
254

255 256 257
        return $this;
    }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
258 259 260 261 262 263 264 265 266
    /**
     * Retrieve EventManager instance
     *
     * @return EventManagerInterface
     */
    public function getEventManager()
    {
        return $this->eventManager;
    }
267

Bertrand Gauthier's avatar
Bertrand Gauthier committed
268
    /**
269
     * {@inheritdoc}
Bertrand Gauthier's avatar
Bertrand Gauthier committed
270 271 272 273 274 275
     */
    public function setEventManager(EventManagerInterface $eventManager)
    {
        $this->eventManager = $eventManager;
        return $this;
    }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
276 277

    /**
278
     * @param RouteInterface $router
Bertrand Gauthier's avatar
Bertrand Gauthier committed
279
     */
280
    public function reconfigureRoutesForCasAuth(RouteInterface $router)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
281
    {
282 283 284 285
        if(!$router instanceof RouteStackInterface) {
            return;
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
        $router->addRoutes([
            // remplace les routes existantes (cf. config du module)
            'zfcuser' => [
                'type'          => 'Literal',
                'priority'      => 1000,
                'options'       => [
                    'route'    => '/auth',
                    'defaults' => [
                        'controller' => 'zfcuser',
                        'action'     => 'index',
                    ],
                ],
                'may_terminate' => true,
                'child_routes'  => [
                    'login'  => [
                        'type'    => 'Literal',
                        'options' => [
                            'route'    => '/connexion',
                            'defaults' => [
                                'controller' => 'zfcuser',
                                'action'     => 'authenticate', // zappe l'action 'login'
                            ],
                        ],
                    ],
                    'logout' => [
                        'type'    => 'Literal',
                        'options' => [
                            'route'    => '/deconnexion',
                            'defaults' => [
                                'controller' => 'zfcuser',
                                'action'     => 'logout',
                            ],
                        ],
                    ],
                ],
            ],
        ]);
    }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
324
}