[New] Allow to reload sessions for LDAP users

This commit is contained in:
Robert von Burg 2018-09-28 13:28:08 +02:00
parent 6e87ffc078
commit 30536cb11c
4 changed files with 97 additions and 58 deletions

View File

@ -1223,6 +1223,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
continue; continue;
} }
if (user.getUserState() == UserState.DISABLED || user.getUserState() == UserState.EXPIRED) {
logger.error("Ignoring session data for disabled/expired user " + username);
continue;
}
Set<String> userRoles = user.getRoles(); Set<String> userRoles = user.getRoles();
if (userRoles.isEmpty()) { if (userRoles.isEmpty()) {
logger.error("Ignoring session data for user " + username + " which has not roles defined!"); logger.error("Ignoring session data for user " + username + " which has not roles defined!");

View File

@ -2,6 +2,7 @@ package li.strolch.privilege.handler;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*; import javax.naming.directory.*;
import javax.naming.ldap.LdapName; import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn; import javax.naming.ldap.Rdn;
@ -49,28 +50,30 @@ public class LdapPrivilegeHandler extends DefaultPrivilegeHandler {
throws InvalidCredentialsException, AccessDeniedException { throws InvalidCredentialsException, AccessDeniedException {
// first see if this is a local user // 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); return super.checkCredentialsAndUserState(username, password);
// Set up the environment for creating the initial context // Set up the environment for creating the initial context
Hashtable<String, String> env = new Hashtable<>(); Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 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 // Authenticate
env.put(Context.SECURITY_AUTHENTICATION, "simple"); 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)); 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 = ""; String memberOfLdapString = "";
Set<String> strolchRoles = new HashSet<>(); Set<String> strolchRoles = new HashSet<>();
// Create the initial context // Create the initial context
DirContext ctx = null;
try { try {
DirContext ctx = new InitialDirContext(env); ctx = new InitialDirContext(env);
//Create the search controls //Create the search controls
SearchControls searchCtls = new SearchControls(); SearchControls searchCtls = new SearchControls();
@ -79,58 +82,91 @@ public class LdapPrivilegeHandler extends DefaultPrivilegeHandler {
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = String searchFilter =
"(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + domain + "))"; "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + this.domain + "))";
// Search for objects using the filter // Search for objects using the filter
NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls); NamingEnumeration<SearchResult> answer = ctx.search(this.searchBase, searchFilter, searchCtls);
//Loop through the search results if (!answer.hasMore())
while (answer.hasMoreElements()) { throw new AccessDeniedException(
SearchResult sr = (SearchResult) answer.next(); "Could not login with user: " + username + this.domain + " on Ldap: no LDAP Data");
Attributes attrs = sr.getAttributes(); SearchResult sr = (SearchResult) answer.next();
Attribute groupMembers = attrs.get("memberOf"); if (answer.hasMore())
throw new AccessDeniedException(
"Could not login with user: " + username + this.domain + " on Ldap: Multiple LDAP Data");
if (groupMembers != null) { Attributes attrs = sr.getAttributes();
for (int i = 0; i < groupMembers.size(); i++) {
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 Attribute givenName = attrs.get("givenName");
LdapName memberOfName = new LdapName(memberOfLdapString); Attribute sn = attrs.get("sn");
for (Rdn rdn : memberOfName.getRdns()) {
if (rdn.getType().equalsIgnoreCase("CN")) { String firstName = givenName == null ? username : givenName.get().toString();
String groupName = rdn.getValue().toString(); String lastName = sn == null ? username : sn.get().toString();
Set<String> foundStrolchRoles = rolesForLdapGroups.get(groupName);
if (foundStrolchRoles != null) // evaluate roles for this user
strolchRoles.addAll(foundStrolchRoles); Attribute groupMembers = attrs.get("memberOf");
break; 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<String> 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<String, String> 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) { } catch (Exception e) {
logger.error("Could not login with user: " + username + domain + " on Ldap", 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); 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<String, String> 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<String, Set<String>> getLdapGroupToRolesMappingFromConfig(Map<String, String> params) { private Map<String, Set<String>> getLdapGroupToRolesMappingFromConfig(Map<String, String> params) {

View File

@ -1,12 +1,12 @@
/* /*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch> * Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -26,9 +26,8 @@ import li.strolch.privilege.model.internal.User;
* <li>DISABLED - the user been disabled by an administrator</li> * <li>DISABLED - the user been disabled by an administrator</li>
* <li>EXPIRED - the user has automatically expired through a predefined time</li> * <li>EXPIRED - the user has automatically expired through a predefined time</li>
* </ul> * </ul>
* *
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
*
*/ */
public enum UserState { public enum UserState {
/** /**
@ -40,6 +39,10 @@ public enum UserState {
* thus login * thus login
*/ */
ENABLED, ENABLED,
/**
* the user is only available remotely, e.g. over LDAP, but allows for session restoration
*/
REMOTE,
/** /**
* the user been disabled by an administrator * the user been disabled by an administrator
*/ */

View File

@ -1,12 +1,12 @@
/* /*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch> * Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,15 +16,15 @@
package li.strolch.privilege.xml; package li.strolch.privilege.xml;
import java.io.File; import java.io.File;
import java.util.Comparator;
import java.util.List; import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import li.strolch.privilege.helper.XmlConstants; import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.IPrivilege; import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.internal.Role; import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.XmlHelper; import li.strolch.utils.helper.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/** /**
* @author Robert von Burg <eitch@eitchnet.ch> * @author Robert von Burg <eitch@eitchnet.ch>
@ -34,11 +34,6 @@ public class PrivilegeRolesDomWriter {
private List<Role> roles; private List<Role> roles;
private File modelFile; private File modelFile;
/**
* @param users
* @param roles
* @param modelFile
*/
public PrivilegeRolesDomWriter(List<Role> roles, File modelFile) { public PrivilegeRolesDomWriter(List<Role> roles, File modelFile) {
this.roles = roles; this.roles = roles;
this.modelFile = modelFile; this.modelFile = modelFile;
@ -51,7 +46,7 @@ public class PrivilegeRolesDomWriter {
Element rootElement = doc.createElement(XmlConstants.XML_ROLES); Element rootElement = doc.createElement(XmlConstants.XML_ROLES);
doc.appendChild(rootElement); 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 // create the role element
Element roleElement = doc.createElement(XmlConstants.XML_ROLE); Element roleElement = doc.createElement(XmlConstants.XML_ROLE);