mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-15 15:02:20 +00:00
Merged 5.1-MC1 (5.1.0) to HEAD (5.1)
119045 adavis: Merged 5.1.N (5.1.1) to 5.1-MC1 (5.1.0) 117327 adavis: Merged 5.0.2-CLOUD42 (Cloud ) to 5.1.N (5.1.1) 117235 adavis: Merged 5.0.2-CLOUD (Cloud ) to 5.0.2-CLOUD42 (Cloud ) 114504 adavis: Merged BCRYPT to 5.0.2-CLOUD (PARTIAL MERGE) 113666 gjames: Initial integration of CompositePasswordEncoder for MNT-14892 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@119883 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -101,6 +101,8 @@ public class AllUnitTestsSuite extends TestSuite
|
||||
suite.addTest(new JUnit4TestAdapter(org.alfresco.util.BeanExtenderUnitTest.class));
|
||||
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class));
|
||||
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class));
|
||||
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class));
|
||||
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.security.authentication.RepositoryAuthenticationDaoHashingTest.class));
|
||||
|
||||
suite.addTest(org.alfresco.traitextender.TraitExtenderUnitTestSuite.suite());
|
||||
suite.addTest(org.alfresco.repo.virtual.VirtualizationUnitTestSuite.suite()); }
|
||||
|
@@ -89,8 +89,7 @@ public class AuthenticationTest extends TestCase
|
||||
private AuthorityService authorityService;
|
||||
private TenantService tenantService;
|
||||
private TenantAdminService tenantAdminService;
|
||||
private MD4PasswordEncoder passwordEncoder;
|
||||
private PasswordEncoder sha256PasswordEncoder;
|
||||
private CompositePasswordEncoder compositePasswordEncoder;
|
||||
private MutableAuthenticationDao dao;
|
||||
private AuthenticationManager authenticationManager;
|
||||
private TicketComponent ticketComponent;
|
||||
@@ -148,8 +147,7 @@ public class AuthenticationTest extends TestCase
|
||||
authorityService = (AuthorityService) ctx.getBean("authorityService");
|
||||
tenantService = (TenantService) ctx.getBean("tenantService");
|
||||
tenantAdminService = (TenantAdminService) ctx.getBean("tenantAdminService");
|
||||
passwordEncoder = (MD4PasswordEncoder) ctx.getBean("passwordEncoder");
|
||||
sha256PasswordEncoder = (PasswordEncoder) ctx.getBean("sha256PasswordEncoder");
|
||||
compositePasswordEncoder = (CompositePasswordEncoder) ctx.getBean("compositePasswordEncoder");
|
||||
ticketComponent = (TicketComponent) ctx.getBean("ticketComponent");
|
||||
authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService");
|
||||
pubAuthenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService");
|
||||
@@ -228,8 +226,7 @@ public class AuthenticationTest extends TestCase
|
||||
dao.setTenantService(tenantService);
|
||||
dao.setNodeService(nodeService);
|
||||
dao.setNamespaceService(getNamespacePrefixReolsver(""));
|
||||
dao.setPasswordEncoder(passwordEncoder);
|
||||
dao.setSha256PasswordEncoder(sha256PasswordEncoder);
|
||||
dao.setCompositePasswordEncoder(compositePasswordEncoder);
|
||||
dao.setPolicyComponent(policyComponent);
|
||||
dao.setAuthenticationCache(authenticationCache);
|
||||
dao.setSingletonCache(immutableSingletonCache);
|
||||
@@ -449,8 +446,7 @@ public class AuthenticationTest extends TestCase
|
||||
dao.setNodeService(nodeService);
|
||||
dao.setAuthorityService(authorityService);
|
||||
dao.setNamespaceService(getNamespacePrefixReolsver(""));
|
||||
dao.setPasswordEncoder(passwordEncoder);
|
||||
dao.setSha256PasswordEncoder(sha256PasswordEncoder);
|
||||
dao.setCompositePasswordEncoder(compositePasswordEncoder);
|
||||
dao.setPolicyComponent(policyComponent);
|
||||
dao.setAuthenticationCache(authenticationCache);
|
||||
dao.setSingletonCache(immutableSingletonCache);
|
||||
@@ -495,10 +491,6 @@ public class AuthenticationTest extends TestCase
|
||||
dao.createUser("Andy", "cabbage".toCharArray());
|
||||
assertNotNull(dao.getUserOrNull("Andy"));
|
||||
|
||||
byte[] decodedHash = passwordEncoder.decodeHash(dao.getMD4HashedPassword("Andy"));
|
||||
byte[] testHash = MessageDigest.getInstance("MD4").digest("cabbage".getBytes("UnicodeLittleUnmarked"));
|
||||
assertEquals(new String(decodedHash), new String(testHash));
|
||||
|
||||
UserDetails AndyDetails = (UserDetails) dao.loadUserByUsername("Andy");
|
||||
assertNotNull(AndyDetails);
|
||||
assertEquals("Andy", AndyDetails.getUsername());
|
||||
@@ -508,7 +500,7 @@ public class AuthenticationTest extends TestCase
|
||||
assertTrue(AndyDetails.isCredentialsNonExpired());
|
||||
assertTrue(AndyDetails.isEnabled());
|
||||
assertNotSame("cabbage", AndyDetails.getPassword());
|
||||
assertEquals(AndyDetails.getPassword(), passwordEncoder.encodePassword("cabbage", dao.getSalt(AndyDetails)));
|
||||
assertEquals(AndyDetails.getPassword(), compositePasswordEncoder.encodePreferred("cabbage", null));
|
||||
assertEquals(1, AndyDetails.getAuthorities().length);
|
||||
|
||||
// Object oldSalt = dao.getSalt(AndyDetails);
|
||||
|
@@ -28,11 +28,11 @@ import static org.junit.Assert.fail;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -43,20 +43,17 @@ import java.util.Map;
|
||||
*/
|
||||
public class CompositePasswordEncoderTest
|
||||
{
|
||||
|
||||
CompositePasswordEncoder encoder;
|
||||
static Map<String,Object> encodersConfig;
|
||||
public static Map<String,Object> encodersConfig;
|
||||
private static final String SOURCE_PASSWORD = "SOURCE PASS Word $%%^ #';/,_+{+} like €this"+"\u4eca\u65e5\u306f\u4e16\u754c";
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception
|
||||
{
|
||||
static {
|
||||
encodersConfig = new HashMap<>();
|
||||
encodersConfig.put("md4", new MD4PasswordEncoderImpl());
|
||||
encodersConfig.put("sha256", new ShaPasswordEncoderImpl(256));
|
||||
encodersConfig.put("bcrypt10",new BCryptPasswordEncoder(10));
|
||||
encodersConfig.put("bcrypt20",new BCryptPasswordEncoder(20));
|
||||
encodersConfig.put("bcrypt30",new BCryptPasswordEncoder(30));
|
||||
encodersConfig.put("bcrypt11",new BCryptPasswordEncoder(11));
|
||||
encodersConfig.put("bcrypt12",new BCryptPasswordEncoder(11));
|
||||
encodersConfig.put("badencoder",new Object());
|
||||
}
|
||||
|
||||
@@ -251,6 +248,14 @@ public class CompositePasswordEncoderTest
|
||||
assertTrue(encoder.matchesPassword(SOURCE_PASSWORD, encoded, salt, mdbChain));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodePreferred() throws Exception
|
||||
{
|
||||
encoder.setPreferredEncoding("bcrypt10");
|
||||
String encoded = encoder.encodePreferred(SOURCE_PASSWORD, null);
|
||||
assertTrue(encoder.matches("bcrypt10", SOURCE_PASSWORD, encoded, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMandatoryProperties() throws Exception
|
||||
{
|
||||
@@ -264,8 +269,6 @@ public class CompositePasswordEncoderTest
|
||||
}
|
||||
|
||||
subject.setEncoders(encodersConfig);
|
||||
subject.setLegacyEncoding("md345");
|
||||
subject.setLegacyEncodingProperty("password");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -280,8 +283,6 @@ public class CompositePasswordEncoderTest
|
||||
subject.init();
|
||||
|
||||
assertEquals("nice_encoding", subject.getPreferredEncoding());
|
||||
assertEquals("md345", subject.getLegacyEncoding());
|
||||
assertEquals("password", subject.getLegacyEncodingProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -289,7 +290,15 @@ public class CompositePasswordEncoderTest
|
||||
{
|
||||
CompositePasswordEncoder subject = new CompositePasswordEncoder();
|
||||
subject.setPreferredEncoding("fish");
|
||||
assertTrue(subject.isPreferredEncoding("fish"));
|
||||
assertTrue(subject.lastEncodingIsPreferred(Arrays.asList("fish")));
|
||||
assertEquals("fish", subject.getPreferredEncoding());
|
||||
|
||||
assertFalse(subject.lastEncodingIsPreferred((List)null));
|
||||
assertFalse(subject.lastEncodingIsPreferred(Collections.<String>emptyList()));
|
||||
assertTrue(subject.lastEncodingIsPreferred(Arrays.asList("fish")));
|
||||
assertFalse(subject.lastEncodingIsPreferred(Arrays.asList("bird")));
|
||||
assertTrue(subject.lastEncodingIsPreferred(Arrays.asList("bird", "fish")));
|
||||
assertFalse(subject.lastEncodingIsPreferred(Arrays.asList("bird", "fish", "dog", "cat")));
|
||||
assertTrue(subject.lastEncodingIsPreferred(Arrays.asList("bird", "dog", "cat","fish")));
|
||||
}
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
package org.alfresco.repo.security.authentication;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by gethin on 01/10/15.
|
||||
*/
|
||||
public class RepositoryAuthenticationDaoHashingTest
|
||||
{
|
||||
RepositoryAuthenticationDao authenticationDao;
|
||||
CompositePasswordEncoder cpe;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
authenticationDao = new RepositoryAuthenticationDao();
|
||||
cpe = new CompositePasswordEncoder();
|
||||
cpe.setEncoders(CompositePasswordEncoderTest.encodersConfig);
|
||||
authenticationDao.setCompositePasswordEncoder(cpe);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRehashedPassword() throws Exception
|
||||
{
|
||||
//Use md4
|
||||
cpe.setPreferredEncoding("md4");
|
||||
String salt = GUID.generate();
|
||||
String md4Hashed = cpe.encode("md4","HASHED_MY_PASSWORD", null);
|
||||
String sha256Hashed = cpe.encode("sha256","HASHED_MY_PASSWORD", salt);
|
||||
|
||||
Map<QName, Serializable> properties = new HashMap<>();
|
||||
|
||||
properties.put(ContentModel.PROP_PASSWORD, "nonsense");
|
||||
assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
//No hashing to do but we need to update the Indicator
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
assertEquals(CompositePasswordEncoder.MD4, properties.get(ContentModel.PROP_HASH_INDICATOR));
|
||||
assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256));
|
||||
assertEquals("nonsense", properties.get(ContentModel.PROP_PASSWORD_HASH));
|
||||
//We copied the plain text (above) but it won't work (see next)
|
||||
|
||||
properties.clear();
|
||||
properties.put(ContentModel.PROP_PASSWORD, "PLAIN TEXT PASSWORD");
|
||||
//We don't support plain text.
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
assertEquals(CompositePasswordEncoder.MD4, properties.get(ContentModel.PROP_HASH_INDICATOR));
|
||||
assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256));
|
||||
assertEquals("PLAIN TEXT PASSWORD", properties.get(ContentModel.PROP_PASSWORD_HASH));
|
||||
assertFalse("We copied a plain text password to the new property but"
|
||||
+" the legacy encoding is set to MD4 so the password would NEVER match.",
|
||||
matches("PLAIN TEXT PASSWORD", properties, cpe));
|
||||
|
||||
properties.clear();
|
||||
properties.put(ContentModel.PROP_PASSWORD, md4Hashed);
|
||||
cpe.setPreferredEncoding("bcrypt10");
|
||||
|
||||
assertTrue("We have the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
//We rehashed this password by taking the md4 and hashing it by bcrypt
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
assertEquals(Arrays.asList("md4","bcrypt10"), properties.get(ContentModel.PROP_HASH_INDICATOR));
|
||||
assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
assertTrue(matches("HASHED_MY_PASSWORD", properties, cpe));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256));
|
||||
|
||||
properties.clear();
|
||||
properties.put(ContentModel.PROP_PASSWORD, "This should be ignored");
|
||||
properties.put(ContentModel.PROP_PASSWORD_SHA256, sha256Hashed);
|
||||
properties.put(ContentModel.PROP_SALT, salt);
|
||||
|
||||
assertTrue("We have the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertTrue("We have the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256));
|
||||
assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
//We rehashed this password by taking the sha256 and hashing it by bcrypt
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
assertEquals(Arrays.asList("sha256","bcrypt10"), properties.get(ContentModel.PROP_HASH_INDICATOR));
|
||||
assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH));
|
||||
assertTrue(matches("HASHED_MY_PASSWORD", properties, cpe));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD));
|
||||
assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256));
|
||||
}
|
||||
@Test
|
||||
public void testRehashedPasswordBcrypt() throws Exception
|
||||
{
|
||||
cpe.setPreferredEncoding("bcrypt10");
|
||||
Map<QName, Serializable> properties = new HashMap<>();
|
||||
properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable) Arrays.asList("bcrypt10"));
|
||||
properties.put(ContentModel.PROP_PASSWORD_HASH, "long hash");
|
||||
//Nothing to do.
|
||||
assertFalse(authenticationDao.rehashedPassword(properties));
|
||||
|
||||
cpe.setPreferredEncoding("bcrypt11");
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
assertEquals(Arrays.asList("bcrypt10","bcrypt11"),authenticationDao.determinePasswordHash(properties).getFirst());
|
||||
|
||||
cpe.setPreferredEncoding("bcrypt12");
|
||||
assertTrue(authenticationDao.rehashedPassword(properties));
|
||||
//Triple hashing!
|
||||
assertEquals(Arrays.asList("bcrypt10","bcrypt11","bcrypt12"),authenticationDao.determinePasswordHash(properties).getFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPasswordHash() throws Exception
|
||||
{
|
||||
|
||||
Map<QName, Serializable> properties = new HashMap<>();
|
||||
cpe.setPreferredEncoding("bcrypt10");
|
||||
|
||||
try
|
||||
{
|
||||
authenticationDao.determinePasswordHash(properties);
|
||||
fail("Should throw exception");
|
||||
}
|
||||
catch (AlfrescoRuntimeException are)
|
||||
{
|
||||
assertTrue(are.getMessage().contains("Unable to find a user password"));
|
||||
}
|
||||
|
||||
//if the PROP_PASSWORD field is the only one availble then we are using MD4
|
||||
properties.put(ContentModel.PROP_PASSWORD, "mypassword");
|
||||
Pair<List<String>, String> passwordHashed = authenticationDao.determinePasswordHash(properties);
|
||||
assertEquals(CompositePasswordEncoder.MD4, passwordHashed.getFirst());
|
||||
assertEquals("mypassword", passwordHashed.getSecond());
|
||||
|
||||
//if the PROP_PASSWORD_SHA256 field is used then we are using SHA256
|
||||
properties.put(ContentModel.PROP_PASSWORD_SHA256, "sha_password");
|
||||
passwordHashed = authenticationDao.determinePasswordHash(properties);
|
||||
assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst());
|
||||
assertEquals("sha_password", passwordHashed.getSecond());
|
||||
|
||||
properties.put(ContentModel.PROP_HASH_INDICATOR, null);
|
||||
//If the indicator is NULL then it still uses the old password field
|
||||
passwordHashed = authenticationDao.determinePasswordHash(properties);
|
||||
assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst());
|
||||
assertEquals("sha_password", passwordHashed.getSecond());
|
||||
|
||||
properties.put(ContentModel.PROP_HASH_INDICATOR, new ArrayList<String>(0));
|
||||
//If the indicator doesn't have a value
|
||||
passwordHashed = authenticationDao.determinePasswordHash(properties);
|
||||
assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst());
|
||||
assertEquals("sha_password", passwordHashed.getSecond());
|
||||
|
||||
//Now it uses the correct property
|
||||
properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable) Arrays.asList("myencoding"));
|
||||
properties.put(ContentModel.PROP_PASSWORD_HASH, "hashed this time");
|
||||
passwordHashed = authenticationDao.determinePasswordHash(properties);
|
||||
assertEquals(Arrays.asList("myencoding"), passwordHashed.getFirst());
|
||||
assertEquals("hashed this time",passwordHashed.getSecond());
|
||||
|
||||
}
|
||||
|
||||
private static boolean matches(String password, Map<QName, Serializable> properties, CompositePasswordEncoder cpe)
|
||||
{
|
||||
return cpe.matchesPassword(password, (String) properties.get(ContentModel.PROP_PASSWORD_HASH), (String) properties.get(ContentModel.PROP_SALT), (List<String>) properties.get(ContentModel.PROP_HASH_INDICATOR) );
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user