JDBC implementation of RegisteredClientRepository
Closes gh-265
This commit is contained in:
committed by
Steve Riesenberg
parent
1f4b369912
commit
769cf8fac7
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.client;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.jdbc.core.*;
|
||||
import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
import org.springframework.jdbc.support.lob.LobCreator;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JDBC-backed registered client repository
|
||||
*
|
||||
* @author Rafal Lewczuk
|
||||
* @since 0.1.2
|
||||
*/
|
||||
public class JdbcRegisteredClientRepository implements RegisteredClientRepository {
|
||||
|
||||
private static final Map<String, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_MAP;
|
||||
private static final Map<String, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_MAP;
|
||||
|
||||
private static final String COLUMN_NAMES = "id, "
|
||||
+ "client_id, "
|
||||
+ "client_id_issued_at, "
|
||||
+ "client_secret, "
|
||||
+ "client_secret_expires_at, "
|
||||
+ "client_name, "
|
||||
+ "client_authentication_methods, "
|
||||
+ "authorization_grant_types, "
|
||||
+ "redirect_uris, "
|
||||
+ "scopes, "
|
||||
+ "client_settings,"
|
||||
+ "token_settings";
|
||||
|
||||
private static final String TABLE_NAME = "oauth2_registered_client";
|
||||
|
||||
private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + " WHERE ";
|
||||
|
||||
private static final String INSERT_REGISTERED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME
|
||||
+ "(" + COLUMN_NAMES + ") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
private RowMapper<RegisteredClient> registeredClientRowMapper;
|
||||
|
||||
private Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper;
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private final LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations, ObjectMapper objectMapper) {
|
||||
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
|
||||
Assert.notNull(objectMapper, "objectMapper cannot be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.objectMapper = objectMapper;
|
||||
this.registeredClientRowMapper = new DefaultRegisteredClientRowMapper();
|
||||
this.registeredClientParametersMapper = new DefaultRegisteredClientParametersMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows changing of {@link RegisteredClient} row mapper implementation
|
||||
*
|
||||
* @param registeredClientRowMapper mapper implementation
|
||||
*/
|
||||
public void setRegisteredClientRowMapper(RowMapper<RegisteredClient> registeredClientRowMapper) {
|
||||
Assert.notNull(registeredClientRowMapper, "registeredClientRowMapper cannot be null");
|
||||
this.registeredClientRowMapper = registeredClientRowMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows changing of SQL parameter mapper for {@link RegisteredClient}
|
||||
*
|
||||
* @param registeredClientParametersMapper mapper implementation
|
||||
*/
|
||||
public void setRegisteredClientParametersMapper(Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper) {
|
||||
Assert.notNull(registeredClientParametersMapper, "registeredClientParameterMapper cannot be null");
|
||||
this.registeredClientParametersMapper = registeredClientParametersMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(RegisteredClient registeredClient) {
|
||||
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||
RegisteredClient foundClient = this.findBy("id = ? OR client_id = ? OR client_secret = ?",
|
||||
registeredClient.getId(), registeredClient.getClientId(),
|
||||
registeredClient.getClientSecret().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
if (null != foundClient) {
|
||||
Assert.isTrue(!foundClient.getId().equals(registeredClient.getId()),
|
||||
"Registered client must be unique. Found duplicate identifier: " + registeredClient.getId());
|
||||
Assert.isTrue(!foundClient.getClientId().equals(registeredClient.getClientId()),
|
||||
"Registered client must be unique. Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
Assert.isTrue(!foundClient.getClientSecret().equals(registeredClient.getClientSecret()),
|
||||
"Registered client must be unique. Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
}
|
||||
|
||||
List<SqlParameterValue> parameters = this.registeredClientParametersMapper.apply(registeredClient);
|
||||
|
||||
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
|
||||
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, parameters.toArray());
|
||||
jdbcOperations.update(INSERT_REGISTERED_CLIENT_SQL, pss);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredClient findById(String id) {
|
||||
Assert.hasText(id, "id cannot be empty");
|
||||
return findBy("id = ?", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredClient findByClientId(String clientId) {
|
||||
Assert.hasText(clientId, "clientId cannot be empty");
|
||||
return findBy("client_id = ?", clientId);
|
||||
}
|
||||
|
||||
private RegisteredClient findBy(String condStr, Object...args) {
|
||||
List<RegisteredClient> lst = jdbcOperations.query(
|
||||
LOAD_REGISTERED_CLIENT_SQL + condStr,
|
||||
registeredClientRowMapper, args);
|
||||
return !lst.isEmpty() ? lst.get(0) : null;
|
||||
}
|
||||
|
||||
private class DefaultRegisteredClientRowMapper implements RowMapper<RegisteredClient> {
|
||||
|
||||
private final LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private Collection<String> parseList(String s) {
|
||||
return s != null ? Arrays.asList(s.split("\\|")) : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
Collection<String> scopes = parseList(rs.getString("scopes"));
|
||||
List<AuthorizationGrantType> authGrantTypes = parseList(rs.getString("authorization_grant_types"))
|
||||
.stream().map(AUTHORIZATION_GRANT_TYPE_MAP::get).collect(Collectors.toList());
|
||||
List<ClientAuthenticationMethod> clientAuthMethods = parseList(rs.getString("client_authentication_methods"))
|
||||
.stream().map(CLIENT_AUTHENTICATION_METHOD_MAP::get).collect(Collectors.toList());
|
||||
Collection<String> redirectUris = parseList(rs.getString("redirect_uris"));
|
||||
Timestamp clientIssuedAt = rs.getTimestamp("client_id_issued_at");
|
||||
Timestamp clientSecretExpiresAt = rs.getTimestamp("client_secret_expires_at");
|
||||
byte[] clientSecretBytes = this.lobHandler.getBlobAsBytes(rs, "client_secret");
|
||||
String clientSecret = clientSecretBytes != null ? new String(clientSecretBytes, StandardCharsets.UTF_8) : null;
|
||||
RegisteredClient.Builder builder = RegisteredClient
|
||||
.withId(rs.getString("id"))
|
||||
.clientId(rs.getString("client_id"))
|
||||
.clientIdIssuedAt(clientIssuedAt != null ? clientIssuedAt.toInstant() : null)
|
||||
.clientSecret(clientSecret)
|
||||
.clientSecretExpiresAt(clientSecretExpiresAt != null ? clientSecretExpiresAt.toInstant() : null)
|
||||
.clientName(rs.getString("client_name"))
|
||||
.clientAuthenticationMethods(coll -> coll.addAll(clientAuthMethods))
|
||||
.authorizationGrantTypes(coll -> coll.addAll(authGrantTypes))
|
||||
.redirectUris(coll -> coll.addAll(redirectUris))
|
||||
.scopes(coll -> coll.addAll(scopes));
|
||||
|
||||
RegisteredClient rc = builder.build();
|
||||
|
||||
TokenSettings ts = rc.getTokenSettings();
|
||||
ClientSettings cs = rc.getClientSettings();
|
||||
|
||||
try {
|
||||
String tokenSettingsJson = rs.getString("token_settings");
|
||||
if (tokenSettingsJson != null) {
|
||||
|
||||
Map<String, Object> m = JdbcRegisteredClientRepository.this.objectMapper.readValue(tokenSettingsJson, Map.class);
|
||||
|
||||
Number accessTokenTTL = (Number) m.get("access_token_ttl");
|
||||
if (accessTokenTTL != null) {
|
||||
ts.accessTokenTimeToLive(Duration.ofMillis(accessTokenTTL.longValue()));
|
||||
}
|
||||
|
||||
Number refreshTokenTTL = (Number) m.get("refresh_token_ttl");
|
||||
if (refreshTokenTTL != null) {
|
||||
ts.refreshTokenTimeToLive(Duration.ofMillis(refreshTokenTTL.longValue()));
|
||||
}
|
||||
|
||||
Boolean reuseRefreshTokens = (Boolean) m.get("reuse_refresh_tokens");
|
||||
if (reuseRefreshTokens != null) {
|
||||
ts.reuseRefreshTokens(reuseRefreshTokens);
|
||||
}
|
||||
}
|
||||
|
||||
String clientSettingsJson = rs.getString("client_settings");
|
||||
if (clientSettingsJson != null) {
|
||||
|
||||
Map<String, Object> m = JdbcRegisteredClientRepository.this.objectMapper.readValue(clientSettingsJson, Map.class);
|
||||
|
||||
Boolean requireProofKey = (Boolean) m.get("require_proof_key");
|
||||
if (requireProofKey != null) {
|
||||
cs.requireProofKey(requireProofKey);
|
||||
}
|
||||
|
||||
Boolean requireUserConsent = (Boolean) m.get("require_user_consent");
|
||||
if (requireUserConsent != null) {
|
||||
cs.requireUserConsent(requireUserConsent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultRegisteredClientParametersMapper implements Function<RegisteredClient, List<SqlParameterValue>> {
|
||||
@Override
|
||||
public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
|
||||
try {
|
||||
List<String> clientAuthenticationMethodNames = new ArrayList<>(registeredClient.getClientAuthenticationMethods().size());
|
||||
for (ClientAuthenticationMethod clientAuthenticationMethod : registeredClient.getClientAuthenticationMethods()) {
|
||||
clientAuthenticationMethodNames.add(clientAuthenticationMethod.getValue());
|
||||
}
|
||||
|
||||
List<String> authorizationGrantTypeNames = new ArrayList<>(registeredClient.getAuthorizationGrantTypes().size());
|
||||
for (AuthorizationGrantType authorizationGrantType : registeredClient.getAuthorizationGrantTypes()) {
|
||||
authorizationGrantTypeNames.add(authorizationGrantType.getValue());
|
||||
}
|
||||
|
||||
Instant issuedAt = registeredClient.getClientIdIssuedAt() != null ?
|
||||
registeredClient.getClientIdIssuedAt() : Instant.now();
|
||||
|
||||
Timestamp clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt() != null ?
|
||||
Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null;
|
||||
|
||||
Map<String, Object> clientSettings = new HashMap<>();
|
||||
clientSettings.put("require_proof_key", registeredClient.getClientSettings().requireProofKey());
|
||||
clientSettings.put("require_user_consent", registeredClient.getClientSettings().requireUserConsent());
|
||||
String clientSettingsJson = JdbcRegisteredClientRepository.this.objectMapper.writeValueAsString(clientSettings);
|
||||
|
||||
Map<String, Object> tokenSettings = new HashMap<>();
|
||||
tokenSettings.put("access_token_ttl", registeredClient.getTokenSettings().accessTokenTimeToLive().toMillis());
|
||||
tokenSettings.put("reuse_refresh_tokens", registeredClient.getTokenSettings().reuseRefreshTokens());
|
||||
tokenSettings.put("refresh_token_ttl", registeredClient.getTokenSettings().refreshTokenTimeToLive().toMillis());
|
||||
String tokenSettingsJson = JdbcRegisteredClientRepository.this.objectMapper.writeValueAsString(tokenSettings);
|
||||
|
||||
return Arrays.asList(
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientId()),
|
||||
new SqlParameterValue(Types.TIMESTAMP, Timestamp.from(issuedAt)),
|
||||
new SqlParameterValue(Types.BLOB, registeredClient.getClientSecret().getBytes(StandardCharsets.UTF_8)),
|
||||
new SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt),
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()),
|
||||
new SqlParameterValue(Types.VARCHAR, String.join("|", clientAuthenticationMethodNames)),
|
||||
new SqlParameterValue(Types.VARCHAR, String.join("|", authorizationGrantTypeNames)),
|
||||
new SqlParameterValue(Types.VARCHAR, String.join("|", registeredClient.getRedirectUris())),
|
||||
new SqlParameterValue(Types.VARCHAR, String.join("|", registeredClient.getScopes())),
|
||||
new SqlParameterValue(Types.VARCHAR, clientSettingsJson),
|
||||
new SqlParameterValue(Types.VARCHAR, tokenSettingsJson));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
|
||||
|
||||
protected final LobCreator lobCreator;
|
||||
|
||||
private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {
|
||||
super(args);
|
||||
this.lobCreator = lobCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
|
||||
if (argValue instanceof SqlParameterValue) {
|
||||
SqlParameterValue paramValue = (SqlParameterValue) argValue;
|
||||
if (paramValue.getSqlType() == Types.BLOB) {
|
||||
if (paramValue.getValue() != null) {
|
||||
Assert.isInstanceOf(byte[].class, paramValue.getValue(),
|
||||
"Value of blob parameter must be byte[]");
|
||||
}
|
||||
byte[] valueBytes = (byte[]) paramValue.getValue();
|
||||
this.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doSetValue(ps, parameterPosition, argValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static {
|
||||
Map<String, AuthorizationGrantType> am = new HashMap<>();
|
||||
for (AuthorizationGrantType a : Arrays.asList(
|
||||
AuthorizationGrantType.AUTHORIZATION_CODE,
|
||||
AuthorizationGrantType.REFRESH_TOKEN,
|
||||
AuthorizationGrantType.CLIENT_CREDENTIALS,
|
||||
AuthorizationGrantType.PASSWORD,
|
||||
AuthorizationGrantType.IMPLICIT)) {
|
||||
am.put(a.getValue(), a);
|
||||
}
|
||||
AUTHORIZATION_GRANT_TYPE_MAP = Collections.unmodifiableMap(am);
|
||||
|
||||
Map<String, ClientAuthenticationMethod> cm = new HashMap<>();
|
||||
for (ClientAuthenticationMethod c : Arrays.asList(
|
||||
ClientAuthenticationMethod.NONE,
|
||||
ClientAuthenticationMethod.BASIC,
|
||||
ClientAuthenticationMethod.POST)) {
|
||||
cm.put(c.getValue(), c);
|
||||
}
|
||||
CLIENT_AUTHENTICATION_METHOD_MAP = Collections.unmodifiableMap(cm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE oauth2_registered_client (
|
||||
id varchar(100) NOT NULL,
|
||||
client_id varchar(100) NOT NULL,
|
||||
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
client_secret blob NOT NULL,
|
||||
client_secret_expires_at timestamp DEFAULT NULL,
|
||||
client_name varchar(200),
|
||||
client_authentication_methods varchar(1000) NOT NULL,
|
||||
authorization_grant_types varchar(1000) NOT NULL,
|
||||
redirect_uris varchar(1000) NOT NULL,
|
||||
scopes varchar(1000) NOT NULL,
|
||||
client_settings varchar(1000) DEFAULT NULL,
|
||||
token_settings varchar(1000) DEFAULT NULL,
|
||||
PRIMARY KEY (id));
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.client;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* JDBC-backed registered client repository tests
|
||||
*
|
||||
* @author Rafal Lewczuk
|
||||
* @since 0.1.2
|
||||
*/
|
||||
public class JdbcRegisteredClientRepositoryTests {
|
||||
|
||||
private final String SCRIPT = "/org/springframework/security/oauth2/server/authorization/client/oauth2_registered_client.sql";
|
||||
|
||||
private DriverManagerDataSource dataSource;
|
||||
|
||||
private JdbcRegisteredClientRepository clients;
|
||||
|
||||
private RegisteredClient registration;
|
||||
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.dataSource = new DriverManagerDataSource();
|
||||
this.dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
|
||||
this.dataSource.setUrl("jdbc:hsqldb:mem:oauthtest");
|
||||
this.dataSource.setUsername("sa");
|
||||
this.dataSource.setPassword("");
|
||||
|
||||
this.jdbc = new JdbcTemplate(this.dataSource);
|
||||
|
||||
// execute scripts
|
||||
try (InputStream is = JdbcRegisteredClientRepositoryTests.class.getResourceAsStream(SCRIPT)) {
|
||||
assertThat(is).isNotNull().describedAs("Cannot open resource file: " + SCRIPT);
|
||||
String ddls = StreamUtils.copyToString(is, Charset.defaultCharset());
|
||||
for (String ddl : ddls.split(";\n")) {
|
||||
if (!ddl.trim().isEmpty()) {
|
||||
this.jdbc.execute(ddl.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.clients = new JdbcRegisteredClientRepository(this.jdbc, new ObjectMapper());
|
||||
this.registration = TestRegisteredClients.registeredClient().build();
|
||||
|
||||
this.clients.save(this.registration);
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroyDatabase() {
|
||||
this.jdbc.update("truncate table oauth2_registered_client");
|
||||
new JdbcTemplate(this.dataSource).execute("SHUTDOWN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenJdbcOperationsNullThenThrow() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcRegisteredClientRepository(null, new ObjectMapper()))
|
||||
.withMessage("jdbcOperations cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenObjectMapperNullThenThrow() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcRegisteredClientRepository(this.jdbc, null))
|
||||
.withMessage("objectMapper cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSetNullRegisteredClientRowMapperThenThrow() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.setRegisteredClientRowMapper(null))
|
||||
.withMessage("registeredClientRowMapper cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSetNullRegisteredClientParameterMapperThenThrow() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.setRegisteredClientParametersMapper(null))
|
||||
.withMessage("registeredClientParameterMapper cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenFoundThenFound() {
|
||||
String id = this.registration.getId();
|
||||
assertRegisteredClientIsEqualTo(this.clients.findById(id), this.registration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenNotFoundThenNull() {
|
||||
RegisteredClient client = this.clients.findById("noooope");
|
||||
assertThat(client).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.findById(null))
|
||||
.withMessage("id cannot be empty");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByClientIdWhenFoundThenFound() {
|
||||
String id = this.registration.getClientId();
|
||||
assertRegisteredClientIsEqualTo(this.clients.findByClientId(id), this.registration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByClientIdWhenNotFoundThenNull() {
|
||||
RegisteredClient client = this.clients.findByClientId("noooope");
|
||||
assertThat(client).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByClientIdWhenNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.findByClientId(null))
|
||||
.withMessage("clientId cannot be empty");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.save(null))
|
||||
.withMessageContaining("registeredClient cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenExistingIdThenThrowIllegalArgumentException() {
|
||||
RegisteredClient registeredClient = createRegisteredClient(
|
||||
this.registration.getId(), "client-id-2", "client-secret-2");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.save(registeredClient))
|
||||
.withMessage("Registered client must be unique. Found duplicate identifier: " + registeredClient.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenExistingClientIdThenThrowIllegalArgumentException() {
|
||||
RegisteredClient registeredClient = createRegisteredClient(
|
||||
"client-2", this.registration.getClientId(), "client-secret-2");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.save(registeredClient))
|
||||
.withMessage("Registered client must be unique. Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenExistingClientSecretThenThrowIllegalArgumentException() {
|
||||
RegisteredClient registeredClient = createRegisteredClient(
|
||||
"client-2", "client-id-2", this.registration.getClientSecret());
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.clients.save(registeredClient))
|
||||
.withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenSavedAndFindByIdThenFound() {
|
||||
RegisteredClient registeredClient = createRegisteredClient();
|
||||
this.clients.save(registeredClient);
|
||||
RegisteredClient savedClient = this.clients.findById(registeredClient.getId());
|
||||
assertRegisteredClientIsEqualTo(savedClient, registeredClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenSavedAndFindByClientIdThenFound() {
|
||||
RegisteredClient registeredClient = createRegisteredClient();
|
||||
this.clients.save(registeredClient);
|
||||
RegisteredClient savedClient = this.clients.findByClientId(registeredClient.getClientId());
|
||||
assertRegisteredClientIsEqualTo(savedClient, registeredClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSaveRegistrationWithAllAttrsThenSaved() {
|
||||
Instant issuedAt = Instant.now(), expiresAt = issuedAt.plus(Duration.ofDays(30));
|
||||
RegisteredClient client = TestRegisteredClients.registeredClient2()
|
||||
.clientIdIssuedAt(issuedAt)
|
||||
.clientSecretExpiresAt(expiresAt)
|
||||
.clientSecret("secret2")
|
||||
.clientName("some_client_name")
|
||||
.redirectUri("https://example2.com")
|
||||
.clientSettings(cs -> {
|
||||
cs.requireProofKey(true);
|
||||
cs.requireUserConsent(true);
|
||||
})
|
||||
.tokenSettings(ts -> {
|
||||
ts.accessTokenTimeToLive(Duration.ofMinutes(3));
|
||||
ts.reuseRefreshTokens(true);
|
||||
ts.refreshTokenTimeToLive(Duration.ofMinutes(300));
|
||||
})
|
||||
.build();
|
||||
|
||||
this.clients.save(client);
|
||||
|
||||
RegisteredClient retrievedClient = this.clients.findById(client.getId());
|
||||
|
||||
assertRegisteredClientIsEqualTo(retrievedClient, client);
|
||||
}
|
||||
|
||||
private void assertRegisteredClientIsEqualTo(RegisteredClient rc, RegisteredClient ref) {
|
||||
assertThat(rc).isNotNull();
|
||||
assertThat(rc.getId()).isEqualTo(ref.getId());
|
||||
assertThat(rc.getClientId()).isEqualTo(ref.getClientId());
|
||||
|
||||
if (ref.getClientIdIssuedAt() != null) {
|
||||
// This can be set to default value
|
||||
Instant inst = ref.getClientIdIssuedAt();
|
||||
assertThat(rc.getClientIdIssuedAt()).isBetween(inst.minusMillis(1), inst.plusMillis(1));
|
||||
}
|
||||
|
||||
assertThat(rc.getClientSecret()).isEqualTo(ref.getClientSecret());
|
||||
|
||||
if (ref.getClientSecretExpiresAt() != null) {
|
||||
Instant inst = ref.getClientSecretExpiresAt();
|
||||
assertThat(rc.getClientSecretExpiresAt()).isBetween(inst.minusMillis(1), inst.plusMillis(1));
|
||||
} else {
|
||||
assertThat(rc.getClientSecretExpiresAt()).isNull();
|
||||
}
|
||||
|
||||
assertThat(rc.getClientName()).isEqualTo(ref.getClientName());
|
||||
assertThat(rc.getClientAuthenticationMethods()).isEqualTo(ref.getClientAuthenticationMethods());
|
||||
assertThat(rc.getAuthorizationGrantTypes()).isEqualTo(ref.getAuthorizationGrantTypes());
|
||||
assertThat(rc.getRedirectUris()).isEqualTo(ref.getRedirectUris());
|
||||
assertThat(rc.getScopes()).isEqualTo(ref.getScopes());
|
||||
assertThat(rc.getClientSettings().requireUserConsent()).isEqualTo(ref.getClientSettings().requireUserConsent());
|
||||
assertThat(rc.getClientSettings().requireProofKey()).isEqualTo(ref.getClientSettings().requireProofKey());
|
||||
assertThat(rc.getTokenSettings().reuseRefreshTokens()).isEqualTo(ref.getTokenSettings().reuseRefreshTokens());
|
||||
assertThat(rc.getTokenSettings().accessTokenTimeToLive()).isEqualTo(ref.getTokenSettings().accessTokenTimeToLive());
|
||||
assertThat(rc.getTokenSettings().refreshTokenTimeToLive()).isEqualTo(ref.getTokenSettings().refreshTokenTimeToLive());
|
||||
}
|
||||
|
||||
private static RegisteredClient createRegisteredClient() {
|
||||
return createRegisteredClient("client-2", "client-id-2", "client-secret-2");
|
||||
}
|
||||
|
||||
|
||||
private static RegisteredClient createRegisteredClient(String id, String clientId, String clientSecret) {
|
||||
// @formatter:off
|
||||
return RegisteredClient.withId(id)
|
||||
.clientId(clientId)
|
||||
.clientSecret(clientSecret)
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||
.redirectUri("https://client.example.com")
|
||||
.scope("scope1")
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user