diff --git a/htroot/Blog.java b/htroot/Blog.java index 2e3cc6c6d..5f88bbb47 100644 --- a/htroot/Blog.java +++ b/htroot/Blog.java @@ -93,7 +93,7 @@ public class Blog { final int num = post.getInt("num",10); //indicates how many entries should be shown if (!hasRights) { - final UserDB.Entry userentry = sb.userDB.proxyAuth(header.get(RequestHeader.AUTHORIZATION)); + final UserDB.Entry userentry = sb.userDB.proxyAuth(header); if (userentry != null && userentry.hasRight(UserDB.AccessRight.BLOG_RIGHT)) { hasRights=true; } else if (post.containsKey("login")) { diff --git a/htroot/BlogComments.java b/htroot/BlogComments.java index 248c15520..d9137c2e9 100644 --- a/htroot/BlogComments.java +++ b/htroot/BlogComments.java @@ -77,7 +77,7 @@ public class BlogComments { } if (!hasRights) { - final UserDB.Entry userentry = sb.userDB.proxyAuth(header.get(RequestHeader.AUTHORIZATION)); + final UserDB.Entry userentry = sb.userDB.proxyAuth(header); if (userentry != null && userentry.hasRight(UserDB.AccessRight.BLOG_RIGHT)) { hasRights = true; } else if (post.containsKey("login")) { diff --git a/htroot/User.java b/htroot/User.java index 22ebba5dd..f38bb276d 100644 --- a/htroot/User.java +++ b/htroot/User.java @@ -54,7 +54,7 @@ public class User{ prop.put("logged-in_username", ""); prop.put("logged-in_returnto", ""); //identified via HTTPPassword - entry=sb.userDB.proxyAuth(requestHeader.get(RequestHeader.AUTHORIZATION)); + entry=sb.userDB.proxyAuth(requestHeader); if(entry != null){ prop.put("logged-in_identified-by", "1"); //try via cookie diff --git a/htroot/env/templates/header.template b/htroot/env/templates/header.template index 639e7ec2a..1ce6e8d1f 100644 --- a/htroot/env/templates/header.template +++ b/htroot/env/templates/header.template @@ -119,6 +119,9 @@
  • + #(authorized)#:: + + #(/authorized)# - #(authorized)#:: + #(authSearch)#:: - #(/authorized)# + #(/authSearch)# #(searchdomswitches)#::
    @@ -72,7 +72,7 @@ #(searchaudio)#:: Audio  #(/searchaudio)# #(searchvideo)#:: Video  #(/searchvideo)# #(searchapp)#:: Applications#(/searchapp)# - #(searchoptions)#  more options...::#(/searchoptions)# + #(searchoptions)#  more options...::#(/searchoptions)#
    #(/searchdomswitches)# diff --git a/htroot/index.java b/htroot/index.java index 725929037..69fec98bf 100644 --- a/htroot/index.java +++ b/htroot/index.java @@ -31,6 +31,7 @@ import net.yacy.cora.document.analysis.Classification; import net.yacy.cora.document.analysis.Classification.ContentDomain; import net.yacy.cora.protocol.RequestHeader; +import net.yacy.data.UserDB; import net.yacy.search.Switchboard; import net.yacy.search.SwitchboardConstants; import net.yacy.search.schema.CollectionSchema; @@ -57,14 +58,25 @@ public class index { if(adminAuthenticated) { authenticatedUserName = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); + } else { + final UserDB.Entry user = sb.userDB != null ? sb.userDB.getUser(header) : null; + if(user != null) { + authenticatedUserName = user.getUserName(); + } } - - if ((post != null) && (post.containsKey("auth") || post.containsKey("publicPage"))) { - if (!adminAuthenticated) { - prop.authenticationRequired(); - return prop; - } - } + boolean authenticated = adminAuthenticated || authenticatedUserName != null; + if (post != null) { + if (post.containsKey("publicPage") && !adminAuthenticated) { // Old style parameter : still in use ? + prop.authenticationRequired(); + return prop; + } + if (post.containsKey("auth") && !authenticated) { // search with authentication required + prop.authenticationRequired(); + return prop; + } + } + + prop.put("authSearch", authenticated); boolean global = (post == null) ? true : post.get("resource", "global").equals("global"); final boolean focus = (post == null) ? true : post.get("focus", "1").equals("1"); @@ -130,6 +142,7 @@ public class index { prop.putHTML("constraint", constraint); prop.put("searchdomswitches", sb.getConfigBool("search.text", true) || sb.getConfigBool("search.audio", true) || sb.getConfigBool("search.video", true) || sb.getConfigBool("search.image", true) || sb.getConfigBool("search.app", true) ? 1 : 0); prop.put("searchdomswitches_searchoptions", searchoptions); + prop.put("searchdomswitches_searchoptions_authSearch", authenticated); prop.put("searchdomswitches_searchtext", sb.getConfigBool("search.text", true) ? 1 : 0); prop.put("searchdomswitches_searchaudio", sb.getConfigBool("search.audio", true) ? 1 : 0); prop.put("searchdomswitches_searchvideo", sb.getConfigBool("search.video", true) ? 1 : 0); @@ -159,7 +172,7 @@ public class index { * @param authenticatedUserName the name of the currently authenticated user or null */ private static void handleTopNavBarLoginSection(final RequestHeader header, final Switchboard sb, - final serverObjects prop, String authenticatedUserName) { + final serverObjects prop, final String authenticatedUserName) { final boolean showLogin = sb.getConfigBool(SwitchboardConstants.SEARCH_PUBLIC_TOP_NAV_BAR_LOGIN, SwitchboardConstants.SEARCH_PUBLIC_TOP_NAV_BAR_LOGIN_DEFAULT); if(showLogin) { diff --git a/htroot/yacysearch.html b/htroot/yacysearch.html index 47b5c605c..4d8a52ea7 100644 --- a/htroot/yacysearch.html +++ b/htroot/yacysearch.html @@ -109,9 +109,9 @@ Use the RSS search result format to add static searches to your RSS reader, if y #(/resortEnabled)# - #(authorized)#:: + #(authSearch)#:: - #(/authorized)# + #(/authSearch)# diff --git a/htroot/yacysearch.java b/htroot/yacysearch.java index 8f07e8244..5913e7ada 100644 --- a/htroot/yacysearch.java +++ b/htroot/yacysearch.java @@ -137,6 +137,7 @@ public class yacysearch { final servletProperties prop = new servletProperties(); prop.put("topmenu", sb.getConfigBool("publicTopmenu", true) ? 1 : 0); + prop.put("authSearch", authenticatedUserName != null); // produce vocabulary navigation sidebars Collection vocabularies = LibraryProvider.autotagging.getVocabularies(); @@ -206,7 +207,7 @@ public class yacysearch { return prop; } - if (post.containsKey("auth") && !extendedSearchRights) { + if (post.containsKey("auth") && authenticatedUserName == null) { /* * Access to authentication protected features is explicitely requested here * but no authentication is provided : ask now for authentication. @@ -784,7 +785,7 @@ public class yacysearch { RequestHeader.FileType.HTML, 0, theQuery, - suggestion, true, extendedSearchRights).toString()); + suggestion, true, authenticatedUserName != null).toString()); prop.put("didYouMean_suggestions_" + meanCount + "_sep", "|"); meanCount++; } catch (final ConcurrentModificationException e) { @@ -862,7 +863,7 @@ 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, extendedSearchRights) + QueryParams.navurlBase(RequestHeader.FileType.HTML, theQuery, null, true, authenticatedUserName != null) .append("&startRecord=").append(startRecord).append("&resortCachedResults=true") .toString()); diff --git a/htroot/yacysearchitem.html b/htroot/yacysearchitem.html index 5043c0df9..6b8941c64 100644 --- a/htroot/yacysearchitem.html +++ b/htroot/yacysearchitem.html @@ -44,7 +44,7 @@ #(showMetadata)#:: | Metadata#(/showMetadata)# #(showParser)#:: | Parser#(/showParser)# #(showCitation)#:: | Citations#(/showCitation)# - #(showPictures)#:: | Pictures#(/showPictures)# + #(showPictures)#:: | Pictures#(/showPictures)# #(showCache)#:: | Cache#(/showCache)# #(showProxy)#:: | View via proxy#(/showProxy)# #(showHostBrowser)#:: | Browse index#(/showHostBrowser)# diff --git a/htroot/yacysearchitem.java b/htroot/yacysearchitem.java index 9a6ce2dee..5ebc555f0 100644 --- a/htroot/yacysearchitem.java +++ b/htroot/yacysearchitem.java @@ -103,13 +103,12 @@ public class yacysearchitem { 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 boolean authenticated = adminAuthenticated || user != null; + final int item = post.getInt("item", -1); final RequestHeader.FileType fileType = header.fileType(); - if (post.containsKey("auth") && !authenticated) { + if (post.containsKey("auth") && !adminAuthenticated && user == null) { /* * Access to authentication protected features is explicitely requested here * but no authentication is provided : ask now for authentication. @@ -120,7 +119,7 @@ public class yacysearchitem { prop.authenticationRequired(); return prop; } - + // default settings for blank item prop.put("content", "0"); prop.put("rss", "0"); @@ -178,6 +177,7 @@ public class yacysearchitem { prop.putXML("content_title-xml", result.title()); prop.putJSON("content_title-json", result.title()); prop.putHTML("content_showPictures_link", resultUrlstring); + prop.put("content_showPictures_authSearch", authenticated); /* Add information about the current search navigators to let browser refresh yacysearchtrailer only if needed */ prop.put("content_nav-generation", theSearch.getNavGeneration()); diff --git a/htroot/yacysearchtrailer.java b/htroot/yacysearchtrailer.java index 5fcf88dcb..4fce45d54 100644 --- a/htroot/yacysearchtrailer.java +++ b/htroot/yacysearchtrailer.java @@ -72,12 +72,11 @@ public class yacysearchtrailer { 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 boolean authenticated = adminAuthenticated || user != null; if (post.containsKey("auth") && !authenticated) { /* - * Access to authentication protected features is explicitely requested here + * Authenticated search 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. diff --git a/source/net/yacy/data/UserDB.java b/source/net/yacy/data/UserDB.java index 1372c8c07..6906b7fbd 100644 --- a/source/net/yacy/data/UserDB.java +++ b/source/net/yacy/data/UserDB.java @@ -28,11 +28,13 @@ package net.yacy.data; import java.io.File; import java.io.IOException; +import java.security.Principal; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Random; import javax.servlet.http.Cookie; @@ -139,34 +141,51 @@ public final class UserDB { /** * Use a ProxyAuth Value to authenticate user from HttpHeader.Authentication. * This supports only Basic authentication - * @param auth "BASIC " followed by base64 Encoded String, which contains "username:pw" for basic authentication + * @param header the current request HTTP headers including 'Authorization' header : + * either "BASIC " followed by base64 Encoded String, which contains "username:pw" for basic authentication, + * or "Digest" followed by valid user name, realm, nonce and other necessary parameters. + * @return the user entry when the authentication header contains valid authentication information or null */ - public Entry proxyAuth(final String authHeader) { + public Entry proxyAuth(final RequestHeader header) { Entry entry = null; - if (authHeader != null) { - if (authHeader.toUpperCase().startsWith(HttpServletRequest.BASIC_AUTH)) { - String auth = authHeader.substring(6); // take out prefix "BASIC" - final String[] tmp = Base64Order.standardCoder.decodeString(auth.trim()).split(":"); - if (tmp.length == 2) { - entry = this.passwordAuth(tmp[0], tmp[1]); - if (entry == null) { - entry = this.md5Auth(tmp[0], tmp[1]); - } + if(header == null) { + return entry; + } + final String authHeader = header.get(RequestHeader.AUTHORIZATION, "").trim(); + if (authHeader.toUpperCase(Locale.ROOT).startsWith(HttpServletRequest.BASIC_AUTH)) { + String auth = authHeader.substring(6); // take out prefix "BASIC" + final String[] tmp = Base64Order.standardCoder.decodeString(auth.trim()).split(":"); + if (tmp.length == 2) { + entry = this.passwordAuth(tmp[0], tmp[1]); + if (entry == null) { + entry = this.md5Auth(tmp[0], tmp[1]); } } + } else if (authHeader.toUpperCase(Locale.ROOT).startsWith(HttpServletRequest.DIGEST_AUTH)) { + // handle DIGEST auth by servlet container + final Principal authenticatedUser = header.getUserPrincipal(); + if (authenticatedUser != null && authenticatedUser.getName() != null) { // user is authenticated by Servlet container + entry = getEntry(authenticatedUser.getName()); + } } return entry; } + /** + * @param header the HTTP request headers + * @return the authenticated user with valid authentication information found in the headers or null + */ public Entry getUser(final RequestHeader header){ - return getUser(header.get(RequestHeader.AUTHORIZATION), header.getCookies()); + return getUser(header, header.getCookies()); } - public Entry getUser(final String auth, final Cookie[] cookies){ - Entry entry=null; - if(auth != null) { - entry=proxyAuth(auth); - } + /** + * @param header HTTP request headers + * @param cookies eventual client cookies + * @return the authenticated user entry from headers or cookies or null + */ + public Entry getUser(final RequestHeader header, final Cookie[] cookies){ + Entry entry = proxyAuth(header); if(entry == null && cookies != null) { entry=cookieAuth(cookies); } @@ -174,24 +193,25 @@ public final class UserDB { } /** - * Determine if a user has admin rights from a authorisation http-headerfield. + * Determine if a user has admin rights from a 'Authorisation' http header field. * Tests both userDB and old style adminpw. * * @param auth http-headerline for authorisation. * @param cookies */ - public boolean hasAdminRight(final String auth, final Cookie[] cookies) { - final Entry entry = getUser(auth, cookies); + public boolean hasAdminRight(final RequestHeader header, final Cookie[] cookies) { + final Entry entry = getUser(header, cookies); return (entry != null) ? entry.hasRight(AccessRight.ADMIN_RIGHT) : false; } /** - * Use ProxyAuth String to authenticate user and save IP/username for ipAuth. - * @param auth base64 Encoded String, which contains "username:pw". + * Use HTTP headers to authenticate user and save IP/username for ipAuth. + * @param header HTTP request headers including 'Authorization' header with either base64 Encoded String, which contains "username:pw", + * or Digest authentication information including notably user name, realm, and nonce. * @param ip IP address. */ - public Entry proxyAuth(final String auth, final String ip) { - final Entry entry = proxyAuth(auth); + public Entry proxyAuth(final RequestHeader header, final String ip) { + final Entry entry = proxyAuth(header); if (entry != null) { entry.updateLastAccess(false); this.ipUsers.put(ip, entry.getUserName()); diff --git a/source/net/yacy/search/Switchboard.java b/source/net/yacy/search/Switchboard.java index ad635b870..05a85ec35 100644 --- a/source/net/yacy/search/Switchboard.java +++ b/source/net/yacy/search/Switchboard.java @@ -3789,7 +3789,7 @@ public final class Switchboard extends serverSwitch { } // authorization by hit in userDB (authtype username:encodedpassword - handed over by DefaultServlet) - if ( this.userDB.hasAdminRight(realmProp, requestHeader.getCookies()) ) { + if ( this.userDB.hasAdminRight(requestHeader, requestHeader.getCookies()) ) { adminAuthenticationLastAccess = System.currentTimeMillis(); return 4; //return, because 4=max }