From 6deafccb33a8e5dc65acae2ef84944c350b1974a Mon Sep 17 00:00:00 2001 From: Reto Breitenmoser Date: Sun, 8 Jul 2018 13:58:37 +0200 Subject: [PATCH] [New] added config for privilege handler, add ldap handler --- .../handler/DefaultPrivilegeHandler.java | 4 +- .../handler/LdapPrivilegeHandler.java | 160 ++++++++++++++++++ .../helper/PrivilegeInitializationHelper.java | 20 ++- .../internal/PrivilegeContainerModel.java | 21 +++ .../xml/PrivilegeConfigDomWriter.java | 9 + .../xml/PrivilegeConfigSaxReader.java | 6 + 6 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 li.strolch.privilege/src/main/java/li/strolch/privilege/handler/LdapPrivilegeHandler.java diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java index 8201c30c8..e7ac760a2 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/DefaultPrivilegeHandler.java @@ -1255,7 +1255,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { * @throws InvalidCredentialsException * if the given credentials are invalid, the user does not exist, or has no password set */ - private synchronized User checkCredentialsAndUserState(String username, char[] password) + protected synchronized User checkCredentialsAndUserState(String username, char[] password) throws InvalidCredentialsException, AccessDeniedException { // and validate the password @@ -1519,7 +1519,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { } /** - * Initializes the concrete {@link EncryptionHandler}. The passed parameter map contains any configuration this + * Initializes the concrete {@link PrivilegeHandler}. The passed parameter map contains any configuration this * {@link PrivilegeHandler} might need. This method may only be called once and this must be enforced by the * concrete implementation * diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/LdapPrivilegeHandler.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/LdapPrivilegeHandler.java new file mode 100644 index 000000000..4abd1e7f0 --- /dev/null +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/handler/LdapPrivilegeHandler.java @@ -0,0 +1,160 @@ +package li.strolch.privilege.handler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +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.LdapName; +import javax.naming.ldap.Rdn; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import li.strolch.privilege.base.AccessDeniedException; +import li.strolch.privilege.base.InvalidCredentialsException; +import li.strolch.privilege.model.UserState; +import li.strolch.privilege.model.internal.User; +import li.strolch.privilege.policy.PrivilegePolicy; +import li.strolch.utils.dbc.DBC; + +public class LdapPrivilegeHandler extends DefaultPrivilegeHandler { + + protected static final Logger logger = LoggerFactory.getLogger(LdapPrivilegeHandler.class); + + private Map> rolesForLdapGroups; + private String providerUrl; + private String searchBase; + private String location; + private String domain; + private String adminUsers; + + @Override + public synchronized void initialize(Map parameterMap, EncryptionHandler encryptionHandler, + PersistenceHandler persistenceHandler, UserChallengeHandler userChallengeHandler, + SingleSignOnHandler ssoHandler, Map> policyMap) { + + rolesForLdapGroups = getLdapGroupToRolesMappingFromConfig(parameterMap); + + this.providerUrl = parameterMap.get("providerUrl"); + this.searchBase = parameterMap.get("searchBase"); + this.location = parameterMap.get("location"); + this.domain = parameterMap.get("domain"); + this.adminUsers = parameterMap.get("adminUsers"); + + super.initialize(parameterMap, encryptionHandler, persistenceHandler, userChallengeHandler, ssoHandler, + policyMap); + } + + @Override + protected synchronized User checkCredentialsAndUserState(String username, char[] password) + throws InvalidCredentialsException, AccessDeniedException { + // Set up the environment for creating the initial context + Hashtable env = new Hashtable<>(); + + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, providerUrl); + + // Authenticate + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, username + domain); + env.put(Context.SECURITY_CREDENTIALS, new String(password)); + + logger.info("User {} tries to login on ldap", username + domain); + + String memberOfLdapString = ""; + Set strolchRoles = new HashSet<>(); + + // Create the initial context + try { + DirContext ctx = new InitialDirContext(env); + + //Create the search controls + SearchControls searchCtls = new SearchControls(); + + //Specify the search scope + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchFilter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + domain + + "))"; + + // Search for objects using the filter + NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls); + + //Loop through the search results + while (answer.hasMoreElements()) { + SearchResult sr = (SearchResult) answer.next(); + + Attributes attrs = sr.getAttributes(); + memberOfLdapString = (attrs.get("memberOf") != null) ? attrs.get("memberOf").get().toString() : ""; + + // extract group name from ldap string -> CN=groupname,OU=company,DC=domain,DC=country + LdapName memberOfName = new LdapName(memberOfLdapString); + for (Rdn rdn : memberOfName.getRdns()) { + if (rdn.getType().equalsIgnoreCase("CN")) { + String groupName = rdn.getValue().toString(); + Set foundStrolchRoles = rolesForLdapGroups.get(groupName); + if (foundStrolchRoles != null) + strolchRoles.addAll(foundStrolchRoles); + break; + } + } + + logger.info("User " + username + " is member of groups: " + memberOfLdapString); + } + + ctx.close(); + } catch (NamingException e) { + throw new AccessDeniedException("Could not login with user: " + username + domain + " on Ldap"); + } + + Map properties = new HashMap<>(); + + // this must be changed, because the location param must be taken from the logged in person + properties.put("location", location); + + if (adminUsers.contains(username)) { + strolchRoles = rolesForLdapGroups.get("admin"); + } + + return new User(username, username, null, null, null, -1, -1, username, username, UserState.ENABLED, + strolchRoles, Locale.GERMAN, properties); + + } + + private Map> getLdapGroupToRolesMappingFromConfig(Map params) { + Map> result = new HashMap<>(); + + String rolesForLdapGroups = params.get("rolesForLdapGroups"); + + DBC.PRE.assertNotEmpty("No roles mapping for ldap directory groups defined (param: rolesForLdapGroups)", + rolesForLdapGroups); + + // rolesForLdapGroups = admin=StrolchAdmin,UserPrivileges;user=UserPrivileges + String[] ldapGroupRoles = rolesForLdapGroups.split(";"); + + for (String ldapGroupRole : ldapGroupRoles) { + // admin=StrolchAdmin,UserPrivileges + String[] splittedGroupRoles = ldapGroupRole.split("="); + String ldapGroupName = splittedGroupRoles[0]; + // StrolchAdmin,UserPrivileges + Set roleNames = new HashSet<>(Arrays.asList(splittedGroupRoles[1].split(","))); + + result.put(ldapGroupName, roleNames); + } + + return result; + } + +} diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PrivilegeInitializationHelper.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PrivilegeInitializationHelper.java index 962c7cffc..1a99674d2 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PrivilegeInitializationHelper.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/helper/PrivilegeInitializationHelper.java @@ -22,7 +22,12 @@ import java.text.MessageFormat; import java.util.Map; import li.strolch.privilege.base.PrivilegeException; -import li.strolch.privilege.handler.*; +import li.strolch.privilege.handler.DefaultPrivilegeHandler; +import li.strolch.privilege.handler.EncryptionHandler; +import li.strolch.privilege.handler.PersistenceHandler; +import li.strolch.privilege.handler.PrivilegeHandler; +import li.strolch.privilege.handler.SingleSignOnHandler; +import li.strolch.privilege.handler.UserChallengeHandler; import li.strolch.privilege.model.internal.PrivilegeContainerModel; import li.strolch.privilege.policy.PrivilegePolicy; import li.strolch.privilege.xml.PrivilegeConfigSaxReader; @@ -149,10 +154,19 @@ public class PrivilegeInitializationHelper { throw new PrivilegeException(msg, e); } } - + // initialize privilege handler - DefaultPrivilegeHandler privilegeHandler = new DefaultPrivilegeHandler(); + DefaultPrivilegeHandler privilegeHandler; parameterMap = containerModel.getParameterMap(); + + if (containerModel.getPrivilegeHandlerClassName() == null) { + privilegeHandler = new DefaultPrivilegeHandler(); + } else { + String privilegeHandlerClassName = containerModel.getPrivilegeHandlerClassName(); + privilegeHandler = ClassHelper.instantiateClass(privilegeHandlerClassName); + parameterMap.putAll(containerModel.getPrivilegeHandlerParameterMap()); + } + Map> policyMap = containerModel.getPolicies(); try { privilegeHandler diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/PrivilegeContainerModel.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/PrivilegeContainerModel.java index c42bf2ad2..6b61e7ba9 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/PrivilegeContainerModel.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/internal/PrivilegeContainerModel.java @@ -40,11 +40,13 @@ public class PrivilegeContainerModel { private String persistenceHandlerClassName; private String userChallengeHandlerClassName; private String ssoHandlerClassName; + private String privilegeHandlerClassName; private Map encryptionHandlerParameterMap; private Map persistenceHandlerParameterMap; private Map challengeHandlerParameterMap; private Map ssoHandlerParameterMap; + private Map privilegeHandlerParameterMap; private Map parameterMap; @@ -56,6 +58,7 @@ public class PrivilegeContainerModel { this.persistenceHandlerParameterMap = new HashMap<>(); this.challengeHandlerParameterMap = new HashMap<>(); this.ssoHandlerParameterMap = new HashMap<>(); + this.privilegeHandlerParameterMap = new HashMap<>(); } public Map getParameterMap() { @@ -113,6 +116,14 @@ public class PrivilegeContainerModel { public void setSsoHandlerClassName(String ssoHandlerClassName) { this.ssoHandlerClassName = ssoHandlerClassName; } + + public String getPrivilegeHandlerClassName() { + return this.privilegeHandlerClassName; + } + + public void setPrivilegeHandlerClassName(String privilegeHandlerClassName) { + this.privilegeHandlerClassName = privilegeHandlerClassName; + } public Map getUserChallengeHandlerParameterMap() { return this.challengeHandlerParameterMap; @@ -129,6 +140,14 @@ public class PrivilegeContainerModel { public void setSsoHandlerParameterMap(Map ssoHandlerParameterMap) { this.ssoHandlerParameterMap = ssoHandlerParameterMap; } + + public Map getPrivilegeHandlerParameterMap() { + return this.privilegeHandlerParameterMap; + } + + public void setPrivilegeHandlerParameterMap(Map privilegeHandlerParameterMap) { + this.privilegeHandlerParameterMap = privilegeHandlerParameterMap; + } public void addPolicy(String privilegeName, String policyClassName) { @@ -181,6 +200,8 @@ public class PrivilegeContainerModel { builder.append(this.challengeHandlerParameterMap.size()); builder.append(", ssoHandlerParameterMap="); builder.append(this.ssoHandlerParameterMap.size()); + builder.append(", privilegeHandlerParameterMap="); + builder.append(this.privilegeHandlerParameterMap.size()); builder.append(", parameterMap="); builder.append(this.parameterMap.size()); builder.append(", policies="); diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigDomWriter.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigDomWriter.java index 4b1d7e31c..cbe3766ca 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigDomWriter.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigDomWriter.java @@ -88,6 +88,15 @@ public class PrivilegeConfigDomWriter { // Parameters fillParameterMap(doc, ssoHandlerElem, this.containerModel.getSsoHandlerParameterMap()); } + + // create PrivilegeHandler + if (this.containerModel.getSsoHandlerClassName() != null) { + Element privilegeHandlerElem = doc.createElement(XML_HANDLER_PRIVILEGE); + containerElement.appendChild(privilegeHandlerElem); + privilegeHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getPrivilegeHandlerClassName()); + // Parameters + fillParameterMap(doc, privilegeHandlerElem, this.containerModel.getPrivilegeHandlerParameterMap()); + } // Policies Element policiesElem = doc.createElement(XML_POLICIES); diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigSaxReader.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigSaxReader.java index 25450531d..3f231ee32 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigSaxReader.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeConfigSaxReader.java @@ -109,6 +109,10 @@ public class PrivilegeConfigSaxReader extends DefaultHandler { this.currentElement = qName; String className = attributes.getValue(XmlConstants.XML_ATTR_CLASS); getContainerModel().setSsoHandlerClassName(className); + } else if (qName.equals(XmlConstants.XML_HANDLER_PRIVILEGE)) { + this.currentElement = qName; + String className = attributes.getValue(XmlConstants.XML_ATTR_CLASS); + getContainerModel().setPrivilegeHandlerClassName(className); } } @@ -129,6 +133,8 @@ public class PrivilegeConfigSaxReader extends DefaultHandler { getContainerModel().setUserChallengeHandlerParameterMap(parametersChild.getParameterMap()); } else if (this.currentElement.equals(XmlConstants.XML_HANDLER_SSO)) { getContainerModel().setSsoHandlerParameterMap(parametersChild.getParameterMap()); + } else if (this.currentElement.equals(XmlConstants.XML_HANDLER_PRIVILEGE)) { + getContainerModel().setPrivilegeHandlerParameterMap(parametersChild.getParameterMap()); } } }