Cas.php 7.39 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
use Zend\Authentication\Result as AuthenticationResult;
11
use Zend\EventManager\EventInterface;
12
13
use Zend\Router\RouteInterface;
use Zend\Router\RouteStackInterface;
14
use ZfcUser\Authentication\Adapter\AdapterChainEvent;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
15
16
17
18
19
20

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

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

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

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

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

42
43
44
45
46
    /**
     * @var LdapPeopleMapper
     */
    protected $ldapPeopleMapper;

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

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

    /**
61
     * @var RouteInterface
62
63
64
65
     */
    private $router;

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

Bertrand Gauthier's avatar
Bertrand Gauthier committed
73
    /**
74
     * @inheritDoc
Bertrand Gauthier's avatar
Bertrand Gauthier committed
75
     */
76
    public function authenticate(EventInterface $e): bool
Bertrand Gauthier's avatar
Bertrand Gauthier committed
77
    {
78
79
80
        // 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().
81
        $event = $e->getTarget(); /* @var $event AdapterChainEvent */
82

83
        $type = $event->getRequest()->getPost()->get('type');
84
        if ($type !== $this->type) {
85
            return false;
86
87
        }

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

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

103
        $this->getCasClient()->forceAuthentication();
Bertrand Gauthier's avatar
Bertrand Gauthier committed
104
105
106
107

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

108
        $identity = $this->createSessionIdentity($this->getCasClient(false)->getUser());
109

Bertrand Gauthier's avatar
Bertrand Gauthier committed
110
        error_reporting($oldErrorReporting);
111

112
        $event->setIdentity($identity);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
113
114
        $this->setSatisfied(true);
        $storage = $this->getStorage()->read();
115
        $storage['identity'] = $event->getIdentity();
Bertrand Gauthier's avatar
Bertrand Gauthier committed
116
        $this->getStorage()->write($storage);
117
118
119
        $event
            ->setCode(AuthenticationResult::SUCCESS)
            ->setMessages(['Authentication successful.']);
120

121
        // recherche de l'individu dans l'annuaire LDAP (il existe forcément puisque l'auth CAS a réussi)
122
        $ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($identity->getUsername());
123
124

        /* @var $userService User */
125
        $this->userService->userAuthenticated($ldapPeople);
126
127

        return true;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
128
    }
129

130
    /**
131
     * @inheritDoc
132
     */
133
    public function logout(EventInterface $e)
134
    {
135
136
        parent::logout($e);

137
138
139
        $storage = $this->getStorage()->read();
        if (! isset($storage['identity'])) {
            return;
140
        }
141

142
        $returnUrl = $this->router->getRequestUri()->setPath($this->router->getBaseUrl())->toString();
143
        $this->getCasClient()->logoutWithRedirectService($returnUrl);
144
    }
145

146
    /**
147
     * Retourne le client CAS.
148
     *
149
150
151
     * @param boolean $initClient
     * @return phpCAS
     * @throws Exception
152
     */
153
    public function getCasClient($initClient = true): phpCAS
154
    {
155
156
        if (null === $this->casClient) {
            $this->casClient = new phpCAS();
157
        }
158

159
160
161
        if (!$initClient) {
            return $this->casClient;
        }
162

163
        if (null === $this->casOptions) {
164
            $config = $this->moduleOptions->getCas();
165
166
167
168
169
            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'];
        }
170

171
        $options = $this->casOptions;
172

173
        if (array_key_exists('debug', $options) && (bool) $options['debug']) {
174
            $this->casClient->setDebug();
175
        }
176

177
        // initialize phpCAS
178
        $this->casClient->client($options['version'], $options['hostname'], $options['port'], $options['uri'], true);
179
        // no SSL validation for the CAS server
180
        $this->casClient->setNoCasServerValidation();
181

182
183
        return $this->casClient;
    }
184

185
186
    /**
     * Spécifie le client CAS.
187
     *
188
189
190
     * @param phpCAS $casClient
     * @return self
     */
191
    public function setCasClient(phpCAS $casClient): self
192
193
194
    {
        $this->casClient = $casClient;
        return $this;
195
    }
196

197
198
199
200
201
    /**
     * get ldap people mapper
     *
     * @return LdapPeopleMapper
     */
202
    public function getLdapPeopleMapper(): LdapPeopleMapper
203
204
205
206
207
208
209
210
211
212
    {
        return $this->ldapPeopleMapper;
    }

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

217
218
219
        return $this;
    }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
220
    /**
221
     * @param RouteInterface $router
Bertrand Gauthier's avatar
Bertrand Gauthier committed
222
     */
223
    public function reconfigureRoutesForCasAuth(RouteInterface $router)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
224
    {
225
226
227
228
        if(!$router instanceof RouteStackInterface) {
            return;
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
        $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'  => [
244
                        'type'    => 'Segment',
Bertrand Gauthier's avatar
Bertrand Gauthier committed
245
                        'options' => [
246
                            'route'    => '/connexion[/:type]',
Bertrand Gauthier's avatar
Bertrand Gauthier committed
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
                            '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
267
}