From 0600f02a1329c221a25b274261e9da8ad6dc3309 Mon Sep 17 00:00:00 2001 From: yohan <783b8c87@scimetis.net> Date: Fri, 18 Sep 2015 19:41:12 +0200 Subject: [PATCH] Shoulder surfing protection with new default option. Enabled only for logged users as we use logged user's tags to decide what should be hidden. Tags linked to the tag defined in the configuration, associated bookmarks, relevant links and "last searches" block will be hidden. Hidden state is active unless disabled by clicking on the top right button : require user's password. This setting is remembered by a session cookie. --- data/config.default.php | 7 ++ data/templates/default/toolbar.inc.php | 50 +++++++- data/templates/default/top.inc.php | 2 + src/SemanticScuttle/Service/Bookmark.php | 38 +++++- src/SemanticScuttle/Service/Bookmark2Tag.php | 37 ++++-- src/SemanticScuttle/Service/SearchHistory.php | 4 + src/SemanticScuttle/Service/Tag2Tag.php | 22 +++- www/about.php | 3 +- www/ajax/checkpassword.php | 33 +++++ www/ajax/getlinkedtags.php | 2 +- www/bookmarks.php | 36 +++++- www/js/jstree-1.0-rc2/jquery.cookie-1.4.1.js | 117 ++++++++++++++++++ www/tag2tagadd.php | 2 +- www/tag2tagdelete.php | 2 +- www/tagrename.php | 1 + 15 files changed, 333 insertions(+), 23 deletions(-) create mode 100644 www/ajax/checkpassword.php create mode 100644 www/js/jstree-1.0-rc2/jquery.cookie-1.4.1.js diff --git a/data/config.default.php b/data/config.default.php index 0ec8b93..34f9460 100644 --- a/data/config.default.php +++ b/data/config.default.php @@ -615,6 +615,13 @@ $menu2Tags = array( 'menu2', 'tags', 'configurable', 'in', 'data/config.php' ); +/** + * Tag protected from shoulder surfing. + * This tag, his children and the associated bookmarks won't appear anywhere unless enabled in the UI. + * + * @var string + */ +$shoulderSurfingProtectedTag = 's_hidden'; /**************************** diff --git a/data/templates/default/toolbar.inc.php b/data/templates/default/toolbar.inc.php index fb6638d..02ffc59 100644 --- a/data/templates/default/toolbar.inc.php +++ b/data/templates/default/toolbar.inc.php @@ -11,14 +11,62 @@ if ($userservice->isLoggedOn() && is_object($currentUser)) {
  • + + +
  • + +
  • ()
  • isAdmin()): ?>
  • - + + + + diff --git a/data/templates/default/top.inc.php b/data/templates/default/top.inc.php index 55be4a7..0550d55 100644 --- a/data/templates/default/top.inc.php +++ b/data/templates/default/top.inc.php @@ -22,9 +22,11 @@ if (isset($rsschannels)) { + + diff --git a/src/SemanticScuttle/Service/Bookmark.php b/src/SemanticScuttle/Service/Bookmark.php index 1315350..1c1bc22 100644 --- a/src/SemanticScuttle/Service/Bookmark.php +++ b/src/SemanticScuttle/Service/Bookmark.php @@ -733,11 +733,26 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService if (!is_array($tags) && !is_null($tags)) { $tags = explode('+', trim($tags)); } - - $tagcount = count($tags); - for ($i = 0; $i < $tagcount; $i ++) { - $tags[$i] = trim($tags[$i]); + if (!is_null($tags)) { + $tags = array_map('trim', $tags); } + // Remove shoulder surfing protected tags. + if (! empty($GLOBALS['shoulderSurfingProtectedTag']) && $userservice->isLoggedOn() && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + $shoulderSurfingProtectedTags = $tag2tagservice->getAllLinkedTags($GLOBALS['shoulderSurfingProtectedTag'], '>', $sId, array()); + $shoulderSurfingProtectedTags[] = $GLOBALS['shoulderSurfingProtectedTag']; + $tags2 = []; + foreach ($tags as $tag) { + if (! in_array($tag, $shoulderSurfingProtectedTags, true)) { + $tags2[] = $tag; + } + } + // If we filtered everything, we stop here and return nothing. + if(! empty($tags) && empty($tags2)) { + return array(); + } + $tags = $tags2; + } + $tagcount = count($tags); // Set up the SQL query. $query_1 = 'SELECT DISTINCT '; @@ -899,6 +914,20 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $query_4 .= ' AND B.bHash = "'. $hash .'"'; } + // Exclude bookmarks with shoulder surfing protected tags. + if (! empty($GLOBALS['shoulderSurfingProtectedTag']) && $userservice->isLoggedOn() && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + $query_4 .= ' AND B.bId NOT IN (SELECT DISTINCT B0.bId FROM '. + $this->getTableName() .' AS B0, ' . $userservice->getTableName() + .' AS U, ' . $b2tservice->getTableName() .' AS T WHERE B0.uId = U.' + . $userservice->getFieldName('primary') . $privacy .' AND B0.uId = ' + . $sId . ' AND ('; + $count_s = count($shoulderSurfingProtectedTags); + for ($i = 0; $i < $count_s - 1; $i++) { + $query_4 .= 'T.tag = "'. $shoulderSurfingProtectedTags[$i] .'" OR '; + } + $query_4 .= 'T.tag = "'. $shoulderSurfingProtectedTags[$count_s - 1] .'")' + .' AND T.bId = B0.bId)'; + } $query = $query_1 . $query_2 . $query_3 . $query_4 . $query_5; @@ -951,6 +980,7 @@ class SemanticScuttle_Service_Bookmark extends SemanticScuttle_DbService $this->db->sql_freeresult($dbresult); $output = array ('bookmarks' => $bookmarks, 'total' => $total); + return $output; } diff --git a/src/SemanticScuttle/Service/Bookmark2Tag.php b/src/SemanticScuttle/Service/Bookmark2Tag.php index a01b5d7..fbb0520 100644 --- a/src/SemanticScuttle/Service/Bookmark2Tag.php +++ b/src/SemanticScuttle/Service/Bookmark2Tag.php @@ -329,7 +329,7 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService $query = 'SELECT tag, bId FROM ' . $this->getTableName() . ' WHERE bId IN (' . implode(',', $bookmarkids) . ')' - . ' AND LEFT(tag, 7) <> "system:"' + . ' AND LEFT(tag, 7) <> "system:"' . ' ORDER BY id, bId ASC'; if (!($dbresult = $this->db->sql_query($query))) { @@ -353,7 +353,7 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService function &getTags($userid = NULL) { - $userservice =SemanticScuttle_Service_Factory::get('User'); + $userservice = SemanticScuttle_Service_Factory::get('User'); $logged_on_user = $userservice->getCurrentUserId(); $query = 'SELECT T.tag, COUNT(B.bId) AS bCount FROM '. $GLOBALS['tableprefix'] .'bookmarks AS B INNER JOIN '. $userservice->getTableName() .' AS U ON B.uId = U.'. $userservice->getFieldName('primary') .' INNER JOIN '. $GLOBALS['tableprefix'] .'bookmarks2tags AS T ON B.bId = T.bId'; @@ -366,7 +366,6 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService } else { $conditions['B.bStatus'] = 0; } - $query .= ' WHERE '. $this->db->sql_build_array('SELECT', $conditions) .' AND LEFT(T.tag, 7) <> "system:" GROUP BY T.tag ORDER BY bCount DESC, tag'; if (!($dbresult = $this->db->sql_query($query))) { @@ -376,9 +375,31 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService $output = $this->db->sql_fetchrowset($dbresult); $this->db->sql_freeresult($dbresult); - return $output; + return $this->filterShoulderSurfingProtectedTags($output); + } + + function &filterShoulderSurfingProtectedTags($dboutput) { + $userservice = SemanticScuttle_Service_Factory::get('User'); + if (! empty($GLOBALS['shoulderSurfingProtectedTag']) && $userservice->isLoggedOn() && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + $logged_on_user = $userservice->getCurrentUserId(); + $ttt = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $shoulderSurfingProtectedTags = $ttt->getAllLinkedTags($GLOBALS['shoulderSurfingProtectedTag'], '>', $logged_on_user, array()); + $shoulderSurfingProtectedTags[] = $GLOBALS['shoulderSurfingProtectedTag']; + $output = array(); + foreach ($dboutput as $array) { + $flag = 1; + foreach ($shoulderSurfingProtectedTags as $tag) { + if ($array['tag'] === $tag) { + $flag = 0; + break; + } + } + if ($flag) {$output[] = $array;} + } + return $output; + } + else {return $dboutput;} } - // Returns the tags related to the specified tags; i.e. attached to the same bookmarks function &getRelatedTags($tags, $for_user = NULL, $logged_on_user = NULL, $limit = 10) { @@ -423,7 +444,7 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService } $output = $this->db->sql_fetchrowset($dbresult); $this->db->sql_freeresult($dbresult); - return $output; + return $this->filterShoulderSurfingProtectedTags($output); } // Returns the most popular tags used for a particular bookmark hash @@ -453,7 +474,7 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService } $output = $this->db->sql_fetchrowset($dbresult); $this->db->sql_freeresult($dbresult); - return $output; + return $this->filterShoulderSurfingProtectedTags($output); } @@ -613,7 +634,7 @@ class SemanticScuttle_Service_Bookmark2Tag extends SemanticScuttle_DbService $output = $this->db->sql_fetchrowset($dbresult); $this->db->sql_freeresult($dbresult); - return $output; + return $this->filterShoulderSurfingProtectedTags($output); } diff --git a/src/SemanticScuttle/Service/SearchHistory.php b/src/SemanticScuttle/Service/SearchHistory.php index ac0b1c7..98c075b 100644 --- a/src/SemanticScuttle/Service/SearchHistory.php +++ b/src/SemanticScuttle/Service/SearchHistory.php @@ -151,6 +151,10 @@ class SemanticScuttle_Service_SearchHistory extends SemanticScuttle_DbService $range = null, $uId = null, $nb = null, $start = null, $distinct = false, $withResults = false ) { + $userservice = SemanticScuttle_Service_Factory::get('User'); + if ($userservice->isLoggedOn() && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + return array(); + } $sql = 'SELECT DISTINCT(shTerms),' . ' shId, shRange, shNbResults, shDatetime, uId'; $sql.= ' FROM '. $this->getTableName(); diff --git a/src/SemanticScuttle/Service/Tag2Tag.php b/src/SemanticScuttle/Service/Tag2Tag.php index 9dddc44..e33798a 100644 --- a/src/SemanticScuttle/Service/Tag2Tag.php +++ b/src/SemanticScuttle/Service/Tag2Tag.php @@ -318,7 +318,8 @@ class SemanticScuttle_Service_Tag2Tag extends SemanticScuttle_DbService } $output = $this->db->sql_fetchrowset($dbresult); $this->db->sql_freeresult($dbresult); - return $output; + $btt = SemanticScuttle_Service_Factory::get('Bookmark2Tag'); + return $btt->filterShoulderSurfingProtectedTags($output); } function getMenuTags($uId) { @@ -377,6 +378,25 @@ class SemanticScuttle_Service_Tag2Tag extends SemanticScuttle_DbService $dbres = $this->db->sql_query($query); $rowset = $this->db->sql_fetchrowset($dbres); $this->db->sql_freeresult($dbres); + + $userservice = SemanticScuttle_Service_Factory::get('User'); + if (count($rowset)>0 && ! empty($GLOBALS['shoulderSurfingProtectedTag']) && $userservice->isLoggedOn() && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + $logged_on_user = $userservice->getCurrentUserId(); + $shoulderSurfingProtectedTags = $this->getAllLinkedTags($GLOBALS['shoulderSurfingProtectedTag'], '>', $logged_on_user, array()); + $shoulderSurfingProtectedTags[] = $GLOBALS['shoulderSurfingProtectedTag']; + $output = array(); + foreach($rowset as $link) { + $flag = 1; + foreach ($shoulderSurfingProtectedTags as $tag) { + if ($link['tag1'] === $tag || $link['tag2'] === $tag) { + $flag = 0; + break; + } + } + if ($flag) {$output[] = $link;} + } + $rowset = $output; + } return $rowset; } diff --git a/www/about.php b/www/about.php index 9bd37b0..19ee6df 100644 --- a/www/about.php +++ b/www/about.php @@ -22,6 +22,7 @@ require_once 'www-header.php'; $tplVars['pagetitle'] = T_('About'); $tplVars['subtitle'] = T_('About'); +$tplVars['loadjs'] = true; $templateservice->loadTemplate('about.tpl', $tplVars); -?> \ No newline at end of file +?> diff --git a/www/ajax/checkpassword.php b/www/ajax/checkpassword.php new file mode 100644 index 0000000..6bc2d8a --- /dev/null +++ b/www/ajax/checkpassword.php @@ -0,0 +1,33 @@ +isLoggedOn()){ + $password = $userservice->sanitisePassword($_POST['password']); + $username = $currentUser->getUsername(); + $db = SemanticScuttle_Service_Factory::getDb(); + + $query = 'SELECT '. $userservice->getFieldName('primary') .' FROM '. $userservice->getTableName() .' WHERE '. $userservice->getFieldName('username') .' = "'. $db->sql_escape($username) .'" AND '. $userservice->getFieldName('password') .' = "'. $db->sql_escape($password) .'"'; + + if (!($dbresult = $db->sql_query($query))) { + message_die( + GENERAL_ERROR, + 'Could not get user', + '', __LINE__, __FILE__, $query, $db + ); + echo 'false'; + } + else { + $row = $db->sql_fetchrow($dbresult); + $db->sql_freeresult($dbresult); + + if ($row) { + echo 'true'; + } + else { + echo 'false'; + } + } +} +else { + echo 'false'; +} +?> diff --git a/www/ajax/getlinkedtags.php b/www/ajax/getlinkedtags.php index 9bb3b1f..63b1712 100644 --- a/www/ajax/getlinkedtags.php +++ b/www/ajax/getlinkedtags.php @@ -138,4 +138,4 @@ $tagData = assembleLinkedTagData( SemanticScuttle_Service_Factory::get('Tag2Tag') ); echo json_encode($tagData); -?> \ No newline at end of file +?> diff --git a/www/bookmarks.php b/www/bookmarks.php index 8926f69..c7b08bc 100644 --- a/www/bookmarks.php +++ b/www/bookmarks.php @@ -262,20 +262,46 @@ if ($templatename == 'editbookmark.tpl') { $tplVars['sidebar_blocks'] = array('watchstatus'); if (!$cat) { //user page without tags - $rssTitle = "My Bookmarks"; + $rssTitle = "My Bookmarks"; $cat = NULL; $tplVars['currenttag'] = NULL; //$tplVars['sidebar_blocks'][] = 'menu2'; $tplVars['sidebar_blocks'][] = 'linked'; $tplVars['sidebar_blocks'][] = 'popular'; } else { //pages with tags - $rssTitle = "Tags" . $catTitle; + $rssTitle = "Tags" . $catTitle; $rssCat = '/'. filter($cat, 'url'); $tplVars['currenttag'] = $cat; - $tplVars['sidebar_blocks'][] = 'tagactions'; //$tplVars['sidebar_blocks'][] = 'menu2'; - $tplVars['sidebar_blocks'][] = 'linked'; - $tplVars['sidebar_blocks'][] = 'related'; + + if (! empty($GLOBALS['shoulderSurfingProtectedTag']) && ! isset($_COOKIE["noshoulderSurfingProtection"])) { + $tag2tagservice = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $b2tservice = SemanticScuttle_Service_Factory::get('Bookmark2Tag'); + $alltags = $b2tservice->getTags($currentUserID); + $shoulderSurfingProtectedTags = $tag2tagservice->getAllLinkedTags($GLOBALS['shoulderSurfingProtectedTag'], '>', $currentUserID, array()); + $shoulderSurfingProtectedTags[] = $GLOBALS['shoulderSurfingProtectedTag']; + $flag = 0; + if (! in_array($cat, $shoulderSurfingProtectedTags, true)) { + foreach ($alltags as $tag) { + if ($tag['tag'] === $cat) { + $flag = 1; + break; + } + + } + } + if ($flag) { + $tplVars['sidebar_blocks'][] = 'tagactions'; + $tplVars['sidebar_blocks'][] = 'linked'; + $tplVars['sidebar_blocks'][] = 'related'; + } + } + else { + $tplVars['sidebar_blocks'][] = 'tagactions'; + $tplVars['sidebar_blocks'][] = 'linked'; + $tplVars['sidebar_blocks'][] = 'related'; + } + /*$tplVars['sidebar_blocks'][] = 'menu';*/ } $tplVars['sidebar_blocks'][] = 'menu2'; diff --git a/www/js/jstree-1.0-rc2/jquery.cookie-1.4.1.js b/www/js/jstree-1.0-rc2/jquery.cookie-1.4.1.js new file mode 100644 index 0000000..c7f3a59 --- /dev/null +++ b/www/js/jstree-1.0-rc2/jquery.cookie-1.4.1.js @@ -0,0 +1,117 @@ +/*! + * jQuery Cookie Plugin v1.4.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + + var pluses = /\+/g; + + function encode(s) { + return config.raw ? s : encodeURIComponent(s); + } + + function decode(s) { + return config.raw ? s : decodeURIComponent(s); + } + + function stringifyCookieValue(value) { + return encode(config.json ? JSON.stringify(value) : String(value)); + } + + function parseCookieValue(s) { + if (s.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape... + s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + + try { + // Replace server-side written pluses with spaces. + // If we can't decode the cookie, ignore it, it's unusable. + // If we can't parse the cookie, ignore it, it's unusable. + s = decodeURIComponent(s.replace(pluses, ' ')); + return config.json ? JSON.parse(s) : s; + } catch(e) {} + } + + function read(s, converter) { + var value = config.raw ? s : parseCookieValue(s); + return $.isFunction(converter) ? converter(value) : value; + } + + var config = $.cookie = function (key, value, options) { + + // Write + + if (value !== undefined && !$.isFunction(value)) { + options = $.extend({}, config.defaults, options); + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setTime(+t + days * 864e+5); + } + + return (document.cookie = [ + encode(key), '=', stringifyCookieValue(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // Read + + var result = key ? undefined : {}; + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling $.cookie(). + var cookies = document.cookie ? document.cookie.split('; ') : []; + + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = parts.join('='); + + if (key && key === name) { + // If second argument (value) is a function it's a converter... + result = read(cookie, value); + break; + } + + // Prevent storing a cookie that we couldn't decode. + if (!key && (cookie = read(cookie)) !== undefined) { + result[name] = cookie; + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) === undefined) { + return false; + } + + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, { expires: -1 })); + return !$.cookie(key); + }; + +})); diff --git a/www/tag2tagadd.php b/www/tag2tagadd.php index 3f4af8c..d865a62 100644 --- a/www/tag2tagadd.php +++ b/www/tag2tagadd.php @@ -63,7 +63,7 @@ if (POST_CONFIRM != '') { } $tplVars['links'] = $tag2tagservice->getLinks($currentUser->getId()); - +$tplVars['loadjs'] = true; $tplVars['tag1'] = $tag1; $tplVars['tag2'] = ''; $tplVars['subtitle'] = T_('Add Tag Link') .': '. $tag1; diff --git a/www/tag2tagdelete.php b/www/tag2tagdelete.php index 06fea98..cab32aa 100644 --- a/www/tag2tagdelete.php +++ b/www/tag2tagdelete.php @@ -75,7 +75,7 @@ if (POST_CONFIRM) { } $tplVars['links'] = $tag2tagservice->getLinks($currentUser->getId()); - +$tplVars['loadjs'] = true; $tplVars['tag1'] = $tag1; $tplVars['tag2'] = $tag2; $tplVars['subtitle'] = T_('Delete Link Between Tags') .': '. $tag1.' > '.$tag2; diff --git a/www/tagrename.php b/www/tagrename.php index 18e26ab..68febec 100644 --- a/www/tagrename.php +++ b/www/tagrename.php @@ -73,6 +73,7 @@ if (POST_CONFIRM) { $tplVars['formaction'] = $_SERVER['SCRIPT_NAME'] .'/'. $tag; $tplVars['referrer'] = $_SERVER['HTTP_REFERER']; $tplVars['old'] = $tag; + $tplVars['loadjs'] = true; } $templateservice->loadTemplate($template, $tplVars); ?>