Merge branch 'master' of ssh://korap.ids-mannheim.de:29418/private/Kustvakt-core
LDAP Authentication: finished.
diff --git a/.gitignore b/.gitignore
index 213a85f..a5cd534 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@
 *.iml
 dependency-reduced-pom.xml
 admin_token
+/bin/
diff --git a/pom.xml b/pom.xml
index 4594c94..ebbc721 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -151,7 +150,7 @@
             </plugin>
 
             <!-- build tests jar, so extensions can use fastjerseytest class to build rest tests -->
-            <plugin>
+            <!-- >plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <version>2.1</version>
@@ -163,7 +162,7 @@
                         </goals>
                     </execution>
                 </executions>
-            </plugin>
+            </plugin -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
@@ -470,6 +469,17 @@
             <artifactId>asm</artifactId>
             <version>3.3.1</version>
         </dependency>
+        <dependency>
+          <groupId>com.novell.ldap</groupId>
+          <artifactId>jldap</artifactId>
+          <version>4.3</version>
+        </dependency>
+	   <!-- https://mvnrepository.com/artifact/com.unboundid/unboundid-ldapsdk -->
+	   <dependency>
+	      <groupId>com.unboundid</groupId>
+	      <artifactId>unboundid-ldapsdk</artifactId>
+	      <version>3.2.1</version>
+	   </dependency>
 
         <!--
         not part of public release
diff --git a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index 66c89b5..b30a915 100644
--- a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -27,6 +27,13 @@
     public static final int NOT_SUPPORTED = 108;
 
     /**
+     * 300 status codes for query language and serialization
+     */
+
+    public static final int NO_QUERY = 301;
+
+
+    /**
      *  400 status codes for authorization and rewrite functions
      */
 
@@ -72,15 +79,18 @@
     public static final int ARGUMENT_VALIDATION_FAILURE = 700;
     // public static final int ARGUMENT_VALIDATION_FAILURE = 701;
 
+    // service status codes
+    public static final int CREATE_ACCOUNT_SUCCESSFUL = 700;
+    public static final int CREATE_ACCOUNT_FAILED = 701;
+    public static final int DELETE_ACCOUNT_SUCCESSFUL = 702;
+    public static final int DELETE_ACCOUNT_FAILED = 703;
+    public static final int UPDATE_ACCOUNT_SUCCESSFUL = 704;
+    public static final int UPDATE_ACCOUNT_FAILED = 705;
 
-    /**
-     * 300 status codes for query language and serialization
-     */
-
-    public static final int NO_QUERY = 301;
-
-
-
+    public static final int GET_ACCOUNT_SUCCESSFUL = 706;
+    public static final int GET_ACCOUNT_FAILED = 707;
+    
+    
     public static final int STATUS_OK = 1000;
     public static final int NOTHING_CHANGED = 1001;
     public static final int REQUEST_INVALID = 1002;
@@ -90,7 +100,6 @@
      * 2000 status and error codes for service level messages and callbacks
      */
 
-
     public static final int ACCOUNT_DEACTIVATED = 2000;
     public static final int ACCOUNT_CONFIRMATION_FAILED = 2001;
     public static final int ALREADY_LOGGED_IN = 2002;
@@ -110,20 +119,10 @@
     public static final int CLIENT_REMOVAL_FAILURE = 2012;
     public static final int CLIENT_AUTHORIZATION_FAILURE = 2013;
 
-
-
-    // service status codes
-    public static final int CREATE_ACCOUNT_SUCCESSFUL = 700;
-    public static final int CREATE_ACCOUNT_FAILED = 701;
-    public static final int DELETE_ACCOUNT_SUCCESSFUL = 702;
-    public static final int DELETE_ACCOUNT_FAILED = 703;
-    public static final int UPDATE_ACCOUNT_SUCCESSFUL = 704;
-    public static final int UPDATE_ACCOUNT_FAILED = 705;
-
-    public static final int GET_ACCOUNT_SUCCESSFUL = 706;
-    public static final int GET_ACCOUNT_FAILED = 707;
-
-
+    // 2020 - 2029 reserviert für LDAP-Fehlercodes - 21.04.17/FB
+    public static final int LDAP_BASE_ERRCODE = 2020;
+    
+    /**/
     private static StatusCodes codes;
 
     private final Properties props;
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 b608834..8829064 100644
--- a/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
+++ b/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
@@ -30,6 +30,11 @@
 import java.util.List;
 import java.util.Map;
 
