Skip to content

Commit

Permalink
Production and Tests should use the appropriate PasswordEncoder
Browse files Browse the repository at this point in the history
- Production should use BCryptPasswordEncoder
- Tests should use FakePasswordEncoder

[#164967385]
  • Loading branch information
joshuatcasey committed Mar 28, 2019
1 parent dabe4af commit cfeaa5b
Show file tree
Hide file tree
Showing 33 changed files with 157 additions and 324 deletions.

This file was deleted.

Expand Up @@ -31,7 +31,6 @@
import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException;
import org.cloudfoundry.identity.uaa.scim.util.ScimUtils; import org.cloudfoundry.identity.uaa.scim.util.ScimUtils;
import org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase; import org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase;
import org.cloudfoundry.identity.uaa.util.PasswordEncoderFactory;
import org.cloudfoundry.identity.uaa.util.TimeService; import org.cloudfoundry.identity.uaa.util.TimeService;
import org.cloudfoundry.identity.uaa.util.TimeServiceImpl; import org.cloudfoundry.identity.uaa.util.TimeServiceImpl;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
Expand All @@ -42,7 +41,6 @@
import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert; import org.springframework.util.Assert;


Expand Down Expand Up @@ -110,7 +108,7 @@ public Logger getLogger() {


protected final JdbcTemplate jdbcTemplate; protected final JdbcTemplate jdbcTemplate;


private PasswordEncoder passwordEncoder = new PasswordEncoderFactory().get(); private final PasswordEncoder passwordEncoder;


private boolean deactivateOnDelete = true; private boolean deactivateOnDelete = true;


Expand All @@ -120,11 +118,15 @@ public Logger getLogger() {


private TimeService timeService = new TimeServiceImpl(); private TimeService timeService = new TimeServiceImpl();


public JdbcScimUserProvisioning(JdbcTemplate jdbcTemplate, JdbcPagingListFactory pagingListFactory) { public JdbcScimUserProvisioning(
JdbcTemplate jdbcTemplate,
JdbcPagingListFactory pagingListFactory,
final PasswordEncoder passwordEncoder) {
super(jdbcTemplate, pagingListFactory, mapper); super(jdbcTemplate, pagingListFactory, mapper);
Assert.notNull(jdbcTemplate); Assert.notNull(jdbcTemplate);
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
setQueryConverter(new SimpleSearchQueryConverter()); setQueryConverter(new SimpleSearchQueryConverter());
this.passwordEncoder = passwordEncoder;
} }


public void setTimeService(TimeService timeService) { public void setTimeService(TimeService timeService) {
Expand Down Expand Up @@ -416,16 +418,6 @@ public void setDeactivateOnDelete(boolean deactivateOnDelete) {
this.deactivateOnDelete = deactivateOnDelete; this.deactivateOnDelete = deactivateOnDelete;
} }


/**
* The encoder used to hash passwords before storing them in the database.
*
* Defaults to a {@link BCryptPasswordEncoder}.
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
this.passwordEncoder = passwordEncoder;
}

/** /**
* Sets the regular expression which will be used to validate the username. * Sets the regular expression which will be used to validate the username.
*/ */
Expand Down
Expand Up @@ -16,6 +16,8 @@


import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.codec.Hex; import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8; import org.springframework.security.crypto.codec.Utf8;
Expand Down Expand Up @@ -61,28 +63,21 @@ public void setEnabled(boolean enabled) {


private volatile Cache<CharSequence, Set<String>> cache = null; private volatile Cache<CharSequence, Set<String>> cache = null;


private PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;


public CachingPasswordEncoder() throws NoSuchAlgorithmException { public CachingPasswordEncoder(final PasswordEncoder passwordEncoder) throws NoSuchAlgorithmException {
this.passwordEncoder = passwordEncoder;
messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest = MessageDigest.getInstance("SHA-256");
this.secret = Utf8.encode(new RandomValueStringGenerator().generate()); this.secret = Utf8.encode(new RandomValueStringGenerator().generate());
this.salt = KeyGenerators.secureRandom().generateKey(); this.salt = KeyGenerators.secureRandom().generateKey();
iterations = 25; iterations = 25;
buildCache(); buildCache();
} }


public PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}

public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}

@Override @Override
public String encode(CharSequence rawPassword) throws AuthenticationException { public String encode(CharSequence rawPassword) throws AuthenticationException {
//we always use the Bcrypt mechanism, we never store repeated information //we always use the Bcrypt mechanism, we never store repeated information
return getPasswordEncoder().encode(rawPassword); return passwordEncoder.encode(rawPassword);
} }


@Override @Override
Expand All @@ -91,7 +86,7 @@ public boolean matches(CharSequence rawPassword, String encodedPassword) throws
String cacheKey = cacheEncode(rawPassword); String cacheKey = cacheEncode(rawPassword);
return internalMatches(cacheKey, rawPassword, encodedPassword); return internalMatches(cacheKey, rawPassword, encodedPassword);
} else { } else {
return getPasswordEncoder().matches(rawPassword, encodedPassword); return passwordEncoder.matches(rawPassword, encodedPassword);
} }
} }


