Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SimulatedSSO
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 4
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 authenticate
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
72
 getSessionInitiator
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 storeExternalSession
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Simulated single sign-on authentication module (for testing purposes only).
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2023.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Authentication
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org Main Page
28 */
29
30namespace VuFind\Auth;
31
32use Laminas\Http\PhpEnvironment\Request;
33use Laminas\Session\ManagerInterface;
34use VuFind\Db\Entity\UserEntityInterface;
35use VuFind\Db\Service\ExternalSessionServiceInterface;
36use VuFind\Exception\Auth as AuthException;
37
38use function is_array;
39
40/**
41 * Simulated single sign-on authentication module (for testing purposes only).
42 *
43 * @category VuFind
44 * @package  Authentication
45 * @author   Demian Katz <demian.katz@villanova.edu>
46 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
47 * @link     https://vufind.org Main Page
48 */
49class SimulatedSSO extends AbstractBase
50{
51    /**
52     * Session initiator URL callback
53     *
54     * @var callable
55     */
56    protected $getSessionInitiatorCallback;
57
58    /**
59     * Configuration settings
60     *
61     * @var array
62     */
63    protected $simulatedSSOConfig;
64
65    /**
66     * Default user attributes, if not overridden by configuration.
67     *
68     * @var array
69     */
70    protected $defaultAttributes = [
71        'firstname' => 'Test',
72        'lastname' => 'User',
73        'email' => 'fake@example.com',
74    ];
75
76    /**
77     * Constructor
78     *
79     * @param callable         $url              Session initiator URL callback
80     * @param array            $config           Configuration settings
81     * @param ILSAuthenticator $ilsAuthenticator ILS authenticator
82     * @param ManagerInterface $sessionManager   Session manager
83     */
84    public function __construct(
85        $url,
86        array $config,
87        protected ILSAuthenticator $ilsAuthenticator,
88        protected ManagerInterface $sessionManager
89    ) {
90        $this->getSessionInitiatorCallback = $url;
91        $this->simulatedSSOConfig = $config;
92    }
93
94    /**
95     * Attempt to authenticate the current user. Throws exception if login fails.
96     *
97     * @param Request $request Request object containing account credentials.
98     *
99     * @throws AuthException
100     * @return UserEntityInterface Object representing logged-in user.
101     */
102    public function authenticate($request)
103    {
104        // If we made it this far, we should log in the user!
105        $username = $this->simulatedSSOConfig['General']['username'] ?? 'fakeuser1';
106        if (!$username) {
107            throw new AuthException('Simulated failure');
108        }
109        $userService = $this->getUserService();
110        $user = $this->getOrCreateUserByUsername($username);
111
112        // Get attribute configuration -- use defaults if no value is set, and use an
113        // empty array if something invalid was provided.
114        $attribs = $this->simulatedSSOConfig['General']['attributes']
115            ?? $this->defaultAttributes;
116        if (!is_array($attribs)) {
117            $attribs = [];
118        }
119
120        $catPassword = null;
121        foreach ($attribs as $attribute => $value) {
122            if ($attribute == 'email') {
123                $userService->updateUserEmail($user, $value);
124            } elseif ($attribute != 'cat_password') {
125                $this->setUserValueByField($user, $attribute, $value ?? '');
126            } else {
127                $catPassword = $value;
128            }
129        }
130        if (!empty($catUsername = $user->getCatUsername())) {
131            $this->ilsAuthenticator->setUserCatalogCredentials(
132                $user,
133                $catUsername,
134                empty($catPassword) ? $this->ilsAuthenticator->getCatPasswordForUser($user) : $catPassword
135            );
136        }
137
138        $this->storeExternalSession();
139
140        // Save and return the user object:
141        $userService->persistEntity($user);
142        return $user;
143    }
144
145    /**
146     * Get the URL to establish a session (needed when the internal VuFind login
147     * form is inadequate). Returns false when no session initiator is needed.
148     *
149     * @param string $target Full URL where external authentication method should
150     * send user after login (some drivers may override this).
151     *
152     * @return bool|string
153     */
154    public function getSessionInitiator($target)
155    {
156        $target .= (str_contains($target, '?') ? '&' : '?') . 'auth_method=SimulatedSSO';
157        return ($this->getSessionInitiatorCallback)($target);
158    }
159
160    /**
161     * Add session id mapping to external_session table for single logout support
162     *
163     * Using 'EXTERNAL_SESSION_ID' as the id -- for testing only.
164     *
165     * @return void
166     */
167    protected function storeExternalSession(): void
168    {
169        $localSessionId = $this->sessionManager->getId();
170        $this->getDbService(ExternalSessionServiceInterface::class)
171            ->addSessionMapping($localSessionId, 'EXTERNAL_SESSION_ID');
172    }
173}