JdbcRegisteredClientRepository hashes client secret on save

Closes gh-378
This commit is contained in:
Ovidiu Popa
2021-07-29 17:37:28 +03:00
committed by Joe Grandja
parent 7546d18a40
commit 1929e3a80a
9 changed files with 107 additions and 21 deletions

View File

@@ -36,6 +36,8 @@ import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
@@ -291,6 +293,7 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
*/
public static class RegisteredClientParametersMapper implements Function<RegisteredClient, List<SqlParameterValue>> {
private ObjectMapper objectMapper = new ObjectMapper();
private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
public RegisteredClientParametersMapper() {
ClassLoader classLoader = JdbcRegisteredClientRepository.class.getClassLoader();
@@ -319,7 +322,7 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientId()),
new SqlParameterValue(Types.TIMESTAMP, clientIdIssuedAt),
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientSecret()),
new SqlParameterValue(Types.VARCHAR, encode(registeredClient.getClientSecret())),
new SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt),
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()),
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)),
@@ -335,6 +338,12 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
this.objectMapper = objectMapper;
}
public final void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
this.passwordEncoder = passwordEncoder;
}
protected final ObjectMapper getObjectMapper() {
return this.objectMapper;
}
@@ -347,6 +356,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
}
}
private String encode(String value) {
if (value != null) {
return this.passwordEncoder.encode(value);
}
return null;
}
}
}

View File

@@ -83,6 +83,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2TokenCusto
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -582,8 +583,12 @@ public class OAuth2AuthorizationCodeGrantTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -68,6 +68,7 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -282,8 +283,12 @@ public class OAuth2ClientCredentialsGrantTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -68,6 +68,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -207,8 +208,12 @@ public class OAuth2RefreshTokenGrantTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -62,6 +62,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -244,8 +245,12 @@ public class OAuth2TokenIntrospectionTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -54,6 +54,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -207,8 +208,12 @@ public class OAuth2TokenRevocationTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -60,6 +60,7 @@ import org.springframework.security.oauth2.core.oidc.http.converter.OidcClientRe
import org.springframework.security.oauth2.jose.TestJwks;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -226,9 +227,12 @@ public class OidcClientRegistrationTests {
static class AuthorizationServerConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}

View File

@@ -73,6 +73,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
@@ -275,8 +276,12 @@ public class OidcTests {
}
@Bean
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
return new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations, PasswordEncoder passwordEncoder) {
JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations);
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(passwordEncoder);
jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
return jdbcRegisteredClientRepository;
}
@Bean

View File

@@ -27,10 +27,10 @@ import java.util.function.Function;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -40,9 +40,13 @@ import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientRowMapper;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
@@ -52,8 +56,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link JdbcRegisteredClientRepository}.
@@ -69,12 +75,27 @@ public class JdbcRegisteredClientRepositoryTests {
private EmbeddedDatabase db;
private JdbcOperations jdbcOperations;
private JdbcRegisteredClientRepository registeredClientRepository;
private PasswordEncoder passwordEncoder;
@Before
public void setUp() {
this.db = createDb(OAUTH2_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE);
this.jdbcOperations = new JdbcTemplate(this.db);
this.registeredClientRepository = new JdbcRegisteredClientRepository(this.jdbcOperations);
this.passwordEncoder = spy(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return NoOpPasswordEncoder.getInstance().encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return NoOpPasswordEncoder.getInstance().matches(rawPassword, encodedPassword);
}
});
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(this.passwordEncoder);
this.registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
}
@After
@@ -144,15 +165,26 @@ public class JdbcRegisteredClientRepositoryTests {
this.registeredClientRepository.save(expectedRegisteredClient);
RegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId());
assertThat(registeredClient).isEqualTo(expectedRegisteredClient);
verify(this.passwordEncoder).encode(anyString());
}
@Test
public void saveWhenClientSecretNullThenSaved() {
RegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient()
.clientSecret(null).build();
this.registeredClientRepository.save(expectedRegisteredClient);
RegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId());
assertThat(registeredClient).isEqualTo(expectedRegisteredClient);
verifyNoInteractions(this.passwordEncoder);
}
@Test
public void saveLoadRegisteredClientWhenCustomStrategiesSetThenCalled() throws Exception {
RowMapper<RegisteredClient> registeredClientRowMapper = spy(
new JdbcRegisteredClientRepository.RegisteredClientRowMapper());
RowMapper<RegisteredClient> registeredClientRowMapper = spy(new RegisteredClientRowMapper());
this.registeredClientRepository.setRegisteredClientRowMapper(registeredClientRowMapper);
Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper = spy(
new JdbcRegisteredClientRepository.RegisteredClientParametersMapper());
RegisteredClientParametersMapper clientParametersMapper = new RegisteredClientParametersMapper();
clientParametersMapper.setPasswordEncoder(this.passwordEncoder);
Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper = spy(clientParametersMapper);
this.registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
@@ -161,6 +193,7 @@ public class JdbcRegisteredClientRepositoryTests {
assertThat(result).isEqualTo(registeredClient);
verify(registeredClientRowMapper).mapRow(any(), anyInt());
verify(registeredClientParametersMapper).apply(any());
verify(this.passwordEncoder).encode(anyString());
}
@Test
@@ -212,14 +245,17 @@ public class JdbcRegisteredClientRepositoryTests {
@Test
public void tableDefinitionWhenCustomThenAbleToOverride() {
EmbeddedDatabase db = createDb(OAUTH2_CUSTOM_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE);
RegisteredClientRepository registeredClientRepository =
new CustomJdbcRegisteredClientRepository(new JdbcTemplate(db));
RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();
registeredClientParametersMapper.setPasswordEncoder(this.passwordEncoder);
CustomJdbcRegisteredClientRepository registeredClientRepository = new CustomJdbcRegisteredClientRepository(new JdbcTemplate(db));
registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
registeredClientRepository.save(registeredClient);
RegisteredClient foundRegisteredClient1 = registeredClientRepository.findById(registeredClient.getId());
assertThat(foundRegisteredClient1).isEqualTo(registeredClient);
RegisteredClient foundRegisteredClient2 = registeredClientRepository.findByClientId(registeredClient.getClientId());
assertThat(foundRegisteredClient2).isEqualTo(registeredClient);
verify(this.passwordEncoder).encode(anyString());
db.shutdown();
}