Merge remote-tracking branch 'origin/master' into new-index

Conflicts:
	src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionRewrite.java
	src/main/java/de/ids_mannheim/korap/security/auth/KustvaktAuthenticationManager.java
	src/main/java/de/ids_mannheim/korap/user/User.java
	src/main/java/de/ids_mannheim/korap/web/service/full/ResourceService.java

Change-Id: I2067b58c279c98915beb0a85462a6f3ea5744f36
diff --git a/src/main/java/de/ids_mannheim/korap/config/Attributes.java b/src/main/java/de/ids_mannheim/korap/config/Attributes.java
index 1789e61..07fc6ab 100644
--- a/src/main/java/de/ids_mannheim/korap/config/Attributes.java
+++ b/src/main/java/de/ids_mannheim/korap/config/Attributes.java
@@ -10,6 +10,9 @@
     public static final String OPENID_AUTHENTICATION = "id_token";
     public static final String BASIC_AUTHENTICATION = "basic";
 
+    public static final String LOCATION = "location"; // location of Client: User.INTERN/EXTERN
+    public static final String CORPUS_ACCESS = "corpusAccess"; // User.ALL/PUB/FREE.
+    
     public static final String CLIENT_ID = "client_id";
     public static final String CLIENT_SECRET = "client_secret";
     public static final String SCOPES = "scopes";
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java b/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
index 86d6d60..ac16556 100644
--- a/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
+++ b/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
@@ -56,53 +56,50 @@
      * getAccount(): KorAPUser:  Account muss vorhanden sein.
      *               DemoUser, DefaultUser: Account nicht vorhanden.
      */
-//    @Override
-//    public User getAccount (String username) throws KustvaktException {
-//    	
-//    	Map<String, String> namedParameters = Collections.singletonMap("username", username);
-//        final String sql = "select a.* from korap_users as a where a.username=:username;";
-//        User user = null;
-//        
-//        try {
-//            user = this.jdbcTemplate.queryForObject(sql, namedParameters,
-//                    new RowMapperFactory.UserMapper());
-//            return user;
-//        	}
-//        catch (EmptyResultDataAccessException ae) {
-//            jlog.warn("No user found for name '{}'", username);
-//            // if no username, so return a DemoUser , FB.
-//            // return User.UserFactory.getDemoUser();
-//            	return UserFactory.getDemoUser();
-//        	}
-//        catch (DataAccessException e) {
-//            jlog.warn("Could not retrieve user for name: " + username, e);
-//            
-//            /* DemoUser and DefaultUser have no account,
-//             * so it's ok they are not found.
-//            throw new DatabaseException(username, "korap_users",
-//                    StatusCodes.DB_GET_FAILED,
-//                    "Could not retrieve the user with username: " + username,
-//                    username);
-//             */
-//            }
-//
-//        // DemoUser or DefaultUser?
-//        
-//        {	// User cannot be found in SQL DB since LDAP Authentication,
-//        	// so create a User Object here.
-//        	// TODO: what more data should be stored into it?
-//        	// 28.04.17/FB
-//        	user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
-//        	user.setUsername(username);
-//        	return user;
-//        }
-//        
-//        
-//    }
+    @Override
+    public User getAccount (String username) throws KustvaktException {
+    	
+    	Map<String, String> namedParameters = Collections.singletonMap("username", username);
+        final String sql = "select a.* from korap_users as a where a.username=:username;";
+        User user = null;
+        
+        try {
+            user = this.jdbcTemplate.queryForObject(sql, namedParameters,
+                    new RowMapperFactory.UserMapper());
+            return user;
+        	}
+        catch (EmptyResultDataAccessException ae) {
+            jlog.warn("No user found for name '{}'", username);
+        	}
+        catch (DataAccessException e) {
+            jlog.warn("Could not retrieve user for name: " + username, e);
+            
+            /* DemoUser and DefaultUser have no account,
+             * so it's ok they are not found.
+            throw new DatabaseException(username, "korap_users",
+                    StatusCodes.DB_GET_FAILED,
+                    "Could not retrieve the user with username: " + username,
+                    username);
+             */
+            }
+
+        // DemoUser or DefaultUser?
+        
+        {	// User cannot be found in SQL DB since LDAP Authentication,
+        	// so create a User Object here.
+        	// TODO: what more data should be stored into it?
+        	// 28.04.17/FB
+        	user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
+        	user.setUsername(username);
+        	return user;
+        }
+        
+        
+    } // getAccount()
 
     // usersettings are fetched plus basic account info, no details, since i rarely use them anyway! (by Hanl)
     /* Version before LDAP Authentication - 09.05.17/FB
-    @Deprecated */
+    @Deprecated
     @Override
     public User getAccount (String username) throws KustvaktException {
     	
@@ -129,7 +126,7 @@
         }
         return user;
     }
