Passed
Push — master ( e0ed8e...0d3aa7 )
by Nils
11:00
created

AuthModel::getUserJWT()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 8
rs 10
1
<?php
2
/**
3
 * Teampass - a collaborative passwords manager.
4
 * ---
5
 * This library is distributed in the hope that it will be useful,
6
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8
 * ---
9
 *
10
 * @project   Teampass API
11
 *
12
 * @file      AuthModel.php
13
 * ---
14
 *
15
 * @author    Nils Laumaillé ([email protected])
16
 *
17
 * @copyright 2009-2022 Teampass.net
18
 *
19
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
20
 * ---
21
 *
22
 * @see       https://www.teampass.net
23
 */
24
require_once PROJECT_ROOT_PATH . "/Model/Database.php";
25
26
 
27
class AuthModel extends Database
28
{
29
    public function getUserAuth($login, $password, $apikey)
30
    {
31
        // Check if user exists
32
        $userInfoRes = $this->select("SELECT id, pw, public_key, private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits FROM " . prefixTable('users') . " WHERE login='".$login."'");
33
        $userInfoRes[0]['special'] = '';
34
        $userInfo = $userInfoRes[0];
35
        
36
        // Check password
37
        include_once PROJECT_ROOT_PATH . '/../sources/SplClassLoader.php';
38
        $pwdlib = new SplClassLoader('PasswordLib', PROJECT_ROOT_PATH . '/../includes/libraries');
39
        $pwdlib->register();
40
        $pwdlib = new PasswordLib\PasswordLib();
41
        if ($pwdlib->verifyPasswordHash($password, $userInfo['pw']) === true) {
42
            // Correct credentials
43
            // Now check apikey
44
            $apiInfo = $this->select("SELECT count(*) FROM " . prefixTable('api') . " WHERE value='".$apikey."'");
45
            if ((int) $apiInfo[0]['count(*)'] === 1) {
46
                // get user keys
47
                $privateKeyClear = decryptPrivateKey($password, (string) $userInfo['private_key']); //prepareUserEncryptionKeys($userInfo, $password);
48
49
                // get user folders list
50
                $folders = $this->buildUserFoldersList($userInfo);
51
52
                // create JWT
53
                return $this->createUserJWT(
54
                    $userInfo[0]['id'],
55
                    $login,
56
                    $userInfo['personal_folder'],
57
                    $userInfo['public_key'],
58
                    $privateKeyClear,
59
                    implode(",", $folders)
60
                );
61
            } else {
62
                return array("error" => "Login failed.", "apikey" => "Not valid");
63
            }
64
        } else {
65
            return array("error" => "Login failed.", "password" => $password);
66
        }
67
    }
68
69
    private function createUserJWT($id, $login, $pf_enabled, $pubkey, $privkey, $folders): array
70
    {
71
        require PROJECT_ROOT_PATH . '/../includes/config/tp.config.php';
72
        $headers = array('alg'=>'HS256','typ'=>'JWT');
73
		$payload = array(
74
            'username' => $login,
75
            'id' => $id, 
76
            'exp' => (time() + $SETTINGS['api_token_duration'] + 600),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SETTINGS seems to be never defined.
Loading history...
77
            'public_key' => $pubkey,
78
            'private_key' => $privkey,
79
            'pf_enabled' => $pf_enabled,
80
            'folders_list' => $folders,
81
        );
82
83
        include_once PROJECT_ROOT_PATH . '/inc/jwt_utils.php';
84
		return array('token' => generate_jwt($headers, $payload));
85
    }
86
87
    private function buildUserFoldersList($userInfo)
88
    {
89
        // Start by adding the manually added folders
90
        $allowedFolders = $userInfo['groupes_visibles'];
91
        $readOnlyFolders = [];
92
        $allowedFoldersByRoles = [];
93
        $restrictedFoldersForItems = [];
94
        $foldersLimited = [];
95
        $foldersLimitedFull = [];
96
97
        $userFunctionId = str_replace(";", ",", $userInfo['fonction_id']);
98
99
        // Get folders from the roles
100
        if (empty($userFunctionId) === false) {
101
            $rows = $this->select("SELECT * FROM " . prefixTable('roles_values') . " WHERE role_id IN (".$userFunctionId.") AND type IN ('W', 'ND', 'NE', 'NDNE', 'R')");
102
            foreach ($rows as $record) {
103
                if ($record['type'] === 'R') {
104
                    array_push($readOnlyFolders, $record['folder_id']);
105
                } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
106
                    array_push($allowedFoldersByRoles, $record['folder_id']);
107
                }
108
            }
109
            $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
110
            $readOnlyFolders = array_unique($readOnlyFolders);
111
            // Clean arrays
112
            foreach ($allowedFoldersByRoles as $value) {
113
                $key = array_search($value, $readOnlyFolders);
114
                if ($key !== false) {
115
                    unset($readOnlyFolders[$key]);
116
                }
117
            }
118
        }
119
120
        // Does this user is allowed to see other items
121
        $rows = $this->select("SELECT id, id_tree FROM " . prefixTable('items') . " WHERE WHERE restricted_to LIKE '".$userInfo['id']."'".
122
            (empty($userFunctionId) === false ? ' AND id_tree NOT IN ('.$userFunctionId.')' : ''));
123
        foreach ($rows as $record) {
124
            // Exclude restriction on item if folder is fully accessible
125
            //if (in_array($record['id_tree'], $allowedFolders) === false) {
126
                $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $inc seems to be never defined.
Loading history...
127
            //}
128
        }
129
130
        // Check for the users roles if some specific rights exist on items
131
        $rows = $this->select("SELECT i.id_tree, r.item_id
132
            FROM " . prefixTable('items') . " as i
133
            INNER JOIN " . prefixTable('restriction_to_roles') . " as r ON (r.item_id=i.id)
134
            WHERE ".(empty($userFunctionId) === false ? ' id_tree NOT IN ('.$userFunctionId.') AND ' : '')." i.id_tree != ''
135
            ORDER BY i.id_tree ASC");
136
        foreach ($rows as $record) {
137
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
138
            array_push($foldersLimitedFull, $record['item_id']);
139
        }
140
141
        // TODO ajouter perso folders
142
        $rows = $this->select(
143
            'SELECT id
144
            FROM ' . prefixTable('nested_tree') . '
145
            WHERE title = '.$userInfo['id'].' AND personal_folder = 1'.
146
            (empty($userFunctionId) === false ? ' AND id NOT IN ('.$userFunctionId.')' : '').
147
            ' LIMIT 0,1'
148
        );
149
        if (empty($rows['id']) === false) {
150
            array_push($personalFolders, $rows['id']);
151
            array_push($allowedFolders, $rows['id']);
152
            // get all descendants
153
            $ids = $tree->getDescendants($rows['id'], false, false, true);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tree seems to be never defined.
Loading history...
154
            foreach ($ids as $id) {
155
                array_push($allowedFolders, $id);
156
                array_push($personalFolders, $id);
157
            }
158
        }
159
160
        // Exclude all other PF
161
        $where = new WhereClause('and');
162
        $where->add('personal_folder=%i', 1);
163
        if (
164
            (int) $enablePfFeature === 1
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $enablePfFeature seems to be never defined.
Loading history...
165
            && (int) $globalsPersonalFolders === 1
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $globalsPersonalFolders seems to be never defined.
Loading history...
166
        ) {
167
            $where->add('title=%s', $globalsUserId);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $globalsUserId seems to be never defined.
Loading history...
168
            $where->negateLast();
169
        }
170
        $persoFlds = DB::query(
0 ignored issues
show
Unused Code introduced by
The assignment to $persoFlds is dead and can be removed.
Loading history...
171
            'SELECT id
172
            FROM ' . prefixTable('nested_tree') . '
173
            WHERE %l',
174
            $wheres
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $wheres does not exist. Did you maybe mean $where?
Loading history...
175
        );
176
177
        $rows = $this->select(
178
            'SELECT id
179
            FROM ' . prefixTable('nested_tree') . '
180
            WHERE title != '.$userInfo['id'].' AND personal_folder = 1'
181
        );
182
183
        foreach ($rows as $persoFldId) {
184
            array_push($noAccessPersonalFolders, $persoFldId['id']);
185
        }
186
187
        // All folders visibles
188
        $allowedFolders = array_merge(
189
            $allowedFolders,
190
            $foldersLimitedFull,
191
            $allowedFoldersByRoles,
192
            $restrictedFoldersForItems,
193
            $readOnlyFolders
194
        );
195
        // Exclude from allowed folders all the specific user forbidden folders
196
        if (count($noAccessFolders) > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $noAccessFolders seems to be never defined.
Loading history...
197
            $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
198
        }
199
200
        return $allowedFolders;
201
    }
202
}