From 30536cb11cc47583a1aa7384909e95f60de73e8d Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Fri, 28 Sep 2018 13:28:08 +0200 Subject: [PATCH] [New] Allow to reload sessions for LDAP users --- .../handler/DefaultPrivilegeHandler.java | 5 + .../handler/LdapPrivilegeHandler.java | 118 ++++++++++++------ .../li/strolch/privilege/model/UserState.java | 13 +- .../xml/PrivilegeRolesDomWriter.java | 19 ++- 4 files changed, 97 insertions(+), 58 deletions(-) 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 d5021f39b..8fcb14fe9 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 @@ -1223,6 +1223,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler { continue; } + if (user.getUserState() == UserState.DISABLED || user.getUserState() == UserState.EXPIRED) { + logger.error("Ignoring session data for disabled/expired user " + username); + continue; + } + Set userRoles = user.getRoles(); if (userRoles.isEmpty()) { logger.error("Ignoring session data for user " + username + " which has not roles defined!"); 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 index c3eb993a9..91b4752f2 100644 --- 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 @@ -2,6 +2,7 @@ package li.strolch.privilege.handler; import javax.naming.Context; import javax.naming.NamingEnumeration; +import javax.naming.NamingException; import javax.naming.directory.*; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; @@ -49,28 +50,30 @@ public class LdapPrivilegeHandler extends DefaultPrivilegeHandler { throws InvalidCredentialsException, AccessDeniedException { // first see if this is a local user - if (this.persistenceHandler.getUser(username) != null) + User internalUser = this.persistenceHandler.getUser(username); + if (internalUser != null && internalUser.getUserState() != UserState.REMOTE) return super.checkCredentialsAndUserState(username, password); // 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); + env.put(Context.PROVIDER_URL, this.providerUrl); // Authenticate env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, username + domain); + env.put(Context.SECURITY_PRINCIPAL, username + this.domain); env.put(Context.SECURITY_CREDENTIALS, new String(password)); - logger.info("User {} tries to login on ldap", username + domain); + logger.info("User {} tries to login on ldap", username + this.domain); String memberOfLdapString = ""; Set strolchRoles = new HashSet<>(); // Create the initial context + DirContext ctx = null; try { - DirContext ctx = new InitialDirContext(env); + ctx = new InitialDirContext(env); //Create the search controls SearchControls searchCtls = new SearchControls(); @@ -79,58 +82,91 @@ public class LdapPrivilegeHandler extends DefaultPrivilegeHandler { searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String searchFilter = - "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + domain + "))"; + "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + this.domain + "))"; // Search for objects using the filter - NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls); + NamingEnumeration answer = ctx.search(this.searchBase, searchFilter, searchCtls); - //Loop through the search results - while (answer.hasMoreElements()) { - SearchResult sr = (SearchResult) answer.next(); + if (!answer.hasMore()) + throw new AccessDeniedException( + "Could not login with user: " + username + this.domain + " on Ldap: no LDAP Data"); - Attributes attrs = sr.getAttributes(); - Attribute groupMembers = attrs.get("memberOf"); + SearchResult sr = (SearchResult) answer.next(); + if (answer.hasMore()) + throw new AccessDeniedException( + "Could not login with user: " + username + this.domain + " on Ldap: Multiple LDAP Data"); - if (groupMembers != null) { - for (int i = 0; i < groupMembers.size(); i++) { + Attributes attrs = sr.getAttributes(); - memberOfLdapString = attrs.get("memberOf").get(i).toString(); + Attribute sAMAccountName = attrs.get("sAMAccountName"); + if (sAMAccountName == null || !username.equals(sAMAccountName.get().toString())) + throw new AccessDeniedException( + "Could not login with user: " + username + this.domain + " on Ldap: Wrong LDAP Data"); - // 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; - } + Attribute givenName = attrs.get("givenName"); + Attribute sn = attrs.get("sn"); + + String firstName = givenName == null ? username : givenName.get().toString(); + String lastName = sn == null ? username : sn.get().toString(); + + // evaluate roles for this user + Attribute groupMembers = attrs.get("memberOf"); + if (groupMembers != null) { + for (int i = 0; i < groupMembers.size(); i++) { + + memberOfLdapString = attrs.get("memberOf").get(i).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 = this.rolesForLdapGroups.get(groupName); + if (foundStrolchRoles != null) + strolchRoles.addAll(foundStrolchRoles); + break; } - - logger.info("User " + username + " is member of groups: " + memberOfLdapString); } + + logger.info("User " + username + " is member of groups: " + memberOfLdapString); } } - ctx.close(); + Map properties = new HashMap<>(); + + // this must be changed, because the location param must be taken from the logged in person + properties.put("location", this.location); + + // see if this is an admin user + if (this.adminUsers.contains(username)) + strolchRoles = this.rolesForLdapGroups.get("admin"); + + User user = new User(username, username, null, null, null, -1, -1, firstName, lastName, UserState.REMOTE, + strolchRoles, Locale.GERMAN, properties); + + // persist this user + if (internalUser == null) + this.persistenceHandler.addUser(user); + else + this.persistenceHandler.replaceUser(user); + + if (this.autoPersistOnUserChangesData) + this.persistenceHandler.persist(); + + return user; + } catch (Exception e) { logger.error("Could not login with user: " + username + domain + " on Ldap", e); throw new AccessDeniedException("Could not login with user: " + username + domain + " on Ldap", e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (NamingException e) { + logger.error("Failed to close DirContext", e); + } + } } - - 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) { diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/UserState.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/UserState.java index 12da7a2a6..29fab2bfd 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/model/UserState.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/model/UserState.java @@ -1,12 +1,12 @@ /* * Copyright 2013 Robert von Burg - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,9 +26,8 @@ import li.strolch.privilege.model.internal.User; *
  • DISABLED - the user been disabled by an administrator
  • *
  • EXPIRED - the user has automatically expired through a predefined time
  • * - * + * * @author Robert von Burg - * */ public enum UserState { /** @@ -40,6 +39,10 @@ public enum UserState { * thus login */ ENABLED, + /** + * the user is only available remotely, e.g. over LDAP, but allows for session restoration + */ + REMOTE, /** * the user been disabled by an administrator */ diff --git a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeRolesDomWriter.java b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeRolesDomWriter.java index f973f7cda..75e974e33 100644 --- a/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeRolesDomWriter.java +++ b/li.strolch.privilege/src/main/java/li/strolch/privilege/xml/PrivilegeRolesDomWriter.java @@ -1,12 +1,12 @@ /* * Copyright 2013 Robert von Burg - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,15 +16,15 @@ package li.strolch.privilege.xml; import java.io.File; +import java.util.Comparator; import java.util.List; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - import li.strolch.privilege.helper.XmlConstants; import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.internal.Role; import li.strolch.utils.helper.XmlHelper; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** * @author Robert von Burg @@ -34,11 +34,6 @@ public class PrivilegeRolesDomWriter { private List roles; private File modelFile; - /** - * @param users - * @param roles - * @param modelFile - */ public PrivilegeRolesDomWriter(List roles, File modelFile) { this.roles = roles; this.modelFile = modelFile; @@ -51,7 +46,7 @@ public class PrivilegeRolesDomWriter { Element rootElement = doc.createElement(XmlConstants.XML_ROLES); doc.appendChild(rootElement); - this.roles.stream().sorted((r1, r2) -> r1.getName().compareTo(r2.getName())).forEach(role -> { + this.roles.stream().sorted(Comparator.comparing(Role::getName)).forEach(role -> { // create the role element Element roleElement = doc.createElement(XmlConstants.XML_ROLE);