+/* WKP: In computer software, a data access object (DAO) is an object that provides an abstract interface to some type 
+ * of database or other persistence mechanism. By mapping application calls to the persistence layer, the DAO provides 
+ * some specific data operations without exposing details of the database. 
+ */
+
 /**
  * @author hanl
  * @date 13/01/2014
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java b/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java
index 8ddeb33..28e848b 100644
--- a/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java
+++ b/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java
@@ -35,8 +35,8 @@
     }
 
 
-    public void authorize (AuthCodeInfo info, User user)
-            throws KustvaktException {
+    public void authorize (AuthCodeInfo info, User user)  throws KustvaktException {
+    	
         info.setUserId(user.getId());
         this.storeInCache(info.getCode(), info);
     }
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java b/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java
index 043b322..711e6d8 100644
--- a/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java
+++ b/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java
@@ -171,8 +171,7 @@
                 @Override
                 public ClientInfo mapRow (ResultSet rs, int rowNum)
                         throws SQLException {
-                    ClientInfo info = new ClientInfo(rs.getString("client_id"),
-                            "*****");
+                    ClientInfo info = new ClientInfo(rs.getString("client_id"), "*****");
                     info.setConfidential(rs.getBoolean("is_confidential"));
                     info.setUrl(rs.getString("url"));
                     info.setId(rs.getInt("id"));
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 c959e5e..483f292 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
@@ -19,8 +19,13 @@
 import de.ids_mannheim.korap.user.*;
 import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
+import de.ids_mannheim.korap.security.auth.LdapAuth3;
 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;
@@ -171,6 +176,10 @@
                 // todo:
                 user = authenticateShib(attributes);
                 break;
+            case 2:
+                // IdM/LDAP: (09.02.17/FB)
+                user = authenticateIdM(username, password, attributes);
+                break;
             default:
                 user = authenticate(username, password, attributes);
                 break;
@@ -232,8 +241,8 @@
 
 
     //todo: what if attributes null?
-    private User authenticate (String username, String password,
-            Map<String, Object> attr) throws KustvaktException {
+    private User authenticate (String username, String password, Map<String, Object> attr) throws KustvaktException {
+    	
         Map<String, Object> attributes = validator.validateMap(attr);
         User unknown;
         // just to make sure that the plain password does not appear anywhere in the logs!
@@ -250,7 +259,8 @@
         else {
             try {
                 unknown = entHandler.getAccount(username);
-            } catch (EmptyResultException e) {
+            } 
+            catch (EmptyResultException e) {
                 // mask exception to disable user guessing in possible attacks
                 throw new WrappedException(new KustvaktException(username,
                         StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
@@ -285,7 +295,7 @@
                         username);
             }
 
-            // bad credentials error has presedence over account locked or unconfirmed codes
+            // bad credentials error has precedence over account locked or unconfirmed codes
             // since latter can lead to account guessing of third parties
             if (user.isAccountLocked()) {
                 URIParam param = (URIParam) user.getField(URIParam.class);
@@ -324,6 +334,152 @@
     }
 
 
+    /**
+     * authenticate using IdM (Identitätsmanagement) accessed by LDAP.
+     * @param username
+     * @param password
+     * @param attr
+     * @return
+     * @throws KustvaktException
+     * @date 09.02.17/FB
+     */
+    //todo: what if attributes null?
+    
+    private User authenticateIdM (String username, String password, Map<String, Object> attr) throws KustvaktException {
+    	
+        Map<String, Object> attributes = validator.validateMap(attr);
+        User unknown = null;
+        // just to make sure that the plain password does not appear anywhere in the logs!
+
+        System.out.printf("Debug: authenticateIdM: entering for '%s'...\n", username);
+        
+        /** wozu Apache Validatoren für User/Passwort für IdM/LDAP?
+         * siehe validation.properties. Abgeschaltet 21.04.17/FB
+        try { 
+            validator.validateEntry(username, Attributes.USERNAME);
+        	} 
+        catch (KustvaktException e) {
+            throw new WrappedException(e, StatusCodes.LOGIN_FAILED, username);
+        }
+		*/
+        if (username == null || username.isEmpty() || password == null || password.isEmpty() )
+            throw new WrappedException(new KustvaktException(username,
+                    StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED);
+
+        // LDAP Access:
+        try {
+        	// todo: unknown = ...
+        	int ret = LdapAuth3.login(username, password);
+        	System.out.printf("Debug: autenticationIdM: Ldap.login(%s) returns: %d.\n", username, ret); 
+        	if( ret != LdapAuth3.LDAP_AUTH_ROK )
+        		{
+        		jlog.error("LdapAuth3.login(username='{}') returns '{}'='{}'!", username, ret,
+        				LdapAuth3.getErrMessage(ret));
+        		
+               	// mask exception to disable user guessing in possible attacks
+        		/* by Hanl
+                throw new WrappedException(new KustvaktException(username,
+                            StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
+                            username);        		 
+        		 */
+                throw new WrappedException(
+                			new KustvaktException(
+                					username,
+                					StatusCodes.LDAP_BASE_ERRCODE+ret,
+                					LdapAuth3.getErrMessage(ret),
+                					null), 
+        					StatusCodes.LOGIN_FAILED,
+                            username);
+        		}
+            }
+        catch ( LDAPException e ) {
+        	
+        	jlog.error("Error: username='{}' -> '{}'!", username, e);
+        	// mask exception to disable user guessing in possible attacks
+        	/* by Hanl:
+            throw new WrappedException(new KustvaktException(username,
+                        StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
+                        username);
+            */
+            throw new WrappedException(
+            		new KustvaktException(
+            				username,
+            				StatusCodes.LDAP_BASE_ERRCODE+LdapAuth3.LDAP_AUTH_RINTERR,
+            				LdapAuth3.getErrMessage(LdapAuth3.LDAP_AUTH_RINTERR),
+            				null), 
+    				StatusCodes.LOGIN_FAILED,
+                    username);
+            }
+
+        // Create a User
+        User user = new KorAPUser();
+        user.setUsername(username);
+        unknown = user;
+        
+        jlog.trace("Authentication: found username " + unknown.getUsername());
+        
+        if (unknown instanceof KorAPUser) {
+            /* password already checked using LDAP:
+            if (password == null || password.isEmpty())
+                throw new WrappedException(new KustvaktException(
+                        unknown.getId(), StatusCodes.BAD_CREDENTIALS),
+                        StatusCodes.LOGIN_FAILED, username);
+
+            KorAPUser user = (KorAPUser) unknown;
+            boolean check = crypto.checkHash(password, user.getPassword());
+
+            if (!check) {
+                // the fail counter only applies for wrong password
+                jlog.warn("Wrong Password!");
+                processLoginFail(unknown);
+                throw new WrappedException(new KustvaktException(user.getId(),
+                        StatusCodes.BAD_CREDENTIALS), StatusCodes.LOGIN_FAILED,
+                        username);
+            }
+            */
+            // bad credentials error has precedence over account locked or unconfirmed codes
+            // since latter can lead to account guessing of third parties
+            /*
+            if (user.isAccountLocked()) {
+            
+                URIParam param = (URIParam) user.getField(URIParam.class);
+
+                if (param.hasValues()) {
+                    jlog.debug("Account is not yet activated for user '{}'",
+                            user.getUsername());
+                    if (TimeUtils.getNow().isAfter(param.getUriExpiration())) {
+                        jlog.error(
+                                "URI token is expired. Deleting account for user {}",
+                                user.getUsername());
+                        deleteAccount(user);
+                        throw new WrappedException(new KustvaktException(
+                                unknown.getId(), StatusCodes.EXPIRED,
+                                "account confirmation uri has expired!",
+                                param.getUriFragment()),
+                                StatusCodes.LOGIN_FAILED, username);
+                    }
+                    throw new WrappedException(new KustvaktException(
+                            unknown.getId(), StatusCodes.ACCOUNT_NOT_CONFIRMED),
+                            StatusCodes.LOGIN_FAILED, username);
+                }
+                jlog.error("ACCESS DENIED: account not active for '{}'",
+                        unknown.getUsername());
+                throw new WrappedException(new KustvaktException(
+                        unknown.getId(), StatusCodes.ACCOUNT_DEACTIVATED),
+                        StatusCodes.LOGIN_FAILED, username);
+            }
+             */
+
+        }
+        else if (unknown instanceof ShibUser) {
+            //todo
+        }
+        
+        jlog.debug("Authentication done: " + username);
+        return unknown;
+        
+    } // authenticateIdM
+
     public boolean isRegistered (String username) {
         User user;
         if (username == null || username.isEmpty())
diff --git a/src/main/java/de/ids_mannheim/korap/security/auth/LdapAuth3.java b/src/main/java/de/ids_mannheim/korap/security/auth/LdapAuth3.java
new file mode 100644
index 0000000..c7f69fd
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/security/auth/LdapAuth3.java
@@ -0,0 +1,355 @@
+/* - Klasse zum Implementieren einer Benutzer-Authentifikation mittels LDAP
+ *   in der IDM-Datenbank (Identit�tsmanagement) von Eric Seubert, IDS.
+ * - externe Bibliothek ist Novel JLDAP.
+ * 27.01.17/FB
+ *
+ * Sourcen:
+ * - https://www.novell.com/documentation/developer/samplecode/jldap_sample/VerifyPassword.java.html
+ * - https://www.novell.com/documentation/developer/samplecode/jldap_sample/LDAPOIDs.java.html
+ * - https://www.novell.com/documentation/developer/jldap/jldapenu/data/a90352e.html
+ * WICHTIG:
+ * - Novell-Bibliothek liefert 0 Treffer, wenn man nacheinander sucht!
+ *   Grund daf�r nicht gefunden.
+ *
+ * Version von unboundID - 19.04.17/FB
+ *
+ * UnboundID LDAP SDK For Java � 3.2.1
+ * The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for 
+ * communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, 
+ * encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package 
+ * contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for 
+ * communicating with LDAPv3 directory servers. 
+ * TODO:
+ * - gesichertes Login mit gesch�tztem Passwort.
+ * - Passwort des Admin verschl�sseln.
+ */
+ 
+package de.ids_mannheim.korap.security.auth;
+
+import com.unboundid.ldap.sdk.*;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * LDAP Login Tests
+ *
+ */
+public class LdapAuth3
+
+{
+  /* For SSL Connection to LDAP, see: https://www.novell.com/documentation/developer/jldap/jldapenu/data/cchcbejj.html.
+	* and use DEFAULT_SSL_PORT.
+   * For now, plain text connection is used.
+	* FB
+	*/
+	final static Boolean DEBUGLOG = false; // log debug output.
+	final static String attC2 		= "idsC2";		// if value == TRUE: registered for COSMAS II (KorAP) Service.
+	final static String attStatus	= "idsStatus";	// value must be 0..2, 3 = locked account.
+	final static int ldapPort 		= 389; //LDAPConnection.DEFAULT_PORT;
+	// final static int ldapVersion 	= LDAPConnection.LDAP_V3;
+	final static String ldapHost 	= "ldap.ids-mannheim.de";
+	final static String ldapBase	= "dc=ids-mannheim,dc=de";
+	final static String sLoginDN 	= "cn=casaling,dc=ids-mannheim,dc=de";
+	static String sPwd 				= null;
+
+	/**
+	 * return   : 0 = OK, User Account + Pwd are OK, no restrictions;
+	 *            1 = internal error: cannot verify User+Pwd;
+	 *            2 = User Account or Pwd unknown;
+	 *            3 = User Account locked;
+	 *            4 = User known, but has not registered to KorAP/C2 Service yet;
+	 */
+
+	public static final int LDAP_AUTH_ROK		= 0; 
+	public static final int LDAP_AUTH_RINTERR	= 1;
+	public static final int LDAP_AUTH_RUNKNOWN	= 2;
+	public static final int LDAP_AUTH_RLOCKED	= 3;
+	public static final int LDAP_AUTH_RNOTREG	= 4;
+
+	/**
+	 * getErrMessage:
+	 * returns String Message for LDAP_AUTH_Rxxx code.
+	 * @date 20.04.17/FB
+	 * @param code
+	 * @return Message in string form.
+	 */
+	static String getErrMessage(int code)
+	
+	{
+	switch(code)
+		{
+	case LDAP_AUTH_ROK: 
+		return "LDAP Authentication successfull.";
+	case LDAP_AUTH_RINTERR: 
+		return "LDAP Authentication failed due to an internal error!";
+	case LDAP_AUTH_RUNKNOWN:
+		return "LDAP Authentication failed due to unknown user or password!";
+	case LDAP_AUTH_RLOCKED:
+		return "LDAP Authentication: known user is locked!";
+	case LDAP_AUTH_RNOTREG:
+		return "LDAP Authentication: known user has not registered yet for COSMAS II/KorAP!";
+	default:
+		return "LDAP Authentication failed with unknown error code!";
+		}
+	} // getErrMessage
+	
+	/**
+	 * ldapCode2StatusCode:
+	 * - converts a LDAP_AUTH_xxx Error Code to an Error Code of StatusCode.java.
+	 * @param base : Base value inside of StatusCode.java reserved for LDAP_AUTH Error Codes.
+	 * @param ldapErrCode : the LDAP_AUTH Error code
+	 * @return the StatusCode in the range reserved for LDAP_AUTH Errors.
+	 * @date 21.04.17/FB
+	 */
+	public int ldapCode2StatusCode(int base, int ldapErrCode)
+	
+	{
+	return base + ldapErrCode;	
+	} // ldapCode2StatusCode
+	
+	/*
+	 *  load properties for LDAP Handling.
+	 *  17.02.17/FB
+	 */
+	
+	static String loadProp() throws IOException
+	
+	{
+		String sPwd = null;
+		String sConfFile = System.getProperty("user.home") + "/.config/ldap.conf";
+		FileInputStream in;
+		Properties prop;
+		
+        try {
+            in = new FileInputStream(sConfFile);
+            }
+        catch( IOException ex )
+            {
+        	System.err.printf("Error: LDAP.loadProp: cannot load Property file '%s'!\n", sConfFile); 
+            ex.printStackTrace();
+            return null;
+            }
+
+        if( in == null )
+	        {
+        	System.err.printf("Error: LDAP.loadProp: cannot load Property file '%s'!\n", sConfFile); 
+	        return null;
+	        }
+	    else
+	        {
+	        if( DEBUGLOG ) System.out.println("Debug: loaded: " + sConfFile);
+	        }
+
+        prop = new Properties();
+        Enumeration<?> e;
+        
+        try {
+            prop.load(in);
+            e = prop.propertyNames();
+
+            while( e.hasMoreElements() )
+                {
+                String key = (String)e.nextElement();
+                String val = prop.getProperty(key);
+                if( key.compareTo("pwd") == 0 )
+                	return val; 
+                
+                //System.out.println("Property '" + key + "' = '" + val + "'.");
+                }
+             }
+          catch( IOException ex )
+             {
+             ex.printStackTrace();
+             }
+
+		return sPwd;
+
+	} // loadProp
+
+	/**
+	 * ldapLogin
+	 * Arguments:
+	 * sUserDN  : either COSMAS II specific Account Name or IDS wide (IDM) account name;
+	 * sUserPwd : either COSMAS II specific Password     or IDS wide (IDM) password;
+	 * return   : 0 = OK, User Account + Pwd are OK, no restrictions;
+	 *            1 = internal error: cannot verify User+Pwd;
+	 *            2 = User Account or Pwd unknown;
+	 *            3 = User Account locked;
+	 *            4 = User known, but has not registered to KorAP/C2 Service yet;
+	 * LDAP Attributes that are checked (definition by Eric Seubert, 02.02.17):
+	 *  idsC2 = TRUE  -> Zugang zu C2 (registriert und zugelassen)
+	 *  idsC2 = FALSE (bzw Attribut nicht vorhanden) 
+	 *		            -> kein Zugang zu C2 (nicht zugelassen, egal ob registriert oder nicht)
+	 *
+	 *	 idsStatus = 0 -> Nutzerkennung OK;
+	 *	 idsStatus = 1 -> Nutzer ist kein aktiver IDS-Mitarbeiter
+	 *  idsStatus = 3 -> Nutzer ist LDAP-weit gesperrt
+	 */
+
+	public static int login(String sUserDN, String sUserPwd) throws LDAPException
+
+	{
+
+	String sUserC2DN	= sUserDN;
+	String sUserC2Pwd	= sUserPwd;
+
+	String ldapFilter = String.format("(|(&(uid=%s)(userPassword=%s))(&(idsC2Profile=%s)(idsC2Password=%s)))",
+												 sUserDN, sUserPwd, sUserC2DN, sUserC2Pwd);
+	SearchResult srchRes = null;
+
+	try{
+		sPwd = loadProp();
+		}
+	catch( IOException e )
+		{
+		System.out.println("Error: LDAPAuth.login: cannot load Property file!");
+		return LDAP_AUTH_RINTERR;
+		}
+															
+	if( DEBUGLOG )
+		{
+		//System.out.printf("LDAP Version      = %d.\n", LDAPConnection.LDAP_V3);
+		System.out.printf("LDAP Host & Port  = '%s':%d.\n", ldapHost, ldapPort);
+		System.out.printf("Login User & Pwd  = '%s' + '%s'\n", sUserDN, sUserPwd);
+		}
+
+	// LDAP Connection:
+	if( DEBUGLOG ) System.out.println("");
+
+	LDAPConnection lc = new LDAPConnection();
+	try {
+		// connect to LDAP Server:
+		lc.connect(ldapHost, ldapPort);
+		if( DEBUGLOG ) System.out.println("LDAP Connection = OK\n");
+		}
+	catch( LDAPException e) 	
+		{
+		System.out.printf("Connecting to LDAP Server: failed: '%s'!\n", e.toString());
+		return LDAP_AUTH_RINTERR;
+		}
+
+	if( DEBUGLOG ) 
+		System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0);
+	
+	try {
+		// bind to server:
+		if( DEBUGLOG ) System.out.printf("Binding with '%s' + '%s'...\n", sLoginDN, sPwd);
+		lc.bind(sLoginDN, sPwd);
+		if( DEBUGLOG ) System.out.printf("Binding: OK.\n");
+		}
+	catch( LDAPException e )
+		{
+		System.out.printf("Binding failed: '%s'!\n", e.toString());
+		return ldapTerminate(lc, LDAP_AUTH_RINTERR);
+		}
+
+	if( DEBUGLOG ) 
+		System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0);
+		
+	if( DEBUGLOG ) System.out.printf("Finding user '%s'...\n", sUserDN);
+	try{
+		// SCOPE_SUB = Scope Subtree.
+		if( DEBUGLOG ) System.out.printf("Finding Filter: '%s'.\n", ldapFilter);
+
+		// hier werden alle Attribute abgefragt:
+		//srchRes = lc.search(ldapBase, SearchScope.SUB, ldapFilter, null);
+		// wir fragen nur diese Attribute ab:
+		srchRes = lc.search(ldapBase, SearchScope.SUB, ldapFilter, attStatus, attC2);
+
+		if( DEBUGLOG ) System.out.printf("Finding '%s': %d entries.\n", sUserDN, srchRes.getEntryCount());
+		}
+	catch( LDAPSearchException e )
+		{
+		System.out.printf("Search for User failed: '%s'!\n", e.toString());
+		return ldapTerminate(lc, LDAP_AUTH_RUNKNOWN);
+		}
+
+	if( srchRes.getEntryCount() == 0 )
+		{
+		if( DEBUGLOG ) System.out.printf("Finding '%s': no entry found!\n", sUserDN);
+		return ldapTerminate(lc, LDAP_AUTH_RUNKNOWN);
+		}
+
+	if( DEBUGLOG ) System.out.println("Display results:");
+
+	Boolean
+		bStatus = false,
+		bC2     = false;
+	
+	// Attribute pr�fen:
+	for (SearchResultEntry e : srchRes.getSearchEntries())
+		{
+		for( Attribute attr : e.getAttributes() )
+			{
+			Integer val;
+
+			if( DEBUGLOG ) 
+				System.out.printf(" att: '%s'='%s'.\n", attr.getName(), attr.getValue());
+
+			// checking pertinent attribut/value pairs:
+			// "idsStatus": values 0=OK, 1-3 = locked account.
+			if( attr.getName().equals(attStatus) )
+				{
+				if( (val = attr.getValueAsInteger()) == null || val != 0 )
+					{
+					if( DEBUGLOG ) System.out.printf("idsStatus = '%s' -> User locked!\n", attr.getValue());
+					return ldapTerminate(lc, LDAP_AUTH_RLOCKED);
+					}
+				if( DEBUGLOG ) System.out.printf(" att: '%s'='%s': OK.\n", attr.getName(), attr.getValue());
+				bStatus = true;
+				}
+
+			// "c2IDS" must be set to "TRUE" = User known, but has not yet registered to C2 Service -> KorAP Service.
+			if( attr.getName().equals(attC2) ) 
+				{
+				if( attr.getValue().equals("FALSE") )
+					{
+					if( DEBUGLOG ) 
+						System.out.printf("idsC2 = '%s'-> User known, but has not registered C2/KorAP Service yet!\n", 
+							attr.getValue());
+					return ldapTerminate(lc, LDAP_AUTH_RNOTREG);
+					}
+				if( DEBUGLOG ) 
+					System.out.printf(" att: idsC2 = '%s'-> registered User: OK.\n", attr.getValue());
+				bC2 = true;
+				}
+			}
+
+		if( DEBUGLOG ) System.out.println();
+		}
+
+	if( bStatus == true && bC2 == true )
+		return ldapTerminate(lc, LDAP_AUTH_ROK); // OK.
+	else
+		return ldapTerminate(lc, LDAP_AUTH_RNOTREG); // Attribute konnten nicht gepr�ft werden.
+	
+	} // ldapLogin
+
+/**
+ * ldapTerminate
+ */
+ 
+public static int ldapTerminate(LDAPConnection lc, int ret)
+
+	{
+	if( DEBUGLOG ) System.out.println("Terminating...");
+	/*
+	try{
+		lc.finalize();
+		if( DEBUGLOG ) System.out.println("Debug: finalize: OK.");
+		}
+	catch( LDAPException e )
+		{
+		System.out.printf("finalize failed: '%s'!\n", e.toString());
+		}
+	*/
+
+	lc.close(null);
+	if( DEBUGLOG ) System.out.println("closing connection: done.\n");
+	return ret;
+	} // ldapTerminate
+
+}
+
diff --git a/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java b/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
index 8fbd509..13b6d23 100644
--- a/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
+++ b/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
@@ -105,7 +105,7 @@
             contextHandler.addServlet(new ServletHolder(
                     new ServletContainer(rc)), "/api/*");
 
