Cas.php 7.9 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;
8
use UnicaenAuth\Options\Traits\ModuleOptionsAwareTrait;
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;
14
15
use Zend\Router\RouteInterface;
use Zend\Router\RouteStackInterface;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
16
use ZfcUser\Authentication\Adapter\ChainableAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
17
18
19
20
21
22

/**
 * CAS authentication adpater
 *
 * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
 */
23
class Cas extends AbstractAdapter
Bertrand Gauthier's avatar
Bertrand Gauthier committed
24
{
25
26
27
    use ModuleOptionsAwareTrait;

    const TYPE = 'cas';
Bertrand Gauthier's avatar
Bertrand Gauthier committed
28

Bertrand Gauthier's avatar
Bertrand Gauthier committed
29
    /**
30
     * @var string
Bertrand Gauthier's avatar
Bertrand Gauthier committed
31
     */
32
    protected $type = self::TYPE;
33
34
35
36
37
38
39
40
41
42

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

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

44
45
46
47
48
    /**
     * @var LdapPeopleMapper
     */
    protected $ldapPeopleMapper;

49
50
51
52
53
54
55
56
57
58
59
60
61
62
    /**
     * @var User
     */
    private $userService;

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

    /**
63
     * @var RouteInterface
64
65
66
67
     */
    private $router;

    /**
68
     * @param RouteInterface $router
69
     */
70
    public function setRouter(RouteInterface $router)
71
72
73
74
    {
        $this->router = $router;
    }

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

89
90
91
92
93
        $type = $e->getRequest()->getPost()->get('type');
        if ($type !== $this->type) {
            return;
        }

94
95
96
//        if ($e->getIdentity()) {
//            return;
//        }
97
98
        /* DS : modification liée à une boucle infinie lors de l'authentification CAS */
        if ($this->isSatisfied()) {
99
100
101
            $storage = $this->getStorage()->read();
            $e->setIdentity($storage['identity'])
                    ->setCode(AuthenticationResult::SUCCESS)
102
                    ->setMessages(['Authentication successful.']);
103
104
            return;
        }
105

106
107
        if (! $this->isEnabled()) {
            return;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
108
        }
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
    /**
     * @return bool
     */
    protected function isEnabled()
    {
        $config = $this->moduleOptions->getCas();

        if (! $config) {
            return false;
        }
        if (isset($config['enabled'])) {
            return (bool) $config['enabled'];
        }

        return true;
    }

153
    /**
154
     *
155
     * @param Event $e
156
157
     * @see ChainableAdapter
     */
158
    public function logout(Event $e)
159
    {
160
161
162
163
164
165
166
        if (! $this->isEnabled()) {
            return;
        }

        $storage = $this->getStorage()->read();
        if (! isset($storage['identity'])) {
            return;
167
        }
168

169
        $returnUrl = $this->router->getRequestUri()->setPath($this->router->getBaseUrl())->toString();
170
        $this->getCasClient()->logoutWithRedirectService($returnUrl);
171
    }
172

173
    /**
174
     * Retourne le client CAS.
175
     *
176
177
178
     * @param boolean $initClient
     * @return phpCAS
     * @throws Exception
179
     */
180
    public function getCasClient($initClient = true)
181
    {
182
183
        if (null === $this->casClient) {
            $this->casClient = new phpCAS();
184
        }
185

186
187
188
        if (!$initClient) {
            return $this->casClient;
        }
189

190
        if (null === $this->casOptions) {
191
            $config = $this->moduleOptions->getCas();
192
193
194
195
196
            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'];
        }
197

198
        $options = $this->casOptions;
199

200
        if (array_key_exists('debug', $options) && (bool) $options['debug']) {
201
            $this->casClient->setDebug();
202
        }
203

204
        // initialize phpCAS
205
        $this->casClient->client($options['version'], $options['hostname'], $options['port'], $options['uri'], true);
206
        // no SSL validation for the CAS server
207
        $this->casClient->setNoCasServerValidation();
208

209
210
        return $this->casClient;
    }
211

212
213
    /**
     * Spécifie le client CAS.
214
     *
215
216
217
218
219
220
221
     * @param phpCAS $casClient
     * @return self
     */
    public function setCasClient(phpCAS $casClient)
    {
        $this->casClient = $casClient;
        return $this;
222
    }
223

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
     * @param RouteInterface $router
Bertrand Gauthier's avatar
Bertrand Gauthier committed
249
     */
250
    public function reconfigureRoutesForCasAuth(RouteInterface $router)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
251
    {
252
253
254
        if (! $this->isEnabled()) {
            return;
        }
255
256
257
258
        if(!$router instanceof RouteStackInterface) {
            return;
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
        $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'  => [
274
                        'type'    => 'Segment',
Bertrand Gauthier's avatar
Bertrand Gauthier committed
275
                        'options' => [
276
                            'route'    => '/connexion[/:type]',
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
                            '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
297
}