diff --git a/api/Model/ItemModel.php b/api/Model/ItemModel.php index 66ff18034..56702b967 100755 --- a/api/Model/ItemModel.php +++ b/api/Model/ItemModel.php @@ -172,7 +172,7 @@ public function addItem( $password = $cryptedData['encrypted']; // Step 8: Insert the new item into the database - $newID = $this->insertNewItem($data, $password, $passwordKey, $userId, $itemInfos, $SETTINGS); + $newID = $this->insertNewItem($data, $password, $itemInfos); // Step 9: Handle post-insert tasks (logging, sharing, tagging) $this->handlePostInsertTasks($newID, $itemInfos, $folderId, $passwordKey, $userId, $username, $tags, $data, $SETTINGS); @@ -330,7 +330,7 @@ private function checkPasswordComplexity(string $password, array $itemInfos) : v */ private function checkForDuplicates(string $label, array $SETTINGS, array $itemInfos) : void { - $existingItem = DB::queryFirstRow( + DB::queryFirstRow( 'SELECT * FROM ' . prefixTable('items') . ' WHERE label = %s AND inactif = %i', $label, @@ -364,13 +364,10 @@ private function encryptPassword(string $password) : array * Inserts the new item into the database with all its associated data. * @param array $data - The item data to insert * @param string $password - The encrypted password - * @param string $passwordKey - The encryption key for the password - * @param int $userId - The ID of the user creating the item * @param array $itemInfos - Folder-specific settings - * @param array $SETTINGS - Global settings * @return int - Returns the ID of the newly created item */ - private function insertNewItem(array $data, string $password, string $passwordKey, int $userId, array $itemInfos, array $SETTINGS) : int + private function insertNewItem(array $data, string $password, array $itemInfos) : int { DB::insert( prefixTable('items'), diff --git a/composer.lock b/composer.lock index 868da37ab..683043868 100644 --- a/composer.lock +++ b/composer.lock @@ -1319,7 +1319,7 @@ }, { "name": "illuminate/collections", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", @@ -1374,7 +1374,7 @@ }, { "name": "illuminate/conditionable", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -1420,7 +1420,7 @@ }, { "name": "illuminate/container", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", @@ -1471,7 +1471,7 @@ }, { "name": "illuminate/contracts", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -1519,7 +1519,7 @@ }, { "name": "illuminate/filesystem", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", @@ -1586,7 +1586,7 @@ }, { "name": "illuminate/macroable", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1632,7 +1632,7 @@ }, { "name": "illuminate/support", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", @@ -1703,7 +1703,7 @@ }, { "name": "illuminate/translation", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/translation.git", @@ -1754,7 +1754,7 @@ }, { "name": "illuminate/validation", - "version": "v10.48.20", + "version": "v10.48.22", "source": { "type": "git", "url": "https://github.com/illuminate/validation.git", @@ -2898,16 +2898,16 @@ }, { "name": "psr/log", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "79dff0b268932c640297f5208d6298f71855c03e" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", - "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -2942,9 +2942,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2024-08-21T13:31:24+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", diff --git a/includes/config/include.php b/includes/config/include.php index baf061a05..4b810ec46 100755 --- a/includes/config/include.php +++ b/includes/config/include.php @@ -28,7 +28,7 @@ define('TP_VERSION', '3.1.2'); define("UPGRADE_MIN_DATE", "1724862801"); -define('TP_VERSION_MINOR', '73'); +define('TP_VERSION_MINOR', '75'); define('TP_TOOL_NAME', 'Teampass'); define('TP_ONE_DAY_SECONDS', 86400); define('TP_ONE_WEEK_SECONDS', 604800); diff --git a/includes/tables_integrity.json b/includes/tables_integrity.json index f99affa09..a640676a7 100644 --- a/includes/tables_integrity.json +++ b/includes/tables_integrity.json @@ -9,23 +9,23 @@ }, { "table_name": "background_subtasks", - "structure_hash": "fa1b91a90831d8551dd15382363055d740c6a276aba8d4ea6179ba47d456b44f" + "structure_hash": "f1e4792ca0704feb8292a112fb4a35762ffa797973a67a6c75aa67ceb93e516f" }, { "table_name": "background_tasks", - "structure_hash": "55abb02b0108c6361447c8063f33ccc95e9e87f128812c44c7902940ab3b680c" + "structure_hash": "4b9509f4f599ed0d740c3a1eba74ca0bc6634bbffb78dc8e6cd38d336ce7feac" }, { "table_name": "background_tasks_logs", - "structure_hash": "f79cfdf6d07763de051307530add35850ff7f36cb9a4bd35f460c74f7b4ae956" + "structure_hash": "ff03765e36130fa35e38db8027227afc81c94b99813e2581b4bdaaedea936638" }, { "table_name": "cache", - "structure_hash": "0a93e028439f6b0767f083683e0b23c80e885624c800d9432195dd61daf4dc8d" + "structure_hash": "d3bf3f986a7f461a7abc1c2b1acf2e51823a7fc359d9d0a6df8e6a8609ca0213" }, { "table_name": "cache_tree", - "structure_hash": "7efb5b1f13896e721918994daa29de092917b8e59e061c9d23441aa8fd7fa6c4" + "structure_hash": "efe3f3abab2e27d61f5df294722f2b9908ef83bfb63c8540a3c800ef29870fa9" }, { "table_name": "categories", @@ -57,7 +57,7 @@ }, { "table_name": "items", - "structure_hash": "d2a8ce666c52f60ffe6876e3da4ac1eada4ac77319ff1cfe07a324124aa2af10" + "structure_hash": "b6db953e2ffe440662d8a58faf384f7a0f68a96f25c7eaf179c334ae079061b8" }, { "table_name": "items_change", @@ -97,19 +97,19 @@ }, { "table_name": "log_items", - "structure_hash": "21f70282e119dd0eb32d2854457025984747ec78415772b611b9f183e02d9e7c" + "structure_hash": "00830c64d14adae076b5b064e645f6c7011d07aacb363d3290f1c82696a20395" }, { "table_name": "log_system", - "structure_hash": "ae03b53ccc71dacb85cf87a6b4da9a7423c3f8090da86261b8b17988e8f1e39d" + "structure_hash": "bb39e2ab38dd6dd307cd4a43c3b1ede3154d94440c486f36c20a009dd5005693" }, { "table_name": "misc", - "structure_hash": "c57a696824200dcf34cf06c293af85efb28bbba0e08338d0f1e4d6d2abc51d45" + "structure_hash": "14b19547bd0d8331108f91697dca663e2d28274c164782c82a9a66316964940c" }, { "table_name": "nested_tree", - "structure_hash": "1eb849da0b5770fd7ef347fe1bb5ceecae92b0b90ac84b01e1ca9ebe0314f8f3" + "structure_hash": "3528774a3840468768c3b3f2c91e510c6a34640144b1f4e2178557aade8170b3" }, { "table_name": "notification", @@ -157,7 +157,7 @@ }, { "table_name": "roles_values", - "structure_hash": "93c3f4668efb28e03cf5e380455808dd6dbfb465313db98e2c24ba4a84f04812" + "structure_hash": "e31b433be42217e41c94f269f159d16a4553f72d0b61b27060a7faf3fafd9cb1" }, { "table_name": "sharekeys_fields", @@ -169,7 +169,7 @@ }, { "table_name": "sharekeys_items", - "structure_hash": "36d7a797caeb4692087c9fb89b43812fe1b5ba8f5341e330274f9a20f6d855a7" + "structure_hash": "f4cfd22fa7cc4a9a352a5d61ec5122d216163bf9d1885708e43851e3ffe73f28" }, { "table_name": "sharekeys_logs", diff --git a/sources/folders.class.php b/sources/folders.class.php index 86b5d0c6e..4435edf09 100644 --- a/sources/folders.class.php +++ b/sources/folders.class.php @@ -30,6 +30,7 @@ */ use TeampassClasses\NestedTree\NestedTree; +use TeampassClasses\SessionManager\SessionManager; class FolderManager { @@ -215,205 +216,215 @@ private function checkComplexityLevel( } /** - * Create folder + * Creates a new folder in the system, applying necessary settings, permissions, + * cache updates, and user role management. * - * @param array $params - * @param array $parentFolderData - * @return array + * @param array $params - Parameters for folder creation (e.g., title, icon, etc.) + * @param array $parentFolderData - Parent folder data (e.g., ID, permissions) + * @return array - Returns an array indicating success or failure */ private function createFolder($params, $parentFolderData) { extract($params); extract($parentFolderData); + + if ($this->canCreateFolder($isPersonal, $user_is_admin, $user_is_manager, $user_can_manage_all_users, $user_can_create_root_folder)) { + $newId = $this->insertFolder($params, $parentFolderData); + $this->addComplexity($newId, $complexity); + $this->setFolderCategories($newId); + $this->updateTimestamp(); + $this->rebuildFolderTree($user_is_admin, $title, $parent_id, $isPersonal, $user_id, $newId); + $this->manageFolderPermissions($parent_id, $newId, $user_roles, $access_rights, $user_is_admin); + $this->copyCustomFieldsCategories($parent_id, $newId); + $this->clearCacheForUsersWithSimilarRoles($user_roles); + + return ['error' => false, 'newId' => $newId]; + } else { + return ['error' => true, 'newId' => null]; + } + } + + /** + * Checks if the user has the permissions to create a folder. + */ + private function canCreateFolder($isPersonal, $user_is_admin, $user_is_manager, $user_can_manage_all_users, $user_can_create_root_folder) + { + return (int)$isPersonal === 1 || + (int)$user_is_admin === 1 || + ((int)$user_is_manager === 1 || (int)$user_can_manage_all_users === 1) || + ($this->settings['enable_user_can_create_folders'] ?? false) || + ((int)$user_can_create_root_folder === 1); + } + + /** + * Inserts a new folder into the database and returns the new folder ID. + */ + private function insertFolder($params, $parentFolderData) + { + DB::insert(prefixTable('nested_tree'), [ + 'parent_id' => $params['parent_id'], + 'title' => $params['title'], + 'personal_folder' => $params['isPersonal'] ?? 0, + 'renewal_period' => $params['duration'] ?? 0, + 'bloquer_creation' => $params['create_auth_without'] ?? $parentFolderData['parentBloquerCreation'], + 'bloquer_modification' => $params['edit_auth_without'] ?? $parentFolderData['parentBloquerModification'], + 'fa_icon' => $params['icon'] ?? TP_DEFAULT_ICON, + 'fa_icon_selected' => $params['icon_selected'] ?? TP_DEFAULT_ICON_SELECTED, + 'categories' => '', + ]); + + return DB::insertId(); + } + + /** + * Adds complexity settings for the newly created folder. + */ + private function addComplexity($folderId, $complexity) + { + DB::insert(prefixTable('misc'), [ + 'type' => 'complex', + 'intitule' => $folderId, + 'valeur' => $complexity, + 'created_at' => time(), + ]); + } + + /** + * Ensures that folder categories are set. + */ + private function setFolderCategories($folderId) + { + handleFoldersCategories([$folderId]); + } + + /** + * Updates the last folder change timestamp. + */ + private function updateTimestamp() + { + DB::update(prefixTable('misc'), [ + 'valeur' => time(), + 'updated_at' => time(), + ], 'type = %s AND intitule = %s', 'timestamp', 'last_folder_change'); + } + + /** + * Rebuilds the folder tree and updates the cache for non-admin users. + */ + private function rebuildFolderTree($user_is_admin, $title, $parent_id, $isPersonal, $user_id, $newId) + { + $session = SessionManager::getSession(); + $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); + $tree->rebuild(); - if ( - (int) $isPersonal === 1 - || (int) $user_is_admin === 1 - || ((int) $user_is_manager === 1 || (int) $user_can_manage_all_users === 1) - || (isset($this->settings['enable_user_can_create_folders']) === true - && (int) $this->settings['enable_user_can_create_folders'] == 1) - || ((int) $user_can_create_root_folder && null !== $user_can_create_root_folder && (int) $user_can_create_root_folder === 1) - ) { - //create folder - DB::insert( - prefixTable('nested_tree'), - array( - 'parent_id' => $parent_id, - 'title' => $title, - 'personal_folder' => null !== $isPersonal ? $isPersonal : 0, - 'renewal_period' => isset($duration) === true && (int) $duration !== 0 ? $duration : 0, - 'bloquer_creation' => isset($create_auth_without) === true && (int) $create_auth_without === 1 ? '1' : $parentBloquerCreation, - 'bloquer_modification' => isset($edit_auth_without) === true && (int) $edit_auth_without === 1 ? '1' : $parentBloquerModification, - 'fa_icon' => empty($icon) === true ? TP_DEFAULT_ICON : $icon, - 'fa_icon_selected' => empty($icon_selected) === true ? TP_DEFAULT_ICON_SELECTED : $icon_selected, - 'categories' => '', - ) - ); - $newId = DB::insertId(); - - //Add complexity - DB::insert( - prefixTable('misc'), - array( - 'type' => 'complex', - 'intitule' => $newId, - 'valeur' => $complexity, - 'created_at' => time(), - ) - ); - - // ensure categories are set - handleFoldersCategories( - [$newId] - ); - - // Update timestamp - DB::update( - prefixTable('misc'), - array( - 'valeur' => time(), - 'updated_at' => time(), - ), - 'type = %s AND intitule = %s', - 'timestamp', - 'last_folder_change' - ); - - // Load tree - $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); - - // rebuild tree - $tree->rebuild(); - - - // --> build json tree if not Admin - if ($user_is_admin === 0) { - // Get path - $path = ''; - $tree_path = $tree->getPath(0, false); - foreach ($tree_path as $fld) { - $path .= empty($path) === true ? $fld->title : '/'.$fld->title; - } - $new_json = [ - "path" => $path, - "id" => $newId, - "level" => count($tree_path), - "title" => $title, - "disabled" => 0, - "parent_id" => $parent_id, - "perso" => $isPersonal, - "is_visible_active" => 0, - ]; - - // update cache_tree - $cache_tree = DB::queryfirstrow( - 'SELECT increment_id, folders, visible_folders - FROM ' . prefixTable('cache_tree').' WHERE user_id = %i', - (int) $user_id - ); - if (empty($cache_tree) === true) { - DB::insert( - prefixTable('cache_tree'), - array( - 'user_id' => $user_id, - 'folders' => json_encode($newId), - 'visible_folders' => json_encode($new_json), - 'timestamp' => time(), - 'data' => '[{}]', - ) - ); - } else { - $a_folders = is_null($cache_tree['folders']) === true ? [] : json_decode($cache_tree['folders'], true); - array_push($a_folders, $newId); - $a_visible_folders = is_null($cache_tree['visible_folders']) === true || empty($cache_tree['visible_folders']) === true ? [] : json_decode($cache_tree['visible_folders'], true); - array_push($a_visible_folders, $new_json); - DB::update( - prefixTable('cache_tree'), - array( - 'folders' => json_encode($a_folders), - 'visible_folders' => json_encode($a_visible_folders), - 'timestamp' => time(), - ), - 'increment_id = %i', - (int) $cache_tree['increment_id'] - ); - } - } - // <-- end - build json tree - - // Create expected groups access rights based upon option selected - if ( - isset($this->settings['subfolder_rights_as_parent']) === true - && (int) $this->settings['subfolder_rights_as_parent'] === 1 - ) { - //If it is a subfolder, then give access to it for all roles that allows the parent folder - $rows = DB::query('SELECT role_id, type FROM ' . prefixTable('roles_values') . ' WHERE folder_id = %i', $parent_id); - foreach ($rows as $record) { - //add access to this subfolder - DB::insert( - prefixTable('roles_values'), - array( - 'role_id' => $record['role_id'], - 'folder_id' => $newId, - 'type' => $record['type'], - ) - ); - } - } elseif ((int) $user_is_admin !== 1) { - // If not admin and no option enabled - // then provide expected rights based upon user's roles - foreach (array_unique(explode(';', $user_roles)) as $role) { - if (empty($role) === false) { - DB::insert( - prefixTable('roles_values'), - array( - 'role_id' => $role, - 'folder_id' => $newId, - 'type' => $access_rights, - ) - ); - } - } - } - - // if parent folder has Custom Fields Categories then add to this child one too - $rows = DB::query('SELECT id_category FROM ' . prefixTable('categories_folders') . ' WHERE id_folder = %i', $parent_id); + SessionManager::addRemoveFromSessionArray('user-accessible_folders', [$newId], 'add'); + + if ($user_is_admin === 0) { + $this->updateUserFolderCache($tree, $title, $parent_id, $isPersonal, $user_id, $newId); + } + } + + /** + * Updates the user folder cache for non-admin users. + */ + private function updateUserFolderCache($tree, $title, $parent_id, $isPersonal, $user_id, $newId) + { + $path = ''; + $tree_path = $tree->getPath(0, false); + foreach ($tree_path as $fld) { + $path .= empty($path) ? $fld->title : '/' . $fld->title; + } + + $new_json = [ + "path" => $path, + "id" => $newId, + "level" => count($tree_path), + "title" => $title, + "disabled" => 0, + "parent_id" => $parent_id, + "perso" => $isPersonal, + "is_visible_active" => 0, + ]; + + $cache_tree = DB::queryFirstRow('SELECT increment_id, folders, visible_folders FROM ' . prefixTable('cache_tree') . ' WHERE user_id = %i', (int)$user_id); + + if (empty($cache_tree)) { + DB::insert(prefixTable('cache_tree'), [ + 'user_id' => $user_id, + 'folders' => json_encode($newId), + 'visible_folders' => json_encode($new_json), + 'timestamp' => time(), + 'data' => '[{}]', + ]); + } else { + $folders = json_decode($cache_tree['folders'] ?? '[]', true); + $visible_folders = json_decode($cache_tree['visible_folders'] ?? '[]', true); + $folders[] = $newId; + $visible_folders[] = $new_json; + + DB::update(prefixTable('cache_tree'), [ + 'folders' => json_encode($folders), + 'visible_folders' => json_encode($visible_folders), + 'timestamp' => time(), + ], 'increment_id = %i', (int)$cache_tree['increment_id']); + } + } + + /** + * Manages folder permissions based on user roles or parent folder settings. + */ + private function manageFolderPermissions($parent_id, $newId, $user_roles, $access_rights, $user_is_admin) + { + if ($this->settings['subfolder_rights_as_parent'] ?? false) { + $rows = DB::query('SELECT role_id, type FROM ' . prefixTable('roles_values') . ' WHERE folder_id = %i', $parent_id); foreach ($rows as $record) { - //add CF Category to this subfolder - DB::insert( - prefixTable('categories_folders'), - array( - 'id_category' => $record['id_category'], - 'id_folder' => $newId, - ) - ); + DB::insert(prefixTable('roles_values'), [ + 'role_id' => $record['role_id'], + 'folder_id' => $newId, + 'type' => $record['type'], + ]); } - - // clear cache cache for each user that have at least one similar role as the current user - $usersWithSimilarRoles = empty($user_roles) === false ? getUsersWithRoles( - explode(";", $user_roles) - ) : []; - foreach ($usersWithSimilarRoles as $user) { - // delete cache tree - DB::delete( - prefixTable('cache_tree'), - 'user_id = %i', - $user - ); + } elseif ((int)$user_is_admin !== 1) { + foreach (array_unique(explode(';', $user_roles)) as $role) { + if (!empty($role)) { + DB::insert(prefixTable('roles_values'), [ + 'role_id' => $role, + 'folder_id' => $newId, + 'type' => $access_rights, + ]); + } } - return array( - 'error' => false, - 'newId' => $newId, - ); - - } else { - return array( - 'error' => true, - 'newId' => $newId, - ); } } + /** + * Copies custom field categories from the parent folder to the newly created folder. + */ + private function copyCustomFieldsCategories($parent_id, $newId) + { + $rows = DB::query('SELECT id_category FROM ' . prefixTable('categories_folders') . ' WHERE id_folder = %i', $parent_id); + foreach ($rows as $record) { + DB::insert(prefixTable('categories_folders'), [ + 'id_category' => $record['id_category'], + 'id_folder' => $newId, + ]); + } + } + + /** + * Clears the cache for users with similar roles to the current user. + */ + private function clearCacheForUsersWithSimilarRoles($user_roles) + { + $usersWithSimilarRoles = getUsersWithRoles(explode(";", $user_roles)); + foreach ($usersWithSimilarRoles as $user) { + DB::delete(prefixTable('cache_tree'), 'user_id = %i', $user); + } + } + + /** + * Returns an error response. + */ private function errorResponse($message, $newIdSuffix = "") { return [ diff --git a/sources/folders.queries.php b/sources/folders.queries.php index ba2a08047..c3d8bdd0b 100755 --- a/sources/folders.queries.php +++ b/sources/folders.queries.php @@ -637,6 +637,42 @@ FILTER_SANITIZE_FULL_SPECIAL_CHARS ); + // Ensure that the root folder is not part of the list + if (in_array(0, $post_folders, true)) { + echo prepareExchangedData( + array( + 'error' => true, + 'message' => $lang->get('error_not_allowed_to'). " (You can't delete the root folder)", + ), + 'encode' + ); + break; + } + + // Ensure that user has access to all folders + $foldersAccessible = DB::query( + 'SELECT id + FROM ' . prefixTable('nested_tree') . ' + WHERE id IN %li AND id IN %li', + $post_folders, + $session->get('user-accessible_folders') + ); + // Extract found folder IDs + $accessibleIds = array_column($foldersAccessible, 'id'); + // Identify those that are not existing in DB or not visible by user + $missingFolders = array_diff($post_folders, $accessibleIds); + if (!empty($missingFolders)) { + // There is some issues + echo prepareExchangedData( + array( + 'error' => true, + 'message' => $lang->get('error_not_allowed_to') . ' (The following folders are not accessible or do not exist: ' . implode(', ', $missingFolders) . ')', + ), + 'encode' + ); + break; + } + //decrypt and retreive data in JSON format $folderForDel = array(); @@ -644,32 +680,6 @@ DB::startTransaction(); foreach ($post_folders as $folderId) { - /** - * WARN: NestedTree->getDescendants: - * Specify an invalid ID (e.g. 0) to retrieve all data. - * - * Which means the root folder may be deleted. - * It is necessary to verify that the ID provided exists, is - * not 0 and that the user is authorized to delete it. - */ - $folderExists = DB::queryFirstRow( - 'SELECT COUNT(id) AS count FROM '.prefixTable('nested_tree').' WHERE id = %i', - (int) $folderId - )['count']; - if ((int) $folderId === 0 || - (int) $folderExists === 0 || - !in_array((int) $folderId, $session->get('user-accessible_folders')) - ) { - echo prepareExchangedData( - array( - 'error' => true, - 'message' => $lang->get('error_not_allowed_to'), - ), - 'encode' - ); - exit(); - } - // Check if parent folder is personal $dataParent = DB::queryfirstrow( 'SELECT personal_folder @@ -730,7 +740,11 @@ $folderForDel[] = $thisSubFolders->id; //delete items & logs - $itemsInSubFolder = DB::query('SELECT id FROM ' . prefixTable('items') . ' WHERE id_tree=%i', $thisSubFolders->id); + $itemsInSubFolder = DB::query( + 'SELECT id FROM ' . prefixTable('items') . ' + WHERE id_tree=%i', + $thisSubFolders->id + ); foreach ($itemsInSubFolder as $item) { DB::update( prefixTable('items'), @@ -758,7 +772,7 @@ //Update CACHE table updateCacheTable('delete_value',(int) $item['id']); - +/* // --> build json tree // update cache_tree $cache_tree = DB::queryfirstrow( @@ -802,28 +816,39 @@ ); } // <-- end - build json tree + */ } //Actualize the variable $session->set('user-nb_folders', $session->get('user-nb_folders') - 1); } } - - // delete folders - $folderForDel = array_unique($folderForDel); - foreach ($folderForDel as $fol) { - DB::delete(prefixTable('nested_tree'), 'id = %i', $fol); - } } } + + // Add new task for building user cache tree + if ((int) $session->get('user-admin') !== 1) { + DB::insert( + prefixTable('background_tasks'), + array( + 'created_at' => time(), + 'process_type' => 'user_build_cache_tree', + 'arguments' => json_encode([ + 'user_id' => (int) $session->get('user-id'), + ], JSON_HEX_QUOT | JSON_HEX_TAG), + 'updated_at' => '', + 'finished_at' => '', + 'output' => '', + ) + ); + } - //rebuild tree - $tree->rebuild(); - - // reload cache table - include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php'; - updateCacheTable('reload', null); - + // delete folders + $folderForDel = array_unique($folderForDel); + foreach ($folderForDel as $fol) { + DB::delete(prefixTable('nested_tree'), 'id = %i', $fol); + } + // Update timestamp DB::update( prefixTable('misc'), @@ -835,9 +860,16 @@ 'timestamp', 'last_folder_change' ); - + // Commit transaction DB::commit(); + + //rebuild tree + $tree->rebuild(); + + // reload cache table + include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php'; + updateCacheTable('reload', null); echo prepareExchangedData( array( diff --git a/sources/main.functions.php b/sources/main.functions.php index 2321bbecd..25906edec 100755 --- a/sources/main.functions.php +++ b/sources/main.functions.php @@ -4369,4 +4369,34 @@ function convertPasswordStrength($passwordStrength): int } else { return TP_PW_STRENGTH_5; } -} \ No newline at end of file +} + +/** + * Vérifie si les IDs d'un tableau existent bien dans la table. + * + * @param array $ids - Tableau d'IDs à vérifier + * @param string $tableName - Nom de la table dans laquelle vérifier les IDs + * @param string $fieldName - Nom du champ dans lequel vérifier les IDs + * @return array - IDs qui n'existent pas dans la table + */ +function checkIdsExist(array $ids, string $tableName, string $fieldName) : array +{ + // Assure-toi que le tableau d'IDs n'est pas vide + if (empty($ids)) { + return []; + } + + // Nettoyage des IDs pour éviter les injections SQL + $ids = array_map('intval', $ids); // Assure que chaque ID est un entier + + // Construction de la requête SQL pour vérifier les IDs dans la table + $result = DB::query('SELECT id FROM ' . prefixTable($tableName) . ' WHERE ' . $fieldName . ' IN %li', $ids); + + // Extraire les IDs existants de la table + $existingIds = array_column($result, 'id'); + + // Trouver les IDs manquants en comparant les deux tableaux + $missingIds = array_diff($ids, $existingIds); + + return $missingIds; // Renvoie les IDs qui n'existent pas dans la table +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index c8d2b392a..50419c4c9 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -1514,6 +1514,7 @@ 'TeampassClasses\\AzureAuthController\\AzureAuthController' => $vendorDir . '/teampassclasses/azureauthcontroller/src/AzureAuthController.php', 'TeampassClasses\\ConfigManager\\ConfigManager' => $vendorDir . '/teampassclasses/configmanager/src/ConfigManager.php', 'TeampassClasses\\EmailService\\EmailService' => $vendorDir . '/teampassclasses/emailservice/src/EmailService.php', + 'TeampassClasses\\EmailService\\EmailSettings' => $vendorDir . '/teampassclasses/emailservice/src/EmailSettings.php', 'TeampassClasses\\Encryption\\Encryption' => $vendorDir . '/teampassclasses/encryption/src/Encryption.php', 'TeampassClasses\\Language\\Language' => $vendorDir . '/teampassclasses/language/src/Language.php', 'TeampassClasses\\LdapExtra\\ActiveDirectoryExtra' => $vendorDir . '/teampassclasses/ldapextra/src/ActiveDirectoryExtra.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 38d2ee460..9695c99da 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -15,8 +15,8 @@ '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', - '06a34129a50df3d9257ee706cf3c875b' => $vendorDir . '/illuminate/filesystem/functions.php', '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php', + '06a34129a50df3d9257ee706cf3c875b' => $vendorDir . '/illuminate/filesystem/functions.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'b46ad4fe52f4d1899a2951c7e6ea56b0' => $vendorDir . '/voku/portable-utf8/bootstrap.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 6e5f84679..606496e19 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -53,7 +53,7 @@ 'LdapRecord\\' => array($vendorDir . '/directorytree/ldaprecord/src'), 'Illuminate\\Validation\\' => array($vendorDir . '/illuminate/validation'), 'Illuminate\\Translation\\' => array($vendorDir . '/illuminate/translation'), - 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/support'), + 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/support'), 'Illuminate\\Filesystem\\' => array($vendorDir . '/illuminate/filesystem'), 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), 'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'), diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 83cf86e85..5987e4c47 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -54,7 +54,7 @@ public static function getLoader() } } - $loader->setApcuPrefix('ydegFeoJWr1RDQTpvXuGv'); + $loader->setApcuPrefix('0fMg2z0y0Idde9L8dPQXR'); $loader->register(true); if ($useStaticLoader) { diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 16cfc16c8..4bf905b97 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -16,8 +16,8 @@ class ComposerStaticInite3f3ee27f81ca21f7bd7499d7b935c11 '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', - '06a34129a50df3d9257ee706cf3c875b' => __DIR__ . '/..' . '/illuminate/filesystem/functions.php', '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php', + '06a34129a50df3d9257ee706cf3c875b' => __DIR__ . '/..' . '/illuminate/filesystem/functions.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'b46ad4fe52f4d1899a2951c7e6ea56b0' => __DIR__ . '/..' . '/voku/portable-utf8/bootstrap.php', @@ -341,9 +341,9 @@ class ComposerStaticInite3f3ee27f81ca21f7bd7499d7b935c11 ), 'Illuminate\\Support\\' => array ( - 0 => __DIR__ . '/..' . '/illuminate/collections', + 0 => __DIR__ . '/..' . '/illuminate/macroable', 1 => __DIR__ . '/..' . '/illuminate/conditionable', - 2 => __DIR__ . '/..' . '/illuminate/macroable', + 2 => __DIR__ . '/..' . '/illuminate/collections', 3 => __DIR__ . '/..' . '/illuminate/support', ), 'Illuminate\\Filesystem\\' => @@ -1997,6 +1997,7 @@ class ComposerStaticInite3f3ee27f81ca21f7bd7499d7b935c11 'TeampassClasses\\AzureAuthController\\AzureAuthController' => __DIR__ . '/..' . '/teampassclasses/azureauthcontroller/src/AzureAuthController.php', 'TeampassClasses\\ConfigManager\\ConfigManager' => __DIR__ . '/..' . '/teampassclasses/configmanager/src/ConfigManager.php', 'TeampassClasses\\EmailService\\EmailService' => __DIR__ . '/..' . '/teampassclasses/emailservice/src/EmailService.php', + 'TeampassClasses\\EmailService\\EmailSettings' => __DIR__ . '/..' . '/teampassclasses/emailservice/src/EmailSettings.php', 'TeampassClasses\\Encryption\\Encryption' => __DIR__ . '/..' . '/teampassclasses/encryption/src/Encryption.php', 'TeampassClasses\\Language\\Language' => __DIR__ . '/..' . '/teampassclasses/language/src/Language.php', 'TeampassClasses\\LdapExtra\\ActiveDirectoryExtra' => __DIR__ . '/..' . '/teampassclasses/ldapextra/src/ActiveDirectoryExtra.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 71dd68587..63474547a 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1370,8 +1370,8 @@ }, { "name": "illuminate/collections", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", @@ -1428,8 +1428,8 @@ }, { "name": "illuminate/conditionable", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -1477,8 +1477,8 @@ }, { "name": "illuminate/container", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", @@ -1531,8 +1531,8 @@ }, { "name": "illuminate/contracts", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -1582,8 +1582,8 @@ }, { "name": "illuminate/filesystem", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", @@ -1652,8 +1652,8 @@ }, { "name": "illuminate/macroable", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1701,8 +1701,8 @@ }, { "name": "illuminate/support", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", @@ -1775,8 +1775,8 @@ }, { "name": "illuminate/translation", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/translation.git", @@ -1829,8 +1829,8 @@ }, { "name": "illuminate/validation", - "version": "v10.48.20", - "version_normalized": "10.48.20.0", + "version": "v10.48.22", + "version_normalized": "10.48.22.0", "source": { "type": "git", "url": "https://github.com/illuminate/validation.git", @@ -3030,23 +3030,23 @@ }, { "name": "psr/log", - "version": "3.0.1", - "version_normalized": "3.0.1.0", + "version": "3.0.2", + "version_normalized": "3.0.2.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "79dff0b268932c640297f5208d6298f71855c03e" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", - "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { "php": ">=8.0.0" }, - "time": "2024-08-21T13:31:24+00:00", + "time": "2024-09-11T13:17:53+00:00", "type": "library", "extra": { "branch-alias": { @@ -3077,7 +3077,7 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, "install-path": "../psr/log" }, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 3493ded23..2db30302f 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -182,8 +182,8 @@ 'dev_requirement' => false, ), 'illuminate/collections' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/collections', 'aliases' => array(), @@ -191,8 +191,8 @@ 'dev_requirement' => false, ), 'illuminate/conditionable' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/conditionable', 'aliases' => array(), @@ -200,8 +200,8 @@ 'dev_requirement' => false, ), 'illuminate/container' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/container', 'aliases' => array(), @@ -209,8 +209,8 @@ 'dev_requirement' => false, ), 'illuminate/contracts' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/contracts', 'aliases' => array(), @@ -218,8 +218,8 @@ 'dev_requirement' => false, ), 'illuminate/filesystem' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/filesystem', 'aliases' => array(), @@ -227,8 +227,8 @@ 'dev_requirement' => false, ), 'illuminate/macroable' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/macroable', 'aliases' => array(), @@ -236,8 +236,8 @@ 'dev_requirement' => false, ), 'illuminate/support' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/support', 'aliases' => array(), @@ -245,8 +245,8 @@ 'dev_requirement' => false, ), 'illuminate/translation' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/translation', 'aliases' => array(), @@ -254,8 +254,8 @@ 'dev_requirement' => false, ), 'illuminate/validation' => array( - 'pretty_version' => 'v10.48.20', - 'version' => '10.48.20.0', + 'pretty_version' => 'v10.48.22', + 'version' => '10.48.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../illuminate/validation', 'aliases' => array(), @@ -470,12 +470,12 @@ ), ), 'psr/log' => array( - 'pretty_version' => '3.0.1', - 'version' => '3.0.1.0', + 'pretty_version' => '3.0.2', + 'version' => '3.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), - 'reference' => '79dff0b268932c640297f5208d6298f71855c03e', + 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3', 'dev_requirement' => false, ), 'psr/simple-cache' => array( diff --git a/vendor/psr/log/src/LoggerInterface.php b/vendor/psr/log/src/LoggerInterface.php index 8afabc90c..cb4cf648b 100644 --- a/vendor/psr/log/src/LoggerInterface.php +++ b/vendor/psr/log/src/LoggerInterface.php @@ -89,6 +89,7 @@ public function debug(string|\Stringable $message, array $context = []): void; /** * Logs with an arbitrary level. * + * @param mixed $level * @param mixed[] $context * * @throws \Psr\Log\InvalidArgumentException