X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=security%2Fruntime%2Forg.argeo.security.ldap%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fsecurity%2Fldap%2Fjcr%2FJcrUserDetailsContextMapper.java;h=b6657f0c597998dc015290ac713d7e1e6651161e;hb=8b8ee149b20e2578a55e17413fa5f7399ff7ba14;hp=6b166d5c5613dfd58483ccc2382758d40e2e5c1a;hpb=7f23c34bcf51716cfb8f3853d47680035747052f;p=lgpl%2Fargeo-commons.git diff --git a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java index 6b166d5c5..b6657f0c5 100644 --- a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java +++ b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java @@ -3,15 +3,17 @@ package org.argeo.security.ldap.jcr; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.Random; +import java.util.SortedSet; import java.util.concurrent.Executor; import javax.jcr.Node; +import javax.jcr.Property; import javax.jcr.RepositoryException; import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -21,6 +23,7 @@ import org.argeo.jcr.JcrUtils; import org.argeo.security.jcr.JcrUserDetails; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; +import org.springframework.security.BadCredentialsException; import org.springframework.security.GrantedAuthority; import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.providers.encoding.PasswordEncoder; @@ -49,6 +52,9 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, private PasswordEncoder passwordEncoder; private final Random random; + /** 0 is always sync */ + private Long syncLatency = 10 * 60 * 1000l; + public JcrUserDetailsContextMapper() { random = createRandom(); } @@ -63,8 +69,10 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, public UserDetails mapUserFromContext(final DirContextOperations ctx, final String username, GrantedAuthority[] authorities) { - // if (repository == null) - // throw new ArgeoException("No JCR repository registered"); + if (ctx == null) + throw new ArgeoException("No LDAP information found for user " + + username); + final StringBuffer userHomePathT = new StringBuffer(""); Runnable action = new Runnable() { public void run() { @@ -75,56 +83,100 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, if (SecurityContextHolder.getContext().getAuthentication() == null) { // authentication - systemExecutor.execute(action); - JcrUtils.logoutQuietly(session); + try { + systemExecutor.execute(action); + } finally { + JcrUtils.logoutQuietly(session); + } } else { // authenticated user action.run(); } // password - byte[] arr = (byte[]) ctx - .getAttributeSortedStringSet(passwordAttribute).first(); + SortedSet passwordAttributes = ctx + .getAttributeSortedStringSet(passwordAttribute); + String password; + if (passwordAttributes == null || passwordAttributes.size() == 0) { + throw new ArgeoException("No password found for user " + username); + } else { + byte[] arr = (byte[]) passwordAttributes.first(); + password = new String(arr); + // erase password + Arrays.fill(arr, (byte) 0); + } JcrUserDetails userDetails = new JcrUserDetails( - userHomePathT.toString(), username, new String(arr), true, - true, true, true, authorities); - // erase password - Arrays.fill(arr, (byte) 0); + userHomePathT.toString(), username, password, true, true, true, + true, authorities); return userDetails; } /** @return path to the user home node */ - protected String mapLdapToJcr(String username, DirContextOperations ctx) { - // Session session = null; + protected synchronized String mapLdapToJcr(String username, + DirContextOperations ctx) { + String usernameLdap = ctx.getStringAttribute(usernameAttribute); + // log.debug("username=" + username + ", usernameLdap=" + usernameLdap); + if (!username.equals(usernameLdap)) { + String msg = "Provided username '" + username + + "' is different from username stored in LDAP '" + + usernameLdap + "'"; + // we log it because the exception may not be displayed + log.error(msg); + throw new BadCredentialsException(msg); + } + try { - // Repository nodeRepo = JcrUtils.getRepositoryByAlias( - // repositoryFactory, ArgeoJcrConstants.ALIAS_NODE); - // session = nodeRepo.login(); + Node userHome = JcrUtils.getUserHome(session, username); - if (userHome == null) + boolean justCreatedHome = false; + if (userHome == null) { userHome = JcrUtils.createUserHome(session, homeBasePath, username); + justCreatedHome = true; + } String userHomePath = userHome.getPath(); - Node userProfile = userHome.getNode(ARGEO_PROFILE); + Node userProfile; // = userHome.getNode(ARGEO_PROFILE); if (userHome.hasNode(ARGEO_PROFILE)) { userProfile = userHome.getNode(ARGEO_PROFILE); + if (syncLatency != 0 && !justCreatedHome) { + Calendar lastModified = userProfile.getProperty( + Property.JCR_LAST_MODIFIED).getDate(); + long timeSinceLastUpdate = System.currentTimeMillis() + - lastModified.getTimeInMillis(); + if (timeSinceLastUpdate < syncLatency)// skip sync + return userHomePath; + } } else { - userProfile = userHome.addNode(ARGEO_PROFILE); - userProfile.addMixin(NodeType.MIX_TITLE); - userProfile.addMixin(NodeType.MIX_CREATED); - userProfile.addMixin(NodeType.MIX_LAST_MODIFIED); + throw new ArgeoException("We should never reach this point"); + // userProfile = userHome.addNode(ARGEO_PROFILE); + // userProfile.addMixin(NodeType.MIX_TITLE); + // userProfile.addMixin(NodeType.MIX_CREATED); + // userProfile.addMixin(NodeType.MIX_LAST_MODIFIED); } + + session.getWorkspace().getVersionManager() + .checkout(userProfile.getPath()); for (String jcrProperty : propertyToAttributes.keySet()) ldapToJcr(userProfile, jcrProperty, ctx); + + // assign default values + if (!userProfile.hasProperty(Property.JCR_DESCRIPTION)) + userProfile.setProperty(Property.JCR_DESCRIPTION, ""); + if (!userProfile.hasProperty(Property.JCR_TITLE)) + userProfile.setProperty(Property.JCR_TITLE, userProfile + .getProperty(ARGEO_FIRST_NAME).getString() + + " " + + userProfile.getProperty(ARGEO_LAST_NAME).getString()); + JcrUtils.updateLastModified(userProfile); session.save(); - if (log.isDebugEnabled()) - log.debug("Mapped " + ctx.getDn() + " to " + userProfile); + session.getWorkspace().getVersionManager() + .checkin(userProfile.getPath()); + if (log.isTraceEnabled()) + log.trace("Mapped " + ctx.getDn() + " to " + userProfile); return userHomePath; - } catch (RepositoryException e) { + } catch (Exception e) { JcrUtils.discardQuietly(session); throw new ArgeoException("Cannot synchronize JCR and LDAP", e); - } finally { - // JcrUtils.logoutQuietly(session); } } @@ -139,26 +191,17 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, encodePassword(user.getPassword())); final JcrUserDetails jcrUserDetails = (JcrUserDetails) user; - // systemExecutor.execute(new Runnable() { - // public void run() { - // Session session = null; try { - // Repository nodeRepo = JcrUtils.getRepositoryByAlias( - // repositoryFactory, ArgeoJcrConstants.ALIAS_NODE); - // session = nodeRepo.login(); Node userProfile = session.getNode(jcrUserDetails.getHomePath() + '/' + ARGEO_PROFILE); for (String jcrProperty : propertyToAttributes.keySet()) jcrToLdap(userProfile, jcrProperty, ctx); - if (log.isDebugEnabled()) - log.debug("Mapped " + userProfile + " to " + ctx.getDn()); + + if (log.isTraceEnabled()) + log.trace("Mapped " + userProfile + " to " + ctx.getDn()); } catch (RepositoryException e) { throw new ArgeoException("Cannot synchronize JCR and LDAP", e); - } finally { - // session.logout(); } - // } - // }); } protected String encodePassword(String password) { @@ -183,9 +226,16 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, + jcrProperty); String value = ctx.getStringAttribute(ldapAttribute); - if (value == null) - return; - userProfile.setProperty(jcrProperty, value); + String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile + .getProperty(jcrProperty).getString() : null; + if (value != null && jcrValue != null) { + if (!value.equals(jcrValue)) + userProfile.setProperty(jcrProperty, value); + } else if (value != null && jcrValue == null) { + userProfile.setProperty(jcrProperty, value); + } else if (value == null && jcrValue != null) { + userProfile.setProperty(jcrProperty, value); + } } catch (Exception e) { throw new ArgeoException("Cannot map JCR property " + jcrProperty + " from LDAP", e); @@ -195,10 +245,6 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, protected void jcrToLdap(Node userProfile, String jcrProperty, DirContextOperations ctx) { try { - if (!userProfile.hasProperty(jcrProperty)) - return; - String value = userProfile.getProperty(jcrProperty).getString(); - String ldapAttribute; if (propertyToAttributes.containsKey(jcrProperty)) ldapAttribute = propertyToAttributes.get(jcrProperty); @@ -206,6 +252,24 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, throw new ArgeoException( "No LDAP attribute mapped for JCR proprty " + jcrProperty); + + // fix issue with empty 'sn' in LDAP + if (ldapAttribute.equals("sn") + && (!userProfile.hasProperty(jcrProperty) || userProfile + .getProperty(jcrProperty).getString().trim() + .equals(""))) + userProfile.setProperty(jcrProperty, "empty"); + + if (ldapAttribute.equals("description")) { + String value = userProfile.getProperty(jcrProperty).getString(); + if (value.trim().equals("")) + return; + } + + if (!userProfile.hasProperty(jcrProperty)) + return; + String value = userProfile.getProperty(jcrProperty).getString(); + ctx.setAttributeValue(ldapAttribute, value); } catch (Exception e) { throw new ArgeoException("Cannot map JCR property " + jcrProperty @@ -225,16 +289,6 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, this.homeBasePath = homeBasePath; } - // public void register(RepositoryFactory repositoryFactory, - // Map parameters) { - // this.repositoryFactory = repositoryFactory; - // } - // - // public void unregister(RepositoryFactory repositoryFactory, - // Map parameters) { - // this.repositoryFactory = null; - // } - public void setUsernameAttribute(String usernameAttribute) { this.usernameAttribute = usernameAttribute; } @@ -255,4 +309,11 @@ public class JcrUserDetailsContextMapper implements UserDetailsContextMapper, this.session = session; } + /** + * Time in ms during which the LDAP server is not checked. 0 is always sync. + */ + public void setSyncLatency(Long syncLatency) { + this.syncLatency = syncLatency; + } + }