Support update when saving with JdbcOAuth2AuthorizedClientService
Before this commit, JdbcOAuth2AuthorizedClientService threw DuplicateKeyException when re-authorizing or when authorizing the same user from a different client. This commit makes JdbcOAuth2AuthorizedClientService's saveAuthorizedClient method consistent with that of InMemoryOAuth2AuthorizedClientService. Fixes gh-8425
This commit is contained in:
		
							parent
							
								
									4d63e2f332
								
							
						
					
					
						commit
						a783fbc641
					
				| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
package org.springframework.security.oauth2.client;
 | 
			
		||||
 | 
			
		||||
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.PreparedStatementSetter;
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +53,7 @@ import java.util.function.Function;
 | 
			
		|||
 * and therefore MUST be defined in the database schema.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @author Stav Shamir
 | 
			
		||||
 * @since 5.3
 | 
			
		||||
 * @see OAuth2AuthorizedClientService
 | 
			
		||||
 * @see OAuth2AuthorizedClient
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +79,11 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 | 
			
		|||
			" (" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
 | 
			
		||||
	private static final String REMOVE_AUTHORIZED_CLIENT_SQL = "DELETE FROM " + TABLE_NAME +
 | 
			
		||||
			" WHERE " + PK_FILTER;
 | 
			
		||||
	private static final String UPDATE_AUTHORIZED_CLIENT_SQL = "UPDATE " + TABLE_NAME +
 | 
			
		||||
			" SET access_token_type = ?, access_token_value = ?, access_token_issued_at = ?," +
 | 
			
		||||
			" access_token_expires_at = ?, access_token_scopes = ?," +
 | 
			
		||||
			" refresh_token_value = ?, refresh_token_issued_at = ?" +
 | 
			
		||||
			" WHERE " + PK_FILTER;
 | 
			
		||||
	protected final JdbcOperations jdbcOperations;
 | 
			
		||||
	protected RowMapper<OAuth2AuthorizedClient> authorizedClientRowMapper;
 | 
			
		||||
	protected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;
 | 
			
		||||
| 
						 | 
				
			
			@ -120,6 +127,35 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
 | 
			
		|||
		Assert.notNull(authorizedClient, "authorizedClient cannot be null");
 | 
			
		||||
		Assert.notNull(principal, "principal cannot be null");
 | 
			
		||||
 | 
			
		||||
		boolean existsAuthorizedClient = null != this.loadAuthorizedClient(
 | 
			
		||||
				authorizedClient.getClientRegistration().getRegistrationId(), principal.getName());
 | 
			
		||||
 | 
			
		||||
		if (existsAuthorizedClient) {
 | 
			
		||||
			updateAuthorizedClient(authorizedClient, principal);
 | 
			
		||||
		} else {
 | 
			
		||||
			try {
 | 
			
		||||
				insertAuthorizedClient(authorizedClient, principal);
 | 
			
		||||
			} catch (DuplicateKeyException e) {
 | 
			
		||||
				updateAuthorizedClient(authorizedClient, principal);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
 | 
			
		||||
		List<SqlParameterValue> parameters = this.authorizedClientParametersMapper.apply(
 | 
			
		||||
				new OAuth2AuthorizedClientHolder(authorizedClient, principal));
 | 
			
		||||
 | 
			
		||||
		SqlParameterValue clientRegistrationIdParameter = parameters.remove(0);
 | 
			
		||||
		SqlParameterValue principalNameParameter = parameters.remove(0);
 | 
			
		||||
		parameters.add(clientRegistrationIdParameter);
 | 
			
		||||
		parameters.add(principalNameParameter);
 | 
			
		||||
 | 
			
		||||
		PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
 | 
			
		||||
 | 
			
		||||
		this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
 | 
			
		||||
		List<SqlParameterValue> parameters = this.authorizedClientParametersMapper.apply(
 | 
			
		||||
				new OAuth2AuthorizedClientHolder(authorizedClient, principal));
 | 
			
		||||
		PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,6 @@ 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +63,7 @@ import static org.mockito.Mockito.when;
 | 
			
		|||
 * Tests for {@link JdbcOAuth2AuthorizedClientService}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joe Grandja
 | 
			
		||||
 * @author Stav Shamir
 | 
			
		||||
 */
 | 
			
		||||
public class JdbcOAuth2AuthorizedClientServiceTests {
 | 
			
		||||
	private static final String OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/client/oauth2-client-schema.sql";
 | 
			
		||||
| 
						 | 
				
			
			@ -236,14 +236,30 @@ public class JdbcOAuth2AuthorizedClientServiceTests {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void saveAuthorizedClientWhenSaveDuplicateThenThrowDuplicateKeyException() {
 | 
			
		||||
	public void saveAuthorizedClientWhenSaveClientWithExistingPrimaryKeyThenUpdate() {
 | 
			
		||||
		// Given a saved authorized client
 | 
			
		||||
		Authentication principal = createPrincipal();
 | 
			
		||||
		OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
 | 
			
		||||
 | 
			
		||||
		this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
 | 
			
		||||
 | 
			
		||||
		assertThatThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal))
 | 
			
		||||
				.isInstanceOf(DuplicateKeyException.class);
 | 
			
		||||
		// When a client with the same principal and registration id is saved
 | 
			
		||||
		OAuth2AuthorizedClient updatedClient = createAuthorizedClient(principal, this.clientRegistration);
 | 
			
		||||
		this.authorizedClientService.saveAuthorizedClient(updatedClient, principal);
 | 
			
		||||
 | 
			
		||||
		// Then the saved client is updated
 | 
			
		||||
		OAuth2AuthorizedClient savedClient = this.authorizedClientService.loadAuthorizedClient(
 | 
			
		||||
				this.clientRegistration.getRegistrationId(), principal.getName());
 | 
			
		||||
 | 
			
		||||
		assertThat(savedClient).isNotNull();
 | 
			
		||||
		assertThat(savedClient.getClientRegistration()).isEqualTo(updatedClient.getClientRegistration());
 | 
			
		||||
		assertThat(savedClient.getPrincipalName()).isEqualTo(updatedClient.getPrincipalName());
 | 
			
		||||
		assertThat(savedClient.getAccessToken().getTokenType()).isEqualTo(updatedClient.getAccessToken().getTokenType());
 | 
			
		||||
		assertThat(savedClient.getAccessToken().getTokenValue()).isEqualTo(updatedClient.getAccessToken().getTokenValue());
 | 
			
		||||
		assertThat(savedClient.getAccessToken().getIssuedAt()).isEqualTo(updatedClient.getAccessToken().getIssuedAt());
 | 
			
		||||
		assertThat(savedClient.getAccessToken().getExpiresAt()).isEqualTo(updatedClient.getAccessToken().getExpiresAt());
 | 
			
		||||
		assertThat(savedClient.getAccessToken().getScopes()).isEqualTo(updatedClient.getAccessToken().getScopes());
 | 
			
		||||
		assertThat(savedClient.getRefreshToken().getTokenValue()).isEqualTo(updatedClient.getRefreshToken().getTokenValue());
 | 
			
		||||
		assertThat(savedClient.getRefreshToken().getIssuedAt()).isEqualTo(updatedClient.getRefreshToken().getIssuedAt());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue