NtlmLogonAction.java

package sk.iway.iwcm.system.ntlm;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import sk.iway.Password;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.DB;
import sk.iway.iwcm.DBPool;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.RequestBean;
import sk.iway.iwcm.SetCharacterEncodingFilter;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.common.LogonTools;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.users.UserDetails;
import sk.iway.iwcm.users.UserGroupDetails;
import sk.iway.iwcm.users.UserGroupsDB;
import sk.iway.iwcm.users.UsersDB;

/**
 *  LogonAction.java - prihlasenie usera do systemu pomocou NTLM filtra
 *
 *@Title        webjet4
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2005
 *@author       $Author: jeeff $
 *@version      $Revision: 1.21 $
 *@created      Date: 4.8.2005 14:04:07
 *@modified     $Date: 2010/01/11 08:15:21 $
 */
@Controller
public class NtlmLogonAction
{
	private static Map<String, String> emptyGroupsTable;
	static
	{
		emptyGroupsTable = new Hashtable<>();
	}

	@ResponseBody
    @RequestMapping("/ntlm/logon.struts")
	public void execute(HttpServletRequest request, HttpServletResponse response)
				throws Exception
	{
		Logger.debug(NtlmLogonAction.class,"NtlmLogonAction");
		try
		{
			if (request instanceof NtlmHttpServletRequest)
			{
				Identity loggedUser = UsersDB.getCurrentUser(request);
				if (loggedUser != null)
				{
					//user je uz zalogovany, asi nema prava tam kde chce ist...
					Logger.debug(NtlmLogonAction.class,"NtlmLogonAction - user je uz lognuty, asi sem nema pristup, admin: " + loggedUser.isAdmin()+" forbiddenURL="+AuthenticationFilter.getForbiddenURL());

					if (AuthenticationFilter.getForbiddenURL()!=null)
					{
						response.sendRedirect(AuthenticationFilter.getForbiddenURL());
					}
					else
					{
						response.sendError(HttpServletResponse.SC_FORBIDDEN);
					}
					return;
				}


				NtlmHttpServletRequest reqAuth = (NtlmHttpServletRequest)request;
				String loginName = reqAuth.getUserPrincipal().getName();

				int index = loginName.indexOf('\\');
				if (index == -1) index = loginName.indexOf('/');
				loginName = (index != -1) ? loginName.substring(index + 1) : loginName;

				//Set groups = reqAuth.getGroups();

				Logger.debug(NtlmLogonAction.class,"NtlmLogonAction - login: " + loginName);

				//nastav default jazyk
				request.getSession().setAttribute(sk.iway.iwcm.i18n.Prop.SESSION_I18N_PROP_LNG, Constants.getString("defaultLanguage"));
				Prop prop = Prop.getInstance(Constants.getServletContext(), request);


				UserDetails user;
				//TODO refaktorovat tak, aby robil normalne redirecty, nie hadzal exception
				try
				{
					user = authentificateUserAgainstLdap(request, response, loginName);
				}
				catch (Exception e)
				{
					return;
				}

				boolean logonSuccess = false;
				String afterLogonUrl = "/";

				String password = user.getPassword();
				if (Constants.getBoolean("passwordUseHash"))
				{
					//v BasicNtlmLogonAction to takto nastavime a ulozime, kedze heslo realne nevieme
					password = user.getLogin();
				}

				if (user.isAdmin())
				{
					//prihlas ho
					Identity identity = new Identity();
					Map<String, String> errors = new Hashtable<>();
					String forward = LogonTools.logon(user.getLogin(), password, identity, errors, request, prop);
					if (forward.compareTo("logon_ok_admin") != 0 || identity == null || identity.isAdmin() == false)
					{
						identity = null;
					}
					else
					{
						identity.setLoginName(user.getLogin());
						identity.setPassword(password);
						identity.setValid(true);
						//je korektne prihlaseny
						LogonTools.setUserToSession(request.getSession(), identity);

						Logger.debug(NtlmLogonAction.class,"NtlmLogonAction: admin prihlaseny");
						if (request.getParameter("admin")!=null)
						{
							afterLogonUrl = "/admin";
						}
						logonSuccess = true;
					}
				}
				else
				{
					List<String> errors = LogonTools.logonUser(request, user.getLogin(), password);
					if (errors.isEmpty())
					{
						//je prihlaseny
						Logger.debug(NtlmLogonAction.class,"NtlmLogonAction: user je prihlaseny");
						logonSuccess = true;
					}
				}

				if (logonSuccess)
				{
					if (request.getParameter("origDocId")!=null)
					{
						afterLogonUrl = "/showdoc.do?docid="+request.getParameter("origDocId");
					}
					String afterLogonRedirect = (String)request.getSession().getAttribute("afterLogonRedirect");
					request.getSession().removeAttribute("afterLogonRedirect");
					request.setAttribute("afterLogonRedirect", afterLogonRedirect);
					if (Tools.isNotEmpty(afterLogonRedirect)) afterLogonUrl = afterLogonRedirect;

					response.sendRedirect(Tools.sanitizeHttpHeaderParam(afterLogonUrl));
					return;
				}
			}
			else
			{
				Logger.debug(NtlmLogonAction.class,"NIE JE TO NtlmHttpServletRequest");
			}
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}

		if (AuthenticationFilter.getForbiddenURL()!=null)
		{
			response.sendRedirect(AuthenticationFilter.getForbiddenURL());
		}
		else
		{
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
		}
		return;
	}

