Ensure private search features are not lost on Digest auth timeout

This is a fix for mantis 766 ( http://mantis.tokeek.de/view.php?id=766 )

Since the upgrade to Digest authentication, access to protected search
features was indeed disabled once the Digest nonce timed out.

After Digest auth timeout the browser no more sent authentication
information and as the search results page is not private, protected
features were simply be hidden without asking browser again for
authentication.

Adding a supplementary parameter when accessing the search results as
authenticated fixes this.
This commit is contained in:
luccioman 2017-09-29 19:18:12 +02:00
parent ba60f65040
commit 27ab733685
9 changed files with 151 additions and 46 deletions

View File

@ -20,6 +20,9 @@
<input type="hidden" name="urlmaskfilter" value=".*" />
<input type="hidden" name="prefermaskfilter" value="" />
<input type="hidden" name="nav" value="all" />
#(authorized)#::
<input type="hidden" name="auth" id="auth" value=""/>
#(/authorized)#
</form>
</div>
<script type="text/javascript">

View File

@ -61,6 +61,9 @@
<button id="Enter" name="Enter" class="btn btn-primary" type="submit">Search</button>
</div>
</div>
#(authorized)#::
<input type="hidden" name="auth" id="auth" value=""/>
#(/authorized)#
<input type="hidden" name="verify" value="#[search.verify]#" />
#(searchdomswitches)#::
<div class="yacysearch">

View File

@ -109,6 +109,9 @@ Use the RSS search result format to add static searches to your RSS reader, if y
#(/resortEnabled)#
</div>
</div>
#(authorized)#::
<input type="hidden" name="auth" id="auth" value=""/>
#(/authorized)#
<input type="hidden" name="contentdom" id="contentdom" value="#[contentdom]#" />
<input type="hidden" name="former" value="#[former]#" />
<input type="hidden" name="maximumRecords" value="#[count]#" />

View File

@ -106,7 +106,7 @@ public class yacysearch {
boolean authenticated = sb.adminAuthenticated(header) >= 2;
if ( !authenticated ) {
final UserDB.Entry user = sb.userDB.getUser(header);
final UserDB.Entry user = sb.userDB != null ? sb.userDB.getUser(header) : null;
authenticated = (user != null && user.hasRight(UserDB.AccessRight.EXTENDED_SEARCH_RIGHT));
}
final boolean localhostAccess = header.accessFromLocalhost();
@ -198,6 +198,18 @@ public class yacysearch {
return prop;
}
if (post.containsKey("auth") && !authenticated) {
/*
* Access to authentication protected features is explicitely requested here
* but no authentication is provided : ask now for authentication.
* Wihout this, after timeout of HTTP Digest authentication nonce, browsers no more send authentication information
* and as this page is not private, protected features would simply be hidden without asking browser again for authentication.
* (see mantis 766 : http://mantis.tokeek.de/view.php?id=766) *
*/
prop.authenticationRequired();
return prop;
}
// check for JSONP
if ( post.containsKey("callback") ) {
final String jsonp = post.get("callback") + "([";
@ -764,7 +776,7 @@ public class yacysearch {
RequestHeader.FileType.HTML,
0,
theQuery,
suggestion, true).toString());
suggestion, true, authenticated).toString());
prop.put("didYouMean_suggestions_" + meanCount + "_sep", "|");
meanCount++;
} catch (final ConcurrentModificationException e) {
@ -842,8 +854,9 @@ public class yacysearch {
* eventually including fetched results with higher ranks from the Solr and RWI stacks */
prop.put("resortEnabled", !jsResort && global && !stealthmode && theSearch.resortCacheAllowed.availablePermits() > 0 ? 1 : 0);
prop.put("resortEnabled_url",
QueryParams.navurlBase(RequestHeader.FileType.HTML, theQuery, null, true).append("&startRecord=")
.append(startRecord).append("&resortCachedResults=true").toString());
QueryParams.navurlBase(RequestHeader.FileType.HTML, theQuery, null, true, authenticated)
.append("&startRecord=").append(startRecord).append("&resortCachedResults=true")
.toString());
// generate the search result lines; the content will be produced by another servlet
for ( int i = 0; i < theQuery.itemsPerPage(); i++ ) {

View File

@ -33,7 +33,7 @@
#(showMetadata)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="solr/select?q=id:%22#[urlhash]#%22&defType=edismax&start=0&rows=1&core=collection1&wt=html" target="_blank">Metadata</a>#(/showMetadata)#
#(showParser)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="ViewFile.html?urlHash=#[urlhash]#&amp;words=#[words]#" target="_blank">Parser</a>#(/showParser)#
#(showCitation)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="api/citation.html?hash=#[urlhash]#&filter=true" target="_blank">Citations</a>#(/showCitation)#
#(showPictures)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="yacysearch.html?contentdom=image&url=#[link]#&query=#[former]#+inurl:#[link]#" target="_blank">Pictures</a>#(/showPictures)#
#(showPictures)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="yacysearch.html?contentdom=image#(authorized)#::&auth#(/authorized)#&url=#[link]#&query=#[former]#+inurl:#[link]#" target="_blank">Pictures</a>#(/showPictures)#
#(showCache)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="CacheResource_p.html?url=#[link]#" target="_blank">Cache</a>#(/showCache)#
#(showProxy)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="proxy.html?url=#[link]#" target="_blank">View via proxy</a>#(/showProxy)#
#(showHostBrowser)#::<span role="separator" aria-orientation="vertical">&nbsp;|&nbsp;</span><a href="HostBrowser.html?path=#[link]#"><img src="env/grafics/minitree.png" width="15" height="8" title="Browse index" alt="Browse index"/></a>#(/showHostBrowser)#

View File

@ -53,7 +53,9 @@ import net.yacy.crawler.data.Transactions;
import net.yacy.crawler.data.Transactions.State;
import net.yacy.crawler.retrieval.Response;
import net.yacy.data.URLLicense;
import net.yacy.data.UserDB;
import net.yacy.document.parser.html.IconEntry;
import net.yacy.http.servlets.TemplateMissingParameterException;
import net.yacy.kelondro.data.meta.URIMetadataNode;
import net.yacy.kelondro.util.Formatter;
import net.yacy.peers.NewsPool;
@ -89,14 +91,35 @@ public class yacysearchitem {
//private static boolean col = true;
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
if (post == null) {
throw new TemplateMissingParameterException("The eventID parameter is required");
}
final Switchboard sb = (Switchboard) env;
final serverObjects prop = new serverObjects();
final String eventID = post.get("eventID", "");
final boolean authenticated = sb.verifyAuthentication(header);
final boolean adminAuthenticated = sb.verifyAuthentication(header);
final UserDB.Entry user = sb.userDB != null ? sb.userDB.getUser(header) : null;
final boolean userAuthenticated = (user != null && user.hasRight(UserDB.AccessRight.EXTENDED_SEARCH_RIGHT));
final boolean authenticated = adminAuthenticated || userAuthenticated;
final int item = post.getInt("item", -1);
final RequestHeader.FileType fileType = header.fileType();
if (post.containsKey("auth") && !authenticated) {
/*
* Access to authentication protected features is explicitely requested here
* but no authentication is provided : ask now for authentication.
* Wihout this, after timeout of HTTP Digest authentication nonce, browsers no more send authentication information
* and as this page is not private, protected features would simply be hidden without asking browser again for authentication.
* (see mantis 766 : http://mantis.tokeek.de/view.php?id=766) *
*/
prop.authenticationRequired();
return prop;
}
// default settings for blank item
prop.put("content", "0");
prop.put("rss", "0");
@ -122,7 +145,9 @@ public class yacysearchitem {
prop.put("statistics_localIndexCount", Formatter.number(theSearch.local_rwi_available.get() + theSearch.local_solr_stored.get() - theSearch.local_solr_evicted.get(), true));
prop.put("statistics_remoteIndexCount", Formatter.number(theSearch.remote_rwi_available.get() + theSearch.remote_solr_available.get(), true));
prop.put("statistics_remotePeerCount", Formatter.number(theSearch.remote_rwi_peerCount.get() + theSearch.remote_solr_peerCount.get(), true));
prop.put("statistics_navurlBase", QueryParams.navurlBase(RequestHeader.FileType.HTML, theSearch.query, null, false).toString());
prop.put("statistics_navurlBase",
QueryParams.navurlBase(RequestHeader.FileType.HTML, theSearch.query, null, false, authenticated)
.toString());
prop.put("statistics_localQuery", theSearch.query.isLocal() ? "1" : "0");
prop.put("statistics_feedRunning", Boolean.toString(!theSearch.isFeedingFinished()));
final String target_special_pattern = sb.getConfig(SwitchboardConstants.SEARCH_TARGET_SPECIAL_PATTERN, "");
@ -143,9 +168,9 @@ public class yacysearchitem {
final String resource = theSearch.query.domType.toString();
final String origQ = theSearch.query.getQueryGoal().getQueryString(true);
prop.put("content", 1); // switch on specific content
prop.put("content_authorized", authenticated ? "1" : "0");
prop.put("content_authorized", adminAuthenticated ? "1" : "0");
final String urlhash = ASCII.String(result.hash());
if (authenticated) { // only needed if authorized
if (adminAuthenticated) { // only needed if authorized
addAuthorizedActions(sb, prop, theSearch, resultUrlstring, resource, origQ, urlhash);
}
prop.putHTML("content_title", result.title());
@ -255,7 +280,8 @@ public class yacysearchitem {
} else { // otherwise just use the keyword as additional query word
rawNavQueryModifier = word;
}
prop.put("content_showKeywords_keywords_" + i + "_tagurl", QueryParams.navurl(fileType, 0, theSearch.query, rawNavQueryModifier, naviAvail).toString());
prop.put("content_showKeywords_keywords_" + i + "_tagurl", QueryParams.navurl(fileType, 0,
theSearch.query, rawNavQueryModifier, naviAvail, authenticated).toString());
i++;
}
prop.put("content_showKeywords_keywords", i);
@ -471,10 +497,10 @@ public class yacysearchitem {
/* Bookmark, delete and recommend action links share the same URL prefix */
StringBuilder linkBuilder = new StringBuilder();
String actionLinkPrefix = linkBuilder.append("yacysearch.html?query=").append(origQ.replace(' ', '+'))
final String actionLinkPrefix = linkBuilder.append("yacysearch.html?query=").append(origQ.replace(' ', '+'))
.append("&Enter=Search&count=").append(theSearch.query.itemsPerPage()).append("&offset=")
.append((theSearch.query.neededResults() - theSearch.query.itemsPerPage())).append("&resource=")
.append(resource).append("&time=3").toString();
.append(resource).append("&time=3").append("auth").toString();
linkBuilder.setLength(0);
String encodedURLString;
@ -484,7 +510,7 @@ public class yacysearchitem {
ConcurrentLog.warn("YACY_SEARCH_ITEM", "UTF-8 encoding is not supported!");
encodedURLString = crypt.simpleEncode(resultUrlstring);
}
String bookmarkLink = linkBuilder.append(actionLinkPrefix).append("&bookmarkref=").append(urlhash)
final String bookmarkLink = linkBuilder.append(actionLinkPrefix).append("&bookmarkref=").append(urlhash)
.append("&bookmarkurl=").append(encodedURLString).append("&urlmaskfilter=.*")
.toString();
linkBuilder.setLength(0);

View File

@ -1,5 +1,8 @@
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.data.UserDB;
import net.yacy.http.servlets.TemplateMissingParameterException;
import net.yacy.kelondro.util.Formatter;
import net.yacy.search.Switchboard;
import net.yacy.search.query.QueryParams;
import net.yacy.search.query.SearchEvent;
import net.yacy.search.query.SearchEventCache;
@ -10,8 +13,17 @@ import net.yacy.server.serverSwitch;
public class yacysearchlatestinfo {
public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, @SuppressWarnings("unused") final serverSwitch env) {
if (post == null) {
throw new TemplateMissingParameterException("The eventID parameter is required");
}
final serverObjects prop = new serverObjects();
//Switchboard sb = (Switchboard) env;
Switchboard sb = (Switchboard) env;
final boolean adminAuthenticated = sb.verifyAuthentication(header);
final UserDB.Entry user = sb.userDB != null ? sb.userDB.getUser(header) : null;
final boolean userAuthenticated = (user != null && user.hasRight(UserDB.AccessRight.EXTENDED_SEARCH_RIGHT));
final boolean authenticated = adminAuthenticated || userAuthenticated;
// find search event
final String eventID = post.get("eventID", "");
@ -44,7 +56,7 @@ public class yacysearchlatestinfo {
prop.put("remoteResourceSize", Formatter.number(theSearch.remote_rwi_stored.get() + theSearch.remote_solr_stored.get(), true));
prop.put("remoteIndexCount", Formatter.number(theSearch.remote_rwi_available.get() + theSearch.remote_solr_available.get(), true));
prop.put("remotePeerCount", Formatter.number(theSearch.remote_rwi_peerCount.get() + theSearch.remote_solr_peerCount.get(), true));
prop.putJSON("navurlBase", QueryParams.navurlBase(RequestHeader.FileType.HTML, theSearch.query, null, false).toString());
prop.putJSON("navurlBase", QueryParams.navurlBase(RequestHeader.FileType.HTML, theSearch.query, null, false, authenticated).toString());
prop.put("feedRunning", Boolean.toString(!theSearch.isFeedingFinished()));
return prop;

View File

@ -35,8 +35,10 @@ import net.yacy.cora.document.id.MultiProtocolURL;
import net.yacy.cora.lod.vocabulary.Tagging;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.cora.sorting.ScoreMap;
import net.yacy.data.UserDB;
import net.yacy.document.DateDetection;
import net.yacy.document.LibraryProvider;
import net.yacy.http.servlets.TemplateMissingParameterException;
import net.yacy.kelondro.util.ISO639;
import net.yacy.peers.graphics.ProfilingGraph;
import net.yacy.search.EventTracker;
@ -59,17 +61,38 @@ public class yacysearchtrailer {
@SuppressWarnings({ })
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
if (post == null) {
throw new TemplateMissingParameterException("The eventID parameter is required");
}
final serverObjects prop = new serverObjects();
final Switchboard sb = (Switchboard) env;
final String eventID = post.get("eventID", "");
final boolean authorized = sb.verifyAuthentication(header);
final boolean adminAuthenticated = sb.verifyAuthentication(header);
final UserDB.Entry user = sb.userDB != null ? sb.userDB.getUser(header) : null;
final boolean userAuthenticated = (user != null && user.hasRight(UserDB.AccessRight.EXTENDED_SEARCH_RIGHT));
final boolean authenticated = adminAuthenticated || userAuthenticated;
if (post.containsKey("auth") && !authenticated) {
/*
* Access to authentication protected features is explicitely requested here
* but no authentication is provided : ask now for authentication.
* Wihout this, after timeout of HTTP Digest authentication nonce, browsers no more send authentication information
* and as this page is not private, protected features would simply be hidden without asking browser again for authentication.
* (see mantis 766 : http://mantis.tokeek.de/view.php?id=766) *
*/
prop.authenticationRequired();
return prop;
}
final boolean clustersearch = sb.isRobinsonMode() && sb.getConfig(SwitchboardConstants.CLUSTER_MODE, "").equals(SwitchboardConstants.CLUSTER_MODE_PUBLIC_CLUSTER);
final boolean indexReceiveGranted = sb.getConfigBool(SwitchboardConstants.INDEX_RECEIVE_ALLOW_SEARCH, true) || clustersearch;
boolean p2pmode = sb.peers != null && sb.peers.sizeConnected() > 0 && indexReceiveGranted;
boolean global = post == null || (!post.get("resource-switch", post.get("resource", "global")).equals("local") && p2pmode);
boolean stealthmode = p2pmode && !global;
prop.put("resource-select", !authorized ? 0 : stealthmode ? 2 : global ? 1 : 0);
prop.put("resource-select", !adminAuthenticated ? 0 : stealthmode ? 2 : global ? 1 : 0);
// find search event
final SearchEvent theSearch = SearchEventCache.getEvent(eventID);
if (theSearch == null) {
@ -130,7 +153,8 @@ public class yacysearchtrailer {
}
String longname = ISO639.country(name);
prop.put(fileType, "nav-languages_element_" + i + "_name", longname == null ? name : longname);
prop.put(fileType, "nav-languages_element_" + i + "_url", QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false).toString());
prop.put(fileType, "nav-languages_element_" + i + "_url",
QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false, authenticated).toString());
prop.put(fileType, "nav-languages_element_" + i + "_id", "languages_" + i);
prop.put("nav-languages_element_" + i + "_count", count);
prop.put("nav-languages_element_" + i + "_nl", 1);
@ -172,7 +196,8 @@ public class yacysearchtrailer {
count = entry.getValue();
prop.put(fileType, "nav-topics_element_" + i + "_modifier", name);
prop.put(fileType, "nav-topics_element_" + i + "_name", name);
prop.put(fileType, "nav-topics_element_" + i + "_url", QueryParams.navurl(fileType, 0, theSearch.query, name, false).toString());
prop.put(fileType, "nav-topics_element_" + i + "_url", QueryParams
.navurl(fileType, 0, theSearch.query, name, false, authenticated).toString());
prop.put("nav-topics_element_" + i + "_count", count);
int fontsize = TOPWORDS_MINSIZE + (TOPWORDS_MAXSIZE - TOPWORDS_MINSIZE) * (count - mincount) / (1 + maxcount - mincount);
fontsize = Math.max(TOPWORDS_MINSIZE, fontsize - (name.length() - 5));
@ -223,7 +248,8 @@ public class yacysearchtrailer {
rawNav = "";
}
prop.put(fileType, "nav-protocols_element_" + i + "_name", name);
String url = QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false).toString();
String url = QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false, authenticated)
.toString();
prop.put("nav-protocols_element_" + i + "_onclick_url", url);
prop.put(fileType, "nav-protocols_element_" + i + "_url", url);
prop.put("nav-protocols_element_" + i + "_count", count);
@ -326,7 +352,8 @@ public class yacysearchtrailer {
rawNav = "";
}
prop.put(fileType, "nav-vocabulary_" + navvoccount + "_element_" + i + "_name", name);
prop.put(fileType, "nav-vocabulary_" + navvoccount + "_element_" + i + "_url", QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false).toString());
prop.put(fileType, "nav-vocabulary_" + navvoccount + "_element_" + i + "_url", QueryParams
.navurl(fileType, 0, theSearch.query, rawNav, false, authenticated).toString());
prop.put(fileType, "nav-vocabulary_" + navvoccount + "_element_" + i + "_id", "vocabulary_" + navname + "_" + i);
prop.put("nav-vocabulary_" + navvoccount + "_element_" + i + "_count", count);
prop.put("nav-vocabulary_" + navvoccount + "_element_" + i + "_nl", 1);
@ -385,7 +412,8 @@ public class yacysearchtrailer {
rawNav = "";
}
prop.put(fileType, "navs_" + ni + "_element_" + i + "_name", navi.getElementDisplayName(name));
prop.put(fileType, "navs_" + ni + "_element_" + i + "_url", QueryParams.navurl(fileType, 0, theSearch.query, rawNav, false).toString());
prop.put(fileType, "navs_" + ni + "_element_" + i + "_url", QueryParams
.navurl(fileType, 0, theSearch.query, rawNav, false, authenticated).toString());
prop.put(fileType, "navs_" + ni + "_element_" + i + "_id", naviname + "_" + i);
prop.put("navs_" + ni + "_element_" + i + "_count", count);
prop.put("navs_" + ni + "_element_" + i + "_nl", 1);

View File

@ -44,6 +44,13 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.lucene.util.automaton.Automata;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.SortClause;
import org.apache.solr.common.params.DisMaxParams;
import org.apache.solr.common.params.FacetParams;
import net.yacy.cora.document.analysis.Classification;
import net.yacy.cora.document.analysis.Classification.ContentDomain;
import net.yacy.cora.document.encoding.ASCII;
@ -71,13 +78,6 @@ import net.yacy.search.ranking.RankingProfile;
import net.yacy.search.schema.CollectionConfiguration;
import net.yacy.search.schema.CollectionSchema;
import org.apache.lucene.util.automaton.Automata;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.SortClause;
import org.apache.solr.common.params.DisMaxParams;
import org.apache.solr.common.params.FacetParams;
public final class QueryParams {
/** The default max count of item lines in navigator */
@ -735,11 +735,17 @@ public final class QueryParams {
/**
* make a query anchor tag
*
* @param authenticatedFeatures
* when true, access to authentication protected search features is
* wanted
* @return the anchor url builder
*/
public static StringBuilder navurl(final RequestHeader.FileType ext, final int page, final QueryParams theQuery, final String newQueryString, boolean newModifierReplacesOld) {
public static StringBuilder navurl(final RequestHeader.FileType ext, final int page, final QueryParams theQuery,
final String newQueryString, boolean newModifierReplacesOld, final boolean authenticatedFeatures) {
final StringBuilder sb = navurlBase(ext, theQuery, newQueryString, newModifierReplacesOld);
final StringBuilder sb = navurlBase(ext, theQuery, newQueryString, newModifierReplacesOld,
authenticatedFeatures);
sb.append("&startRecord=");
sb.append(page * theQuery.itemsPerPage());
@ -750,15 +756,22 @@ public final class QueryParams {
/**
* construct navigator url
*
* @param ext extension of servlet (e.g. html, rss)
* @param theQuery search query
* @param newModifier optional new modifier.
* - if null existing modifier of theQuery is appended
* - if not null this new modifier is appended in addition to existing modifier
* - if isEmpty overwrites (clears) existing modifier
* @param ext
* extension of servlet (e.g. html, rss)
* @param theQuery
* search query
* @param newModifier
* optional new modifier. - if null existing modifier of theQuery is
* appended - if not null this new modifier is appended in addition
* to existing modifier - if isEmpty overwrites (clears) existing
* modifier
* @param authenticatedFeatures
* when true, access to authentication protected search features is
* wanted
* @return url to new search result page
*/
public static StringBuilder navurlBase(final RequestHeader.FileType ext, final QueryParams theQuery, final String newModifier, boolean newModifierReplacesOld) {
public static StringBuilder navurlBase(final RequestHeader.FileType ext, final QueryParams theQuery,
final String newModifier, final boolean newModifierReplacesOld, final boolean authenticatedFeatures) {
StringBuilder sb = new StringBuilder(120);
sb.append("yacysearch.");
@ -811,6 +824,10 @@ public final class QueryParams {
sb.append("&former=");
sb.append(theQuery.getQueryGoal().getQueryString(true));
if(authenticatedFeatures) {
sb.append("&auth");
}
return sb;
}