|
@@ -0,0 +1,474 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright 2002-2020 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.client;
|
|
|
|
+
|
|
|
|
+import org.junit.After;
|
|
|
|
+import org.junit.Before;
|
|
|
|
+import org.junit.Test;
|
|
|
|
+import org.springframework.dao.DataRetrievalFailureException;
|
|
|
|
+import org.springframework.dao.DuplicateKeyException;
|
|
|
|
+import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
|
|
|
+import org.springframework.jdbc.core.JdbcOperations;
|
|
|
|
+import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
|
+import org.springframework.jdbc.core.PreparedStatementSetter;
|
|
|
|
+import org.springframework.jdbc.core.RowMapper;
|
|
|
|
+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.authentication.TestingAuthenticationToken;
|
|
|
|
+import org.springframework.security.core.Authentication;
|
|
|
|
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
|
|
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
|
|
|
+import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
|
|
|
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|
|
|
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
|
|
|
+import org.springframework.security.oauth2.core.TestOAuth2AccessTokens;
|
|
|
|
+import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;
|
|
|
|
+import org.springframework.util.Assert;
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
+
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
+import java.sql.ResultSet;
|
|
|
|
+import java.sql.SQLException;
|
|
|
|
+import java.sql.Timestamp;
|
|
|
|
+import java.sql.Types;
|
|
|
|
+import java.time.Instant;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Set;
|
|
|
|
+
|
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
|
+import static org.mockito.ArgumentMatchers.anyInt;
|
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
|
+import static org.mockito.Mockito.spy;
|
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Tests for {@link JdbcOAuth2AuthorizedClientService}.
|
|
|
|
+ *
|
|
|
|
+ * @author Joe Grandja
|
|
|
|
+ */
|
|
|
|
+public class JdbcOAuth2AuthorizedClientServiceTests {
|
|
|
|
+ private static final String OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/client/oauth2-client-schema.sql";
|
|
|
|
+ private static int principalId = 1000;
|
|
|
|
+ private ClientRegistration clientRegistration;
|
|
|
|
+ private ClientRegistrationRepository clientRegistrationRepository;
|
|
|
|
+ private EmbeddedDatabase db;
|
|
|
|
+ private JdbcOperations jdbcOperations;
|
|
|
|
+ private JdbcOAuth2AuthorizedClientService authorizedClientService;
|
|
|
|
+
|
|
|
|
+ @Before
|
|
|
|
+ public void setUp() {
|
|
|
|
+ this.clientRegistration = TestClientRegistrations.clientRegistration().build();
|
|
|
|
+ this.clientRegistrationRepository = mock(ClientRegistrationRepository.class);
|
|
|
|
+ when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(this.clientRegistration);
|
|
|
|
+ this.db = createDb();
|
|
|
|
+ this.jdbcOperations = new JdbcTemplate(this.db);
|
|
|
|
+ this.authorizedClientService = new JdbcOAuth2AuthorizedClientService(
|
|
|
|
+ this.jdbcOperations, this.clientRegistrationRepository);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @After
|
|
|
|
+ public void tearDown() {
|
|
|
|
+ this.db.shutdown();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> new JdbcOAuth2AuthorizedClientService(null, this.clientRegistrationRepository))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("jdbcOperations cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> new JdbcOAuth2AuthorizedClientService(this.jdbcOperations, null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("clientRegistrationRepository cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void setAuthorizedClientRowMapperWhenNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.setAuthorizedClientRowMapper(null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("authorizedClientRowMapper cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void setAuthorizedClientParametersMapperWhenNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.setAuthorizedClientParametersMapper(null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("authorizedClientParametersMapper cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(null, "principalName"))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("clientRegistrationId cannot be empty");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void loadAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("principalName cannot be empty");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void loadAuthorizedClientWhenDoesNotExistThenReturnNull() {
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ "registration-not-found", "principalName");
|
|
|
|
+ assertThat(authorizedClient).isNull();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void loadAuthorizedClientWhenExistsThenReturnAuthorizedClient() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(expected, principal);
|
|
|
|
+
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ assertThat(authorizedClient).isNotNull();
|
|
|
|
+ assertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());
|
|
|
|
+ assertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenType()).isEqualTo(expected.getAccessToken().getTokenType());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenValue()).isEqualTo(expected.getAccessToken().getTokenValue());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getIssuedAt()).isEqualTo(expected.getAccessToken().getIssuedAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getExpiresAt()).isEqualTo(expected.getAccessToken().getExpiresAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());
|
|
|
|
+ assertThat(authorizedClient.getRefreshToken().getTokenValue()).isEqualTo(expected.getRefreshToken().getTokenValue());
|
|
|
|
+ assertThat(authorizedClient.getRefreshToken().getIssuedAt()).isEqualTo(expected.getRefreshToken().getIssuedAt());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void loadAuthorizedClientWhenExistsButNotFoundInClientRegistrationRepositoryThenThrowDataRetrievalFailureException() {
|
|
|
|
+ when(this.clientRegistrationRepository.findByRegistrationId(any())).thenReturn(null);
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(expected, principal);
|
|
|
|
+
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName()))
|
|
|
|
+ .isInstanceOf(DataRetrievalFailureException.class)
|
|
|
|
+ .hasMessage("The ClientRegistration with id '" + this.clientRegistration.getRegistrationId() +
|
|
|
|
+ "' exists in the data source, however, it was not found in the ClientRegistrationRepository.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(null, principal))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("authorizedClient cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void saveAuthorizedClientWhenPrincipalIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("principal cannot be null");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void saveAuthorizedClientWhenSaveThenLoadReturnsSaved() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(expected, principal);
|
|
|
|
+
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ assertThat(authorizedClient).isNotNull();
|
|
|
|
+ assertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());
|
|
|
|
+ assertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenType()).isEqualTo(expected.getAccessToken().getTokenType());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenValue()).isEqualTo(expected.getAccessToken().getTokenValue());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getIssuedAt()).isEqualTo(expected.getAccessToken().getIssuedAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getExpiresAt()).isEqualTo(expected.getAccessToken().getExpiresAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());
|
|
|
|
+ assertThat(authorizedClient.getRefreshToken().getTokenValue()).isEqualTo(expected.getRefreshToken().getTokenValue());
|
|
|
|
+ assertThat(authorizedClient.getRefreshToken().getIssuedAt()).isEqualTo(expected.getRefreshToken().getIssuedAt());
|
|
|
|
+
|
|
|
|
+ // Test save/load of NOT NULL attributes only
|
|
|
|
+ principal = createPrincipal();
|
|
|
|
+ expected = createAuthorizedClient(principal, this.clientRegistration, true);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(expected, principal);
|
|
|
|
+
|
|
|
|
+ authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ assertThat(authorizedClient).isNotNull();
|
|
|
|
+ assertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());
|
|
|
|
+ assertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenType()).isEqualTo(expected.getAccessToken().getTokenType());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getTokenValue()).isEqualTo(expected.getAccessToken().getTokenValue());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getIssuedAt()).isEqualTo(expected.getAccessToken().getIssuedAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getExpiresAt()).isEqualTo(expected.getAccessToken().getExpiresAt());
|
|
|
|
+ assertThat(authorizedClient.getAccessToken().getScopes()).isEmpty();
|
|
|
|
+ assertThat(authorizedClient.getRefreshToken()).isNull();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void saveAuthorizedClientWhenSaveDuplicateThenThrowDuplicateKeyException() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
|
|
|
+
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal))
|
|
|
|
+ .isInstanceOf(DuplicateKeyException.class);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void saveLoadAuthorizedClientWhenCustomStrategiesSetThenCalled() throws Exception {
|
|
|
|
+ JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientRowMapper authorizedClientRowMapper =
|
|
|
|
+ spy(new JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientRowMapper(this.clientRegistrationRepository));
|
|
|
|
+ this.authorizedClientService.setAuthorizedClientRowMapper(authorizedClientRowMapper);
|
|
|
|
+ JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientParametersMapper authorizedClientParametersMapper =
|
|
|
|
+ spy(new JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientParametersMapper());
|
|
|
|
+ this.authorizedClientService.setAuthorizedClientParametersMapper(authorizedClientParametersMapper);
|
|
|
|
+
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
|
|
|
+ this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ verify(authorizedClientRowMapper).mapRow(any(), anyInt());
|
|
|
|
+ verify(authorizedClientParametersMapper).apply(any());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(null, "principalName"))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("clientRegistrationId cannot be empty");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void removeAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {
|
|
|
|
+ assertThatThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(this.clientRegistration.getRegistrationId(), null))
|
|
|
|
+ .isInstanceOf(IllegalArgumentException.class)
|
|
|
|
+ .hasMessage("principalName cannot be empty");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void removeAuthorizedClientWhenExistsThenRemoved() {
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
|
|
|
+
|
|
|
|
+ authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+ assertThat(authorizedClient).isNotNull();
|
|
|
|
+
|
|
|
|
+ this.authorizedClientService.removeAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ authorizedClient = this.authorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+ assertThat(authorizedClient).isNull();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void tableDefinitionWhenCustomThenAbleToOverride() {
|
|
|
|
+ CustomTableDefinitionJdbcOAuth2AuthorizedClientService customAuthorizedClientService =
|
|
|
|
+ new CustomTableDefinitionJdbcOAuth2AuthorizedClientService(
|
|
|
|
+ new JdbcTemplate(createDb("custom-oauth2-client-schema.sql")),
|
|
|
|
+ this.clientRegistrationRepository);
|
|
|
|
+
|
|
|
|
+ Authentication principal = createPrincipal();
|
|
|
|
+ OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
|
|
|
|
+
|
|
|
|
+ customAuthorizedClientService.saveAuthorizedClient(authorizedClient, principal);
|
|
|
|
+
|
|
|
|
+ authorizedClient = customAuthorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+ assertThat(authorizedClient).isNotNull();
|
|
|
|
+
|
|
|
|
+ customAuthorizedClientService.removeAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+
|
|
|
|
+ authorizedClient = customAuthorizedClientService.loadAuthorizedClient(
|
|
|
|
+ this.clientRegistration.getRegistrationId(), principal.getName());
|
|
|
|
+ assertThat(authorizedClient).isNull();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static EmbeddedDatabase createDb() {
|
|
|
|
+ return createDb(OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static EmbeddedDatabase createDb(String schema) {
|
|
|
|
+ return new EmbeddedDatabaseBuilder()
|
|
|
|
+ .generateUniqueName(true)
|
|
|
|
+ .setType(EmbeddedDatabaseType.HSQL)
|
|
|
|
+ .setScriptEncoding("UTF-8")
|
|
|
|
+ .addScript(schema)
|
|
|
|
+ .build();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static Authentication createPrincipal() {
|
|
|
|
+ return new TestingAuthenticationToken("principal-" + principalId++, "password");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal, ClientRegistration clientRegistration) {
|
|
|
|
+ return createAuthorizedClient(principal, clientRegistration, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal,
|
|
|
|
+ ClientRegistration clientRegistration, boolean requiredAttributesOnly) {
|
|
|
|
+ OAuth2AccessToken accessToken;
|
|
|
|
+ if (!requiredAttributesOnly) {
|
|
|
|
+ accessToken = TestOAuth2AccessTokens.scopes("read", "write");
|
|
|
|
+ } else {
|
|
|
|
+ accessToken = TestOAuth2AccessTokens.noScopes();
|
|
|
|
+ }
|
|
|
|
+ OAuth2RefreshToken refreshToken = null;
|
|
|
|
+ if (!requiredAttributesOnly) {
|
|
|
|
+ refreshToken = TestOAuth2RefreshTokens.refreshToken();
|
|
|
|
+ }
|
|
|
|
+ return new OAuth2AuthorizedClient(
|
|
|
|
+ clientRegistration, principal.getName(), accessToken, refreshToken);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static class CustomTableDefinitionJdbcOAuth2AuthorizedClientService extends JdbcOAuth2AuthorizedClientService {
|
|
|
|
+ private static final String COLUMN_NAMES =
|
|
|
|
+ "clientRegistrationId, " +
|
|
|
|
+ "principalName, " +
|
|
|
|
+ "accessTokenType, " +
|
|
|
|
+ "accessTokenValue, " +
|
|
|
|
+ "accessTokenIssuedAt, " +
|
|
|
|
+ "accessTokenExpiresAt, " +
|
|
|
|
+ "accessTokenScopes, " +
|
|
|
|
+ "refreshTokenValue, " +
|
|
|
|
+ "refreshTokenIssuedAt";
|
|
|
|
+ private static final String TABLE_NAME = "oauth2AuthorizedClient";
|
|
|
|
+ private static final String PK_FILTER = "clientRegistrationId = ? AND principalName = ?";
|
|
|
|
+ private static final String LOAD_AUTHORIZED_CLIENT_SQL = "SELECT " + COLUMN_NAMES +
|
|
|
|
+ " FROM " + TABLE_NAME + " WHERE " + PK_FILTER;
|
|
|
|
+ private static final String SAVE_AUTHORIZED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME +
|
|
|
|
+ " (" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
|
|
+ private static final String REMOVE_AUTHORIZED_CLIENT_SQL = "DELETE FROM " + TABLE_NAME +
|
|
|
|
+ " WHERE " + PK_FILTER;
|
|
|
|
+
|
|
|
|
+ private CustomTableDefinitionJdbcOAuth2AuthorizedClientService(
|
|
|
|
+ JdbcOperations jdbcOperations, ClientRegistrationRepository clientRegistrationRepository) {
|
|
|
|
+ super(jdbcOperations, clientRegistrationRepository);
|
|
|
|
+ setAuthorizedClientRowMapper(new OAuth2AuthorizedClientRowMapper(clientRegistrationRepository));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, String principalName) {
|
|
|
|
+ SqlParameterValue[] parameters = new SqlParameterValue[] {
|
|
|
|
+ new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
|
|
|
+ new SqlParameterValue(Types.VARCHAR, principalName)
|
|
|
|
+ };
|
|
|
|
+ PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
|
|
|
+ List<OAuth2AuthorizedClient> result = this.jdbcOperations.query(
|
|
|
|
+ LOAD_AUTHORIZED_CLIENT_SQL, pss, this.authorizedClientRowMapper);
|
|
|
|
+ return !result.isEmpty() ? (T) result.get(0) : null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
|
|
|
+ List<SqlParameterValue> parameters = this.authorizedClientParametersMapper.apply(
|
|
|
|
+ new OAuth2AuthorizedClientHolder(authorizedClient, principal));
|
|
|
|
+ PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
|
|
|
+ this.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void removeAuthorizedClient(String clientRegistrationId, String principalName) {
|
|
|
|
+ SqlParameterValue[] parameters = new SqlParameterValue[] {
|
|
|
|
+ new SqlParameterValue(Types.VARCHAR, clientRegistrationId),
|
|
|
|
+ new SqlParameterValue(Types.VARCHAR, principalName)
|
|
|
|
+ };
|
|
|
|
+ PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
|
|
|
+ this.jdbcOperations.update(REMOVE_AUTHORIZED_CLIENT_SQL, pss);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static class OAuth2AuthorizedClientRowMapper implements RowMapper<OAuth2AuthorizedClient> {
|
|
|
|
+ private final ClientRegistrationRepository clientRegistrationRepository;
|
|
|
|
+
|
|
|
|
+ private OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {
|
|
|
|
+ Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
|
|
|
+ this.clientRegistrationRepository = clientRegistrationRepository;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {
|
|
|
|
+ String clientRegistrationId = rs.getString("clientRegistrationId");
|
|
|
|
+ ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(
|
|
|
|
+ clientRegistrationId);
|
|
|
|
+ if (clientRegistration == null) {
|
|
|
|
+ throw new DataRetrievalFailureException("The ClientRegistration with id '" +
|
|
|
|
+ clientRegistrationId + "' exists in the data source, " +
|
|
|
|
+ "however, it was not found in the ClientRegistrationRepository.");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ OAuth2AccessToken.TokenType tokenType = null;
|
|
|
|
+ if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(
|
|
|
|
+ rs.getString("accessTokenType"))) {
|
|
|
|
+ tokenType = OAuth2AccessToken.TokenType.BEARER;
|
|
|
|
+ }
|
|
|
|
+ String tokenValue = new String(rs.getBytes("accessTokenValue"), StandardCharsets.UTF_8);
|
|
|
|
+ Instant issuedAt = rs.getTimestamp("accessTokenIssuedAt").toInstant();
|
|
|
|
+ Instant expiresAt = rs.getTimestamp("accessTokenExpiresAt").toInstant();
|
|
|
|
+ Set<String> scopes = Collections.emptySet();
|
|
|
|
+ String accessTokenScopes = rs.getString("accessTokenScopes");
|
|
|
|
+ if (accessTokenScopes != null) {
|
|
|
|
+ scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);
|
|
|
|
+ }
|
|
|
|
+ OAuth2AccessToken accessToken = new OAuth2AccessToken(
|
|
|
|
+ tokenType, tokenValue, issuedAt, expiresAt, scopes);
|
|
|
|
+
|
|
|
|
+ OAuth2RefreshToken refreshToken = null;
|
|
|
|
+ byte[] refreshTokenValue = rs.getBytes("refreshTokenValue");
|
|
|
|
+ if (refreshTokenValue != null) {
|
|
|
|
+ tokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);
|
|
|
|
+ issuedAt = null;
|
|
|
|
+ Timestamp refreshTokenIssuedAt = rs.getTimestamp("refreshTokenIssuedAt");
|
|
|
|
+ if (refreshTokenIssuedAt != null) {
|
|
|
|
+ issuedAt = refreshTokenIssuedAt.toInstant();
|
|
|
|
+ }
|
|
|
|
+ refreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String principalName = rs.getString("principalName");
|
|
|
|
+
|
|
|
|
+ return new OAuth2AuthorizedClient(
|
|
|
|
+ clientRegistration, principalName, accessToken, refreshToken);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|