	public static UserDetails authentificateUserAgainstLdap(HttpServletRequest request, HttpServletResponse response, String loginName) throws IOException, IllegalAccessException, InvocationTargetException, RedirectedException
	{
		//Skontroluj usera voci DB
		loginName = loginName.toLowerCase();
		UserDetails user = UsersDB.getUser(loginName);
		if (user == null)
		{
			//este neexistuje, treba vytvorit
			user = new UserDetails();
			user.setUserId(-1);
			user.setLogin(loginName);
			user.setPassword(Password.generatePassword(10));
		}

		doLdapQuery(user);

		if (Tools.isEmpty(user.getLogin()))
		{
			Logger.debug(NtlmLogonAction.class,"UserLogin je null: forbiddenURL="+AuthenticationFilter.getForbiddenURL());
			if (AuthenticationFilter.getForbiddenURL()!=null)
			{
				response.sendRedirect(AuthenticationFilter.getForbiddenURL());
			}
			else
			{
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
			}
			throw new RedirectedException();
		}

		if (Constants.getBoolean("passwordProtectedAutoIdDontCreateUser")==true) {
			if (Tools.isNotEmpty(Constants.getString("passwordProtectedAutoId")) && isUserInProtectedGroup(user)) {
				logonAndRedirectUserInProtectedGroup(request, response, user);
				throw new RedirectedException();
			}
		}

		//ak nie je prideleny do ziadnej skupiny (a nie je to admin), posli ho prec
		if (Tools.isEmpty(user.getUserGroupsIds()) && user.isAdmin()==false && Constants.getBoolean("ntlmAllowEmptyGroups")==false)
		{
			Logger.debug(NtlmLogonAction.class,"UserGroups je null: forbiddenURL="+AuthenticationFilter.getForbiddenURL());
			if (AuthenticationFilter.getForbiddenURL()!=null)
			{
				response.sendRedirect(AuthenticationFilter.getForbiddenURL());
			}
			else
			{
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
			}
			throw new RedirectedException();
		}

		if (Tools.isEmpty(user.getUserGroupsIds())) user.setUserGroupsIds("");

		user.setAuthorized(true);


		//toto je uz uplna haluz kvoli Transgasu, maju drbnuto nastaveny server
		/*String fn = user.getFirstName();
		Logger.debug(NtlmLogonAction.class, "PRED RECODE: "+fn);
		String fn1 = new String(user.getFirstName().getBytes(), "windows-1250");
		String fn2 = new String(user.getFirstName().getBytes("windows-1250"));
		String fn3 = new String(user.getFirstName().getBytes(), "iso-8859-1");
		String fn4 = new String(user.getFirstName().getBytes("iso-8859-1"));
		String fn5 = new String(user.getFirstName().getBytes("iso-8859-1"), "windows-1250");

		fn = "o:"+fn+" 1:"+fn1+" 2:"+fn2+" 3:"+fn3+" 4:"+fn4+" 5:"+fn5;
		Logger.debug(NtlmLogonAction.class, "RECODED: fn="+fn);*/
		String recode = Constants.getString("ntlmLogonAction.charsetEncoding");
		if (Tools.isNotEmpty(recode)) {
			//weird backward compatibility where user details are wrongly encoded
			user.setFirstName(new String(user.getFirstName().getBytes(), recode));
			user.setLastName(new String(user.getLastName().getBytes(), recode));
			user.setAdress(new String(user.getAdress().getBytes(), recode));
			user.setCity(new String(user.getCity().getBytes(), recode));
			user.setCompany(new String(user.getCompany().getBytes(), recode));
		}

		UserDetails adminUser = null;
		int defaultAdminUserId = Constants.getInt("NTLMDefaultAdminUserId");
		if (user.isAdmin() && user.getUserId()<1 && defaultAdminUserId>0)
		{
			//nacitaj default admin usera
			adminUser = UsersDB.getUser(defaultAdminUserId);
			if (adminUser!=null)
			{
				adminUser.setEmail(user.getEmail());
				adminUser.setTitle(user.getTitle());
				adminUser.setFirstName(user.getFirstName());
				adminUser.setLastName(user.getLastName());
				adminUser.setAdress(user.getAdress());
				adminUser.setCity(user.getCity());
				adminUser.setPSC(user.getPSC());
				adminUser.setCountry(user.getCountry());
				adminUser.setCompany(user.getCompany());
				adminUser.setPhone(user.getPhone());
				adminUser.setUserGroupsIds(user.getUserGroupsIds());
				adminUser.setAdmin(true);

				user = adminUser;
				user.setUserId(-1);
				user.setLogin(loginName);
				user.setPassword(Password.generatePassword(10));
			}
		}

		//dolezite, inak nas neprihlasi
		String password = user.getPassword();
		if (Constants.getBoolean("passwordUseHash"))
		{
			password = user.getLogin();
			user.setPassword(password);
			Logger.debug(NtlmLogonAction.class, "NtlmLogonAction - Pass use hash, nastavujem na "+user.getLogin());
		}

		UsersDB.saveUser(user);

		if (adminUser != null)
		{
			//uloz prava
			copyAdminUserGroupsApprove(defaultAdminUserId, user.getUserId());
			copyUserDisabledItems(defaultAdminUserId, user.getUserId());
		}

		if (user.isAuthorized())
        {
            try
            {
                //prihlasenie pre SPRING / REST
                RequestBean requestBean = SetCharacterEncodingFilter.getCurrentRequestBean();
                if (requestBean != null)
                {
                    AuthenticationManager authenticationManager = requestBean.getSpringBean("authenticationManagerBean", AuthenticationManager.class);
                    if (authenticationManager != null)
                    {
                        final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getLogin(), password);
                        final Authentication authentication = authenticationManager.authenticate(authRequest);
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            }
            catch (Exception ex)
            {
                sk.iway.iwcm.Logger.error(ex);
            }
        }

		return user;
	}

	private static void logonAndRedirectUserInProtectedGroup(HttpServletRequest request, HttpServletResponse response, UserDetails user) throws IllegalAccessException, InvocationTargetException, IOException
	{
		//stranky vidia len pouzivatelia ktory su spravne zalogovany
		String passwordProtectedAutoId = Constants.getString("passwordProtectedAutoId");
		Logger.debug(NtlmLogonAction.class, "passwordProtectedAutoId="+passwordProtectedAutoId);

		Logger.debug(NtlmLogonAction.class, "prihlasujem a nevytvaram v DB");

		//	akoze ho prihlasime a pouzijeme (takychto pouzivatelov v DB nevytvarame)
		user.setUserGroupsIds(passwordProtectedAutoId);
		user.setAuthorized(true);
		Identity userLogged = new Identity();
		BeanUtils.copyProperties(userLogged, user);
		LogonTools.setUserToSession(request.getSession(), userLogged);
		String afterLogonUrl = "/";
		if (request.getParameter("origDocId")!=null)
		{
			afterLogonUrl = "/showdoc.do?docid="+request.getParameter("origDocId");
		}
		response.sendRedirect(Tools.sanitizeHttpHeaderParam(afterLogonUrl));

		if (user.getUserId()>0)
		{
			Logger.debug(NtlmLogonAction.class, "userID="+user.getUserId()+" --- td");
		}
	}


	private static boolean isUserInProtectedGroup(UserDetails user)
	{
		//pouzivatelia, ktory patria (len) do tejto skupiny sa neukladaju do WebJETu
		String passwordProtectedAutoId = Constants.getString("passwordProtectedAutoId");
		return user.isAdmin()==false && passwordProtectedAutoId.equals(user.getUserGroupsIds()) && Constants.getBoolean("NTLMdontCreateAutoIdUser")==true;
	}


	public static void doLdapQuery(UserDetails user)
	{
		String ldapQueryMethod = Constants.getString("NTLMldapQueryMethod");
		if (Tools.isNotEmpty(ldapQueryMethod))
		{
			int i = ldapQueryMethod.lastIndexOf('.');
			String ldapQueryClass = ldapQueryMethod.substring(0, i);
			ldapQueryMethod = ldapQueryMethod.substring(i+1);
			//String
			try
			{
				Class<?> c = Class.forName(ldapQueryClass);
				Object o = c.getDeclaredConstructor().newInstance();
				Method m;
				Class<?>[] parameterTypes = new Class[] {UserDetails.class};
				Object[] arguments = new Object[] {user};
				m = c.getMethod(ldapQueryMethod, parameterTypes);
				m.invoke(o, arguments);
			}
			catch (Exception ex)
			{
				sk.iway.iwcm.Logger.error(ex);
			}
			return;
		}

		DirContext ctx = null;

		String CNUserName = null;

		try
		{
	       Hashtable<String, String> env = new Hashtable<>();

	       boolean ldapUseSslProtocol = Constants.getBoolean("ldapUseSslProtocol");
	       if(ldapUseSslProtocol) env.put(Context.SECURITY_PROTOCOL, "ssl");
	       env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
	       env.put(Context.STATE_FACTORIES, "PersonStateFactory");
	       env.put(Context.OBJECT_FACTORIES, "PersonObjectFactory");
	       env.put(Context.PROVIDER_URL, AuthenticationFilter.getLdapProvider());  // SET YOUR SERVER AND STARTING CONTEXT HERE
	       env.put(Context.SECURITY_PRINCIPAL,  AuthenticationFilter.getLdapUsername()); //"cn=Administrator, o=oracle.local");  // SET USER THAT CAN SEARCH AND MODIFY FULL NAME HERE
	       env.put(Context.SECURITY_CREDENTIALS, AuthenticationFilter.getLdapPassword());  // SET PASSWORD HERE
	       env.put(Context.REFERRAL, "follow");
	       env.put(LdapContext.CONTROL_FACTORIES, "com.sun.jndi.ldap.ControlFactory");

	       ctx = new InitialDirContext(env);

	       // Specify the search filter to match all users with no full name
	       String filter = "(&(objectClass=Person) (&(sAMAccountName="+user.getLogin()+")))"; // "DC=oracle, (&(objectClass=Person) (&(sn=priezvisko)))";
	       // limit returned attributes to those we care about
	       //String[] attrIDs = {"sn", "givenName", "mail"};
	       SearchControls ctls = new SearchControls();
	       //ctls.setReturningAttributes(attrIDs);
	       // comment out next line to limit to one container otherwise it'll walk down the tree
	       ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);

	       // Search for objects using filter and controls
	       NamingEnumeration<SearchResult> answer = ctx.search("", filter, ctls);

	       //String ldapGroups = null;
	       // cycle through result set
	       if (answer.hasMore())
	       {
	           SearchResult sr = answer.next();
	           Attributes attrs = sr.getAttributes();

	           //DEBUG: treba vymazat
	           /*NamingEnumeration all = attrs.getAll();
	           //Logger.println(this,"POCET: " + attrs.size());
	           while (all.hasMoreElements())
	           {
	              Attribute a = (Attribute)all.next();
	              //Logger.println(this,"ENUM: " + a);
	           }*/

	           user.setEmail(getAtrValue(attrs, "mail", user.getEmail()));
	           user.setFirstName(getAtrValue(attrs, "givenName", "")); //user.getFirstName()
	           user.setLastName(getAtrValue(attrs, "sn", "")); //user.getLastName()
	           user.setAdress(getAtrValue(attrs, "streetAddress", user.getAdress()));
	           user.setCity(getAtrValue(attrs, "l", user.getCity()));
	           user.setPSC(getAtrValue(attrs, "postalCode", user.getPSC()));
	           user.setCountry(getAtrValue(attrs, "co", user.getCountry()));
	           user.setCompany(getAtrValue(attrs, "company", user.getCompany()));
	           user.setPhone(getAtrValue(attrs, "telephoneNumber", user.getPhone()));
	           user.setFax(getAtrValue(attrs, "mobile", user.getFax()));

	           CNUserName = getAtrValue(attrs, "cn", "");

	           //ldapGroups = getAtrValue(attrs, "memberOf", null) + ", ";

	           // eDir returns "attribute name : attribute value on get method so strip off up to ": "
	           //attrs.put("fullName", givenName.substring(givenName.indexOf(':')+2) + ' ' + surName.substring(surName.indexOf(':')+2));
	           //ctx.modifyAttributes(sr.getName(), DirContext.REPLACE_ATTRIBUTE, attrs);
	       }
	       else
	       {
	      	 user.setLogin(null);
	      	 return;
	       }

	       // Close the context when we're done
		    ctx.close();
		    ctx = null;

		    /*Logger.debug(NtlmLogonAction.class,"Nastavujem skupiny:\n"+ldapGroups);
	       if (ldapGroups != null)
	       {
	       	String groupsString = null;
				List allGroups = UserGroupsDB.getInstance().getUserGroups();
				Iterator iter = allGroups.iterator();
				UserGroupDetails ugd;
				while (iter.hasNext())
				{
					ugd = (UserGroupDetails)iter.next();
					Logger.debug(NtlmLogonAction.class,"testujem skupinu: " + ugd.getUserGroupName());
					if (ldapGroups.indexOf("CN="+ugd.getUserGroupName()+",")!=-1)
					{
						Logger.debug(NtlmLogonAction.class,"je clenom skupiny");
						if (groupsString == null)
						{
							groupsString = ""+ugd.getUserGroupId();
						}
						else
						{
							groupsString += ","+ugd.getUserGroupId();
						}
					}
				}
				Logger.debug(NtlmLogonAction.class,"nastavujem skupinyIDS: " + groupsString);
				user.setUserGroupsIds(groupsString);

		       if (ldapGroups.indexOf("CN="+Constants.getString("NTLMAdminGroupName")+",")!=-1)
		       {
		       	Logger.debug(NtlmLogonAction.class,"USER JE ADMIN");
					user.setAdmin(true);
		       }
		       else
		       {
		       	Logger.debug(NtlmLogonAction.class,"USER NIE JE ADMIN");
		       	user.setAdmin(false);
		       }
	       }*/

			 StringBuilder groupsString = null;

			 if (Tools.isNotEmpty(Constants.getString("passwordProtectedAutoId")))
			 {
			 	groupsString = new StringBuilder(Constants.getString("passwordProtectedAutoId"));
			 }

			 if (Tools.isEmpty(CNUserName))
			 {
				 CNUserName = user.getLastName() + " " + user.getFirstName();
				 if (Constants.getBoolean("NTLMldapIsSlovak")==true)
				 {
					 CNUserName = user.getFirstName() + " " + user.getLastName();
				 }
				 //odstran medzeru na konci/zaciatku, ked ma zadane len meno
				 CNUserName = DB.internationalToEnglish(CNUserName).toLowerCase().trim();
			 }

			 for (UserGroupDetails ugd : UserGroupsDB.getInstance().getUserGroups())
			 {
				 Logger.debug(NtlmLogonAction.class,"testujem skupinu: " + ugd.getUserGroupName());
				 if (isMemberOf(user.getLogin(), CNUserName, ugd.getUserGroupName(), 1, null))
				 {
					Logger.debug(NtlmLogonAction.class,"je clenom skupiny " + ugd.getUserGroupName());
					if (groupsString == null)
					{
						groupsString = new StringBuilder(String.valueOf(ugd.getUserGroupId()));
					}
					else
					{
						groupsString.append(',').append(ugd.getUserGroupId());
					}
				 }
				 else
				 {
					 Logger.debug(NtlmLogonAction.class,"NIEje clenom skupiny " + ugd.getUserGroupName());
				 }
			 }
			 Logger.debug(NtlmLogonAction.class,"nastavujem skupiny: " + groupsString);
			 if (groupsString != null)
			 {
				 user.setUserGroupsIds(groupsString.toString());

				 if (groupsString.toString().equals(Constants.getString("passwordProtectedAutoId"))==false)
				 {
				 	//ak to nie je autoid neries nastavenie admina, zacovaj z DB
					 //otestuj admina
					 if (isMemberOf(user.getLogin(), CNUserName, Constants.getString("NTLMAdminGroupName"), 1, null)) {
						 Logger.debug(NtlmLogonAction.class, "USER JE ADMIN");
						 user.setAdmin(true);
					 } else {
						 Logger.debug(NtlmLogonAction.class, "USER NIE JE ADMIN");
						 user.setAdmin(false);
					 }
				 }
			 }

			if (Constants.getBoolean("passwordUseHash"))
			{
				user.setPassword(user.getLogin());
			}

	     }
	     catch(NamingException ne)
		  {
	       Logger.error(NtlmLogonAction.class,ne.getMessage());
	       sk.iway.iwcm.Logger.error(ne);
	     }
	     finally
	     {
	   	   if (ctx != null)
				{
					try
					{
						ctx.close();
					}
					catch (Exception e2)
					{
					}
				}
	     }
	}

