Skip to content

Commit 763ef22

Browse files
author
Steve Riesenberg
committed
Polish gh-291
1 parent 769cf8f commit 763ef22

File tree

3 files changed

+116
-47
lines changed

3 files changed

+116
-47
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java

Lines changed: 97 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,31 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.client;
1717

18+
import java.nio.charset.StandardCharsets;
19+
import java.sql.PreparedStatement;
20+
import java.sql.ResultSet;
21+
import java.sql.SQLException;
22+
import java.sql.Timestamp;
23+
import java.sql.Types;
24+
import java.time.Duration;
25+
import java.time.Instant;
26+
import java.util.ArrayList;
27+
import java.util.Arrays;
28+
import java.util.Collections;
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Set;
33+
import java.util.function.Function;
34+
1835
import com.fasterxml.jackson.core.JsonProcessingException;
1936
import com.fasterxml.jackson.databind.ObjectMapper;
20-
import org.springframework.jdbc.core.*;
37+
38+
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
39+
import org.springframework.jdbc.core.JdbcOperations;
40+
import org.springframework.jdbc.core.PreparedStatementSetter;
41+
import org.springframework.jdbc.core.RowMapper;
42+
import org.springframework.jdbc.core.SqlParameterValue;
2143
import org.springframework.jdbc.support.lob.DefaultLobHandler;
2244
import org.springframework.jdbc.support.lob.LobCreator;
2345
import org.springframework.jdbc.support.lob.LobHandler;
@@ -26,14 +48,7 @@
2648
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
2749
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
2850
import org.springframework.util.Assert;
29-
30-
import java.nio.charset.StandardCharsets;
31-
import java.sql.*;
32-
import java.time.Duration;
33-
import java.time.Instant;
34-
import java.util.*;
35-
import java.util.function.Function;
36-
import java.util.stream.Collectors;
51+
import org.springframework.util.StringUtils;
3752

3853
/**
3954
* JDBC-backed registered client repository
@@ -72,17 +87,44 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
7287

7388
private final JdbcOperations jdbcOperations;
7489

75-
private final LobHandler lobHandler = new DefaultLobHandler();
90+
private final LobHandler lobHandler;
7691

77-
private final ObjectMapper objectMapper;
92+
/**
93+
* Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters.
94+
*
95+
* @param jdbcOperations the JDBC operations
96+
*/
97+
public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations) {
98+
this(jdbcOperations, new ObjectMapper());
99+
}
78100

101+
/**
102+
* Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters.
103+
*
104+
* @param jdbcOperations the JDBC operations
105+
* @param objectMapper the object mapper
106+
*/
79107
public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations, ObjectMapper objectMapper) {
108+
this(jdbcOperations, new DefaultLobHandler(), objectMapper);
109+
}
110+
111+
/**
112+
* Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters.
113+
*
114+
* @param jdbcOperations the JDBC operations
115+
* @param lobHandler the handler for large binary fields and large text fields
116+
* @param objectMapper the object mapper
117+
*/
118+
public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations, LobHandler lobHandler, ObjectMapper objectMapper) {
80119
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
120+
Assert.notNull(lobHandler, "lobHandler cannot be null");
81121
Assert.notNull(objectMapper, "objectMapper cannot be null");
82122
this.jdbcOperations = jdbcOperations;
83-
this.objectMapper = objectMapper;
84-
this.registeredClientRowMapper = new DefaultRegisteredClientRowMapper();
85-
this.registeredClientParametersMapper = new DefaultRegisteredClientParametersMapper();
123+
this.lobHandler = lobHandler;
124+
DefaultRegisteredClientRowMapper registeredClientRowMapper = new DefaultRegisteredClientRowMapper(objectMapper);
125+
registeredClientRowMapper.setLobHandler(lobHandler);
126+
this.registeredClientRowMapper = registeredClientRowMapper;
127+
this.registeredClientParametersMapper = new DefaultRegisteredClientParametersMapper(objectMapper);
86128
}
87129

