diff --git a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java index 29b9f24814..42753bc333 100644 --- a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java @@ -86,6 +86,19 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao * @throws AlfrescoRuntimeException if the the operation is not allowed */ public void createUser(String userName, char[] rawPassword) throws AuthenticationException + { + createUser(userName, null, rawPassword); + } + + /** + * {@inheritDoc} + *

+ * If enabled does nothing + * + * @throws AlfrescoRuntimeException if the the operation is not allowed + */ + @Override + public void createUser(String caseSensitiveUserName, String hashedpassword, char[] rawPassword) throws AuthenticationException { if (!allowCreateUser) { diff --git a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java index a368668850..004b10e342 100644 --- a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java @@ -35,7 +35,15 @@ public interface MutableAuthenticationDao extends AuthenticationDao, SaltSource * Create a user with the given userName and password */ public void createUser(String userName, char[] rawPassword) throws AuthenticationException; - + + /** + * Create a user with the given userName and password hash + * If hashedPassword is passed in then this is used, otherwise it falls back to using the rawPassword. + * It is assumed the hashed password has been encoded using system.preferred.password.encoding and doesn't use its + * own salt. + */ + public void createUser(String caseSensitiveUserName, String hashedPassword, char[] rawPassword) throws AuthenticationException; + /** * Update a user's password. */ diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index cda21593e7..ec7daad74b 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -78,7 +78,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In protected PolicyComponent policyComponent; private TransactionService transactionService; - private CompositePasswordEncoder compositePasswordEncoder; + protected CompositePasswordEncoder compositePasswordEncoder; // note: cache is tenant-aware (if using TransctionalCache impl) @@ -326,6 +326,12 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In @Override public void createUser(String caseSensitiveUserName, char[] rawPassword) throws AuthenticationException + { + createUser(caseSensitiveUserName, null, rawPassword); + } + + @Override + public void createUser(String caseSensitiveUserName, String hashedPassword, char[] rawPassword) throws AuthenticationException { tenantService.checkDomainUser(caseSensitiveUserName); @@ -339,7 +345,24 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In properties.put(ContentModel.PROP_USER_USERNAME, caseSensitiveUserName); String salt = GUID.generate(); properties.put(ContentModel.PROP_SALT, salt); - properties.put(ContentModel.PROP_PASSWORD_HASH, compositePasswordEncoder.encodePreferred(new String(rawPassword), salt)); + + if (hashedPassword == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Hashing raw password to "+compositePasswordEncoder.getPreferredEncoding() + +" for "+caseSensitiveUserName); + } + hashedPassword = compositePasswordEncoder.encodePreferred(new String(rawPassword), salt); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Using hashed password for "+caseSensitiveUserName); + } + } + properties.put(ContentModel.PROP_PASSWORD_HASH, hashedPassword); properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable) Arrays.asList(compositePasswordEncoder.getPreferredEncoding())); properties.put(ContentModel.PROP_ACCOUNT_EXPIRES, Boolean.valueOf(false)); properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false)); diff --git a/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java index 02353c29a2..64cba67f60 100644 --- a/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java @@ -54,7 +54,16 @@ public class NullMutableAuthenticationDao implements MutableAuthenticationDao { throw new AlfrescoRuntimeException("Not implemented"); } - + + /** + * @throws AlfrescoRuntimeException Not implemented + */ + @Override + public void createUser(String caseSensitiveUserName, String hashedpassword, char[] rawPassword) throws AuthenticationException + { + throw new AlfrescoRuntimeException("Not implemented"); + } + /** * @throws AlfrescoRuntimeException Not implemented */ diff --git a/source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java index aedfe471e7..fd7747f8c6 100644 --- a/source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -22,6 +22,8 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -1851,7 +1853,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.createAuthentication("Andy", "auth1".toCharArray()); // find the node representing the Andy user and it's properties - NodeRef andyUserNodeRef = getAndyUserNodeRef(); + NodeRef andyUserNodeRef = getRepositoryAuthenticationDao(). getUserOrNull("Andy"); assertNotNull(andyUserNodeRef); // ensure the properties are in the state we're expecting @@ -1928,7 +1930,34 @@ public class AuthenticationTest extends TestCase authenticationComponent.clearCurrentSecurityContext(); } - private NodeRef getAndyUserNodeRef() + + /** + * Tests creating a user with a Hashed password + */ + public void testCreateUserWithHashedPassword() throws Exception + { + String SOME_PASSWORD = "1 passw0rd"; + List encs = Arrays.asList("bcrypt10", "md4"); + for (String enc : encs) + { + compositePasswordEncoder.setPreferredEncoding(enc); + String hash = compositePasswordEncoder.encodePreferred(SOME_PASSWORD,null); + assertCreateHashed(SOME_PASSWORD, hash, null, "me@you.com"); + assertCreateHashed(SOME_PASSWORD, null, SOME_PASSWORD.toCharArray(), "you@me.com"); + } + } + + private void assertCreateHashed(String rawString, String hash, char[] rawPassword, String user) + { + dao.createUser(user, hash, rawPassword); + UserDetails userDetails = (UserDetails) dao.loadUserByUsername(user); + assertNotNull(userDetails); + assertNotNull(userDetails.getPassword()); + assertTrue(compositePasswordEncoder.matches(compositePasswordEncoder.getPreferredEncoding(), rawString, userDetails.getPassword(), null)); + dao.deleteUser(user); + } + + private RepositoryAuthenticationDao getRepositoryAuthenticationDao() { RepositoryAuthenticationDao dao = new RepositoryAuthenticationDao(); dao.setTransactionService(transactionService); @@ -1940,8 +1969,7 @@ public class AuthenticationTest extends TestCase dao.setPolicyComponent(policyComponent); dao.setAuthenticationCache(authenticationCache); dao.setSingletonCache(immutableSingletonCache); - - return dao.getUserOrNull("Andy"); + return dao; } private String getUserName(Authentication authentication)