	/**
	 * Ziska atribut a dekoduje jeho hodnotu, alebo vrati defaultValue
	 * @param attrs
	 * @param name
	 * @param defaultValue
	 * @return
	 */
	public static String getAtrValue(Attributes attrs, String name, String defaultValue)
	{
		String ret = defaultValue;
		try
		{
			Attribute a;
         a = attrs.get(name);
         //Logger.println(this,"LDAP: "+name+"="+a);
         if (a!=null)
         {
         	Object o = a.get();

         	Logger.debug(NtlmLogonAction.class, "o="+o.getClass());

	         ret = recodeString((String)o);
         }
         Logger.debug(NtlmLogonAction.class, "getAtrValue("+name+")="+ret);
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
		if (ret == null) ret = "";
		return(ret);
	}

	private static String recodeString(String str)
	{
		//je to spravne nemusime robit nic
		if (Tools.isEmpty(Constants.getString("NTLMldapEncoding")) || Constants.getString("NTLMldapEncoding").length()<2)
		{
			return(str);
		}
		if (str == null) return(null);
		try
		{
			String newStr = new String(str.getBytes(Constants.getString("NTLMldapEncoding")));
			Logger.debug(NtlmLogonAction.class, "Encoding string ("+str+") to: "+Constants.getString("NTLMldapEncoding")+" new="+newStr);
			return(newStr);
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
		return(str);
	}

	public static boolean isMemberOf(String login, String userCN, String groupCN, int actualLevel, DirContext ctx)
	{
		String isMemberOfMethod = Constants.getString("NTLMisMemberOfMethod");
		if (Tools.isNotEmpty(isMemberOfMethod))
		{
			int i = isMemberOfMethod.lastIndexOf('.');
			String isMemberOfClass = isMemberOfMethod.substring(0, i);
			isMemberOfMethod = isMemberOfMethod.substring(i+1);
			//String
			try
			{
				Class<?> c = Class.forName(isMemberOfClass);
				Object o = c.getDeclaredConstructor().newInstance();
				Method m;
				Class<?>[] parameterTypes = new Class[] {String.class, String.class, String.class, int.class, DirContext.class};
				Object[] arguments = new Object[] {login, userCN, groupCN, actualLevel, ctx};
				m = c.getMethod(isMemberOfMethod, parameterTypes);
				return((Boolean)m.invoke(o, arguments));
			}
			catch (Exception ex)
			{
				sk.iway.iwcm.Logger.error(ex);
			}
		}

		//groupCN = getNameFromCNString(groupCN);

		String spacer = "                                                                                                           ";
		try
		{
			spacer = spacer.substring(0, actualLevel*3);
		}
		catch (Exception e)
		{
			spacer = "";
		}

		Logger.debug(NtlmLogonAction.class, spacer+"isMemberOf -------- START userCN="+userCN+" groupCN="+groupCN+" level="+actualLevel);

		//DirContext ctx = null;
		boolean otvorilSomCTX = false;
		String filter = null;
		try
		{
			//	najskor ziskaj pouzivatelov tejto skupiny
			String baseProvider = AuthenticationFilter.getLdapProvider();
			int i = baseProvider.indexOf('/', 10);
			String providerURL = baseProvider.substring(0, i);
			String searchBase = baseProvider.substring(i+1);
			i = searchBase.indexOf('?');
			if (i>0) searchBase = searchBase.substring(0, i);

			boolean groupHasCN = false;
			if (groupCN.indexOf("CN=")!=-1)
			{
				groupHasCN = true;
			}

			filter = "(&(objectClass=*)(CN=" + groupCN + "))";
			if (groupHasCN)
			{
				//filter = "(objectClass=*)";
				searchBase = getBaseFromCNString(groupCN); // "OU=Limuzska Groups,DC=oracle,DC=local";
				filter = "(&(objectClass=*)(CN=" + getNameFromCNString(groupCN) + "))";
			}

			//	over ci to nie je prazdne
			if (emptyGroupsTable.get(filter)!=null)
			{
				Logger.debug(NtlmLogonAction.class, spacer+"toto je prazdne: " + groupCN + " vraciam sa z isMemeberOf");
				return(false);
			}

			Logger.debug(NtlmLogonAction.class, spacer+"provider="+providerURL);

			Hashtable<String, String> env = new Hashtable<>();
			boolean ldapUseSslProtocol = Constants.getBoolean("ldapUseSslProtocol");
			if(ldapUseSslProtocol) env.put(Context.SECURITY_PROTOCOL, "ssl");
			env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
			env.put(Context.STATE_FACTORIES, "PersonStateFactory");
			env.put(Context.OBJECT_FACTORIES, "PersonObjectFactory");
			env.put(Context.PROVIDER_URL, providerURL); //AuthenticationFilter.getLdapProvider()); // SET YOUR SERVER AND STARTING CONTEXT HERE
			env.put(Context.SECURITY_PRINCIPAL, AuthenticationFilter.getLdapUsername()); //"cn=Administrator, o=oracle.local");  // SET USER THAT CAN SEARCH AND MODIFY FULL NAME HERE
			env.put(Context.SECURITY_CREDENTIALS, AuthenticationFilter.getLdapPassword()); // SET PASSWORD HERE
			env.put(LdapContext.CONTROL_FACTORIES, "com.sun.jndi.ldap.ControlFactory");

			//teraz sprav skupiny
			if (ctx == null)
			{
				Logger.debug(NtlmLogonAction.class, spacer+"Otvaram CTX");
				ctx = new InitialDirContext(env);
				otvorilSomCTX = true;
			}

			Logger.debug(NtlmLogonAction.class, spacer+"searchBase="+searchBase);
			Logger.debug(NtlmLogonAction.class, spacer+"filter="+filter);

			//	     Create the search controls
			SearchControls searchCtls = new SearchControls();
			//Specify the search scope
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
			String[] returnedAtts = {"member"};
			//	     Specify the attributes to return
			searchCtls.setReturningAttributes(returnedAtts);
			// Search for objects using filter and controls
			NamingEnumeration<SearchResult> answer = ctx.search(searchBase, filter, searchCtls);
			// cycle through result set
			int totalResults = 0;

			List<String> groupNames = new ArrayList<>();
			boolean membersIsEmpty = true;
			if (answer.hasMore())
			{
				SearchResult sr = answer.next();
				//Logger.debug(NtlmLogonAction.class, spacer+"sr=" + sr.getName());
				Attributes attrs = sr.getAttributes();
				if (attrs != null)
				{
					try
					{
						for (NamingEnumeration<? extends Attribute> ae = attrs.getAll(); ae.hasMore();)
						{
							Attribute attr = ae.next();
							//Logger.debug(NtlmLogonAction.class, spacer+"Attribute: " + attr.getID());
							for (NamingEnumeration<?> e = attr.getAll(); e.hasMore(); totalResults++)
							{
								membersIsEmpty = false;
								String name = recodeString((String)e.next());
								String nameLC = DB.internationalToEnglish(name).toLowerCase().trim();
								String userCNLC = DB.internationalToEnglish(userCN).toLowerCase().trim();
								//to druhe je tam kvoli Meno Priezvisko Ing.
								Logger.debug(NtlmLogonAction.class, spacer+" testing: nameLC=" + nameLC + " userCNLC="+userCNLC);
								if ((nameLC+",").indexOf("cn="+userCNLC+",")!=-1 || (nameLC+",").indexOf("cn="+userCNLC+" ")!=-1)
								{
									Logger.debug(NtlmLogonAction.class, spacer+"GRP: " + totalResults + ". " + name);
									Logger.debug(NtlmLogonAction.class, spacer+userCN+"-------------------user najdeny !!!!");
									return(true);
								}
								groupNames.add(name);
							}
						}
					}
					catch (NamingException e)
					{
						Logger.error(NtlmLogonAction.class, spacer+"Problem listing members: " + e);
					}
				}
			}
			if (membersIsEmpty)
			{
				Logger.debug(NtlmLogonAction.class, spacer+"vrateny zoznam je prazdny, davam do cache: " + groupCN);
				emptyGroupsTable.put(filter, groupCN);
			}
			// Close the context when we're done
			//ctx.close();

			totalResults = 0;
			for (String name : groupNames)
			{
				totalResults++;
				Logger.debug(NtlmLogonAction.class, spacer+"GRP: " + totalResults + ". " + name);

				//zavolaj rekurziu
				boolean found = isMemberOf(login, userCN, name, actualLevel+1, ctx);
				if (found)
				{
					return(true);
				}
			}
		}
		catch (NameNotFoundException nfe)
		{
			Logger.debug(NtlmLogonAction.class, spacer+"NFE group="+groupCN+" user="+userCN+" chyba: "+nfe.getMessage());
			Logger.debug(NtlmLogonAction.class, spacer+"davam do cache: " + groupCN);
			emptyGroupsTable.put(filter, groupCN);
		}
		catch (PartialResultException pre)
		{
			Logger.debug(NtlmLogonAction.class, spacer+pre.getMessage()+" PRE group="+groupCN+" user="+userCN);
		}
		catch (Exception e)
		{
			Logger.error(NtlmLogonAction.class, spacer+e);
			sk.iway.iwcm.Logger.error(e);
		}
		finally
		{
			if (otvorilSomCTX && ctx != null)
			{
				try
				{
					Logger.debug(NtlmLogonAction.class, "Zatvaram CTX");
					ctx.close();
				}
				catch (Exception e2)
				{
				}
			}
		}

		Logger.debug(NtlmLogonAction.class, spacer+"isMemberOf -------- END");
		return (false);
	}

	/**
	 * Vrati len hodnotu CN z retazca
	 * @param cnString
	 * @return
	 */
	private static String getNameFromCNString(String cnString)
	{
		int start = cnString.indexOf("CN=");
		int end = cnString.indexOf(',');
		if (start == -1) return(cnString);
		if (end == -1) return(cnString.substring(start+3).trim());
		return(cnString.substring(start+3, end).trim());
	}

	/**
	 * Vrati vsetko okrem CN z retazca
	 * @param cnString
	 * @return
	 */
	private static String getBaseFromCNString(String cnString)
	{
		int start = cnString.indexOf("CN=");
		int end = cnString.indexOf(',');
		if (start == -1) return(cnString);
		if (end == -1) return("");
		return(cnString.substring(end+1).trim());
	}

	private static void copyAdminUserGroupsApprove(int originalUserId, int newUserId)
	{
		Connection db_conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			Map<Integer, Integer> approveTable = new Hashtable<>();

			db_conn = DBPool.getConnection();
			ps = db_conn.prepareStatement("SELECT group_id, approve_mode FROM groups_approve WHERE user_id=?");
			ps.setInt(1, originalUserId);
			rs = ps.executeQuery();
			while (rs.next())
			{
				approveTable.put(rs.getInt("group_id"), rs.getInt("approve_mode"));
			}
			rs.close();
			ps.close();

			ps = db_conn.prepareStatement("DELETE FROM groups_approve WHERE user_id=?");
			ps.setInt(1, newUserId);
			ps.execute();
			ps.close();

			ps = db_conn.prepareStatement("INSERT INTO groups_approve (user_id, group_id, approve_mode) VALUES (?, ?, ?)");
			ps.setInt(1, newUserId);

			for (Map.Entry<Integer, Integer> entry : approveTable.entrySet())
			{
				Integer groupId = entry.getKey();
				Integer approveMode = entry.getValue();
				ps.setInt(2, groupId);
				ps.setInt(3, approveMode);
				ps.execute();

				Logger.debug(NtlmLogonAction.class, "copyAdminUserGroupsApprove userId="+newUserId+" groupId="+groupId+" approveMode="+approveMode);
			}

			ps.close();

			db_conn.close();
			rs = null;
			ps = null;
			db_conn = null;
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		finally
		{
			try
			{
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
				if (db_conn != null)
					db_conn.close();
			}
			catch (Exception ex2)
			{
			}
		}
	}

	private static void copyUserDisabledItems(int originalUserId, int newUserId)
	{
		Connection db_conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			Map<String, String> permsTable = new Hashtable<>();

			db_conn = DBPool.getConnection();
			ps = db_conn.prepareStatement("SELECT item_name FROM user_disabled_items WHERE user_id=?");
			ps.setInt(1, originalUserId);
			rs = ps.executeQuery();
			while (rs.next())
			{
				permsTable.put(DB.getDbString(rs, "item_name"), "1");
			}
			rs.close();
			ps.close();

			ps = db_conn.prepareStatement("DELETE FROM user_disabled_items WHERE user_id=?");
			ps.setInt(1, newUserId);
			ps.execute();
			ps.close();

			ps = db_conn.prepareStatement("INSERT INTO user_disabled_items (user_id, item_name) VALUES (?, ?)");
			ps.setInt(1, newUserId);

			for (Map.Entry<String, String> entry : permsTable.entrySet())
			{
				String itemName = entry.getKey();

				ps.setString(2, itemName);
				ps.execute();

				Logger.debug(NtlmLogonAction.class, "copyUserDisabledItems userId="+newUserId+" itemName="+itemName);
			}

			ps.close();

			db_conn.close();
			rs = null;
			ps = null;
			db_conn = null;
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		finally
		{
			try
			{
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
				if (db_conn != null)
					db_conn.close();
			}
			catch (Exception ex2)
			{
			}
		}
	}

	/**
	 *
	 * Odstrani zo zadaneho stringu znaky, ktore
	 * by mohli zmenit formatovanie a vyznam
	 * LDAPovskeho filtru
	 *
	 * @param originalString
	 * @return escaped {@link String}
	 */
	public static String escapeLdapString(String originalString)
	{
		StringBuilder escapedString = new StringBuilder( originalString.length() );

		for (int i=0;i<originalString.length();i++)
		{
			Character toBeInvestigated = originalString.charAt(i);
			if ( Character.isLetterOrDigit( toBeInvestigated ) ||
					toBeInvestigated.toString().matches("^["+Constants.getString("ldapLoginChars")+"]$") )
				escapedString.append(toBeInvestigated);

		}

		return escapedString.toString();
	}
}