Cas.php 8.54 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
use Zend\Router\Http\TreeRouteStack;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
18
19
use ZfcUser\Authentication\Adapter\AbstractAdapter;
use ZfcUser\Authentication\Adapter\ChainableAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
20
21
22
23
24
25

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

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

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

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

48
49
50
51
52
    /**
     * @var LdapPeopleMapper
     */
    protected $ldapPeopleMapper;

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

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

    /**
67
     * @var TreeRouteStack
68
69
70
71
     */
    private $router;

    /**
72
     * @param TreeRouteStack $router
73
     */
74
    public function setRouter(TreeRouteStack $router)
75
76
77
78
    {
        $this->router = $router;
    }

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

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

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

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

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

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

117
        $identity = $this->getCasClient(false)->getUser();
118

Bertrand Gauthier's avatar
Bertrand Gauthier committed
119
        error_reporting($oldErrorReporting);
120

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

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

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

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

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

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

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

176
        $options = $this->casOptions;
177

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

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

187
188
        return $this->casClient;
    }
189

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

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

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

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

244
245
246
        return $this;
    }

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

Bertrand Gauthier's avatar
Bertrand Gauthier committed
257
    /**
258
     * {@inheritdoc}
Bertrand Gauthier's avatar
Bertrand Gauthier committed
259
260
261
262
263
264
     */
    public function setEventManager(EventManagerInterface $eventManager)
    {
        $this->eventManager = $eventManager;
        return $this;
    }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
265
266
267
268
269
270
271
272
273
274
275
276
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

    /**
     * @param TreeRouteStack $router
     */
    public function reconfigureRoutesForCasAuth(TreeRouteStack $router)
    {
        $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
309
}