-	
+	*/
 
     @Override
     public int updateAccount (User user) throws KustvaktException {
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
index e110a5b..f7bc10d 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
@@ -1,5 +1,6 @@
 package de.ids_mannheim.korap.interfaces;
 
+import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.KustvaktCacheable;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.user.TokenContext;
@@ -10,6 +11,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import javax.ws.rs.core.HttpHeaders;
+
 /**
  * @author hanl
  * @date 15/06/2015
@@ -68,6 +71,7 @@
             Map<String, Object> attr, String provider_key)
             throws KustvaktException;
 
+    public abstract void setAccessAndLocation(User user, HttpHeaders headers);
 
     public abstract void logout (TokenContext context) throws KustvaktException;
 
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionRewrite.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionRewrite.java
index e4c0022..6fcae2f 100644
--- a/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionRewrite.java
@@ -33,10 +33,9 @@
 			if (subnode.has("collection")) {
 				builder.setBaseQuery(JsonUtils.toJSON(subnode));
 			}
-			// EM 
-			// fix me: later store the collection queries as KoralQuery in the database
+
 			switch (user.getCorpusAccess()) {
-			case PUBLIC:
+			case PUB:
 				builder = new KoralCollectionQueryBuilder();
 				builder.with("availability = /CC-BY.*/ | availablity = /ACA.*/");
 				break;
diff --git a/src/main/java/de/ids_mannheim/korap/security/auth/KustvaktAuthenticationManager.java b/src/main/java/de/ids_mannheim/korap/security/auth/KustvaktAuthenticationManager.java
index 43440f3..b083a9b 100644
--- a/src/main/java/de/ids_mannheim/korap/security/auth/KustvaktAuthenticationManager.java
+++ b/src/main/java/de/ids_mannheim/korap/security/auth/KustvaktAuthenticationManager.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap.security.auth;
 
 import com.sun.org.apache.xpath.internal.SourceTree;
+
 import de.ids_mannheim.korap.auditing.AuditRecord;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.BeansFactory;
@@ -17,23 +18,37 @@
 import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
 import de.ids_mannheim.korap.interfaces.defaults.ApacheValidator;
 import de.ids_mannheim.korap.user.*;
+import de.ids_mannheim.korap.user.User.Location;
 import de.ids_mannheim.korap.user.User.CorpusAccess;
 import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
 import de.ids_mannheim.korap.security.auth.LdapAuth3;
+
+import javax.ws.rs.core.HttpHeaders;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+
 // import com.novell.ldap.*; search() funktioniert nicht korrekt, ausgewechselt gegen unboundID's Bibliothek 20.04.17/FB
 //Using JAR from unboundID:
 import com.unboundid.ldap.sdk.LDAPException;
 
 
+
+
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
+import javax.ws.rs.core.MultivaluedMap;
+
 /**
  * contains the logic to authentication and registration processes.
  * Uses
@@ -190,7 +205,60 @@
         return user;
     }
 
-
+    // a. set location depending on X-Forwarded-For.
+    // X-Forwarded-For: clientIP, ProxyID, ProxyID...
+    // the following private address spaces may be used to define intranet spaces:
+    // 10.0.0.0        -   10.255.255.255  (10/8 prefix)
+    // 172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
+    // 192.168.0.0     -   192.168.255.255 (192.168/16 prefix)
+    // b. set corpusAccess depending on location:
+    // 16.05.17/FB
+    
+    @Override
+    public void setAccessAndLocation(User user, HttpHeaders headers)
+    
+    {
+    Boolean DEBUG_LOG = true;
+    MultivaluedMap<String,String> headerMap = headers.getRequestHeaders();
+    Location location = Location.INTERN;
+    CorpusAccess corpusAccess = CorpusAccess.FREE;
+    
+    if( headerMap != null && headerMap.size() > 0 )
+    	{
+    	Iterator<String> it = headerMap.keySet().iterator();
+    	while( it.hasNext() )
+    		{
+    		String key = (String)it.next();
+    		if( key.equals("X-Forwarded-For"))
+        		{
+        		List<String> vals = new ArrayList<String>(Arrays.asList(headerMap.getFirst(key).split(",")));
+        		String clientAddress = vals.get(0);
+        		
+        		if( clientAddress.startsWith("10.0.") || clientAddress.startsWith("172.16.") || clientAddress.startsWith("192.168."))
+        			location = Location.INTERN;
+        		else
+        			location = Location.EXTERN;
+        		if( location == Location.EXTERN )
+        			corpusAccess = CorpusAccess.PUB;
+        		else
+        			corpusAccess = CorpusAccess.ALL;
+        		
+        		if( DEBUG_LOG == true )
+        			{
+	        		System.out.printf("Debug: X-Forwarded-For : '%s' (%d values) -> %s\n", vals, vals.size(), vals.get(0));
+	        		System.out.printf("Debug: X-Forwarded-For : location = %s corpusAccess = %s\n", 
+	        				location == Location.INTERN ? "INTERN" : "EXTERN",
+	        				corpusAccess == CorpusAccess.ALL ? "ALL" : corpusAccess == CorpusAccess.PUB ? "PUB" : "FREE"); 
+	        		}
+        		}	
+    		}
+    	}
+    
+    user.setLocation(location);
+    user.setCorpusAccess(corpusAccess);
+        
+    } // getAccess
+    
     @Override
     public TokenContext createTokenContext (User user,
             Map<String, Object> attr, String provider_key)
@@ -417,6 +485,14 @@
         //       DefaultUser sonst.
         User user = new KorAPUser();
         user.setUsername(username);
+        /* folgender Code funktioniert hier noch nicht, da die Headers noch nicht ausgewertet
+         * worden sind - 23.05.17/FB
+        Object o = attr.get(Attributes.LOCATION);
+        String loc = (String)o.toString();
+        int location = Integer.parseInt(loc);
+        user.setLocation(location); 
+        user.setCorpusAccess(Integer.parseInt(attr.get(Attributes.CORPUS_ACCESS).toString()));
+         */
         unknown = user;
         
         jlog.trace("Authentication: found username " + unknown.getUsername());
diff --git a/src/main/java/de/ids_mannheim/korap/user/User.java b/src/main/java/de/ids_mannheim/korap/user/User.java
index 68e59ac..5846b73 100644
--- a/src/main/java/de/ids_mannheim/korap/user/User.java
+++ b/src/main/java/de/ids_mannheim/korap/user/User.java
@@ -45,15 +45,28 @@
     private List<Userdata> userdata;
 
     private boolean isAdmin;
-    
-    private CorpusAccess corpusAccess = CorpusAccess.FREE;
-    
+
+    // Values for corpusAccess:
     public enum CorpusAccess	 {
-        FREE, // without login   
-        PUBLIC, // extern
-        ALL; // intern
-    }
-   
+    	FREE, 	// Access to licence free corpora only, without login   
+        PUB,	// Access to public (= öffentliche Korpora) only, externes Login.
+        ALL 	// Access to all corpora, internes Login.
+    	};
+    	
+    @Getter
+    @Setter
+    private CorpusAccess corpusAccess = CorpusAccess.FREE;
+        
+    // values for location (set using the X-forwarded-for Header):
+    public enum Location  {
+        INTERN, 	// KorAP accessed by internal Client (inside intranet).
+        EXTERN		// KorAP accessed by external Client (outside intranet).
+    };
+        
+    @Getter
+    @Setter
+    private Location location = Location.EXTERN;
+
     
     protected User () {
         this.fields = new ParamFields();
@@ -62,7 +75,8 @@
         this.username = "";
         this.id = -1;
         this.userdata = new ArrayList<>();
-        this.corpusAccess = CorpusAccess.FREE;
+        this.location 		= Location.EXTERN;
+        this.corpusAccess 	= CorpusAccess.FREE;
     }
 
 
