- added full-String search function: find things that match exactly what is quoted in the query

- re-structuring authentification methods to fix a problem with API steering

git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@7697 6c8d7289-2bf4-0310-a012-ef5d649a1542
pull/1/head
orbiter 14 years ago
parent 8e10b82280
commit 6e42d4de88

@ -39,8 +39,8 @@ import net.yacy.kelondro.order.Digest;
import de.anomic.data.UserDB; import de.anomic.data.UserDB;
import de.anomic.data.UserDB.AccessRight; import de.anomic.data.UserDB.AccessRight;
import de.anomic.http.server.HTTPDemon;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
import de.anomic.search.SwitchboardConstants;
import de.anomic.server.serverObjects; import de.anomic.server.serverObjects;
import de.anomic.server.serverSwitch; import de.anomic.server.serverSwitch;
import java.util.EnumMap; import java.util.EnumMap;
@ -65,7 +65,7 @@ public class ConfigAccounts_p {
// may be overwritten if new password is given // may be overwritten if new password is given
if (user.length() > 0 && pw1.length() > 3 && pw1.equals(pw2)) { if (user.length() > 0 && pw1.length() > 3 && pw1.equals(pw2)) {
// check passed. set account: // check passed. set account:
env.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1))); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1)));
env.setConfig("adminAccount", ""); env.setConfig("adminAccount", "");
} }
@ -74,21 +74,21 @@ public class ConfigAccounts_p {
sb.setConfig("adminAccountForLocalhost", true); sb.setConfig("adminAccountForLocalhost", true);
// if an localhost access is configured, check if a local password is given // if an localhost access is configured, check if a local password is given
// if not, set a random password // if not, set a random password
if (env.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").length() == 0) { if (env.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").length() == 0) {
// make a 'random' password // make a 'random' password
env.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "0000" + sb.genRandomPassword()); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "0000" + sb.genRandomPassword());
env.setConfig("adminAccount", ""); env.setConfig("adminAccount", "");
} }
} else { } else {
sb.setConfig("adminAccountForLocalhost", false); sb.setConfig("adminAccountForLocalhost", false);
if (env.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").startsWith("0000")) { if (env.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").startsWith("0000")) {
// make shure that the user can still use the interface after a random password was set // make shure that the user can still use the interface after a random password was set
env.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, ""); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
} }
} }
} }
if (env.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").length() == 0 && !env.getConfigBool("adminAccountForLocalhost", false)) { if (env.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").length() == 0 && !env.getConfigBool("adminAccountForLocalhost", false)) {
prop.put("passwordNotSetWarning", 1); prop.put("passwordNotSetWarning", 1);
} }

@ -40,7 +40,6 @@ import net.yacy.kelondro.workflow.InstantBusyThread;
import de.anomic.data.WorkTables; import de.anomic.data.WorkTables;
import de.anomic.data.Translator; import de.anomic.data.Translator;
import de.anomic.http.server.HTTPDemon;
import de.anomic.http.server.HTTPDFileHandler; import de.anomic.http.server.HTTPDFileHandler;
import de.anomic.net.UPnP; import de.anomic.net.UPnP;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
@ -218,7 +217,7 @@ public class ConfigBasic {
prop.put("setUseCase_repositoryPath", sb.getConfig("repositoryPath", "/DATA/HTROOT/repository")); prop.put("setUseCase_repositoryPath", sb.getConfig("repositoryPath", "/DATA/HTROOT/repository"));
// check if values are proper // check if values are proper
final boolean properPassword = (sb.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").length() > 0) || sb.getConfigBool("adminAccountForLocalhost", false); final boolean properPassword = (sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").length() > 0) || sb.getConfigBool("adminAccountForLocalhost", false);
final boolean properName = (sb.peers.mySeed().getName().length() >= 3) && (!(yacySeed.isDefaultPeerName(sb.peers.mySeed().getName()))); final boolean properName = (sb.peers.mySeed().getName().length() >= 3) && (!(yacySeed.isDefaultPeerName(sb.peers.mySeed().getName())));
final boolean properPort = (sb.peers.mySeed().isSenior()) || (sb.peers.mySeed().isPrincipal()); final boolean properPort = (sb.peers.mySeed().isSenior()) || (sb.peers.mySeed().isPrincipal());

@ -45,6 +45,7 @@ import net.yacy.kelondro.util.Formatter;
import de.anomic.http.server.HTTPDemon; import de.anomic.http.server.HTTPDemon;
import de.anomic.http.server.HTTPDProxyHandler; import de.anomic.http.server.HTTPDProxyHandler;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
import de.anomic.search.SwitchboardConstants;
import de.anomic.server.serverCore; import de.anomic.server.serverCore;
import de.anomic.server.serverObjects; import de.anomic.server.serverObjects;
import de.anomic.server.serverSwitch; import de.anomic.server.serverSwitch;
@ -92,7 +93,7 @@ public class SettingsAck_p {
return prop; return prop;
} }
// check passed. set account: // check passed. set account:
env.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1))); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1)));
env.setConfig("adminAccount", ""); env.setConfig("adminAccount", "");
prop.put("info", "5");//admin account changed prop.put("info", "5");//admin account changed
prop.putHTML("info_user", user); prop.putHTML("info_user", user);

