Shib.php 5.04 KB
Newer Older
1
2
3
4
5
6
7
<?php

namespace UnicaenAuth\Authentication\Adapter;

use UnicaenAuth\Controller\AuthController;
use UnicaenAuth\Options\Traits\ModuleOptionsAwareTrait;
use UnicaenAuth\Service\Traits\ShibServiceAwareTrait;
8
use UnicaenAuth\Service\User;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
9
10
11
12
13
use Laminas\Authentication\AuthenticationService;
use Laminas\Authentication\Result as AuthenticationResult;
use Laminas\EventManager\EventInterface;
use Laminas\Http\Response;
use Laminas\Router\RouteInterface;
14
use ZfcUser\Authentication\Adapter\AdapterChainEvent;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/**
 * CAS authentication adpater
 *
 * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
 */
class Shib extends AbstractAdapter
{
    use ModuleOptionsAwareTrait;
    use ShibServiceAwareTrait;

    const TYPE = 'shib';

    /**
     * @var string
     */
    protected $type = self::TYPE;

    /**
     * @var AuthenticationService
     */
    protected $authenticationService;

    /**
     * @param AuthenticationService $authenticationService
     * @return self
     */
42
    public function setAuthenticationService(AuthenticationService $authenticationService): self
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    {
        $this->authenticationService = $authenticationService;
        return $this;
    }

    /**
     * @var RouteInterface
     */
    private $router;

    /**
     * @param RouteInterface $router
     */
    public function setRouter(RouteInterface $router)
    {
        $this->router = $router;
    }

61
62
63
64
65
66
67
68
69
70
71
72
73
    /**
     * @var User
     */
    private $userService;

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

74
    /**
75
     * @inheritDoc
76
77
78
79
80
81
     */
    public function authenticate(EventInterface $e)
    {
        // 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().
82
        $event = $e->getTarget(); /** @var AdapterChainEvent $event */
83

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

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

        $shibUser = $this->shibService->getAuthenticatedUser();

        if ($shibUser === null) {
            $redirectUrl = $this->router->assemble(['type' => 'shib'], [
                    'name' => 'zfcuser/authenticate',
104
                    'query' => ['redirect' => $event->getRequest()->getQuery()->get('redirect')]]
105
106
107
108
109
110
111
112
113
114
115
116
            );
            $shibbolethTriggerUrl = $this->router->assemble([], [
                    'name' => 'auth/shibboleth',
                    'query' => ['redirect' => $redirectUrl]]
            );
            $response = new Response();
            $response->getHeaders()->addHeaderLine('Location', $shibbolethTriggerUrl);
            $response->setStatusCode(302);

            return $response;
        }

117
        $identity = $this->createSessionIdentity($shibUser->getEppn());
118

119
        $event->setIdentity($identity);
120
121
        $this->setSatisfied(true);
        $storage = $this->getStorage()->read();
122
        $storage['identity'] = $event->getIdentity();
123
        $this->getStorage()->write($storage);
124
125
126
        $event
            ->setCode(AuthenticationResult::SUCCESS)
            ->setMessages(['Authentication successful.']);
127
128
129

        /* @var $userService User */
        $this->userService->userAuthenticated($shibUser);
130
131

        return true;
132
133
134
    }

    /**
135
     * @inheritDoc
136
     */
137
    public function logout(EventInterface $e)
138
    {
139
140
        parent::logout($e);

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
        $storage = $this->getStorage()->read();
        if (! isset($storage['identity'])) {
            return;
        }

        // désactivation de l'usurpation d'identité éventuelle
        $this->shibService->deactivateUsurpation();

        // URL vers laquelle on redirige après déconnexion
        $returnUrl = $this->router->assemble([], [
                'name' => 'zfcuser/logout',
                'force_canonical' => true,
        ]);
        $shibbolethLogoutUrl = $this->shibService->getLogoutUrl($returnUrl);

        $response = new Response();
        $response->getHeaders()->addHeaderLine('Location', $shibbolethLogoutUrl);
        $response->setStatusCode(302);

        /**
         * Problème : l'IDP Shibboleth ne redirige pas correctement vers l'URL demandée après déconnexion.
         * Solution : pour l'instant, on fait le nécessaire ici (qui devrait être fait normalement dans
         * {@see AuthController::logoutAction()} si l'IDP redonnait la main à l'appli.
         * todo: supprimer cette verrue lorsque l'IDP redirigera correctement.
         */
        $this->authenticationService->clearIdentity();
    }
}