diff --git a/src/main/java/de/ids_mannheim/korap/web/service/full/AuthService.java b/src/main/java/de/ids_mannheim/korap/web/service/full/AuthService.java
index ee63565..c2fa34d 100644
--- a/src/main/java/de/ids_mannheim/korap/web/service/full/AuthService.java
+++ b/src/main/java/de/ids_mannheim/korap/web/service/full/AuthService.java
@@ -26,10 +26,12 @@
 import javax.xml.ws.handler.MessageContext; // FB
 import javax.annotation.Resource; // FB
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.ArrayList;
 import java.util.Iterator; // 07.02.17/FB
 
 //import com.sun.xml.internal.messaging.saaj.util.Base64;
@@ -43,7 +45,7 @@
 @Produces(MediaType.TEXT_HTML + ";charset=utf-8")
 public class AuthService {
 
-	private static Boolean DEBUG_LOG = false;
+	private static Boolean DEBUG_LOG = true;
 	
     //todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
     // default usersettings, etc.)
@@ -148,7 +150,7 @@
 	        }
 	        System.out.printf("Debug: requestAPIToken: isSecure = %s.\n", secCtx.isSecure() ? "yes" : "no");
 	        } // DEBUG_LOG        
-
+        
         // "Invalid syntax for username and password"
         if (values == null)
             throw KustvaktResponseHandler