88130
/**
@@ -148,23 +190,27 @@ private RegisteredClient findBy(String condStr, Object...args) {
148190
return !lst.isEmpty() ? lst.get(0) : null;
149191
}
150192

151-
private class DefaultRegisteredClientRowMapper implements RowMapper<RegisteredClient> {
193+
public static class DefaultRegisteredClientRowMapper implements RowMapper<RegisteredClient> {
194+
195+
private final ObjectMapper objectMapper;
152196

153-
private final LobHandler lobHandler = new DefaultLobHandler();
197+
private LobHandler lobHandler = new DefaultLobHandler();
154198

155-
private Collection<String> parseList(String s) {
156-
return s != null ? Arrays.asList(s.split("\\|")) : Collections.emptyList();
199+
public DefaultRegisteredClientRowMapper(ObjectMapper objectMapper) {
200+
this.objectMapper = objectMapper;
201+
}
202+
203+
private Set<String> parseList(String s) {
204+
return s != null ? StringUtils.commaDelimitedListToSet(s) : Collections.emptySet();
157205
}
158206

159207
@Override
160208
@SuppressWarnings("unchecked")
161209
public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
162-
Collection<String> scopes = parseList(rs.getString("scopes"));
163-
List<AuthorizationGrantType> authGrantTypes = parseList(rs.getString("authorization_grant_types"))
164-
.stream().map(AUTHORIZATION_GRANT_TYPE_MAP::get).collect(Collectors.toList());
165-
List<ClientAuthenticationMethod> clientAuthMethods = parseList(rs.getString("client_authentication_methods"))
166-
.stream().map(CLIENT_AUTHENTICATION_METHOD_MAP::get).collect(Collectors.toList());
167-
Collection<String> redirectUris = parseList(rs.getString("redirect_uris"));
210+
Set<String> scopes = parseList(rs.getString("scopes"));
211+
Set<String> authGrantTypes = parseList(rs.getString("authorization_grant_types"));
212+
Set<String> clientAuthMethods = parseList(rs.getString("client_authentication_methods"));
213+
Set<String> redirectUris = parseList(rs.getString("redirect_uris"));
168214
Timestamp clientIssuedAt = rs.getTimestamp("client_id_issued_at");
169215
Timestamp clientSecretExpiresAt = rs.getTimestamp("client_secret_expires_at");
170216
byte[] clientSecretBytes = this.lobHandler.getBlobAsBytes(rs, "client_secret");
@@ -176,8 +222,10 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
176222
.clientSecret(clientSecret)
177223
.clientSecretExpiresAt(clientSecretExpiresAt != null ? clientSecretExpiresAt.toInstant() : null)
178224
.clientName(rs.getString("client_name"))
179-
.clientAuthenticationMethods(coll -> coll.addAll(clientAuthMethods))
180-
.authorizationGrantTypes(coll -> coll.addAll(authGrantTypes))
225+
.authorizationGrantTypes(coll -> authGrantTypes.forEach(authGrantType ->
226+
coll.add(AUTHORIZATION_GRANT_TYPE_MAP.get(authGrantType))))
227+
.clientAuthenticationMethods(coll -> clientAuthMethods.forEach(clientAuthMethod ->
228+
coll.add(CLIENT_AUTHENTICATION_METHOD_MAP.get(clientAuthMethod))))
181229
.redirectUris(coll -> coll.addAll(redirectUris))
182230
.scopes(coll -> coll.addAll(scopes));
183231

@@ -189,8 +237,7 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
189237
try {
190238
String tokenSettingsJson = rs.getString("token_settings");
191239
if (tokenSettingsJson != null) {
192-
193-
Map<String, Object> m = JdbcRegisteredClientRepository.this.objectMapper.readValue(tokenSettingsJson, Map.class);
240+
Map<String, Object> m = this.objectMapper.readValue(tokenSettingsJson, Map.class);
194241

195242
Number accessTokenTTL = (Number) m.get("access_token_ttl");
196243
if (accessTokenTTL != null) {
@@ -210,8 +257,7 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
210257

211258
String clientSettingsJson = rs.getString("client_settings");
212259
if (clientSettingsJson != null) {
213-
214-
Map<String, Object> m = JdbcRegisteredClientRepository.this.objectMapper.readValue(clientSettingsJson, Map.class);
260+
Map<String, Object> m = this.objectMapper.readValue(clientSettingsJson, Map.class);
215261

216262
Boolean requireProofKey = (Boolean) m.get("require_proof_key");
217263
if (requireProofKey != null) {
@@ -223,17 +269,28 @@ public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
223269
cs.requireUserConsent(requireUserConsent);
224270
}
225271
}
226-
227-
228272
} catch (JsonProcessingException e) {
229273
throw new IllegalArgumentException(e.getMessage(), e);
230274
}
231275

232276
return rc;
233277
}
278+
279+
public final void setLobHandler(LobHandler lobHandler) {
280+
Assert.notNull(lobHandler, "lobHandler cannot be null");
281+
this.lobHandler = lobHandler;
282+
}
283+
234284
}
235285

236-
private class DefaultRegisteredClientParametersMapper implements Function<RegisteredClient, List<SqlParameterValue>> {
286+
public static class DefaultRegisteredClientParametersMapper implements Function<RegisteredClient, List<SqlParameterValue>> {
287+
288+
private final ObjectMapper objectMapper;
289+
290+
private DefaultRegisteredClientParametersMapper(ObjectMapper objectMapper) {
291+
this.objectMapper = objectMapper;
292+
}
293+
237294
@Override
238295
public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
239296
try {
@@ -256,13 +313,13 @@ public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
256313
Map<String, Object> clientSettings = new HashMap<>();
257314
clientSettings.put("require_proof_key", registeredClient.getClientSettings().requireProofKey());
258315
clientSettings.put("require_user_consent", registeredClient.getClientSettings().requireUserConsent());
259-
String clientSettingsJson = JdbcRegisteredClientRepository.this.objectMapper.writeValueAsString(clientSettings);
316+
String clientSettingsJson = this.objectMapper.writeValueAsString(clientSettings);
260317

261318
Map<String, Object> tokenSettings = new HashMap<>();
262319
tokenSettings.put("access_token_ttl", registeredClient.getTokenSettings().accessTokenTimeToLive().toMillis());
263320
tokenSettings.put("reuse_refresh_tokens", registeredClient.getTokenSettings().reuseRefreshTokens());
264321
tokenSettings.put("refresh_token_ttl", registeredClient.getTokenSettings().refreshTokenTimeToLive().toMillis());
265-
String tokenSettingsJson = JdbcRegisteredClientRepository.this.objectMapper.writeValueAsString(tokenSettings);
322+
String tokenSettingsJson = this.objectMapper.writeValueAsString(tokenSettings);
266323

267324
return Arrays.asList(
268325
new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),
@@ -271,16 +328,17 @@ public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
271328
new SqlParameterValue(Types.BLOB, registeredClient.getClientSecret().getBytes(StandardCharsets.UTF_8)),
272329
new SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt),
273330
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()),
274-
new SqlParameterValue(Types.VARCHAR, String.join("|", clientAuthenticationMethodNames)),
275-
new SqlParameterValue(Types.VARCHAR, String.join("|", authorizationGrantTypeNames)),
276-
new SqlParameterValue(Types.VARCHAR, String.join("|", registeredClient.getRedirectUris())),
277-
new SqlParameterValue(Types.VARCHAR, String.join("|", registeredClient.getScopes())),
331+
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethodNames)),
332+
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(authorizationGrantTypeNames)),
333+
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),
334+
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),
278335
new SqlParameterValue(Types.VARCHAR, clientSettingsJson),
279336
new SqlParameterValue(Types.VARCHAR, tokenSettingsJson));
280337
} catch (JsonProcessingException e) {
281338
throw new IllegalArgumentException(e.getMessage(), e);
282339
}
283340
}
341+
284342
}
285343

286344
private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,24 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.client;
1717

18+
import java.io.InputStream;
19+
import java.nio.charset.Charset;
20+
import java.time.Duration;
21+
import java.time.Instant;
22+
1823
import com.fasterxml.jackson.databind.ObjectMapper;
1924
import org.junit.After;
2025
import org.junit.Before;
2126
import org.junit.Test;
27+
2228
import org.springframework.jdbc.core.JdbcTemplate;
2329
import org.springframework.jdbc.datasource.DriverManagerDataSource;
2430
import org.springframework.security.oauth2.core.AuthorizationGrantType;
2531
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
2632
import org.springframework.util.StreamUtils;
2733

28-
import java.io.InputStream;
29-
import java.nio.charset.Charset;
30-
import java.time.Duration;
31-
import java.time.Instant;
32-
33-
import static org.assertj.core.api.Assertions.*;
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3436

3537
/**
3638
* JDBC-backed registered client repository tests
@@ -40,7 +42,7 @@
4042
*/
4143
public class JdbcRegisteredClientRepositoryTests {
4244

43-
private final String SCRIPT = "/org/springframework/security/oauth2/server/authorization/client/oauth2_registered_client.sql";
45+
private final String SCRIPT = "/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client.sql";
4446

4547
private DriverManagerDataSource dataSource;
4648

@@ -71,7 +73,7 @@ public void setup() throws Exception {
7173
}
7274
}
7375

74-
this.clients = new JdbcRegisteredClientRepository(this.jdbc, new ObjectMapper());
76+
this.clients = new JdbcRegisteredClientRepository(this.jdbc);
7577
this.registration = TestRegisteredClients.registeredClient().build();
7678

7779
this.clients.save(this.registration);
@@ -101,6 +103,15 @@ public void whenObjectMapperNullThenThrow() {
101103
// @formatter:on
102104
}
103105

106+
@Test
107+
public void whenLobHandlerNullThenThrow() {
108+
// @formatter:off
109+
assertThatIllegalArgumentException()
110+
.isThrownBy(() -> new JdbcRegisteredClientRepository(this.jdbc, null, new ObjectMapper()))
111+
.withMessage("lobHandler cannot be null");
112+
// @formatter:on
113+
}
114+
104115
@Test
105116
public void whenSetNullRegisteredClientRowMapperThenThrow() {
106117
// @formatter:off

0 commit comments

Comments
 (0)