UserContext.php 11.4 KB
Newer Older
1
2
3
4
<?php

namespace UnicaenAuth\Service;

5
use BjyAuthorize\Acl\Role;
6
use UnicaenApp\Exception\RuntimeException;
7
use UnicaenApp\Traits\SessionContainerTrait;
8
9
use UnicaenAuth\Acl\NamedRole;
use UnicaenAuth\Entity\Ldap\People;
10
use UnicaenAuth\Entity\Shibboleth\ShibUser;
11
use UnicaenAuth\Event\UserRoleSelectedEvent;
12
use UnicaenAuth\Formatter\RoleFormatter;
13
use UnicaenAuth\Provider\Identity\Chain;
14
use Zend\Authentication\AuthenticationService;
15
use Zend\EventManager\EventManagerAwareInterface;
16
use Zend\EventManager\EventManagerAwareTrait;
17
use Zend\Permissions\Acl\Role\RoleInterface;
18
19
20
use ZfcUser\Entity\UserInterface;

/**
21
 * Service centralisant des méthodes utiles concernant l'utilisateur authentifié.
22
 *
23
 * @author Unicaen
24
 */
25
class UserContext extends AbstractService implements EventManagerAwareInterface
26
{
27
    use EventManagerAwareTrait;
28
    use SessionContainerTrait;
29
30
31
32
33

    /**
     * @var mixed
     */
    protected $identity;
34

35
    /**
36
     * @var array
37
     */
38
    protected $identityRoles;
39

40
41
42
43
44
45
46
47
48
49
50
51
52
    /**
     * @var AuthenticationService
     */
    protected $authenticationService;

    /**
     * @param AuthenticationService $authenticationService
     */
    public function setAuthenticationService(AuthenticationService $authenticationService)
    {
        $this->authenticationService = $authenticationService;
    }

53
54
55
56
57
58
59
60
61
62
63
64
    /**
     * Retourne l'utilisateur BDD courant
     *
     * @return UserInterface
     */
    public function getDbUser()
    {
        if (($identity = $this->getIdentity())) {
            if (isset($identity['db']) && $identity['db'] instanceof UserInterface) {
                return $identity['db'];
            }
        }
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
        return null;
    }

    /**
     * Retourne l'utilisateur LDAP courant
     *
     * @return People
     */
    public function getLdapUser()
    {
        if (($identity = $this->getIdentity())) {
            if (isset($identity['ldap']) && $identity['ldap'] instanceof People) {
                return $identity['ldap'];
            }
        }
81

82
83
84
        return null;
    }

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    /**
     * Retourne l'éventuel utilisateur Shibboleth courant.
     *
     * @return ShibUser|null
     */
    public function getShibUser()
    {
        if (($identity = $this->getIdentity())) {
            if (isset($identity['shib']) && $identity['shib'] instanceof ShibUser) {
                return $identity['shib'];
            }
        }

        return null;
    }

101
    /**
102
     * Retourne les données d'identité correspondant à l'utilisateur courant.
103
     *
104
     * @return mixed
105
     */
106
    public function getIdentity()
107
    {
108
        if (null === $this->identity) {
109
            $authenticationService = $this->authenticationService;
110
111
112
113
            if ($authenticationService->hasIdentity()) {
                $this->identity = $authenticationService->getIdentity();
            }
        }
114

115
116
        return $this->identity;
    }
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    /**
     * Retourne l'identifiant de connexion de l'utilisateur courant.
     *
     * @return string|null
     */
    public function getIdentityUsername()
    {
        if ($user = $this->getShibUser()) {
            return $user->getUsername();
        }
        if ($user = $this->getLdapUser()) {
            return $user->getUsername();
        }
        if ($user = $this->getDbUser()) {
            return $user->getUsername();
        }

        return null;
    }

Laurent Lécluse's avatar
Laurent Lécluse committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
    /**
     * @param string $roleId
     *
     * @return Role
     */
    public function getIdentityRole($roleId)
    {
        $roles = $this->getServiceAuthorize()->getRoles();
        if (isset($roles[$roleId])) {
            return $roles[$roleId];
        }

        return null;
    }

153
    /**
154
     * Retourne tous les rôles de l'utilisateur courant, pas seulement le rôle courant sélectionné.
155
     *
156
157
158
     * Les clés du tableau sont les ID de rôles, les valeurs sont les objets Role
     *
     * @return Role[]
159
160
161
162
     */
    public function getIdentityRoles()
    {
        if (null === $this->identityRoles) {
163
164
165
            $this->identityRoles = [];

            $roles            = $this->getServiceAuthorize()->getRoles();
166
167
            $identityProvider = $this->getIdentityProvider();
            if ($identityProvider instanceof Chain) {
168
                $identityRoles = $identityProvider->getAllIdentityRoles();
169
            } else {
170
                $identityRoles = $identityProvider->getIdentityRoles();
171
            }
172
173
            foreach ($identityRoles as $role) {
                if ($role instanceof RoleInterface) {
174
                    $this->identityRoles[$role->getRoleId()] = $role;
175
176
177
                } elseif (is_string($role) && isset($roles[$role])) {
                    $role = $roles[$role];
                    /** @var RoleInterface $role */
178
179
                    $this->identityRoles[$role->getRoleId()] = $role;
                }
180
            }
181
        }
182

183
184
        return $this->identityRoles;
    }
185

186
187
188
189
190
191
192
193


    public function clearIdentityRoles()
    {
        $this->identityRoles = null;
    }


194
195
196
197
198
199
200
201
202
203
    /**
     * Retourne tous les rôles de l'utilisateur courant au format littéral.
     *
     * @return array
     * @see getIdentityRoles()
     */
    public function getIdentityRolesToString()
    {
        $f = new RoleFormatter();
        $rolesToStrings = [];
204

205
206
207
208
209
210
        foreach ($this->getIdentityRoles() as $identityRole) {
            $rolesToStrings[$identityRole->getRoleId()] = $f->format($identityRole);
        }

        return $rolesToStrings;
    }
211

212
    /**
213
     * Retourne parmi tous les rôles de l'utilisateur courant ceux qui peuvent être sélectionnés.
214
     *
215
216
     * NB: si plus d'un rôle sont sélectionnables, on zappe le rôle "Authentifié".
     *
217
218
219
220
     * @return array
     */
    public function getSelectableIdentityRoles()
    {
221
222
223
        $filter = function ($r) {
            return !($r instanceof NamedRole && !$r->getSelectable());
        };
Bertrand Gauthier's avatar
Bertrand Gauthier committed
224
        $roles  = array_filter($this->getIdentityRoles(), $filter);
225

226
227
228
229
230
        // si plus d'un rôle sont sélectionnables, on zappe le rôle "Authentifié"
        if (count($roles) > 1 && isset($roles['user'])) {
            unset($roles['user']);
        }

Bertrand Gauthier's avatar
Bertrand Gauthier committed
231
        return $roles;
232
    }
233

234
    /**
235
     * Si un utilisateur est authentifié, retourne le rôle utilisateur sélectionné,
236
     * ou alors le premier sélectionnable si aucun n'a été sélectionné.
237
     *
238
     * @return RoleInterface|null
239
240
241
     */
    public function getSelectedIdentityRole()
    {
242
243
244
        // Si aucun utilisateur n'est authentifié, basta !
        if (! $this->getIdentity()) {
            return null;
245
246
        }

247
248
249
250
        // NB: Si un rôle est spécifié en session comme devant être le prochain rôle sélectionné,
        // c'est lui qui est pris en compte.
        if ($next = $this->getNextSelectedIdentityRole()) {
            $this->getSessionContainer()->selectedIdentityRole = $next; // écriture en session
251
        }
252

253
254
255
256
257
258
259
260
        // Si en session aucun rôle n'est sélectionné ou si cette sélection n'est pas valide,
        // on sélectionne le 1er rôle sélectionnable.
        $selectedRoleId = $this->getSessionContainer()->selectedIdentityRole;
        $selectableRoles = $this->getSelectableIdentityRoles();
        if (null === $selectedRoleId || ! isset($selectableRoles[$selectedRoleId])) {
            $firstSelectableRoleId = reset($selectableRoles);
            $this->setSelectedIdentityRole($firstSelectableRoleId); // écriture en session
        }
261

262
263
264
265
        // Rôle sélectionné en session.
        $roleId = $this->getSessionContainer()->selectedIdentityRole; // lecture en session
        if (! $roleId) {
            return null;
266
        }
267

268
269
270
271
272
273
        $role = $selectableRoles[$roleId];
        if (! $this->isRoleValid($role)) {
            return null;
        }

        return $role;
274
    }
275

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
    /**
     * Retourne le rôle utilisateur sélectionné éventuel au format littéral.
     *
     * @return string
     * @see getSelectedIdentityRole()
     */
    public function getSelectedIdentityRoleToString()
    {
        $role = $this->getSelectedIdentityRole();

        if (! $role) {
            return null;
        }

        $f = new RoleFormatter();
291

292
293
        return $f->format($role);
    }
294

295
    /**
Bertrand Gauthier's avatar
Bertrand Gauthier committed
296
     * Mémorise en session le rôle spécifié comme étant le rôle courant de l'utilisateur.
297
     *
Bertrand Gauthier's avatar
Bertrand Gauthier committed
298
     * NB: seul l'id du rôle est mémorisé en session.
299
     *
Bertrand Gauthier's avatar
Bertrand Gauthier committed
300
     * @param RoleInterface|string $role
301
     *
302
     * @return \UnicaenAuth\Service\UserContext
303
     * @throws RuntimeException
304
305
306
307
308
     */
    public function setSelectedIdentityRole($role)
    {
        if ($role) {
            if (!$this->isRoleValid($role)) {
309
                throw new RuntimeException("Rôle spécifié invalide.");
310
            }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
311
312
313
314
            if ($role instanceof RoleInterface) {
                $role = $role->getRoleId();
            }
            $this->getSessionContainer()->selectedIdentityRole = $role;
315
        } else {
316
317
            unset($this->getSessionContainer()->selectedIdentityRole);
        }
318

319
320
        $this->triggerUserRoleSelectedEvent(UserRoleSelectedEvent::POST_SELECTION, $role);

321
322
323
324
325
326
327
328
        return $this;
    }

    /**
     * Retourne l'éventuel rôle spécifié en session devant être le prochain rôle sélectionné.
     *
     * @return string|null
     */
329
    public function getNextSelectedIdentityRole()
330
331
332
333
334
335
336
337
338
339
    {
        return $this->getSessionContainer()->nextSelectedIdentityRole;
    }

    /**
     * Mémorise en session le rôle devant être le prochain rôle sélectionné.
     *
     * NB: seul l'id du rôle est mémorisé en session ; la durée de vie du stockage est de 1 requête seulement.
     *
     * @param RoleInterface|string $role
340
     *
341
342
343
344
345
346
347
348
349
350
351
     * @return \UnicaenAuth\Service\UserContext
     */
    public function setNextSelectedIdentityRole($role)
    {
        if ($role instanceof RoleInterface) {
            $role = $role->getRoleId();
        }

        if ($role) {
            $this->getSessionContainer()->nextSelectedIdentityRole = $role;
            $this->getSessionContainer()->setExpirationHops(1, 'nextSelectedIdentityRole');
352
        } else {
353
354
355
            unset($this->getSessionContainer()->nextSelectedIdentityRole);
        }

356
357
        $this->triggerUserRoleSelectedEvent(UserRoleSelectedEvent::POST_SELECTION, $role);

358
359
        return $this;
    }
360

361
362
363
364
365
366
367
368
369
370
371
372
    /**
     * Déclenche l'événement donnant à l'application l'opportunité de réagir à la sélection d'un rôle.
     *
     * @param string $name Ex: UserRoleSelectedEvent::POST_SELECTION
     * @param RoleInterface|string|null $role Rôle sélectionné
     */
    private function triggerUserRoleSelectedEvent($name, $role)
    {
        $event = new UserRoleSelectedEvent($name);
        $event
            ->setRole($role)
            ->setTarget($this);
373
        $this->getEventManager()->triggerEvent($event);
374
    }
375

376
    /**
377
     * Teste si le rôle spécifié fait partie des rôles disponibles.
378
379
     *
     * @param RoleInterface|string $role
380
     *
381
     * @return boolean
382
     */
383
    public function isRoleValid($role)
384
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
385
386
387
        if ($role instanceof RoleInterface) {
            $role = $role->getRoleId();
        }
388

389
        foreach ($this->getIdentityRoles() as $r) {
390
            if ($r instanceof RoleInterface) {
391
392
393
394
395
396
                $r = $r->getRoleId();
            }
            if ($role === $r) {
                return true;
            }
        }
397

398
399
        return false;
    }
400

401
    /**
402
     *
403
     * @return Chain
404
405
406
     */
    private function getIdentityProvider()
    {
407
408
        /* @var $identityProvider Chain */
        $identityProvider = $this->getServiceAuthorize()->getIdentityProvider();
409

410
411
        return $identityProvider;
    }
412
}