@@ -164,6 +166,7 @@
             attr.put(Attributes.SCOPES, scopes);
         attr.put(Attributes.HOST, host);
         attr.put(Attributes.USER_AGENT, agent);
+        
         TokenContext context;
         try {
             // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
@@ -171,6 +174,10 @@
             // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
             // todo: is this necessary?
             //            attr.putAll(data.fields());
+            controller.setAccessAndLocation(user, headers);
+            
+            attr.put(Attributes.LOCATION, user.getLocation());
+            attr.put(Attributes.CORPUS_ACCESS,  user.getCorpusAccess());
             context = controller.createTokenContext(user, attr, Attributes.API_AUTHENTICATION);
         }
         catch (KustvaktException e) {
diff --git a/src/main/java/de/ids_mannheim/korap/web/service/full/ResourceService.java b/src/main/java/de/ids_mannheim/korap/web/service/full/ResourceService.java
index 3d9e186..c877b04 100644
--- a/src/main/java/de/ids_mannheim/korap/web/service/full/ResourceService.java
+++ b/src/main/java/de/ids_mannheim/korap/web/service/full/ResourceService.java
@@ -17,6 +17,7 @@
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -468,11 +469,14 @@
     }
 
 
+    // was heißt search by name all? FB
     @SuppressWarnings("unchecked")
     @GET
     @Path("search")
