From b787dfd2918bbc491c9a66a8f6f0ccbd631a51df Mon Sep 17 00:00:00 2001 From: plegall Date: Wed, 9 Aug 2023 19:18:30 +0200 Subject: [PATCH] issue #1953 improved privacy on searches and associate each search to its creator * remove temporary functions ws_gallery_getSearch and ws_gallery_updateSearch * split get_search_array into sub-functions to use them in web API * use search_uuid as search_id instead of the numeric search.id : better privacy * only the creator of the search can update it * if a visitors tries to open the search of another user, it (the search) gets forked into a new search --- include/functions_search.inc.php | 113 ++++++++++++++++-- include/functions_url.inc.php | 8 +- include/section_init.inc.php | 1 + include/ws_functions/pwg.images.php | 24 ++-- include/ws_functions/pwg.php | 39 ------ install/db/166-database.php | 26 ++++ search.php | 23 +--- .../template/include/search_filters.inc.tpl | 2 +- ws.php | 4 +- 9 files changed, 152 insertions(+), 88 deletions(-) create mode 100644 install/db/166-database.php diff --git a/include/functions_search.inc.php b/include/functions_search.inc.php index 4f816e515..ce1ca3c30 100644 --- a/include/functions_search.inc.php +++ b/include/functions_search.inc.php @@ -10,6 +10,45 @@ * @package functions\search */ +function get_search_id_pattern($candidate) +{ + $clause_pattern = null; + if (preg_match('/^psk-\d{8}-[a-z0-9]{10}$/i', $candidate)) + { + $clause_pattern = 'search_uuid = \'%s\''; + } + elseif (preg_match('/^\d+$/', $candidate)) + { + $clause_pattern = 'id = %u'; + } + + return $clause_pattern; +} + +function get_search_info($candidate) +{ + // $candidate might be a search.id or a search_uuid + $clause_pattern = get_search_id_pattern($candidate); + + if (empty($clause_pattern)) + { + die('Invalid search identifier'); + } + + $query = ' +SELECT * + FROM '.SEARCH_TABLE.' + WHERE '.sprintf($clause_pattern, $candidate).' +;'; + $searches = query2array($query); + + if (count($searches) > 0) + { + return $searches[0]; + } + + return null; +} /** * Returns search rules stored into a serialized array in "search" @@ -20,24 +59,24 @@ */ function get_search_array($search_id) { - if (!is_numeric($search_id)) - { - die('Search id must be an integer'); - } + global $user; - $query = ' -SELECT rules - FROM '.SEARCH_TABLE.' - WHERE id = '.$search_id.' -;'; - $rules_list = query2array($query); + $search = get_search_info($search_id); - if (count($rules_list) == 0) + if (empty($search)) { bad_request('this search identifier does not exist'); } + else + { + if (!empty($search['created_by']) and $search['created_by'] != $user['user_id']) + { + // we need to fork this search + save_search_and_redirect(unserialize($search['rules']), $search['id']); + } + } - return unserialize($rules_list[0]['rules']); + return unserialize($search['rules']); } /** @@ -1614,4 +1653,54 @@ function split_allwords($raw_allwords) return $words; } +function get_available_search_uuid() +{ + $candidate = 'psk-'.date('Ymd').'-'.generate_key(10); + + $query = ' +SELECT + COUNT(*) + FROM '.SEARCH_TABLE.' + WHERE search_uuid = \''.$candidate.'\' +;'; + list($counter) = pwg_db_fetch_row(pwg_query($query)); + if (0 == $counter) + { + return $candidate; + } + else + { + return get_available_search_uuid(); + } +} + +function save_search_and_redirect($rules, $forked_from=null) +{ + global $user; + + list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW()')); + $search_uuid = get_available_search_uuid(); + + single_insert( + SEARCH_TABLE, + array( + 'rules' => pwg_db_real_escape_string(serialize($rules)), + 'created_on' => $dbnow, + 'created_by' => $user['user_id'], + 'search_uuid' => $search_uuid, + 'last_seen' => $dbnow, + 'forked_from' => $forked_from, + ) + ); + + redirect( + make_index_url( + array( + 'section' => 'search', + 'search' => $search_uuid, + ) + ) + ); +} + ?> diff --git a/include/functions_url.inc.php b/include/functions_url.inc.php index d6cbc3227..a1bc2e4e6 100644 --- a/include/functions_url.inc.php +++ b/include/functions_url.inc.php @@ -648,10 +648,14 @@ function parse_section_url( $tokens, &$next_token) $page['section'] = 'search'; $next_token++; - preg_match('/(\d+)/', @$tokens[$next_token], $matches); + preg_match('/^(psk-\d{8}-[a-zA-Z0-9]{10})$/', @$tokens[$next_token], $matches); if (!isset($matches[1])) { - bad_request('search identifier is missing'); + preg_match('/(\d+)/', @$tokens[$next_token], $matches); + if (!isset($matches[1])) + { + bad_request('search identifier is missing'); + } } $page['search'] = $matches[1]; $next_token++; diff --git a/include/section_init.inc.php b/include/section_init.inc.php index d864b4713..b6d3b5bcf 100644 --- a/include/section_init.inc.php +++ b/include/section_init.inc.php @@ -362,6 +362,7 @@ else include_once( PHPWG_ROOT_PATH .'include/functions_search.inc.php' ); $search_result = get_search_results($page['search'], @$page['super_order_by'] ); + //save the details of the query search if ( isset($search_result['qs']) ) { diff --git a/include/ws_functions/pwg.images.php b/include/ws_functions/pwg.images.php index 21dd061b0..cac9d82f8 100644 --- a/include/ws_functions/pwg.images.php +++ b/include/ws_functions/pwg.images.php @@ -699,24 +699,28 @@ SELECT * */ function ws_images_filteredSearch_update($params, $service) { - // echo json_encode($params); exit(); + global $user; + include_once(PHPWG_ROOT_PATH.'include/functions_search.inc.php'); // * check the search exists - $query = ' -SELECT id - FROM '.SEARCH_TABLE.' - WHERE id = '.$params['search_id'].' -;'; + if (empty(get_search_id_pattern($params['search_id']))) + { + return new PwgError(WS_ERR_INVALID_PARAM, 'Invalid search_id input parameter.'); + } - if (count(query2array($query)) == 0) + $search_info = get_search_info($params['search_id']); + if (empty($search_info)) { return new PwgError(WS_ERR_INVALID_PARAM, 'This search does not exist.'); } - $search = array('mode' => 'AND'); + if (!empty($search_info['created_by']) and $search_info['created_by'] != $user['user_id']) + { + return new PwgError(WS_ERR_INVALID_PARAM, 'This search was created by another user.'); + } - // TODO we should check that this search is updated by the user who created the search. + $search = array('mode' => 'AND'); // * check all parameters if (isset($params['allwords'])) @@ -848,7 +852,7 @@ SELECT id UPDATE '.SEARCH_TABLE.' SET rules = \''.pwg_db_real_escape_string(serialize($search)).'\' , last_seen = NOW() - WHERE id = '.$params['search_id'].' + WHERE id = '.$search_info['id'].' ;'; pwg_query($query); } diff --git a/include/ws_functions/pwg.php b/include/ws_functions/pwg.php index 7fc174364..5510be027 100644 --- a/include/ws_functions/pwg.php +++ b/include/ws_functions/pwg.php @@ -1083,43 +1083,4 @@ SELECT 'summary' => $search_summary ); } - -function ws_gallery_getSearch($param, &$service) -{ - // return $param; - if (is_null($param['search_id'])) - { - // Créer une recherche - return new PwgError(404, 'Search id is null'); - } - include_once(PHPWG_ROOT_PATH.'include/functions_search.inc.php'); - - if (get_search_array($param['search_id']) == false) - { - return new PwgError(1404, 'Search associated to id '.$param['search_id'].' not found'); - } - - return get_search_array($param['search_id']); -} - -function ws_gallery_updateSearch($param, &$service) -{ - // return $param; - if (is_null($param['search_id'])) - { - // Créer une recherche - return new PwgError(404, 'Search id is null'); - } - include_once(PHPWG_ROOT_PATH.'include/functions_search.inc.php'); - - if (get_search_array($param['search_id']) == false) - { - return new PwgError(404, 'Search associated to id '.$param['search_id'].' not found'); - } - - $tmp = get_search_array($param['search_id']); - - // return 'search #'.$param['search_id'].' updated'; - return $param; -} ?> diff --git a/install/db/166-database.php b/install/db/166-database.php new file mode 100644 index 000000000..dc3142ca1 --- /dev/null +++ b/install/db/166-database.php @@ -0,0 +1,26 @@ + diff --git a/search.php b/search.php index 35422d3cf..f2d34effe 100644 --- a/search.php +++ b/search.php @@ -9,6 +9,7 @@ //--------------------------------------------------------------------- include define('PHPWG_ROOT_PATH','./'); include_once( PHPWG_ROOT_PATH.'include/common.inc.php' ); +include_once(PHPWG_ROOT_PATH.'include/functions_search.inc.php'); // +-----------------------------------------------------------------------+ // | Check Access and exit when user status is not ok | @@ -24,7 +25,6 @@ trigger_notify('loc_begin_search'); $words = array(); if (!empty($_GET['q'])) { - include_once(PHPWG_ROOT_PATH.'include/functions_search.inc.php'); $words = split_allwords($_GET['q']); } @@ -78,24 +78,5 @@ if (count($first_author) > 0) ); } -list($dbnow) = pwg_db_fetch_row(pwg_query('SELECT NOW()')); - -single_insert( - SEARCH_TABLE, - array( - 'rules' => pwg_db_real_escape_string(serialize($search)), - 'last_seen' => $dbnow, - ) -); - -$search_id = pwg_db_insert_id(SEARCH_TABLE); - -redirect( - make_index_url( - array( - 'section' => 'search', - 'search' => $search_id, - ) - ) -); +save_search_and_redirect($search); ?> diff --git a/themes/default/template/include/search_filters.inc.tpl b/themes/default/template/include/search_filters.inc.tpl index 4aaba9aac..a5b3b0aa5 100644 --- a/themes/default/template/include/search_filters.inc.tpl +++ b/themes/default/template/include/search_filters.inc.tpl @@ -8,7 +8,7 @@ fullname_of_cat = {$fullname_of}; {/if} {if isset($SEARCH_ID)} -search_id = {$SEARCH_ID}; +search_id = '{$SEARCH_ID}'; {/if} str_word_widget_label = "{'Search for words'|@translate|escape:javascript}"; diff --git a/ws.php b/ws.php index d019044a9..f7ea27030 100644 --- a/ws.php +++ b/ws.php @@ -1372,9 +1372,7 @@ enabled_high, registration_date, registration_date_string, registration_date_sin 'pwg.images.filteredSearch.update', 'ws_images_filteredSearch_update', array( - 'search_id' => array( - 'type' => WS_TYPE_ID, - ), + 'search_id' => array(), 'allwords' => array( 'flags' => WS_PARAM_OPTIONAL, 'info' => 'query to search by words',