AuthModel::createUserJWT()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 48
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 23
c 0
b 0
f 0
nc 1
nop 18
dl 0
loc 48
rs 9.552

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Teampass - a collaborative passwords manager.
4
 * ---
5
 * This file is part of the TeamPass project.
6
 * 
7
 * TeamPass is free software: you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, version 3 of the License.
10
 * 
11
 * TeamPass is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 * 
19
 * Certain components of this file may be under different licenses. For
20
 * details, see the `licenses` directory or individual file headers.
21
 * ---
22
 * @version    API
23
 *
24
 * @file      AuthModel.php
25
 * @author    Nils Laumaillé ([email protected])
26
 * @copyright 2009-2025 Teampass.net
27
 * @license   GPL-3.0
28
 * @see       https://www.teampass.net
29
 */
30
31
use TeampassClasses\PasswordManager\PasswordManager;
32
use TeampassClasses\NestedTree\NestedTree;
33
use TeampassClasses\ConfigManager\ConfigManager;
34
use Firebase\JWT\JWT;
35
use Firebase\JWT\Key;
36
37
class AuthModel
38
{
39
40
41
    /**
42
     * Is the user allowed
43
     *
44
     * @param string $login
45
     * @param string $password
46
     * @param string $apikey
47
     * @return array
48
     */
49
    public function getUserAuth(string $login, string $password, string $apikey): array
50
    {
51
        // Sanitize
52
        include_once API_ROOT_PATH . '/../sources/main.functions.php';
53
        $inputData = dataSanitizer(
54
            [
55
                'login' => isset($login) === true ? $login : '',
56
                'password' => isset($password) === true ? $password : '',
57
                'apikey' => isset($apikey) === true ? $apikey : '',
58
            ],
59
            [
60
                'login' => 'trim|escape|strip_tags',
61
                'password' => 'trim|escape',
62
                'apikey' => 'trim|escape|strip_tags',
63
            ]
64
        );
65
66
        // Check apikey and credentials
67
        if (empty($inputData['login']) === true || empty($inputData['apikey']) === true || empty($inputData['password']) === true) {
68
            // case where it is a generic key
69
            // Not allowed to use this API
70
71
            return ["error" => "Login failed.", "info" => "User password is requested"];
72
        } else {
73
            // case where it is a user api key
74
            // Check if user exists
75
            $userInfo = DB::queryfirstrow(
76
                "SELECT u.id, u.pw, u.login, u.admin, u.gestionnaire, u.can_manage_all_users, u.fonction_id, u.can_create_root_folder, u.public_key, u.private_key, u.personal_folder, u.fonction_id, u.groupes_visibles, u.groupes_interdits, a.value AS user_api_key, a.allowed_folders as user_api_allowed_folders, a.enabled, a.allowed_to_create, a.allowed_to_read, a.allowed_to_update, a.allowed_to_delete
77
                FROM " . prefixTable('users') . " AS u
78
                INNER JOIN " . prefixTable('api') . " AS a ON (a.user_id=u.id)
79
                WHERE login = %s",
80
                $inputData['login']
81
            );
82
            if (DB::count() === 0) {
83
                return ["error" => "Login failed.", "info" => "apikey : Not valid"];
84
            }
85
86
            // Check if user is enabled
87
            if ((int) $userInfo['enabled'] === 0) {
88
                return ["error" => "Login failed.", "info" => "User not allowed to use API"];
89
            }
90
            
91
            // Check password
92
            $passwordManager = new PasswordManager();
93
            if ($passwordManager->verifyPassword($userInfo['pw'], $inputData['password']) === true) {
94
                // Correct credentials
95
                // get user keys
96
                $privateKeyClear = decryptPrivateKey($inputData['password'], (string) $userInfo['private_key']);
97
98
                // check API key
99
                if ($inputData['apikey'] !== base64_decode(decryptUserObjectKey($userInfo['user_api_key'], $privateKeyClear))) {
100
                    return ["error" => "Login failed.", "apikey" => "Not valid"];
101
                }
102
103
                // Update user's key_tempo
104
                $keyTempo = bin2hex(random_bytes(16));
105
                DB::update(
106
                    prefixTable('users'),
107
                    [
108
                        'key_tempo' => $keyTempo,
109
                    ],
110
                    'id = %i',
111
                    $userInfo['id']
112
                );
113
                
114
                // get user folders list
115
                $ret = $this->buildUserFoldersList($userInfo);
116
117
                // Load config
118
                $configManager = new ConfigManager();
119
                $SETTINGS = $configManager->getAllSettings();
120
121
                // Log user
122
                logEvents($SETTINGS, 'api', 'user_connection', (string) $userInfo['id'], stripslashes($userInfo['login']));
123
124
                // create JWT
125
                return $this->createUserJWT(
126
                    (int) $userInfo['id'],
127
                    (string) $inputData['login'],
128
                    (int) $userInfo['personal_folder'],
129
                    (string) $userInfo['public_key'],
130
                    (string) $privateKeyClear,
131
                    (string) implode(",", $ret['folders']),
132
                    (string) implode(",", $ret['items']),
133
                    (string) $keyTempo,
134
                    (int) $userInfo['admin'],
135
                    (int) $userInfo['gestionnaire'],
136
                    (int) $userInfo['can_create_root_folder'],
137
                    (int) $userInfo['can_manage_all_users'],
138
                    (string) $userInfo['fonction_id'],
139
                    (string) $userInfo['user_api_allowed_folders'],
140
                    (int) $userInfo['allowed_to_create'],
141
                    (int) $userInfo['allowed_to_read'],
142
                    (int) $userInfo['allowed_to_update'],
143
                    (int) $userInfo['allowed_to_delete'],
144
                );
145
            } else {
146
                return ["error" => "Login failed.", "info" => "password : Not valid"];
147
            }
148
        }
149
    }
150
    //end getUserAuth
151
152
    /**
153
     * Create a JWT
154
     *
155
     * @param integer $id
156
     * @param string $login
157
     * @param integer $pf_enabled
158
     * @param string $pubkey
159
     * @param string $privkey
160
     * @param string $folders
161
     * @param string $keyTempo
162
     * @param integer $admin
163
     * @param integer $manager
164
     * @param integer $can_create_root_folder
165
     * @param integer $can_manage_all_users
166
     * @param string $roles
167
     * @param string $allowed_folders
168
     * @param integer $allowed_to_create
169
     * @param integer $allowed_to_read
170
     * @param integer $allowed_to_update
171
     * @param integer $allowed_to_delete
172
     * @return array
173
     */
174
    private function createUserJWT(
175
        int $id,
176
        string $login,
177
        int $pf_enabled,
178
        string $pubkey,
179
        string $privkey,
180
        string $folders,
181
        string $items,
182
        string $keyTempo,
183
        int $admin,
184
        int $manager,
185
        int $can_create_root_folder,
186
        int $can_manage_all_users,
187
        string $roles,
188
        string $allowed_folders,
189
        int $allowed_to_create,
190
        int $allowed_to_read,
191
        int $allowed_to_update,
192
        int $allowed_to_delete,
193
    ): array
194
    {
195
        // Load config
196
        $configManager = new ConfigManager();
197
        $SETTINGS = $configManager->getAllSettings();
198
        
199
		$payload = [
200
            'username' => $login,
201
            'id' => $id, 
202
            'exp' => (time() + $SETTINGS['api_token_duration'] + 600),
203
            'public_key' => $pubkey,
204
            'private_key' => $privkey,
205
            'pf_enabled' => $pf_enabled,
206
            'folders_list' => $folders,
207
            'restricted_items_list' => $items,
208
            'key_tempo' => $keyTempo,
209
            'is_admin' => $admin,
210
            'is_manager' => $manager,
211
            'user_can_create_root_folder' => $can_create_root_folder,
212
            'user_can_manage_all_users' => $can_manage_all_users,
213
            'roles' => $roles,
214
            'allowed_folders' => $allowed_folders,
215
            'allowed_to_create' => $allowed_to_create,
216
            'allowed_to_read' => $allowed_to_read,
217
            'allowed_to_update' => $allowed_to_update,
218
            'allowed_to_delete' => $allowed_to_delete,
219
        ];
220
        
221
        return ['token' => JWT::encode($payload, DB_PASSWD, 'HS256')];
222
    }
223
224
    //end createUserJWT
225
226
227
    /**
228
     * Permit to build the list of folders the user can access
229
     *
230
     * @param array $userInfo
231
     * @return array
232
     */
233
    private function buildUserFoldersList(array $userInfo): array
234
    {
235
        //Build tree
236
        $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
237
        
238
        // Start by adding the manually added folders
239
        $allowedFolders = array_map('intval', explode(";", $userInfo['groupes_visibles']));
240
        $readOnlyFolders = [];
241
        $allowedFoldersByRoles = [];
242
        $restrictedFoldersForItems = [];
243
        $foldersLimited = [];
244
        $foldersLimitedFull = [];
245
        $restrictedItems = [];
246
        $personalFolders = [];
247
248
        $userFunctionId = explode(";", $userInfo['fonction_id']);
249
250
        // Get folders from the roles
251
        if (count($userFunctionId) > 0) {
252
            $rows = DB::query(
253
                'SELECT * 
254
                FROM ' . prefixTable('roles_values') . '
255
                WHERE role_id IN %li  AND type IN ("W", "ND", "NE", "NDNE", "R")',
256
                $userFunctionId
257
            );
258
            foreach ($rows as $record) {
259
                if ($record['type'] === 'R') {
260
                    array_push($readOnlyFolders, $record['folder_id']);
261
                } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
262
                    array_push($allowedFoldersByRoles, $record['folder_id']);
263
                }
264
            }
265
            $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
266
            $readOnlyFolders = array_unique($readOnlyFolders);
267
            // Clean arrays
268
            foreach ($allowedFoldersByRoles as $value) {
269
                $key = array_search($value, $readOnlyFolders);
270
                if ($key !== false) {
271
                    unset($readOnlyFolders[$key]);
272
                }
273
            }
274
        }
275
        
276
        // Does this user is allowed to see other items
277
        $inc = 0;
278
        $rows = DB::query(
279
            'SELECT id, id_tree 
280
            FROM ' . prefixTable('items') . '
281
            WHERE restricted_to LIKE %s'.
282
            (count($userFunctionId) > 0 ? ' AND id_tree NOT IN %li' : ''),
283
            $userInfo['id'],
284
            count($userFunctionId) > 0 ? $userFunctionId : DB::sqleval('0')
285
        );
286
        foreach ($rows as $record) {
287
            // Exclude restriction on item if folder is fully accessible
288
            $restrictedFoldersForItems[$inc] = $record['id_tree'];
289
            ++$inc;
290
        }
291
292
        // Check for the users roles if some specific rights exist on items
293
        $rows = DB::query(
294
            'SELECT i.id_tree, r.item_id
295
            FROM ' . prefixTable('items') . ' AS i
296
            INNER JOIN ' . prefixTable('restriction_to_roles') . ' AS r ON (r.item_id=i.id)
297
            WHERE '.(count($userFunctionId) > 0 ? ' id_tree NOT IN %li AND ' : '').' i.id_tree != ""
298
            ORDER BY i.id_tree ASC',
299
            count($userFunctionId) > 0 ? $userFunctionId : DB::sqleval('0')
300
        );
301
        foreach ($rows as $record) {
302
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
303
            //array_push($foldersLimitedFull, $record['item_id']);
304
            array_push($restrictedItems, $record['item_id']);
305
            array_push($foldersLimitedFull, $record['id_tree']);
306
            ++$inc;
307
        }
308
309
        // Add all personal folders
310
        $rows = DB::queryFirstRow(
311
            'SELECT id 
312
            FROM ' . prefixTable('nested_tree') . '
313
            WHERE title = %i AND personal_folder = 1'.
314
            (count($userFunctionId) > 0 ? ' AND id NOT IN %li' : ''),
315
            $userInfo['id'],
316
            count($userFunctionId) > 0 ? $userFunctionId : DB::sqleval('0')
317
        );
318
        if (empty($rows['id']) === false) {
319
            array_push($personalFolders, $rows['id']);
320
            // get all descendants
321
            $ids = $tree->getDescendants($rows['id'], false, false, true);
322
            foreach ($ids as $id) {
323
                array_push($personalFolders, $id);
324
            }
325
        }
326
327
        // All folders visibles
328
        return [
329
            'folders' => array_unique(
330
            array_filter(
331
                array_merge(
332
                    $allowedFolders,
333
                    $foldersLimitedFull,
334
                    $allowedFoldersByRoles,
335
                    $restrictedFoldersForItems,
336
                    $readOnlyFolders,
337
                    $personalFolders
338
                )
339
                )
340
            ),
341
            'items' => array_unique($restrictedItems),
342
        ];
343
    }
344
    //end buildUserFoldersList
345
}