Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Email
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 authenticate
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
72
 needsCsrfCheck
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 processUser
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3/**
4 * Email authentication module.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) The National Library of Finland 2019.
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   Ere Maijala <ere.maijala@helsinki.fi>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development:plugins:authentication_handlers Wiki
28 */
29
30namespace VuFind\Auth;
31
32use VuFind\Db\Entity\UserEntityInterface;
33use VuFind\Db\Service\UserServiceInterface;
34use VuFind\Exception\Auth as AuthException;
35
36/**
37 * Email authentication module.
38 *
39 * @category VuFind
40 * @package  Authentication
41 * @author   Ere Maijala <ere.maijala@helsinki.fi>
42 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
43 * @link     https://vufind.org/wiki/development:plugins:authentication_handlers Wiki
44 */
45class Email extends AbstractBase
46{
47    /**
48     * Constructor
49     *
50     * @param EmailAuthenticator $emailAuthenticator Email authenticator
51     * @param ILSAuthenticator   $ilsAuthenticator   ILS authenticator
52     */
53    public function __construct(
54        protected EmailAuthenticator $emailAuthenticator,
55        protected ILSAuthenticator $ilsAuthenticator
56    ) {
57    }
58
59    /**
60     * Attempt to authenticate the current user. Throws exception if login fails.
61     *
62     * @param \Laminas\Http\PhpEnvironment\Request $request Request object containing
63     * account credentials.
64     *
65     * @throws AuthException
66     * @return UserEntityInterface Object representing logged-in user.
67     */
68    public function authenticate($request)
69    {
70        // This is a dual-mode method:
71        // First, try to find a user account with the provided email address and send
72        // a login link.
73        // Second, log the user in with the hash from the login link.
74
75        $email = trim($request->getPost()->get('username', ''));
76        $hash = $request->getQuery('hash');
77        if (!$email && !$hash) {
78            throw new AuthException('authentication_error_blank');
79        }
80
81        if (!$hash) {
82            // Validate the credentials:
83            $user = $this->getUserService()->getUserByEmail($email);
84            if ($user) {
85                $loginData = [
86                    'vufind_id' => $user->getId(),
87                ];
88                $this->emailAuthenticator->sendAuthenticationLink(
89                    $user->getEmail(),
90                    $loginData,
91                    ['auth_method' => 'Email']
92                );
93            }
94            // Don't reveal the result
95            throw new \VuFind\Exception\AuthInProgress('email_login_link_sent');
96        }
97
98        $loginData = $this->emailAuthenticator->authenticate($hash);
99        if (isset($loginData['vufind_id'])) {
100            return $this->getUserService()->getUserById($loginData['vufind_id']);
101        } else {
102            // Check if we have more granular data available:
103            if (isset($loginData['userData'])) {
104                $userData = $loginData['userData'];
105                if ($loginData['rememberMe'] ?? false) {
106                    // TODO: This is not a very nice way of carrying this information
107                    // over to the authentication manager:
108                    $request->getPost()->set('remember_me', '1');
109                }
110            } else {
111                $userData = $loginData;
112            }
113            return $this->processUser($userData);
114        }
115
116        // If we got this far, we have a problem:
117        throw new AuthException('authentication_error_invalid');
118    }
119
120    /**
121     * Whether this authentication method needs CSRF checking for the request.
122     *
123     * @param \Laminas\Http\PhpEnvironment\Request $request Request object.
124     *
125     * @return bool
126     */
127    public function needsCsrfCheck($request)
128    {
129        // Disable CSRF if we get a hash in the request
130        return $request->getQuery('hash') ? false : true;
131    }
132
133    /**
134     * Update the database using login user details, then return the User object.
135     *
136     * @param array $info User details returned by the login initiator like ILS.
137     *
138     * @throws AuthException
139     * @return UserEntityInterface Processed User object.
140     */
141    protected function processUser($info)
142    {
143        // Check to see if we already have an account for this user:
144        if (!empty($info['id'])) {
145            $user = $this->getUserService()->getUserByCatId($info['id']);
146            if (empty($user)) {
147                $user = $this->getOrCreateUserByUsername($info['email']);
148                $user->setCatId($info['id']);
149                $this->getDbService(UserServiceInterface::class)->persistEntity($user);
150            }
151        } else {
152            $user = $this->getOrCreateUserByUsername($info['email']);
153        }
154
155        // No need to store a password in VuFind's main password field:
156        $user->setRawPassword('');
157
158        // Update user information based on received data:
159        $fields = ['firstname', 'lastname', 'email', 'major', 'college'];
160        foreach ($fields as $field) {
161            $this->setUserValueByField($user, $field, $info[$field] ?? ' ');
162        }
163
164        // Update the user in the database, then return it to the caller:
165        $this->ilsAuthenticator->saveUserCatalogCredentials(
166            $user,
167            $info['cat_username'] ?? ' ',
168            $info['cat_password'] ?? ' '
169        );
170
171        return $user;
172    }
173}