-    public Response searchbyNameAll (@Context SecurityContext securityContext,
-            @Context Locale locale, @QueryParam("q") String q,
+    public Response searchbyNameAll (
+    		@Context SecurityContext securityContext,
+    		@Context HttpHeaders headers,
+    		@Context Locale locale, @QueryParam("q") String q,
             @QueryParam("ql") String ql, @QueryParam("v") String v,
             @QueryParam("context") String ctx,
             @QueryParam("cutoff") Boolean cutoff,
@@ -487,8 +491,8 @@
         User user;
         try {
             user = controller.getUser(context.getUsername());
-            // EM: set user.corpusAccess, default is CorpusAccess.FREE
-        }
+            controller.setAccessAndLocation(user, headers);
+        	}
         catch (KustvaktException e) {
             jlog.error("Failed retrieving user in the search service: {}",
                     e.string());
@@ -1102,6 +1106,7 @@
 
     // EM: legacy support
     // should be deprecated after a while
+    /*
     @GET
     @Path("/corpus/{corpusId}/{docId}/{matchId}/matchInfo")
     public Response getMatchInfo (@Context SecurityContext ctx,
@@ -1122,12 +1127,16 @@
     	return getMatchInfo(ctx, locale, corpusId, ids[0], ids[1], matchId, foundries, layers, spans);
     	
     }
+    */
     
     // fixme: only allowed for corpus?!
     @GET
     @Path("/corpus/{corpusId}/{docId}/{textId}/{matchId}/matchInfo")
-    public Response getMatchInfo (@Context SecurityContext ctx,
-            @Context Locale locale, @PathParam("corpusId") String corpusId,
+    public Response getMatchInfo (
+    		@Context SecurityContext ctx,
+    		@Context HttpHeaders headers,
+            @Context Locale locale, 
+            @PathParam("corpusId") String corpusId,
             @PathParam("docId") String docId,
             @PathParam("textId") String textId,
             @PathParam("matchId") String matchId,
@@ -1138,31 +1147,31 @@
         TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
         spans = spans != null ? spans : false;
 
-        String matchid = searchKrill.getMatchId(corpusId, docId, textId,
-                matchId);
+        String matchid = searchKrill.getMatchId(corpusId, docId, textId, matchId);
         if (layers == null || layers.isEmpty())
             layers = new HashSet<>();
 
         boolean match_only = foundries == null || foundries.isEmpty();
 
-//        User user;
-//        try {
-//            user = controller.getUser(tokenContext.getUsername());
-//        }
-//        catch (KustvaktException e) {
-//            jlog.error("Failed getting user in the matchInfo service: {}",
-//                    e.string());
-//            throw KustvaktResponseHandler.throwit(e);
-//        }
-//        if (user instanceof DemoUser){
-//	        try {
-//	            ResourceFinder.searchPublicFiltered(Corpus.class, corpusId);
-//	        }
-//	        catch (KustvaktException e) {
-//	            throw KustvaktResponseHandler.throwit(e);
-//	        }
-//        }
-//        
+        User user;
+        try {
+            user = controller.getUser(tokenContext.getUsername());
+            controller.setAccessAndLocation(user, headers);
+            System.out.println("Debug: getMatchInfo: setting Access & Location: done.");
+            }
+        catch (KustvaktException e) {
+            jlog.error("Failed getting user in the matchInfo service: {}",
+                    e.string());
+            throw KustvaktResponseHandler.throwit(e);
+        }
+        if (user instanceof DemoUser){
+	        try {
+	            ResourceFinder.searchPublicFiltered(Corpus.class, corpusId);
+	        }
+	        catch (KustvaktException e) {
+	            throw KustvaktResponseHandler.throwit(e);
+	        }
+        }
         String results;
 //        // fixme: checks for policy matching
 //        // fixme: currently disabled, due to mishab in foundry/layer spec
@@ -1223,7 +1232,7 @@
         CorpusAccess corpusAccess = CorpusAccess.FREE;
         Pattern p;
         switch (corpusAccess) {
-		case PUBLIC:
+		case PUB:
 			p = config.getPublicLicensePattern();
 			break;
 		case ALL:
@@ -1263,7 +1272,6 @@
                     e.getMessage(), "");
         }
         jlog.debug("MatchInfo results: "+results);
-        
         return Response.ok(results).build();
     }