Expand All @@ -116,7 +111,7 @@ private boolean internalMatches(String cacheKey, CharSequence rawPassword, Strin
} }
} }
if (!result) { if (!result) {
if (getPasswordEncoder().matches(rawPassword, encodedPassword)) { if (passwordEncoder.matches(rawPassword, encodedPassword)) {
result = true; result = true;
cacheValue = getOrCreateHashList(cacheKey); cacheValue = getOrCreateHashList(cacheKey);
if (cacheValue!=null) { if (cacheValue!=null) {
Expand Down

This file was deleted.

@@ -0,0 +1,20 @@
package org.cloudfoundry.identity.uaa.util.beans;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordEncoderConfig {

private static Logger logger = LoggerFactory.getLogger(PasswordEncoderConfig.class);

@Bean
public PasswordEncoder nonCachingPasswordEncoder() {
logger.info("Created instance of BCryptPasswordEncoder");
return new BCryptPasswordEncoder();
}
}
Expand Up @@ -106,26 +106,20 @@ public class MultitenantJdbcClientDetailsService extends MultitenantClientServic


private String selectClientDetailsSql = DEFAULT_SELECT_STATEMENT; private String selectClientDetailsSql = DEFAULT_SELECT_STATEMENT;


private PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;


private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;


private JdbcListFactory listFactory; private JdbcListFactory listFactory;


public MultitenantJdbcClientDetailsService( public MultitenantJdbcClientDetailsService(
final JdbcTemplate jdbcTemplate, final JdbcTemplate jdbcTemplate,
final IdentityZoneManager identityZoneManager) { final IdentityZoneManager identityZoneManager,
final PasswordEncoder passwordEncoder) {
super(identityZoneManager); super(identityZoneManager);
Assert.notNull(jdbcTemplate, "JDbcTemplate required"); Assert.notNull(jdbcTemplate, "JDbcTemplate required");
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(jdbcTemplate)); this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(jdbcTemplate));
}

/**
* @param passwordEncoder
* the password encoder to set
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;
} }


Expand Down

This file was deleted.

This file was deleted.

Expand Up @@ -21,6 +21,7 @@
import org.cloudfoundry.identity.uaa.scim.test.TestUtils; import org.cloudfoundry.identity.uaa.scim.test.TestUtils;
import org.cloudfoundry.identity.uaa.scim.validate.PasswordValidator; import org.cloudfoundry.identity.uaa.scim.validate.PasswordValidator;
import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor; import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
import org.cloudfoundry.identity.uaa.util.FakePasswordEncoder;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.MigrationVersion;
Expand All @@ -33,7 +34,6 @@
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;


import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -67,9 +67,10 @@ public static void init() {
@Before @Before
public void setup() { public void setup() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(database); JdbcTemplate jdbcTemplate = new JdbcTemplate(database);
JdbcScimUserProvisioning dao = new JdbcScimUserProvisioning(jdbcTemplate, JdbcScimUserProvisioning dao = new JdbcScimUserProvisioning(
new JdbcPagingListFactory(jdbcTemplate, LimitSqlAdapterFactory.getLimitSqlAdapter())); jdbcTemplate,
dao.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); new JdbcPagingListFactory(jdbcTemplate, LimitSqlAdapterFactory.getLimitSqlAdapter()),
new FakePasswordEncoder());


endpoints = new PasswordChangeEndpoint(); endpoints = new PasswordChangeEndpoint();
endpoints.setScimUserProvisioning(dao); endpoints.setScimUserProvisioning(dao);
Expand Down
@@ -1,11 +1,8 @@
package org.cloudfoundry.identity.uaa.annotations; package org.cloudfoundry.identity.uaa.annotations;


import org.cloudfoundry.identity.uaa.FakePasswordEncoderConfig;
import org.cloudfoundry.identity.uaa.security.PollutionPreventionExtension;
import org.cloudfoundry.identity.uaa.test.HoneycombAuditEventTestListenerExtension; import org.cloudfoundry.identity.uaa.test.HoneycombAuditEventTestListenerExtension;
import org.cloudfoundry.identity.uaa.test.HoneycombJdbcInterceptorExtension; import org.cloudfoundry.identity.uaa.test.HoneycombJdbcInterceptorExtension;
import org.cloudfoundry.identity.uaa.test.TestWebAppContext; import org.cloudfoundry.identity.uaa.test.TestWebAppContext;
import org.cloudfoundry.identity.uaa.util.FakePasswordEncoder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
Expand All @@ -25,8 +22,7 @@
@ActiveProfiles("default") @ActiveProfiles("default")
@WebAppConfiguration @WebAppConfiguration
@ContextConfiguration(classes = { @ContextConfiguration(classes = {
TestWebAppContext.class, TestWebAppContext.class
FakePasswordEncoderConfig.class
}) })
public @interface WithSpring { public @interface WithSpring {
} }
Expand Up @@ -49,8 +49,7 @@ public void setUpForClientTests() {
IdentityZoneManager mockIdentityZoneManager = mock(IdentityZoneManager.class); IdentityZoneManager mockIdentityZoneManager = mock(IdentityZoneManager.class);
when(mockIdentityZoneManager.getCurrentIdentityZoneId()).thenReturn(OriginKeys.UAA); when(mockIdentityZoneManager.getCurrentIdentityZoneId()).thenReturn(OriginKeys.UAA);


jdbcClientDetailsService = new MultitenantJdbcClientDetailsService(jdbcTemplate, mockIdentityZoneManager); jdbcClientDetailsService = new MultitenantJdbcClientDetailsService(jdbcTemplate, mockIdentityZoneManager, encoder);
jdbcClientDetailsService.setPasswordEncoder(encoder);
ClientDetailsUserDetailsService clientDetailsService = new ClientDetailsUserDetailsService(jdbcClientDetailsService); ClientDetailsUserDetailsService clientDetailsService = new ClientDetailsUserDetailsService(jdbcClientDetailsService);
client = createClient(); client = createClient();
authenticationProvider = new ClientDetailsAuthenticationProvider(clientDetailsService, encoder); authenticationProvider = new ClientDetailsAuthenticationProvider(clientDetailsService, encoder);
Expand Down
Expand Up @@ -63,8 +63,7 @@ void setUpClientAdminTests() {
IdentityZoneManager mockIdentityZoneManager = mock(IdentityZoneManager.class); IdentityZoneManager mockIdentityZoneManager = mock(IdentityZoneManager.class);
when(mockIdentityZoneManager.getCurrentIdentityZoneId()).thenReturn(OriginKeys.UAA); when(mockIdentityZoneManager.getCurrentIdentityZoneId()).thenReturn(OriginKeys.UAA);


multitenantJdbcClientDetailsService = spy(new MultitenantJdbcClientDetailsService(jdbcTemplate, mockIdentityZoneManager)); multitenantJdbcClientDetailsService = spy(new MultitenantJdbcClientDetailsService(jdbcTemplate, mockIdentityZoneManager, fakePasswordEncoder));
multitenantJdbcClientDetailsService.setPasswordEncoder(fakePasswordEncoder);


clientMetadataProvisioning = new JdbcClientMetadataProvisioning(multitenantJdbcClientDetailsService, jdbcTemplate); clientMetadataProvisioning = new JdbcClientMetadataProvisioning(multitenantJdbcClientDetailsService, jdbcTemplate);


Expand Down
@@ -1,6 +1,7 @@
package org.cloudfoundry.identity.uaa.client; package org.cloudfoundry.identity.uaa.client;


import org.cloudfoundry.identity.uaa.annotations.WithDatabaseContext; import org.cloudfoundry.identity.uaa.annotations.WithDatabaseContext;
import org.cloudfoundry.identity.uaa.util.FakePasswordEncoder;
import org.cloudfoundry.identity.uaa.zone.MultitenantJdbcClientDetailsService; import org.cloudfoundry.identity.uaa.zone.MultitenantJdbcClientDetailsService;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -44,7 +45,7 @@ void createDatasource() {
identityZoneId = "identityZoneId-" + randomValueStringGenerator.generate(); identityZoneId = "identityZoneId-" + randomValueStringGenerator.generate();
clientId = "clientId-" + randomValueStringGenerator.generate(); clientId = "clientId-" + randomValueStringGenerator.generate();


MultitenantJdbcClientDetailsService clientService = new MultitenantJdbcClientDetailsService(jdbcTemplate, null); MultitenantJdbcClientDetailsService clientService = new MultitenantJdbcClientDetailsService(jdbcTemplate, null, new FakePasswordEncoder());
jdbcClientMetadataProvisioning = new JdbcClientMetadataProvisioning(clientService, jdbcTemplate); jdbcClientMetadataProvisioning = new JdbcClientMetadataProvisioning(clientService, jdbcTemplate);
} }


Expand Down
Expand Up @@ -16,6 +16,7 @@
import org.cloudfoundry.identity.uaa.resources.jdbc.JdbcPagingListFactory; import org.cloudfoundry.identity.uaa.resources.jdbc.JdbcPagingListFactory;
import org.cloudfoundry.identity.uaa.resources.jdbc.LimitSqlAdapter; import org.cloudfoundry.identity.uaa.resources.jdbc.LimitSqlAdapter;
import org.cloudfoundry.identity.uaa.test.JdbcTestBase; import org.cloudfoundry.identity.uaa.test.JdbcTestBase;
import org.cloudfoundry.identity.uaa.util.FakePasswordEncoder;
import org.cloudfoundry.identity.uaa.zone.*; import org.cloudfoundry.identity.uaa.zone.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
Expand All @@ -35,7 +36,7 @@ public class JdbcQueryableClientDetailsServiceTests extends JdbcTestBase {
@Before @Before
public void initJdbcScimClientDetailsServiceTests() { public void initJdbcScimClientDetailsServiceTests() {
limitSqlAdapter = webApplicationContext.getBean(LimitSqlAdapter.class); limitSqlAdapter = webApplicationContext.getBean(LimitSqlAdapter.class);
delegate = new MultitenantJdbcClientDetailsService(jdbcTemplate, null); delegate = new MultitenantJdbcClientDetailsService(jdbcTemplate, null, new FakePasswordEncoder());
service = new JdbcQueryableClientDetailsService(delegate, jdbcTemplate, new JdbcPagingListFactory(jdbcTemplate, service = new JdbcQueryableClientDetailsService(delegate, jdbcTemplate, new JdbcPagingListFactory(jdbcTemplate,
limitSqlAdapter)); limitSqlAdapter));


Expand Down

0 comments on commit cfeaa5b

Please sign in to comment.