Ldap.php 9.84 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
6
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
7
use UnicaenAuth\Options\ModuleOptions;
8
use UnicaenAuth\Service\User;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
9
use Zend\Authentication\Adapter\Ldap as LdapAuthAdapter;
10
11
use Zend\Authentication\Exception\ExceptionInterface;
use Zend\Authentication\Result as AuthenticationResult;
Laurent Lécluse's avatar
Laurent Lécluse committed
12
use Zend\EventManager\Event;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
13
14
15
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
16
17
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
18
19
20
use ZfcUser\Authentication\Adapter\AbstractAdapter;
use ZfcUser\Authentication\Adapter\AdapterChainEvent as AuthEvent;
use ZfcUser\Authentication\Adapter\ChainableAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
21
22
23
24
25
26
27
28

/**
 * LDAP authentication adpater
 *
 * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
 */
class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, EventManagerAwareInterface
{
29
    const USURPATION_USERNAMES_SEP = '=';
30

Bertrand Gauthier's avatar
Bertrand Gauthier committed
31
32
33
34
35
36
    /**
     * @var ServiceManager
     */
    protected $serviceManager;

    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
37
     * @var EventManager
Bertrand Gauthier's avatar
Bertrand Gauthier committed
38
39
     */
    protected $eventManager;
40

Bertrand Gauthier's avatar
Bertrand Gauthier committed
41
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
42
     * @var LdapAuthAdapter
Bertrand Gauthier's avatar
Bertrand Gauthier committed
43
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
44
    protected $ldapAuthAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
45

46
47
48
49
50
    /**
     * @var LdapPeopleMapper
     */
    protected $ldapPeopleMapper;

Bertrand Gauthier's avatar
Bertrand Gauthier committed
51
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
52
     * @var ModuleOptions
Bertrand Gauthier's avatar
Bertrand Gauthier committed
53
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
54
    protected $options;
55

56
    /**
57
     * @var string
58
59
     */
    protected $usernameUsurpe;
60

Bertrand Gauthier's avatar
Bertrand Gauthier committed
61
    /**
62
     *
Bertrand Gauthier's avatar
Bertrand Gauthier committed
63
64
     * @param AuthEvent $e
     * @return boolean
65
66
     * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
     * @throws \Zend\Ldap\Exception\LdapException
Bertrand Gauthier's avatar
Bertrand Gauthier committed
67
68
69
70
71
     * @see ChainableAdapter
     */
    public function authenticate(AuthEvent $e)
    {
        if ($this->isSatisfied()) {
72
73
74
75
76
            try {
                $storage = $this->getStorage()->read();
            } catch (ExceptionInterface $e) {
                throw new RuntimeException("Erreur de lecture du storage");
            }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
77
            $e->setIdentity($storage['identity'])
78
79
                ->setCode(AuthenticationResult::SUCCESS)
                ->setMessages(['Authentication successful.']);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
80
81
82
            return;
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
83
        $username   = $e->getRequest()->getPost()->get('identity');
Bertrand Gauthier's avatar
Bertrand Gauthier committed
84
85
        $credential = $e->getRequest()->getPost()->get('credential');

86
87
88
89
90
91
        if (function_exists('mb_strtolower')) {
            $username = mb_strtolower($username, 'UTF-8');
        } else {
            $username = strtolower($username);
        }

92
        $success = $this->authenticateUsername($username, $credential);
93

Bertrand Gauthier's avatar
Bertrand Gauthier committed
94
        // Failure!
95
        if (! $success) {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
96
            $e->setCode(AuthenticationResult::FAILURE)
97
98
99
100
101
102
103
104
105
106
107
                ->setMessages(['LDAP bind failed.']);
            $this->setSatisfied(false);
            return false;
        }

        // recherche de l'individu dans l'annuaire LDAP
        $ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($username);
        if (!$ldapPeople) {
            $e
                ->setCode(AuthenticationResult::FAILURE)
                ->setMessages(['Authentication failed.']);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
108
109
110
            $this->setSatisfied(false);
            return false;
        }
111

112
        $e->setIdentity($this->usernameUsurpe ?: $username);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
113
        $this->setSatisfied(true);
114
115
116
117
118
119
120
        try {
            $storage = $this->getStorage()->read();
            $storage['identity'] = $e->getIdentity();
            $this->getStorage()->write($storage);
        } catch (ExceptionInterface $e) {
            throw new RuntimeException("Erreur de concernant le storage");
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
121
        $e->setCode(AuthenticationResult::SUCCESS)
122
            ->setMessages(['Authentication successful.']);
123

124
125
126
        /* @var $userService User */
        $userService = $this->getServiceManager()->get('unicaen-auth_user_service');
        $userService->userAuthenticated($ldapPeople);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
127
128
    }

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
    /**
     * Extrait le loginUsurpateur et le loginUsurpé si l'identifiant spécifé est de la forme
     * "loginUsurpateur=loginUsurpé".
     *
     * @param string $identifiant Identifiant, éventuellement de la forme "loginUsurpateur=loginUsurpé"
     * @return array
     * [loginUsurpateur, loginUsurpé] si l'identifiant est de la forme "loginUsurpateur=loginUsurpé" ;
     * [] sinon.
     */
    static public function extractUsernamesUsurpation($identifiant)
    {
        if (strpos($identifiant, self::USURPATION_USERNAMES_SEP) > 0) {
            list($identifiant, $usernameUsurpe) = explode(self::USURPATION_USERNAMES_SEP, $identifiant, 2);

            return [
                $identifiant,
                $usernameUsurpe
            ];
        }
148

149
        return [];
Bertrand Gauthier's avatar
Bertrand Gauthier committed
150
151
    }

152
153
    /**
     * Authentifie l'identifiant et le mot de passe spécifiés.
154
     *
155
     * @param string $username   Identifiant de connexion
156
157
     * @param string $credential Mot de passe
     * @return boolean
158
159
     * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
     * @throws \Zend\Ldap\Exception\LdapException
160
161
162
163
164
165
166
     */
    public function authenticateUsername($username, $credential)
    {
        // si 2 logins sont fournis, cela active l'usurpation d'identité (à n'utiliser que pour les tests) :
        // - le format attendu est "loginUsurpateur=loginUsurpé"
        // - le mot de passe attendu est celui du compte usurpateur (loginUsurpateur)
        $this->usernameUsurpe = null;
167
168
169
        $usernames = self::extractUsernamesUsurpation($username);
        if (count($usernames) === 2) {
            list ($username, $this->usernameUsurpe) = $usernames;
170
171
172
173
174
175
176
            if (!in_array($username, $this->getOptions()->getUsurpationAllowedUsernames())) {
                $this->usernameUsurpe = null;
            }
        }

        // LDAP auth
        $result  = $this->getLdapAuthAdapter()->setUsername($username)->setPassword($credential)->authenticate();
177
178
179
180
181
182
183
184
185
186
187

        if ($result && count($result->getMessages())) {
            // Obtenir le message LDAP
            // $msg = preg_replace('/\[0x\d* \((.*)\):/','$1', $event->getParam('result')->getMessages()[1]);
            $eventClasse = new Event('authentication.ldap.error');
            $eventClasse->setTarget($this)->setParams([
                'result' => $result
            ]);
            $this->getEventManager()->trigger($eventClasse);
        }

188
        $success = $result->isValid();
189

190
191
        // verif existence du login usurpé
        if ($this->usernameUsurpe) {
192
            // s'il nexiste pas, échec de l'authentification
193
194
            if (!$this->getLdapAuthAdapter()->getLdap()->searchEntries("(supannAliasLogin=$this->usernameUsurpe)")) {
                $this->usernameUsurpe = null;
195
                $success              = false;
196
197
            }
        }
198

199
200
        return $success;
    }
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
    /**
     * get ldap people mapper
     *
     * @return LdapPeopleMapper
     */
    public function getLdapPeopleMapper()
    {
        if (null === $this->ldapPeopleMapper) {
            $this->ldapPeopleMapper = $this->getServiceManager()->get('ldap_people_mapper');
        }
        return $this->ldapPeopleMapper;
    }

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

Bertrand Gauthier's avatar
Bertrand Gauthier committed
227
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
228
     * @param ModuleOptions $options
Bertrand Gauthier's avatar
Bertrand Gauthier committed
229
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
230
    public function setOptions(ModuleOptions $options)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
231
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
232
        $this->options = $options;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
233
234
235
    }

    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
236
     * @return ModuleOptions
Bertrand Gauthier's avatar
Bertrand Gauthier committed
237
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
238
    public function getOptions()
Bertrand Gauthier's avatar
Bertrand Gauthier committed
239
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
240
241
        if (!$this->options instanceof ModuleOptions) {
            $options = array_merge(
242
243
                $this->getServiceManager()->get('zfcuser_module_options')->toArray(),
                $this->getServiceManager()->get('unicaen-auth_module_options')->toArray());
Bertrand Gauthier's avatar
Bertrand Gauthier committed
244
245
246
            $this->setOptions(new ModuleOptions($options));
        }
        return $this->options;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
247
    }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
248

Bertrand Gauthier's avatar
Bertrand Gauthier committed
249
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
250
     * @return \UnicaenApp\Options\ModuleOptions
Bertrand Gauthier's avatar
Bertrand Gauthier committed
251
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
252
    public function getAppModuleOptions()
Bertrand Gauthier's avatar
Bertrand Gauthier committed
253
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
254
        return $this->getServiceManager()->get('unicaen-app_module_options');
Bertrand Gauthier's avatar
Bertrand Gauthier committed
255
256
257
258
    }

    /**
     * get ldap connection adapter
259
     *
Bertrand Gauthier's avatar
Bertrand Gauthier committed
260
     * @return LdapAuthAdapter
Bertrand Gauthier's avatar
Bertrand Gauthier committed
261
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
262
    public function getLdapAuthAdapter()
Bertrand Gauthier's avatar
Bertrand Gauthier committed
263
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
264
        if (null === $this->ldapAuthAdapter) {
265
            $options = [];
Bertrand Gauthier's avatar
Bertrand Gauthier committed
266
267
268
269
270
271
            if (($config = $this->getAppModuleOptions()->getLdap())) {
                foreach ($config['connection'] as $name => $connection) {
                    $options[$name] = $connection['params'];
                }
            }
            $this->ldapAuthAdapter = new LdapAuthAdapter($options); // NB: array(array)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
272
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
273
        return $this->ldapAuthAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
274
275
276
277
    }

    /**
     * set ldap connection adapter
278
     *
Bertrand Gauthier's avatar
Bertrand Gauthier committed
279
     * @param LdapAuthAdapter $authAdapter
Bertrand Gauthier's avatar
Bertrand Gauthier committed
280
281
     * @return Ldap
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
282
    public function setLdapAuthAdapter(LdapAuthAdapter $authAdapter)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
283
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
284
        $this->ldapAuthAdapter = $authAdapter;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
285
286
287
288
        return $this;
    }

    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
289
290
291
     * Get service manager
     *
     * @return ServiceManager
Bertrand Gauthier's avatar
Bertrand Gauthier committed
292
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
293
    public function getServiceManager()
Bertrand Gauthier's avatar
Bertrand Gauthier committed
294
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
295
        return $this->serviceManager;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
296
297
298
    }

    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
299
300
301
302
     * Set service manager
     *
     * @param ServiceManager $serviceManager
     * @return Ldap
Bertrand Gauthier's avatar
Bertrand Gauthier committed
303
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
304
    public function setServiceManager(ServiceManager $serviceManager)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
305
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
306
307
        $this->serviceManager = $serviceManager;
        return $this;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
308
    }
309

Bertrand Gauthier's avatar
Bertrand Gauthier committed
310
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
311
312
313
     * Retrieve EventManager instance
     *
     * @return EventManagerInterface
Bertrand Gauthier's avatar
Bertrand Gauthier committed
314
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
315
    public function getEventManager()
Bertrand Gauthier's avatar
Bertrand Gauthier committed
316
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
317
        return $this->eventManager;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
318
    }
319

Bertrand Gauthier's avatar
Bertrand Gauthier committed
320
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
321
322
323
324
     * Inject an EventManager instance
     *
     * @param  EventManagerInterface $eventManager
     * @return Ldap
Bertrand Gauthier's avatar
Bertrand Gauthier committed
325
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
326
    public function setEventManager(EventManagerInterface $eventManager)
Bertrand Gauthier's avatar
Bertrand Gauthier committed
327
    {
328
329
330
331
        $eventManager->setIdentifiers([
            __NAMESPACE__,
            __CLASS__,
        ]);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
332
333
        $this->eventManager = $eventManager;
        return $this;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
334
335
    }
}