Cas.php 8.7 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;
    }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
81
    /**
82
     * Réalise l'authentification.
83
     *
84
     * @param EventInterface $e
Bertrand Gauthier's avatar
Bertrand Gauthier committed
85
86
87
     * @throws UnexpectedValueException
     * @see ChainableAdapter
     */
88
    public function authenticate(EventInterface $e)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
89
    {
90
91
92
93
94
        // 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();

95
96
97
98
99
100
101
102
//        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)
103
                    ->setMessages(['Authentication successful.']);
104
105
            return;
        }
106

Bertrand Gauthier's avatar
Bertrand Gauthier committed
107
108
109
110
        $config = $this->getOptions()->getCas();
        if (!$config) {
            return; // NB: l'authentification CAS est désactivée ssi le tableau des options est vide
        }
111

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

114
        $this->getCasClient()->forceAuthentication();
Bertrand Gauthier's avatar
Bertrand Gauthier committed
115
116
117
118

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

119
        $identity = $this->getCasClient(false)->getUser();
120

Bertrand Gauthier's avatar
Bertrand Gauthier committed
121
        error_reporting($oldErrorReporting);
122

Bertrand Gauthier's avatar
Bertrand Gauthier committed
123
124
125
126
127
128
        $e->setIdentity($identity);
        $this->setSatisfied(true);
        $storage = $this->getStorage()->read();
        $storage['identity'] = $e->getIdentity();
        $this->getStorage()->write($storage);
        $e->setCode(AuthenticationResult::SUCCESS)
129
130
          ->setMessages(['Authentication successful.']);

131
132
133
134
        // 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 */
135
        $this->userService->userAuthenticated($ldapPeople);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
136
    }
137

138
    /**
139
     *
140
     * @param Event $e
141
142
     * @see ChainableAdapter
     */
143
    public function logout(Event $e)
144
145
146
147
    {
        if (!$this->getOptions()->getCas()) {
            return; // NB: l'authentification CAS est désactivée ssi le tableau des options est vide
        }
148

149
        $returnUrl = $this->router->getRequestUri()->setPath($this->router->getBaseUrl())->toString();
150
        $this->getCasClient()->logoutWithRedirectService($returnUrl);
151
    }
152

153
    /**
154
     * Retourne le client CAS.
155
     *
156
157
158
     * @param boolean $initClient
     * @return phpCAS
     * @throws Exception
159
     */
160
    public function getCasClient($initClient = true)
161
    {
162
163
        if (null === $this->casClient) {
            $this->casClient = new phpCAS();
164
        }
165

166
167
168
        if (!$initClient) {
            return $this->casClient;
        }
169

170
171
172
173
174
175
176
        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'];
        }
177

178
        $options = $this->casOptions;
179

180
        if (array_key_exists('debug', $options) && (bool) $options['debug']) {
181
            $this->casClient->setDebug();
182
        }
183

184
        // initialize phpCAS
185
        $this->casClient->client($options['version'], $options['hostname'], $options['port'], $options['uri'], true);
186
        // no SSL validation for the CAS server
187
        $this->casClient->setNoCasServerValidation();
188

189
190
        return $this->casClient;
    }
191

192
193
    /**
     * Spécifie le client CAS.
194
     *
195
196
197
198
199
200
201
     * @param phpCAS $casClient
     * @return self
     */
    public function setCasClient(phpCAS $casClient)
    {
        $this->casClient = $casClient;
        return $this;
202
    }
203

Bertrand Gauthier's avatar
Bertrand Gauthier committed
204
205
206
207
208
209
210
211
212
213
214
215
216
    /**
     * @param ModuleOptions $options
     */
    public function setOptions(ModuleOptions $options)
    {
        $this->options = $options;
    }

    /**
     * @return ModuleOptions
     */
    public function getOptions()
    {
217
218
219
220
221
222
//        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
223
224
225
        return $this->options;
    }

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
    /**
     * 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;
245

246
247
248
        return $this;
    }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
249
250
251
252
253
254
255
256
257
    /**
     * Retrieve EventManager instance
     *
     * @return EventManagerInterface
     */
    public function getEventManager()
    {
        return $this->eventManager;
    }
258

Bertrand Gauthier's avatar
Bertrand Gauthier committed
259
    /**
260
     * {@inheritdoc}
Bertrand Gauthier's avatar
Bertrand Gauthier committed
261
262
263
264
265
266
     */
    public function setEventManager(EventManagerInterface $eventManager)
    {
        $this->eventManager = $eventManager;
        return $this;
    }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
267
268

    /**
269
     * @param RouteInterface $router
Bertrand Gauthier's avatar
Bertrand Gauthier committed
270
     */
271
    public function reconfigureRoutesForCasAuth(RouteInterface $router)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
272
    {
273
274
275
276
        if(!$router instanceof RouteStackInterface) {
            return;
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
277
278
279
280
281
282
283
284
285
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
        $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
315
}