-            server.setHandler(contextHandler);
+	            server.setHandler(contextHandler);
 
 //            if (kargs.sslContext != null) {
 //                SslSocketConnector sslConnector = new SslSocketConnector(
diff --git a/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
index ab37c80..7441edd 100644
--- a/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
+++ b/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -38,6 +38,8 @@
      */
     // todo: use korap.config to get index location
     public SearchKrill (String path) {
+    	
+    	System.out.println("Debug: SearchKrill: path='" + path + "'.");
         try {
             if (path.equals(":temp:")) {
                 this.index = new KrillIndex();
@@ -46,7 +48,7 @@
                 File f = new File(path);
                 jlog.info("Loading index from " + path);
                 if (!f.exists()) {
-                    jlog.error("Index not found!");
+                    jlog.error("Index not found: " + path + "!");
                     System.exit(-1);
                 }
                 this.index = new KrillIndex(new MMapDirectory(Paths.get(path)));
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 8a7515a..ee63565 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
@@ -2,6 +2,7 @@
 
 import com.sun.jersey.spi.container.ContainerRequest;
 import com.sun.jersey.spi.container.ResourceFilters;
+
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.BeansFactory;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -15,14 +16,21 @@
 import de.ids_mannheim.korap.web.KustvaktServer;
 import de.ids_mannheim.korap.web.filter.*;
 import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
+
 import org.slf4j.Logger;
 
+import javax.servlet.http.HttpServletRequest; // FB
 import javax.ws.rs.*;
 import javax.ws.rs.core.*;
+import javax.xml.ws.WebServiceContext; // FB
+import javax.xml.ws.handler.MessageContext; // FB
+import javax.annotation.Resource; // FB
+
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Iterator; // 07.02.17/FB
 
 //import com.sun.xml.internal.messaging.saaj.util.Base64;
 
@@ -35,13 +43,14 @@
 @Produces(MediaType.TEXT_HTML + ";charset=utf-8")
 public class AuthService {
 
+	private static Boolean DEBUG_LOG = false;
+	
     //todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
     // default usersettings, etc.)
     private static Logger jlog = KustvaktLogger.getLogger(AuthService.class);
 
     private AuthenticationManagerIface controller;
 
-
     //    private SendMail mail;
 
     public AuthService () {
@@ -51,7 +60,7 @@
         //        this.mail = new SendMail(ExtConfiguration.getMailProperties());
     }
 
-
+  
     /**
      * represents json string with data. All GUI clients can access
      * this method to get certain default values
@@ -90,17 +99,56 @@
     @GET
     @Path("apiToken")
     //@ResourceFilters({HeaderFilter.class})
-    public Response requestAPIToken (@Context HttpHeaders headers,
+    public Response requestAPIToken (
+    		@Context HttpHeaders headers,
             @Context Locale locale,
             @HeaderParam(ContainerRequest.USER_AGENT) String agent,
             @HeaderParam(ContainerRequest.HOST) String host,
             @HeaderParam("referer-url") String referer,
-            @QueryParam("scope") String scopes) {
+            @QueryParam("scope") String scopes,
+         //   @Context WebServiceContext wsContext, // FB
+            @Context SecurityContext secCtx) {
+    	
         List<String> auth = headers
                 .getRequestHeader(ContainerRequest.AUTHORIZATION);
 
         String[] values = BasicHttpAuth.decode(auth.get(0));
 
+        if( DEBUG_LOG == true )
+        	{
+            System.out.printf("Debug: AuthService.requestAPIToken...:\n");
+	        System.out.printf("Debug: auth.size=%d\n",  auth.size());
+	        System.out.printf("auth.get(0)='%s'\n", auth.get(0));
+	        System.out.printf("Debug: values.length=%d\n",  values.length);
+	        if( auth.size() > 0 )
+	        	{
+	        	Iterator it = auth.iterator();
+	        	while( it.hasNext() )
+	        		System.out.printf(" header '%s'\n",  it.next());
+	        	}
+	        if( values.length > 0 )
+	        	{
+	        	for(int i=0; i< values.length; i++)
+	        		{
+	        		System.out.printf(" values[%d]='%s'\n",  i, values[i]);
+	        		}
+	        	}
+
+	        MultivaluedMap<String,String> headerMap = headers.getRequestHeaders();
+	        if( headerMap != null && headerMap.size() > 0 )
+	        {
+	        	Iterator<String> it = headerMap.keySet().iterator();
+	        	while( it.hasNext() )
+	        	{
+	        		String key = (String)it.next();
+	        		List<String> vals= headerMap.get(key);
+	        		System.out.printf("Debug: requestAPIToken: '%s' = '%s'\n", key, vals);	
+	        	}
+	        	
+	        }
+	        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
@@ -118,13 +166,12 @@
         attr.put(Attributes.USER_AGENT, agent);
         TokenContext context;
         try {
-            User user = controller.authenticate(0, values[0], values[1], attr);
-            Userdata data = this.controller
-                    .getUserData(user, UserDetails.class);
+            // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
+            User user = controller.authenticate(2, values[0], values[1], attr); // Implementation with IdM/LDAP
+            // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
             // todo: is this necessary?
             //            attr.putAll(data.fields());
-            context = controller.createTokenContext(user, attr,
-                    Attributes.API_AUTHENTICATION);
+            context = controller.createTokenContext(user, attr, Attributes.API_AUTHENTICATION);
         }
         catch (KustvaktException e) {
             throw KustvaktResponseHandler.throwit(e);
@@ -175,8 +222,11 @@
             throw KustvaktResponseHandler
                     .throwit(StatusCodes.BAD_CREDENTIALS);
 
+        // Implementation Hanl mit '|'. 16.02.17/FB
+        //if (values[0].equalsIgnoreCase("null")
+        //        | values[1].equalsIgnoreCase("null"))
         if (values[0].equalsIgnoreCase("null")
-                | values[1].equalsIgnoreCase("null"))
+                || values[1].equalsIgnoreCase("null"))
             throw KustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
 
         Map<String, Object> attr = new HashMap<>();
diff --git a/src/main/resources/validation.properties b/src/main/resources/validation.properties
index 22f27c5..a38ae80 100644
--- a/src/main/resources/validation.properties
+++ b/src/main/resources/validation.properties
@@ -32,5 +32,6 @@
 # as used by apache commons validator for strings
 # Validator.string=^[\\.;:,\\=&\\*\\/\\/_()\\[\\]@\\|\\-0-9\\p{L}\\p{Space}]{0,1024}$
 
-Validator.username=^[A-Za-z_.\\d]{6,15}$
+#Validator.username=^[A-Za-z_.\\d]{6,15}$ by Hanl
+Validator.username=^[A-Za-z_.\\d]{3,20}$ # 21.04.17/FB
 Validator.password=^((?=.*\\d)(?=.*[A-Za-z])(?!.*[\\(\\)-]).{8,20})$
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/config/TestHelper.java b/src/test/java/de/ids_mannheim/korap/config/TestHelper.java
index 73324ea..50b1e6e 100644
--- a/src/test/java/de/ids_mannheim/korap/config/TestHelper.java
+++ b/src/test/java/de/ids_mannheim/korap/config/TestHelper.java
@@ -75,8 +75,8 @@
     private static Logger jlog = LoggerFactory.getLogger(TestHelper.class);
     private static final Map<String, Object> data = new HashMap<>();
     static  {
-        data.put(Attributes.ID, 2);
-        data.put(Attributes.USERNAME, "testUser1");
+        data.put(Attributes.ID, 3); // 2);
+        data.put(Attributes.USERNAME, "testUser1"); // bodmer funktioniert noch nicht
         data.put(Attributes.PASSWORD, "testPass2015");
         data.put(Attributes.FIRSTNAME, "test");
         data.put(Attributes.LASTNAME, "user");
diff --git a/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java b/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java
index c282708..87457bd 100644
--- a/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java
@@ -40,7 +40,7 @@
     protected static Client client;
     private static String[] classPackages = null;
 
-    private static int PORT = 9000;
+    private static int PORT = 8089; // FB, was: 9000;
     private static int PORT_IT = 1;
     protected static String containerURI = "http://localhost/";
 
diff --git a/src/test/java/de/ids_mannheim/korap/web/service/full/KustvaktCoreRestTest.java b/src/test/java/de/ids_mannheim/korap/web/service/full/KustvaktCoreRestTest.java
index 72a4a01..c36e48f 100644
--- a/src/test/java/de/ids_mannheim/korap/web/service/full/KustvaktCoreRestTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/service/full/KustvaktCoreRestTest.java
@@ -21,7 +21,9 @@
 
     @BeforeClass
     public static void configure () {
-        FastJerseyTest.setPackages("de.ids_mannheim.korap.web.service.light",
+        
+    	// FastJerseyTest.setPackages("de.ids_mannheim.korap.web.service.light", version hanl
+        FastJerseyTest.setPackages("de.ids_mannheim.korap.web.service.full", // volle Version FB
                 "de.ids_mannheim.korap.web.filter",
                 "de.ids_mannheim.korap.web.utils");
     }