]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/ldap/ArgeoSecurityDaoLdap.java
Improve Security
[lgpl/argeo-commons.git] / security / runtime / org.argeo.security.core / src / main / java / org / argeo / security / ldap / ArgeoSecurityDaoLdap.java
index c9ba367c6ec58d450d5328bf719d81bf20cc9b88..6aa31bbdc6620ef05dd8e354af0df6a0ca7f9718 100644 (file)
@@ -1,9 +1,28 @@
+/*
+ * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ *
+ * 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.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;
@@ -16,20 +35,24 @@ 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.security.Authentication;
+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 {
@@ -46,13 +69,28 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean
        private String defaultRole = "ROLE_USER";
        private String rolePrefix = "ROLE_";
 
+       private final BaseLdapPathContextSource contextSource;
        private final LdapTemplate ldapTemplate;
 
        private LdapUsernameToDnMapper usernameMapper = null;
 
        private UserDetailsContextMapper userDetailsMapper;
+       private LdapUserDetailsService ldapUserDetailsService;
        private List<UserNatureMapper> 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,
@@ -82,39 +120,41 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean
                        userDetailsManager = ludm;
                }
 
+               if (ldapUserDetailsService == null) {
+                       FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(
+                                       userBase, "(" + usernameAttributeName + "={0})",
+                                       contextSource);
+                       ldapUserDetailsService = new LdapUserDetailsService(ldapUserSearch,
+                                       authoritiesPopulator);
+                       ldapUserDetailsService.setUserDetailsMapper(userDetailsMapper);
+               }
        }
 
-       public ArgeoSecurityDaoLdap(ContextSource contextSource) {
-               ldapTemplate = new LdapTemplate(contextSource);
-       }
-
-       public void create(ArgeoUser user) {
+       public synchronized void create(ArgeoUser user) {
                userDetailsManager.createUser(new ArgeoUserDetails(user));
        }
 
-       public ArgeoUser getUser(String uname) {
+       public synchronized ArgeoUser getUser(String uname) {
                SimpleArgeoUser user = createSimpleArgeoUser(getDetails(uname));
                user.setPassword(null);
                return user;
        }
 
-       public ArgeoUser getUserWithPassword(String uname) {
+       public synchronized ArgeoUser getUserWithPassword(String uname) {
                return createSimpleArgeoUser(getDetails(uname));
        }
 
-       public ArgeoUser getCurrentUser() {
-               Authentication authentication = SecurityContextHolder.getContext()
-                               .getAuthentication();
-               ArgeoUser argeoUser = ArgeoUserDetails.asArgeoUser(authentication);
-               if (argeoUser == null)
-                       return null;
-               if (argeoUser.getRoles().contains(defaultRole))
-                       argeoUser.getRoles().remove(defaultRole);
-               return argeoUser;
-       }
+       // 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<ArgeoUser> listUsers() {
+       public synchronized List<ArgeoUser> listUsers() {
                List<String> usernames = (List<String>) ldapTemplate.listBindings(
                                new DistinguishedName(userBase), new ContextMapper() {
                                        public Object mapFromContext(Object ctxArg) {
@@ -143,15 +183,43 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean
                                });
        }
 
-       public void update(ArgeoUser user) {
+       @SuppressWarnings("unchecked")
+       public List<ArgeoUser> listUsersInRole(String role) {
+               return (List<ArgeoUser>) ldapTemplate.lookup(
+                               buildGroupDn(convertRoleToGroup(role)), new ContextMapper() {
+                                       public Object mapFromContext(Object ctxArg) {
+                                               DirContextAdapter ctx = (DirContextAdapter) ctxArg;
+                                               String[] userDns = ctx
+                                                               .getStringAttributes(groupMemberAttributeName);
+                                               List<ArgeoUser> lst = new ArrayList<ArgeoUser>();
+                                               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 delete(String username) {
+       public synchronized void delete(String username) {
                userDetailsManager.deleteUser(username);
        }
 
-       public Boolean userExists(String username) {
+       public synchronized Boolean userExists(String username) {
                return userDetailsManager.userExists(username);
        }
 
@@ -161,8 +229,8 @@ 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);
                                        }
                                });
 
@@ -172,8 +240,8 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean
                context.setAttributeValue("cn", group);
 
                // Add superuser because cannot create empty group
-               context.setAttributeValue(groupMemberAttributeName, superuserDn
-                               .toString());
+               context.setAttributeValue(groupMemberAttributeName,
+                               superuserDn.toString());
 
                ldapTemplate.bind(groupDn, context, null);
        }
@@ -184,6 +252,18 @@ 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) {
                String group = role;
                if (group.startsWith(rolePrefix)) {
@@ -272,4 +352,9 @@ public class ArgeoSecurityDaoLdap implements ArgeoSecurityDao, InitializingBean
        public void setGroupClasses(String[] groupClasses) {
                this.groupClasses = groupClasses;
        }
+
+       public UserDetailsService getUserDetailsService() {
+               return ldapUserDetailsService;
+       }
+
 }