@ -38,7 +38,6 @@ import net.yacy.kelondro.util.MemoryControl;
import net.yacy.kelondro.util.OS; import net.yacy.kelondro.util.OS;
import net.yacy.kelondro.workflow.WorkflowProcessor; import net.yacy.kelondro.workflow.WorkflowProcessor;
import de.anomic.http.server.HTTPDemon;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
import de.anomic.search.SwitchboardConstants; import de.anomic.search.SwitchboardConstants;
import de.anomic.server.serverCore; import de.anomic.server.serverCore;
@ -125,7 +124,7 @@ public class Status {
} }
// password protection // password protection
if ((sb.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").length() == 0) && (!sb.getConfigBool("adminAccountForLocalhost", false))) { if ((sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").length() == 0) && (!sb.getConfigBool("adminAccountForLocalhost", false))) {
prop.put("protection", "0"); // not protected prop.put("protection", "0"); // not protected
prop.put("urgentSetPassword", "1"); prop.put("urgentSetPassword", "1");
} else { } else {

@ -35,8 +35,8 @@ import net.yacy.kelondro.order.Base64Order;
import net.yacy.kelondro.order.Digest; import net.yacy.kelondro.order.Digest;
import de.anomic.data.UserDB; import de.anomic.data.UserDB;
import de.anomic.http.server.HTTPDemon;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
import de.anomic.search.SwitchboardConstants;
import de.anomic.server.serverObjects; import de.anomic.server.serverObjects;
import de.anomic.server.serverSwitch; import de.anomic.server.serverSwitch;
import de.anomic.server.servletProperties; import de.anomic.server.servletProperties;
@ -95,7 +95,7 @@ public class User{
final String password=post.get("password"); final String password=post.get("password");
entry=sb.userDB.passwordAuth(username, password); entry=sb.userDB.passwordAuth(username, password);
final boolean staticAdmin = sb.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").equals( final boolean staticAdmin = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").equals(
Digest.encodeMD5Hex( Digest.encodeMD5Hex(
Base64Order.standardCoder.encodeString(username + ":" + password) Base64Order.standardCoder.encodeString(username + ":" + password)
) )

@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.regex.Pattern;
import net.yacy.cora.document.RSSMessage; import net.yacy.cora.document.RSSMessage;
import net.yacy.cora.document.UTF8; import net.yacy.cora.document.UTF8;
@ -102,6 +103,7 @@ public final class search {
final String prefer = post.get("prefer", ""); final String prefer = post.get("prefer", "");
final String contentdom = post.get("contentdom", "text"); final String contentdom = post.get("contentdom", "text");
final String filter = post.get("filter", ".*"); final String filter = post.get("filter", ".*");
final Pattern snippetPattern = Pattern.compile(post.get("snippet", ".*"));
String sitehash = post.get("sitehash", ""); if (sitehash.length() == 0) sitehash = null; String sitehash = post.get("sitehash", ""); if (sitehash.length() == 0) sitehash = null;
String authorhash = post.get("authorhash", ""); if (authorhash.length() == 0) authorhash = null; String authorhash = post.get("authorhash", ""); if (authorhash.length() == 0) authorhash = null;
String language = post.get("language", ""); String language = post.get("language", "");
@ -214,6 +216,7 @@ public final class search {
abstractSet, abstractSet,
new HandleSet(WordReferenceRow.urlEntryRow.primaryKeyLength, WordReferenceRow.urlEntryRow.objectOrder, 0), new HandleSet(WordReferenceRow.urlEntryRow.primaryKeyLength, WordReferenceRow.urlEntryRow.objectOrder, 0),
null, null,
snippetPattern,
null, null,
maxdist, maxdist,
prefer, prefer,
@ -273,6 +276,7 @@ public final class search {
queryhashes, queryhashes,
excludehashes, excludehashes,
null, null,
snippetPattern,
null, null,
maxdist, maxdist,
prefer, prefer,

@ -531,6 +531,7 @@ public class yacysearch {
// do the search // do the search
final HandleSet queryHashes = Word.words2hashesHandles(query[0]); final HandleSet queryHashes = Word.words2hashesHandles(query[0]);
final Pattern snippetPattern = QueryParams.stringSearchPattern(originalquerystring);
// check filters // check filters
try { try {
@ -556,6 +557,7 @@ public class yacysearch {
queryHashes, queryHashes,
Word.words2hashesHandles(query[1]), Word.words2hashesHandles(query[1]),
Word.words2hashesHandles(query[2]), Word.words2hashesHandles(query[2]),
snippetPattern,
tenant, tenant,
maxDistance, maxDistance,
prefermask, prefermask,

@ -96,6 +96,7 @@ import net.yacy.document.parser.html.ContentScraper;
import net.yacy.document.parser.html.ScraperInputStream; import net.yacy.document.parser.html.ScraperInputStream;
import net.yacy.kelondro.data.meta.DigestURI; import net.yacy.kelondro.data.meta.DigestURI;
import net.yacy.kelondro.logging.Log; import net.yacy.kelondro.logging.Log;
import net.yacy.kelondro.order.Digest;
import net.yacy.kelondro.util.ByteBuffer; import net.yacy.kelondro.util.ByteBuffer;
import net.yacy.kelondro.util.FileUtils; import net.yacy.kelondro.util.FileUtils;
import net.yacy.kelondro.util.MemoryControl; import net.yacy.kelondro.util.MemoryControl;
@ -276,10 +277,6 @@ public final class HTTPDFileHandler {
return; return;
} }
// check permission/granted access
String authorization = requestHeader.get(RequestHeader.AUTHORIZATION);
if (authorization != null && authorization.length() == 0) authorization = null;
final String adminAccountBase64MD5 = switchboard.getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "");
// cache settings // cache settings
boolean nocache = path.contains("?") || body != null; boolean nocache = path.contains("?") || body != null;
@ -297,46 +294,61 @@ public final class HTTPDFileHandler {
path = "/api/bookmarks/" + path.substring(11); path = "/api/bookmarks/" + path.substring(11);
} }
final boolean adminAccountForLocalhost = sb.getConfigBool("adminAccountForLocalhost", false); // these are the 5 cases where an access granted:
// (the alternative is that we deliver a 401 to request authorization)
// -1- the page is not protected; or
final boolean protectedPage = path.indexOf("_p.") > 0;
boolean accessGranted = !protectedPage;
// -2- a password is not configured; or
final String adminAccountBase64MD5 = switchboard.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
if (!accessGranted) {
accessGranted = adminAccountBase64MD5.length() == 0;
}
// -3- access from localhost is granted and access comes from localhost; or
final String refererHost = requestHeader.refererHost(); final String refererHost = requestHeader.refererHost();
if (!accessGranted) {
final boolean adminAccountForLocalhost = sb.getConfigBool("adminAccountForLocalhost", false);
boolean accessFromLocalhost = Domains.isLocalhost(clientIP) && (refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost)); boolean accessFromLocalhost = Domains.isLocalhost(clientIP) && (refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost));
final boolean grantedForLocalhost = adminAccountForLocalhost && accessFromLocalhost; accessGranted = adminAccountForLocalhost && accessFromLocalhost;
final boolean protectedPage = path.indexOf("_p.") > 0; }
final boolean accountEmpty = adminAccountBase64MD5.length() == 0;
final boolean softauth = accessFromLocalhost && authorization != null && authorization.length() > 6 && (adminAccountBase64MD5.equals(authorization.substring(6)));
if (protectedPage && !softauth && ((!grantedForLocalhost && !accountEmpty) || requestHeader.userAgent().startsWith("yacybot"))) { // -4- a password is configured and access comes from localhost
// authentication required // and the realm-value of a http-authentify String is equal to the stored base64MD5; or
if (authorization == null) { String realmProp = requestHeader.get(RequestHeader.AUTHORIZATION);
// no authorization given in response. Ask for that if (realmProp != null && realmProp.length() == 0) realmProp = null;
final ResponseHeader responseHeader = getDefaultHeaders(path); String realmValue = realmProp == null ? null : realmProp.substring(6);
responseHeader.put(RequestHeader.WWW_AUTHENTICATE,"Basic realm=\"admin log-in\""); if (!accessGranted) {
//httpd.sendRespondHeader(conProp,out,httpVersion,401,headers); boolean accessFromLocalhost = Domains.isLocalhost(clientIP) && (refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost));
final servletProperties tp=new servletProperties(); accessGranted = accessFromLocalhost && realmValue != null && realmProp.length() > 6 && (adminAccountBase64MD5.equals(realmValue));
tp.put("returnto", path); }
//TODO: separate error page Wrong Login / No Login
HTTPDemon.sendRespondError(conProp, out, 5, 401, "Wrong Authentication", "", new File("proxymsg/authfail.inc"), tp, null, responseHeader); // -5- a password is configured and access comes with matching http-authentify
return; if (!accessGranted) {
} else if ( accessGranted = realmProp != null && realmValue != null && (sb.userDB.hasAdminRight(realmProp, requestHeader.getHeaderCookies()) || adminAccountBase64MD5.equals(Digest.encodeMD5Hex(realmValue)));
(HTTPDemon.staticAdminAuthenticated(authorization.trim().substring(6), switchboard) == 4) || }
(sb.userDB.hasAdminRight(authorization, requestHeader.getHeaderCookies()))) {
//Authentication successful. remove brute-force flag // in case that we are still not granted we ask for a password
serverCore.bfHost.remove(conProp.getProperty(HeaderFramework.CONNECTION_PROP_CLIENTIP)); if (!accessGranted) {
} else { Log.logInfo("HTTPD", "Wrong log-in for path '" + path + "' from host '" + clientIP + "'");
// a wrong authentication was given or the userDB user does not have admin access. Ask again
Log.logInfo("HTTPD", "Wrong log-in for account 'admin' in http file handler for path '" + path + "' from host '" + clientIP + "'");
final Integer attempts = serverCore.bfHost.get(clientIP); final Integer attempts = serverCore.bfHost.get(clientIP);
if (attempts == null) if (attempts == null)
serverCore.bfHost.put(clientIP, Integer.valueOf(1)); serverCore.bfHost.put(clientIP, Integer.valueOf(1));
else else
serverCore.bfHost.put(clientIP, Integer.valueOf(attempts.intValue() + 1)); serverCore.bfHost.put(clientIP, Integer.valueOf(attempts.intValue() + 1));
final ResponseHeader headers = getDefaultHeaders(path); final ResponseHeader responseHeader = getDefaultHeaders(path);
headers.put(RequestHeader.WWW_AUTHENTICATE,"Basic realm=\"admin log-in\""); responseHeader.put(RequestHeader.WWW_AUTHENTICATE,"Basic realm=\"admin log-in\"");
HTTPDemon.sendRespondHeader(conProp,out,httpVersion,401,headers); final servletProperties tp=new servletProperties();
tp.put("returnto", path);
HTTPDemon.sendRespondError(conProp, out, 5, 401, "Wrong Authentication", "", new File("proxymsg/authfail.inc"), tp, null, responseHeader);
return; return;
} }
}
// Authentication successful. remove brute-force flag
serverCore.bfHost.remove(conProp.getProperty(HeaderFramework.CONNECTION_PROP_CLIENTIP));
// parse arguments // parse arguments
serverObjects args = new serverObjects(); serverObjects args = new serverObjects();
@ -818,7 +830,7 @@ public final class HTTPDFileHandler {
// check if the servlets requests authentication // check if the servlets requests authentication
if (templatePatterns.containsKey(servletProperties.ACTION_AUTHENTICATE)) { if (templatePatterns.containsKey(servletProperties.ACTION_AUTHENTICATE)) {
// handle brute-force protection // handle brute-force protection
if (authorization != null) { if (realmProp != null) {
Log.logInfo("HTTPD", "dynamic log-in for account 'admin' in http file handler for path '" + path + "' from host '" + clientIP + "'"); Log.logInfo("HTTPD", "dynamic log-in for account 'admin' in http file handler for path '" + path + "' from host '" + clientIP + "'");
final Integer attempts = serverCore.bfHost.get(clientIP); final Integer attempts = serverCore.bfHost.get(clientIP);
if (attempts == null) if (attempts == null)
@ -948,7 +960,7 @@ public final class HTTPDFileHandler {
null, "chunked", nocache); null, "chunked", nocache);
// send the content in chunked parts, see RFC 2616 section 3.6.1 // send the content in chunked parts, see RFC 2616 section 3.6.1
final ChunkedOutputStream chos = new ChunkedOutputStream(out); final ChunkedOutputStream chos = new ChunkedOutputStream(out);
ServerSideIncludes.writeSSI(o, chos, authorization, clientIP); ServerSideIncludes.writeSSI(o, chos, realmProp, clientIP);
//chos.write(result); //chos.write(result);
chos.finish(); chos.finish();
} else { } else {
@ -962,14 +974,14 @@ public final class HTTPDFileHandler {
if (zipContent) { if (zipContent) {
GZIPOutputStream zippedOut = new GZIPOutputStream(o); GZIPOutputStream zippedOut = new GZIPOutputStream(o);
ServerSideIncludes.writeSSI(o1, zippedOut, authorization, clientIP); ServerSideIncludes.writeSSI(o1, zippedOut, realmProp, clientIP);
//httpTemplate.writeTemplate(fis, zippedOut, tp, "-UNRESOLVED_PATTERN-".getBytes("UTF-8")); //httpTemplate.writeTemplate(fis, zippedOut, tp, "-UNRESOLVED_PATTERN-".getBytes("UTF-8"));
zippedOut.finish(); zippedOut.finish();
zippedOut.flush(); zippedOut.flush();
zippedOut.close(); zippedOut.close();
zippedOut = null; zippedOut = null;
} else { } else {
ServerSideIncludes.writeSSI(o1, o, authorization, clientIP); ServerSideIncludes.writeSSI(o1, o, realmProp, clientIP);
//httpTemplate.writeTemplate(fis, o, tp, "-UNRESOLVED_PATTERN-".getBytes("UTF-8")); //httpTemplate.writeTemplate(fis, o, tp, "-UNRESOLVED_PATTERN-".getBytes("UTF-8"));
} }
if (method.equals(HeaderFramework.METHOD_HEAD)) { if (method.equals(HeaderFramework.METHOD_HEAD)) {

@ -60,11 +60,9 @@ import net.yacy.document.parser.html.CharacterCoding;
import net.yacy.kelondro.data.meta.DigestURI; import net.yacy.kelondro.data.meta.DigestURI;
import net.yacy.kelondro.logging.Log; import net.yacy.kelondro.logging.Log;
import net.yacy.kelondro.order.Base64Order; import net.yacy.kelondro.order.Base64Order;
import net.yacy.kelondro.order.Digest;
import net.yacy.kelondro.util.ByteBuffer; import net.yacy.kelondro.util.ByteBuffer;
import net.yacy.kelondro.util.FileUtils; import net.yacy.kelondro.util.FileUtils;
import net.yacy.kelondro.util.MemoryControl; import net.yacy.kelondro.util.MemoryControl;
import net.yacy.kelondro.util.MapTools;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileItemFactory;
@ -94,12 +92,6 @@ import java.util.concurrent.ConcurrentMap;
*/ */
public final class HTTPDemon implements serverHandler, Cloneable { public final class HTTPDemon implements serverHandler, Cloneable {
/**
* <p><code>public static final String <strong>ADMIN_ACCOUNT_B64MD5</strong> = "adminAccountBase64MD5"</code></p>
* <p>Name of the setting holding the authentication hash for the static <code>admin</code>-account. It is calculated
* by first encoding <code>username:password</code> as Base64 and hashing it using {@link MapTools#encodeMD5Hex(String)}.</p>
*/
public static final String ADMIN_ACCOUNT_B64MD5 = "adminAccountBase64MD5";
public static final int ERRORCASE_MESSAGE = 4; public static final int ERRORCASE_MESSAGE = 4;
public static final int ERRORCASE_FILE = 5; public static final int ERRORCASE_FILE = 5;
@ -259,15 +251,6 @@ public final class HTTPDemon implements serverHandler, Cloneable {
return persistent; return persistent;
} }
public static int staticAdminAuthenticated(final String authorization, final serverSwitch sw) {
// the authorization string must be given with the truncated 6 bytes at the beginning
final String adminAccountBase64MD5 = sw.getConfig(ADMIN_ACCOUNT_B64MD5, "");
if (adminAccountBase64MD5.length() == 0) return 2; // no password stored
if (authorization == null || authorization.length() == 0) return 1;
if (adminAccountBase64MD5.equals(Digest.encodeMD5Hex(authorization))) return 4; // hard-authenticated, all ok
return 1;
}
private boolean handleYaCyHopAuthentication(final RequestHeader header, Properties prop, Session session) { private boolean handleYaCyHopAuthentication(final RequestHeader header, Properties prop, Session session) {
// check if the user has allowed that his/her peer is used for hops // check if the user has allowed that his/her peer is used for hops
if (!allowYaCyHop(session)) return false; if (!allowYaCyHop(session)) return false;

@ -33,6 +33,7 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
@ -77,6 +78,7 @@ public final class QueryParams {
public final String queryString; public final String queryString;
public HandleSet fullqueryHashes, queryHashes, excludeHashes; public HandleSet fullqueryHashes, queryHashes, excludeHashes;
public Pattern snippetMatcher;
public final int itemsPerPage; public final int itemsPerPage;
public int offset; public int offset;
public final Pattern urlMask, prefer; public final Pattern urlMask, prefer;
@ -131,6 +133,7 @@ public final class QueryParams {
this.excludeHashes = Word.words2hashesHandles(cq[1]); this.excludeHashes = Word.words2hashesHandles(cq[1]);
this.fullqueryHashes = Word.words2hashesHandles(cq[2]); this.fullqueryHashes = Word.words2hashesHandles(cq[2]);
} }
this.snippetMatcher = Pattern.compile(".*");
this.ranking = ranking; this.ranking = ranking;
this.tenant = null; this.tenant = null;
this.maxDistance = Integer.MAX_VALUE; this.maxDistance = Integer.MAX_VALUE;
@ -165,6 +168,7 @@ public final class QueryParams {
final String queryString, final HandleSet queryHashes, final String queryString, final HandleSet queryHashes,
final HandleSet excludeHashes, final HandleSet excludeHashes,
final HandleSet fullqueryHashes, final HandleSet fullqueryHashes,
final Pattern snippetMatcher,
final String tenant, final String tenant,
final int maxDistance, final String prefer, final ContentDomain contentdom, final int maxDistance, final String prefer, final ContentDomain contentdom,
final String language, final String language,
@ -187,6 +191,7 @@ public final class QueryParams {
this.queryHashes = queryHashes; this.queryHashes = queryHashes;
this.excludeHashes = excludeHashes; this.excludeHashes = excludeHashes;
this.fullqueryHashes = fullqueryHashes; this.fullqueryHashes = fullqueryHashes;
this.snippetMatcher = snippetMatcher;
this.tenant = (tenant != null && tenant.length() == 0) ? null : tenant; this.tenant = (tenant != null && tenant.length() == 0) ? null : tenant;
this.ranking = ranking; this.ranking = ranking;
this.maxDistance = maxDistance; this.maxDistance = maxDistance;
@ -533,4 +538,28 @@ public final class QueryParams {
return sb.toString(); return sb.toString();
} }
private static Pattern StringMatchPattern = Pattern.compile(".*?(\".*?\").*");
/**
* calculate a pattern to match with a string search
* @param query
* @return
*/
public static Pattern stringSearchPattern(String query) {
String p = "";
while (query.length() > 0) {
Matcher m = StringMatchPattern.matcher(query);
if (!m.matches()) break;
p += ".*" + query.substring(m.start(1) + 1, m.end(1) - 1);
query = query.substring(m.end(1));
}
p += ".*";
return Pattern.compile(p);
}
public static void main(String[] args) {
Pattern p = stringSearchPattern("die \"peer-to-peer Suchmaschine\" ohne Zensur als \"freie Software\" runterladen");
System.out.println(p.toString());
}
} }

@ -29,6 +29,7 @@ package de.anomic.search;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.regex.Pattern;
import net.yacy.cora.document.MultiProtocolURI; import net.yacy.cora.document.MultiProtocolURI;
@ -254,7 +255,7 @@ public class ResultFetcher {
this.workerThreads = new Worker[deployCount]; this.workerThreads = new Worker[deployCount];
synchronized(this.workerThreads) { synchronized(this.workerThreads) {
for (int i = 0; i < workerThreads.length; i++) { for (int i = 0; i < workerThreads.length; i++) {
Worker worker = new Worker(i, 10000, query.snippetCacheStrategy, neededResults); Worker worker = new Worker(i, 10000, query.snippetCacheStrategy, query.snippetMatcher, neededResults);
worker.start(); worker.start();
this.workerThreads[i] = worker; this.workerThreads[i] = worker;
} }
@ -266,7 +267,7 @@ public class ResultFetcher {
for (int i = 0; i < this.workerThreads.length; i++) { for (int i = 0; i < this.workerThreads.length; i++) {
if (deployCount <= 0) break; if (deployCount <= 0) break;
if (this.workerThreads[i] == null || !this.workerThreads[i].isAlive()) { if (this.workerThreads[i] == null || !this.workerThreads[i].isAlive()) {
Worker worker = new Worker(i, 10000, query.snippetCacheStrategy, neededResults); Worker worker = new Worker(i, 10000, query.snippetCacheStrategy, query.snippetMatcher, neededResults);
worker.start(); worker.start();
this.workerThreads[i] = worker; this.workerThreads[i] = worker;
deployCount--; deployCount--;
@ -295,11 +296,13 @@ public class ResultFetcher {
private final int id; private final int id;
private final CrawlProfile.CacheStrategy cacheStrategy; private final CrawlProfile.CacheStrategy cacheStrategy;
private final int neededResults; private final int neededResults;
private final Pattern snippetPattern;
public Worker(final int id, final long maxlifetime, CrawlProfile.CacheStrategy cacheStrategy, int neededResults) { public Worker(final int id, final long maxlifetime, CrawlProfile.CacheStrategy cacheStrategy, Pattern snippetPattern, int neededResults) {
this.id = id; this.id = id;
this.cacheStrategy = cacheStrategy; this.cacheStrategy = cacheStrategy;
this.lastLifeSign = System.currentTimeMillis(); this.lastLifeSign = System.currentTimeMillis();
this.snippetPattern = snippetPattern;
this.timeout = System.currentTimeMillis() + Math.max(1000, maxlifetime); this.timeout = System.currentTimeMillis() + Math.max(1000, maxlifetime);
this.neededResults = neededResults; this.neededResults = neededResults;
} }
@ -340,10 +343,12 @@ public class ResultFetcher {
loops++; loops++;
final ResultEntry resultEntry = fetchSnippet(page, cacheStrategy); // does not fetch snippets if snippetMode == 0 final ResultEntry resultEntry = fetchSnippet(page, cacheStrategy); // does not fetch snippets if snippetMode == 0
if (resultEntry == null) continue; // the entry had some problems, cannot be used if (resultEntry == null) continue; // the entry had some problems, cannot be used
//if (result.contains(resultEntry)) continue; String rawLine = resultEntry.textSnippet().getLineRaw();
//System.out.println("***SNIPPET*** raw='" + rawLine + "', pattern='" + this.snippetPattern.toString() + "'");
if (!this.snippetPattern.matcher(rawLine).matches()) continue;
//if (result.contains(resultEntry)) continue;
urlRetrievalAllTime += resultEntry.dbRetrievalTime; urlRetrievalAllTime += resultEntry.dbRetrievalTime;
snippetComputationAllTime += resultEntry.snippetComputationTime; snippetComputationAllTime += resultEntry.snippetComputationTime;

@ -108,6 +108,7 @@ import net.yacy.kelondro.index.HandleSet;
import net.yacy.kelondro.index.RowSpaceExceededException; import net.yacy.kelondro.index.RowSpaceExceededException;
import net.yacy.kelondro.logging.Log; import net.yacy.kelondro.logging.Log;
import net.yacy.kelondro.order.Base64Order; import net.yacy.kelondro.order.Base64Order;
import net.yacy.kelondro.order.Digest;
import net.yacy.kelondro.order.NaturalOrder; import net.yacy.kelondro.order.NaturalOrder;
import net.yacy.kelondro.util.EventTracker; import net.yacy.kelondro.util.EventTracker;
import net.yacy.kelondro.util.FileUtils; import net.yacy.kelondro.util.FileUtils;
@ -149,7 +150,6 @@ import de.anomic.data.wiki.WikiBoard;
import de.anomic.data.wiki.WikiCode; import de.anomic.data.wiki.WikiCode;
import de.anomic.data.wiki.WikiParser; import de.anomic.data.wiki.WikiParser;
import de.anomic.http.client.Cache; import de.anomic.http.client.Cache;
import de.anomic.http.server.HTTPDemon;
import de.anomic.http.server.RobotsTxtConfig; import de.anomic.http.server.RobotsTxtConfig;
import de.anomic.net.UPnP; import de.anomic.net.UPnP;
import de.anomic.server.serverSwitch; import de.anomic.server.serverSwitch;
@ -601,10 +601,10 @@ public final class Switchboard extends serverSwitch {
// addresses are blocked to prevent attack szenarios where remote pages contain links to localhost // addresses are blocked to prevent attack szenarios where remote pages contain links to localhost
// addresses that can steer a YaCy peer // addresses that can steer a YaCy peer
if ((getConfigBool("adminAccountForLocalhost", false))) { if ((getConfigBool("adminAccountForLocalhost", false))) {
if (getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").startsWith("0000")) { if (getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").startsWith("0000")) {
// the password was set automatically with a random value. // the password was set automatically with a random value.
// We must remove that here to prevent that a user cannot log in any more // We must remove that here to prevent that a user cannot log in any more
setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, ""); setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
// after this a message must be generated to alert the user to set a new password // after this a message must be generated to alert the user to set a new password
log.logInfo("RANDOM PASSWORD REMOVED! User must set a new password"); log.logInfo("RANDOM PASSWORD REMOVED! User must set a new password");
} }
@ -1525,9 +1525,9 @@ public final class Switchboard extends serverSwitch {
} }
// set a random password if no password is configured // set a random password if no password is configured
if (getConfigBool("adminAccountForLocalhost", false) && getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "").length() == 0) { if (getConfigBool("adminAccountForLocalhost", false) && getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "").length() == 0) {
// make a 'random' password // make a 'random' password
setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, "0000" + this.genRandomPassword()); setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "0000" + this.genRandomPassword());
setConfig("adminAccount", ""); setConfig("adminAccount", "");
} }
@ -2216,27 +2216,45 @@ public final class Switchboard extends serverSwitch {
return refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost); return refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost);
} }
/**
* check authentication status for request
* access shall be granted if return value >= 2;
* these are the cases where an access is granted to protected pages:
* - a password is not configured: auth-level 2
* - access from localhost is granted and access comes from localhost: auth-level 3
* - a password is configured and access comes from localhost
* and the realm-value of a http-authentify String is equal to the stored base64MD5: auth-level 3
* - a password is configured and access comes with matching http-authentify: auth-level 4
* @param requestHeader
* @return the auth-level as described above or 1 which means 'not authorized'. a 0 is returned in case of fraud attempts
*/
public int adminAuthenticated(final RequestHeader requestHeader) { public int adminAuthenticated(final RequestHeader requestHeader) {
// authorization in case that there is no account stored
final String adminAccountBase64MD5 = getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
if (adminAccountBase64MD5.length() == 0) return 2; // no password stored; this should not happen for older peers
// authorization for localhost, only if flag is set to grant localhost access as admin // authorization for localhost, only if flag is set to grant localhost access as admin
final boolean accessFromLocalhost = accessFromLocalhost(requestHeader); final boolean accessFromLocalhost = accessFromLocalhost(requestHeader);
if (getConfigBool("adminAccountForLocalhost", false) && accessFromLocalhost) return 3; // soft-authenticated for localhost if (getConfigBool("adminAccountForLocalhost", false) && accessFromLocalhost) return 3; // soft-authenticated for localhost
// get the authorization string from the header // get the authorization string from the header
final String authorization = (requestHeader.get(RequestHeader.AUTHORIZATION, "xxxxxx")).trim().substring(6); String realmProp = (requestHeader.get(RequestHeader.AUTHORIZATION, "xxxxxx")).trim();
final String realmValue = realmProp.substring(6);
// security check against too long authorization strings // security check against too long authorization strings
if (authorization.length() > 256) return 0; if (realmValue.length() > 256) return 0;
// authorization by encoded password, only for localhost access // authorization by encoded password, only for localhost access
final String adminAccountBase64MD5 = getConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, ""); if (accessFromLocalhost && (adminAccountBase64MD5.equals(realmValue))) return 3; // soft-authenticated for localhost
if (accessFromLocalhost && (adminAccountBase64MD5.equals(authorization))) return 3; // soft-authenticated for localhost
// authorization by hit in userDB // authorization by hit in userDB
if (userDB.hasAdminRight(requestHeader.get(RequestHeader.AUTHORIZATION, "xxxxxx"), requestHeader.getHeaderCookies())) return 4; //return, because 4=max if (userDB.hasAdminRight(realmProp, requestHeader.getHeaderCookies())) return 4; //return, because 4=max
// authorization with admin keyword in configuration // authorization with admin keyword in configuration
return HTTPDemon.staticAdminAuthenticated(authorization, this); if (realmValue == null || realmValue.length() == 0) return 1;
if (adminAccountBase64MD5.equals(Digest.encodeMD5Hex(realmValue))) return 4; // hard-authenticated, all ok
return 1;
} }
public boolean verifyAuthentication(final RequestHeader header, final boolean strict) { public boolean verifyAuthentication(final RequestHeader header, final boolean strict) {

@ -26,6 +26,7 @@
package de.anomic.search; package de.anomic.search;
import net.yacy.kelondro.util.MapTools;
import de.anomic.http.server.RobotsTxtConfig; import de.anomic.http.server.RobotsTxtConfig;
/** /**
@ -34,6 +35,14 @@ import de.anomic.http.server.RobotsTxtConfig;
*/ */
public final class SwitchboardConstants { public final class SwitchboardConstants {
/**
* <p><code>public static final String <strong>ADMIN_ACCOUNT_B64MD5</strong> = "adminAccountBase64MD5"</code></p>
* <p>Name of the setting holding the authentication hash for the static <code>admin</code>-account. It is calculated
* by first encoding <code>username:password</code> as Base64 and hashing it using {@link MapTools#encodeMD5Hex(String)}.</p>
*/
public static final String ADMIN_ACCOUNT_B64MD5 = "adminAccountBase64MD5";
public static final int CRAWLJOB_SYNC = 0; public static final int CRAWLJOB_SYNC = 0;
public static final int CRAWLJOB_STATUS = 1; public static final int CRAWLJOB_STATUS = 1;
// 20_dhtdistribution // 20_dhtdistribution

@ -30,7 +30,6 @@ import net.yacy.kelondro.order.Base64Order;
import net.yacy.kelondro.order.Digest; import net.yacy.kelondro.order.Digest;
import net.yacy.kelondro.util.FileUtils; import net.yacy.kelondro.util.FileUtils;
import de.anomic.http.server.HTTPDemon;
import de.anomic.search.Switchboard; import de.anomic.search.Switchboard;
import de.anomic.search.SwitchboardConstants; import de.anomic.search.SwitchboardConstants;
@ -207,7 +206,7 @@ public class migration {
// set preset accounts/passwords // set preset accounts/passwords
String acc; String acc;
if ((acc = sb.getConfig("adminAccount", "")).length() > 0) { if ((acc = sb.getConfig("adminAccount", "")).length() > 0) {
sb.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(acc))); sb.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(acc)));
sb.setConfig("adminAccount", ""); sb.setConfig("adminAccount", "");
} }
@ -217,7 +216,7 @@ public class migration {
sb.setConfig("proxyAccountBase64", ""); sb.setConfig("proxyAccountBase64", "");
} }
if ((acc = sb.getConfig("adminAccountBase64", "")).length() > 0) { if ((acc = sb.getConfig("adminAccountBase64", "")).length() > 0) {
sb.setConfig(HTTPDemon.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(acc)); sb.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(acc));
sb.setConfig("adminAccountBase64", ""); sb.setConfig("adminAccountBase64", "");
} }
if ((acc = sb.getConfig("uploadAccountBase64", "")).length() > 0) { if ((acc = sb.getConfig("uploadAccountBase64", "")).length() > 0) {

@ -528,7 +528,7 @@ public final class yacy {
final int port = serverCore.getPortNr(config.getProperty("port", "8090")); final int port = serverCore.getPortNr(config.getProperty("port", "8090"));
// read password // read password
String encodedPassword = (String) config.get(HTTPDemon.ADMIN_ACCOUNT_B64MD5); String encodedPassword = (String) config.get(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5);
if (encodedPassword == null) encodedPassword = ""; // not defined if (encodedPassword == null) encodedPassword = ""; // not defined
// send 'wget' to web interface // send 'wget' to web interface

Loading…
Cancel
Save