X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=security%2Fruntime%2Forg.argeo.security.core%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fsecurity%2Fldap%2FArgeoSecurityDaoLdap.java;h=6aa31bbdc6620ef05dd8e354af0df6a0ca7f9718;hb=814f2e01731aca304d826d070922d51fd84efa29;hp=ae1fceea319ea0a122ab8bd0ef425c8082a1c194;hpb=ec59a58bc368dc922a454d52eb70bb91dfd68793;p=lgpl%2Fargeo-commons.git diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/ldap/ArgeoSecurityDaoLdap.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/ldap/ArgeoSecurityDaoLdap.java index ae1fceea3..6aa31bbdc 100644 --- a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/ldap/ArgeoSecurityDaoLdap.java +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/ldap/ArgeoSecurityDaoLdap.java @@ -1,9 +1,28 @@ +/* + * Copyright (C) 2010 Mathieu Baudier + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.argeo.security.ldap; -import static org.argeo.security.core.ArgeoUserDetails.createBasicArgeoUser; +import static org.argeo.security.core.ArgeoUserDetails.createSimpleArgeoUser; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Random; import javax.naming.Name; import javax.naming.NamingException; @@ -11,96 +30,196 @@ import javax.naming.directory.DirContext; import org.argeo.security.ArgeoSecurityDao; import org.argeo.security.ArgeoUser; +import org.argeo.security.SimpleArgeoUser; import org.argeo.security.core.ArgeoUserDetails; import org.springframework.beans.factory.InitializingBean; import org.springframework.ldap.core.ContextExecutor; import org.springframework.ldap.core.ContextMapper; -import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.BaseLdapPathContextSource; +import org.springframework.security.context.SecurityContextHolder; import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper; +import org.springframework.security.ldap.LdapAuthoritiesPopulator; import org.springframework.security.ldap.LdapUsernameToDnMapper; import org.springframework.security.ldap.LdapUtils; +import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder; import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsManager; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.security.userdetails.ldap.LdapUserDetailsManager; +import org.springframework.security.userdetails.ldap.LdapUserDetailsService; +import org.springframework.security.userdetails.ldap.UserDetailsContextMapper; public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean { // private final static Log log = LogFactory.getLog(UserDaoLdap.class); private UserDetailsManager userDetailsManager; - private ArgeoLdapAuthoritiesPopulator authoritiesPopulator; - private String userBase = "ou=users"; - private String usernameAttribute = "uid"; + private LdapAuthoritiesPopulator authoritiesPopulator; + private String userBase = "ou=People"; + private String usernameAttributeName = "uid"; + private String groupBase = "ou=Roles"; + private String[] groupClasses = { "top", "groupOfNames" }; + private String groupRoleAttributeName = "cn"; + private String groupMemberAttributeName = "member"; + private String defaultRole = "ROLE_USER"; + private String rolePrefix = "ROLE_"; + private final BaseLdapPathContextSource contextSource; private final LdapTemplate ldapTemplate; - /* TODO: factorize with user details manager */ private LdapUsernameToDnMapper usernameMapper = null; + private UserDetailsContextMapper userDetailsMapper; + private LdapUserDetailsService ldapUserDetailsService; + private List userNatureMappers; + + private LdapShaPasswordEncoder ldapShaPasswordEncoder = new LdapShaPasswordEncoder(); + private Random random; + + public ArgeoSecurityDaoLdap(BaseLdapPathContextSource contextSource) { + this.contextSource = contextSource; + ldapTemplate = new LdapTemplate(this.contextSource); + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + random = new Random(System.currentTimeMillis()); + } + } + public void afterPropertiesSet() throws Exception { if (usernameMapper == null) usernameMapper = new DefaultLdapUsernameToDnMapper(userBase, - usernameAttribute); - } + usernameAttributeName); + + if (authoritiesPopulator == null) { + DefaultLdapAuthoritiesPopulator ap = new DefaultLdapAuthoritiesPopulator( + ldapTemplate.getContextSource(), groupBase); + ap.setDefaultRole(defaultRole); + ap.setGroupSearchFilter(groupMemberAttributeName + "={0}"); + authoritiesPopulator = ap; + } - public ArgeoSecurityDaoLdap(ContextSource contextSource) { - ldapTemplate = new LdapTemplate(contextSource); + if (userDetailsMapper == null) { + ArgeoUserDetailsContextMapper audm = new ArgeoUserDetailsContextMapper(); + audm.setUserNatureMappers(userNatureMappers); + userDetailsMapper = audm; + } + + if (userDetailsManager == null) { + LdapUserDetailsManager ludm = new LdapUserDetailsManager( + ldapTemplate.getContextSource()); + ludm.setGroupSearchBase(groupBase); + ludm.setUserDetailsMapper(userDetailsMapper); + ludm.setUsernameMapper(usernameMapper); + ludm.setGroupMemberAttributeName(groupMemberAttributeName); + userDetailsManager = ludm; + } + + if (ldapUserDetailsService == null) { + FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch( + userBase, "(" + usernameAttributeName + "={0})", + contextSource); + ldapUserDetailsService = new LdapUserDetailsService(ldapUserSearch, + authoritiesPopulator); + ldapUserDetailsService.setUserDetailsMapper(userDetailsMapper); + } } - public void create(ArgeoUser user) { + public synchronized void create(ArgeoUser user) { userDetailsManager.createUser(new ArgeoUserDetails(user)); } - public ArgeoUser getUser(String uname) { - return createBasicArgeoUser(getDetails(uname)); + public synchronized ArgeoUser getUser(String uname) { + SimpleArgeoUser user = createSimpleArgeoUser(getDetails(uname)); + user.setPassword(null); + return user; + } + + public synchronized ArgeoUser getUserWithPassword(String uname) { + return createSimpleArgeoUser(getDetails(uname)); } + // public ArgeoUser getCurrentUser() { + // ArgeoUser argeoUser = ArgeoUserDetails.securityContextUser(); + // if (argeoUser == null) + // return null; + // if (argeoUser.getRoles().contains(defaultRole)) + // argeoUser.getRoles().remove(defaultRole); + // return argeoUser; + // } + @SuppressWarnings("unchecked") - public List listUsers() { + public synchronized List listUsers() { List usernames = (List) ldapTemplate.listBindings( new DistinguishedName(userBase), new ContextMapper() { public Object mapFromContext(Object ctxArg) { DirContextAdapter ctx = (DirContextAdapter) ctxArg; - return ctx.getStringAttribute(usernameAttribute); + return ctx.getStringAttribute(usernameAttributeName); } }); List lst = new ArrayList(); for (String username : usernames) { - lst.add(createBasicArgeoUser(getDetails(username))); + lst.add(createSimpleArgeoUser(getDetails(username))); } return lst; } @SuppressWarnings("unchecked") public List listEditableRoles() { - return (List) ldapTemplate.listBindings(authoritiesPopulator - .getGroupSearchBase(), new ContextMapper() { - public Object mapFromContext(Object ctxArg) { - String groupName = ((DirContextAdapter) ctxArg) - .getStringAttribute(authoritiesPopulator - .getGroupRoleAttribute()); - String roleName = authoritiesPopulator - .convertGroupToRole(groupName); - return roleName; - } - }); - } - - public void update(ArgeoUser user) { - userDetailsManager.updateUser(new ArgeoUserDetails(user)); + return (List) ldapTemplate.listBindings(groupBase, + new ContextMapper() { + public Object mapFromContext(Object ctxArg) { + String groupName = ((DirContextAdapter) ctxArg) + .getStringAttribute(groupRoleAttributeName); + String roleName = convertGroupToRole(groupName); + return roleName; + } + }); } - public void delete(String username) { - userDetailsManager.deleteUser(username); + @SuppressWarnings("unchecked") + public List listUsersInRole(String role) { + return (List) ldapTemplate.lookup( + buildGroupDn(convertRoleToGroup(role)), new ContextMapper() { + public Object mapFromContext(Object ctxArg) { + DirContextAdapter ctx = (DirContextAdapter) ctxArg; + String[] userDns = ctx + .getStringAttributes(groupMemberAttributeName); + List lst = new ArrayList(); + for (String userDn : userDns) { + DistinguishedName dn = new DistinguishedName(userDn); + String username = dn + .getValue(usernameAttributeName); + lst.add(createSimpleArgeoUser(getDetails(username))); + } + return lst; + } + }); + } + + public synchronized void update(ArgeoUser user) { + ArgeoUserDetails argeoUserDetails = new ArgeoUserDetails(user); + userDetailsManager.updateUser(new ArgeoUserDetails(user)); + // refresh logged in user + if (ArgeoUserDetails.securityContextUser().getUsername() + .equals(argeoUserDetails.getUsername())) { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken(argeoUserDetails, + null, argeoUserDetails.getAuthorities())); + } } - public void updatePassword(String oldPassword, String newPassword) { - userDetailsManager.changePassword(oldPassword, newPassword); + public synchronized void delete(String username) { + userDetailsManager.deleteUser(username); } - public Boolean userExists(String username) { + public synchronized Boolean userExists(String username) { return userDetailsManager.userExists(username); } @@ -110,19 +229,19 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean .executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { - return LdapUtils.getFullDn(usernameMapper - .buildDn(superuserName), ctx); + return LdapUtils.getFullDn( + usernameMapper.buildDn(superuserName), ctx); } }); Name groupDn = buildGroupDn(group); DirContextAdapter context = new DirContextAdapter(); - context.setAttributeValues("objectClass", new String[] { "top", - "groupOfUniqueNames" }); + context.setAttributeValues("objectClass", groupClasses); context.setAttributeValue("cn", group); // Add superuser because cannot create empty group - context.setAttributeValue("uniqueMember", superuserDn.toString()); + context.setAttributeValue(groupMemberAttributeName, + superuserDn.toString()); ldapTemplate.bind(groupDn, context, null); } @@ -133,19 +252,36 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean ldapTemplate.unbind(dn); } + public Boolean isPasswordValid(String encoded, String raw) { + return ldapShaPasswordEncoder.isPasswordValid(encoded, raw, null); + } + + public String encodePassword(String raw) { + byte[] salt = null; + // TODO: check that Linux auth supports SSHA + // byte[] salt = new byte[16]; + // random.nextBytes(salt); + return ldapShaPasswordEncoder.encodePassword(raw, salt); + } + protected String convertRoleToGroup(String role) { - // FIXME: factorize with spring security String group = role; - if (group.startsWith("ROLE_")) { - group = group.substring("ROLE_".length()); + if (group.startsWith(rolePrefix)) { + group = group.substring(rolePrefix.length()); group = group.toLowerCase(); } return group; } + public String convertGroupToRole(String groupName) { + groupName = groupName.toUpperCase(); + + return rolePrefix + groupName; + } + protected Name buildGroupDn(String name) { - return new DistinguishedName("cn=" + name + "," - + authoritiesPopulator.getGroupSearchBase()); + return new DistinguishedName(groupRoleAttributeName + "=" + name + "," + + groupBase); } public void setUserDetailsManager(UserDetailsManager userDetailsManager) { @@ -156,16 +292,69 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean this.userBase = userBase; } - public void setUsernameAttribute(String usernameAttribute) { - this.usernameAttribute = usernameAttribute; + public void setUsernameAttributeName(String usernameAttribute) { + this.usernameAttributeName = usernameAttribute; } public void setAuthoritiesPopulator( - ArgeoLdapAuthoritiesPopulator authoritiesPopulator) { + LdapAuthoritiesPopulator authoritiesPopulator) { this.authoritiesPopulator = authoritiesPopulator; } protected UserDetails getDetails(String username) { return userDetailsManager.loadUserByUsername(username); } + + public void setGroupBase(String groupBase) { + this.groupBase = groupBase; + } + + public void setGroupRoleAttributeName(String groupRoleAttributeName) { + this.groupRoleAttributeName = groupRoleAttributeName; + } + + public void setGroupMemberAttributeName(String groupMemberAttributeName) { + this.groupMemberAttributeName = groupMemberAttributeName; + } + + public void setDefaultRole(String defaultRole) { + this.defaultRole = defaultRole; + } + + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + + public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) { + this.usernameMapper = usernameMapper; + } + + public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) { + this.userDetailsMapper = userDetailsMapper; + } + + public LdapAuthoritiesPopulator getAuthoritiesPopulator() { + return authoritiesPopulator; + } + + public UserDetailsContextMapper getUserDetailsMapper() { + return userDetailsMapper; + } + + public void setUserNatureMappers(List userNatureMappers) { + this.userNatureMappers = userNatureMappers; + } + + public String getDefaultRole() { + return defaultRole; + } + + public void setGroupClasses(String[] groupClasses) { + this.groupClasses = groupClasses; + } + + public UserDetailsService getUserDetailsService() { + return ldapUserDetailsService; + } + }