AuthModel::buildUserFoldersList()   C
last analyzed

Complexity

Conditions 17
Paths 16

Size

Total Lines 109
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 69
c 0
b 0
f 0
nc 16
nop 1
dl 0
loc 109
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
}