From ad562b491724a11e10be5590a5079a9264589960 Mon Sep 17 00:00:00 2001
From: guqing <38999863+guqing@users.noreply.github.com>
Date: Tue, 19 Apr 2022 16:06:09 +0800
Subject: [PATCH] feat: Add InMemoryOAuth2AuthorizationService for development
or testing (#1854)
---
.../halo/app/config/WebSecurityConfig.java | 5 +-
.../InMemoryOAuth2AuthorizationService.java | 169 ++++++++++++
.../JwtDaoAuthenticationProvider.java | 21 +-
.../authentication/OAuth2Authorization.java | 26 ++
.../OAuth2AuthorizationService.java | 54 ++--
...nMemoryOAuth2AuthorizationServiceTest.java | 240 ++++++++++++++++++
6 files changed, 480 insertions(+), 35 deletions(-)
create mode 100644 src/main/java/run/halo/app/identity/authentication/InMemoryOAuth2AuthorizationService.java
create mode 100644 src/test/java/run/halo/app/authentication/InMemoryOAuth2AuthorizationServiceTest.java
diff --git a/src/main/java/run/halo/app/config/WebSecurityConfig.java b/src/main/java/run/halo/app/config/WebSecurityConfig.java
index 22df1a8ff..50cf3d20d 100644
--- a/src/main/java/run/halo/app/config/WebSecurityConfig.java
+++ b/src/main/java/run/halo/app/config/WebSecurityConfig.java
@@ -30,10 +30,10 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
+import run.halo.app.identity.authentication.InMemoryOAuth2AuthorizationService;
import run.halo.app.identity.authentication.JwtDaoAuthenticationProvider;
import run.halo.app.identity.authentication.JwtGenerator;
import run.halo.app.identity.authentication.JwtUsernamePasswordAuthenticationFilter;
-import run.halo.app.identity.authentication.OAuth2AuthorizationService;
import run.halo.app.identity.authentication.ProviderContextFilter;
import run.halo.app.identity.authentication.ProviderSettings;
import run.halo.app.identity.entrypoint.JwtAccessDeniedHandler;
@@ -110,7 +110,8 @@ public class WebSecurityConfig {
@Bean
JwtDaoAuthenticationProvider jwtDaoAuthenticationProvider() {
JwtDaoAuthenticationProvider authenticationProvider =
- new JwtDaoAuthenticationProvider(jwtGenerator(), new OAuth2AuthorizationService());
+ new JwtDaoAuthenticationProvider(jwtGenerator(),
+ new InMemoryOAuth2AuthorizationService());
authenticationProvider.setUserDetailsService(userDetailsService());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
diff --git a/src/main/java/run/halo/app/identity/authentication/InMemoryOAuth2AuthorizationService.java b/src/main/java/run/halo/app/identity/authentication/InMemoryOAuth2AuthorizationService.java
new file mode 100644
index 000000000..61fbe84d7
--- /dev/null
+++ b/src/main/java/run/halo/app/identity/authentication/InMemoryOAuth2AuthorizationService.java
@@ -0,0 +1,169 @@
+package run.halo.app.identity.authentication;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.util.Assert;
+
+/**
+ * An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s in-memory.
+ * NOTE: This implementation should ONLY be used during development/testing.
+ *
+ * @author guqing
+ * @see OAuth2AuthorizationService
+ * @since 2.0.0
+ */
+public class InMemoryOAuth2AuthorizationService implements OAuth2AuthorizationService {
+ private int maxInitializedAuthorizations = 100;
+
+ /*
+ * Stores "initialized" (uncompleted) authorizations, where an access token has not yet been
+ * granted.
+ * This state occurs with the authorization_code grant flow during the user consent step OR
+ * when the code is returned in the authorization response but the access token request is
+ * not yet initiated.
+ */
+ private Map initializedAuthorizations =
+ Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
+
+ /*
+ * Stores "completed" authorizations, where an access token has been granted.
+ */
+ private final Map authorizations = new ConcurrentHashMap<>();
+
+ /*
+ * Constructor used for testing only.
+ */
+ public InMemoryOAuth2AuthorizationService(int maxInitializedAuthorizations) {
+ this.maxInitializedAuthorizations = maxInitializedAuthorizations;
+ this.initializedAuthorizations =
+ Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
+ }
+
+ /**
+ * Constructs an {@code InMemoryOAuth2AuthorizationService}.
+ */
+ public InMemoryOAuth2AuthorizationService() {
+ this(Collections.emptyList());
+ }
+
+ /**
+ * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters.
+ *
+ * @param authorizations the authorization(s)
+ */
+ public InMemoryOAuth2AuthorizationService(OAuth2Authorization... authorizations) {
+ this(Arrays.asList(authorizations));
+ }
+
+ /**
+ * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters.
+ *
+ * @param authorizations the authorization(s)
+ */
+ public InMemoryOAuth2AuthorizationService(List authorizations) {
+ Assert.notNull(authorizations, "authorizations cannot be null");
+ authorizations.forEach(authorization -> {
+ Assert.notNull(authorization, "authorization cannot be null");
+ Assert.isTrue(!this.authorizations.containsKey(authorization.getId()),
+ "The authorization must be unique. Found duplicate identifier: "
+ + authorization.getId());
+ this.authorizations.put(authorization.getId(), authorization);
+ });
+ }
+
+ @Override
+ public void save(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+ if (isComplete(authorization)) {
+ this.authorizations.put(authorization.getId(), authorization);
+ } else {
+ this.initializedAuthorizations.put(authorization.getId(), authorization);
+ }
+ }
+
+ @Override
+ public void remove(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+ if (isComplete(authorization)) {
+ this.authorizations.remove(authorization.getId(), authorization);
+ } else {
+ this.initializedAuthorizations.remove(authorization.getId(), authorization);
+ }
+ }
+
+ @Nullable
+ @Override
+ public OAuth2Authorization findById(String id) {
+ Assert.hasText(id, "id cannot be empty");
+ OAuth2Authorization authorization = this.authorizations.get(id);
+ return authorization != null
+ ? authorization :
+ this.initializedAuthorizations.get(id);
+ }
+
+ @Nullable
+ @Override
+ public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
+ Assert.hasText(token, "token cannot be empty");
+ for (OAuth2Authorization authorization : this.authorizations.values()) {
+ if (hasToken(authorization, token, tokenType)) {
+ return authorization;
+ }
+ }
+ for (OAuth2Authorization authorization : this.initializedAuthorizations.values()) {
+ if (hasToken(authorization, token, tokenType)) {
+ return authorization;
+ }
+ }
+ return null;
+ }
+
+ private static boolean isComplete(OAuth2Authorization authorization) {
+ return authorization.getAccessToken() != null;
+ }
+
+ private static boolean hasToken(OAuth2Authorization authorization, String token,
+ @Nullable OAuth2TokenType tokenType) {
+ if (tokenType == null) {
+ return matchesAccessToken(authorization, token)
+ || matchesRefreshToken(authorization, token);
+ } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
+ return matchesAccessToken(authorization, token);
+ } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
+ return matchesRefreshToken(authorization, token);
+ }
+ return false;
+ }
+
+ private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {
+ OAuth2Authorization.Token accessToken =
+ authorization.getToken(OAuth2AccessToken.class);
+ return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
+ }
+
+ private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
+ OAuth2Authorization.Token refreshToken =
+ authorization.getToken(OAuth2RefreshToken.class);
+ return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
+ }
+
+ private static final class MaxSizeHashMap extends LinkedHashMap {
+ private final int maxSize;
+
+ private MaxSizeHashMap(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > this.maxSize;
+ }
+ }
+}
diff --git a/src/main/java/run/halo/app/identity/authentication/JwtDaoAuthenticationProvider.java b/src/main/java/run/halo/app/identity/authentication/JwtDaoAuthenticationProvider.java
index 28b56504f..79397b8a2 100644
--- a/src/main/java/run/halo/app/identity/authentication/JwtDaoAuthenticationProvider.java
+++ b/src/main/java/run/halo/app/identity/authentication/JwtDaoAuthenticationProvider.java
@@ -8,6 +8,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -21,7 +22,6 @@ import org.springframework.security.oauth2.core.OAuth2Token;
* @since 2.0.0
*/
public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
- private static final OAuth2TokenType PASSWORD_TOKEN = new OAuth2TokenType("password");
private static final String ERROR_URI =
"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private final OAuth2TokenGenerator extends OAuth2Token> tokenGenerator;
@@ -41,18 +41,6 @@ public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
(UsernamePasswordAuthenticationToken) super.createSuccessAuthentication(principal,
authentication, user);
- OAuth2Authorization authorization = this.authorizationService.findByUsername(
- usernamePasswordAuthenticationToken.getName(), PASSWORD_TOKEN);
-
- OAuth2Authorization.Token refreshToken =
- authorization.getRefreshToken();
- if (refreshToken == null || !refreshToken.isActive()) {
- // As per https://tools.ietf.org/html/rfc6749#section-5.2
- // invalid_grant: The provided authorization grant (e.g., authorization code,
- // resource owner credentials) or refresh token is invalid, expired, revoked [...].
- throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
- }
-
Set scopes = usernamePasswordAuthenticationToken.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
@@ -61,7 +49,10 @@ public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
.providerContext(ProviderContextHolder.getProviderContext())
.authorizedScopes(scopes);
- OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization);
+ OAuth2Authorization.Builder authorizationBuilder = new OAuth2Authorization.Builder()
+ .principalName(authentication.getName())
+ .authorizationGrantType(AuthorizationGrantType.PASSWORD)
+ .attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, scopes);
// ----- Access token -----
OAuth2TokenContext tokenContext =
@@ -89,7 +80,7 @@ public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
ProviderContextHolder.getProviderContext().providerSettings();
// ----- Refresh token -----
- OAuth2RefreshToken currentRefreshToken = refreshToken.getToken();
+ OAuth2RefreshToken currentRefreshToken = null;
if (!providerSettings.isReuseRefreshTokens()) {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
diff --git a/src/main/java/run/halo/app/identity/authentication/OAuth2Authorization.java b/src/main/java/run/halo/app/identity/authentication/OAuth2Authorization.java
index d3739b5a0..dac682d49 100644
--- a/src/main/java/run/halo/app/identity/authentication/OAuth2Authorization.java
+++ b/src/main/java/run/halo/app/identity/authentication/OAuth2Authorization.java
@@ -9,6 +9,7 @@ import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
@@ -31,6 +32,7 @@ public class OAuth2Authorization implements Serializable {
private String id;
private String principalName;
+ private AuthorizationGrantType authorizationGrantType;
private Map, Token>> tokens;
private Map attributes;
@@ -55,6 +57,16 @@ public class OAuth2Authorization implements Serializable {
return this.principalName;
}
+ /**
+ * Returns the {@link AuthorizationGrantType authorization grant type} used for the
+ * authorization.
+ *
+ * @return the {@link AuthorizationGrantType} used for the authorization
+ */
+ public AuthorizationGrantType getAuthorizationGrantType() {
+ return this.authorizationGrantType;
+ }
+
/**
* Returns the {@link Token} of type {@link OAuth2AccessToken}.
*
@@ -319,6 +331,7 @@ public class OAuth2Authorization implements Serializable {
public static class Builder implements Serializable {
private String id;
private String principalName;
+ private AuthorizationGrantType authorizationGrantType;
private Map, Token>> tokens = new HashMap<>();
private final Map attributes = new HashMap<>();
@@ -344,6 +357,18 @@ public class OAuth2Authorization implements Serializable {
return this;
}
+ /**
+ * Sets the {@link AuthorizationGrantType authorization grant type} used for the
+ * authorization.
+ *
+ * @param authorizationGrantType the {@link AuthorizationGrantType}
+ * @return the {@link Builder}
+ */
+ public Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) {
+ this.authorizationGrantType = authorizationGrantType;
+ return this;
+ }
+
/**
* Sets the {@link OAuth2AccessToken access token}.
*
@@ -444,6 +469,7 @@ public class OAuth2Authorization implements Serializable {
}
authorization.id = this.id;
authorization.principalName = this.principalName;
+ authorization.authorizationGrantType = this.authorizationGrantType;
authorization.tokens = Collections.unmodifiableMap(this.tokens);
authorization.attributes = Collections.unmodifiableMap(this.attributes);
return authorization;
diff --git a/src/main/java/run/halo/app/identity/authentication/OAuth2AuthorizationService.java b/src/main/java/run/halo/app/identity/authentication/OAuth2AuthorizationService.java
index 74da9cbd6..7e5bb5487 100644
--- a/src/main/java/run/halo/app/identity/authentication/OAuth2AuthorizationService.java
+++ b/src/main/java/run/halo/app/identity/authentication/OAuth2AuthorizationService.java
@@ -1,27 +1,45 @@
package run.halo.app.identity.authentication;
-import java.time.Instant;
-import org.springframework.security.oauth2.core.OAuth2AccessToken;
-import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.lang.Nullable;
/**
* @author guqing
* @since 2.0.0
*/
-public class OAuth2AuthorizationService {
- OAuth2Authorization findByUsername(String username, OAuth2TokenType oauth2TokenType) {
- // TODO to be implementation
- return new OAuth2Authorization.Builder().id("id")
- .accessToken(new OAuth2AccessToken(
- OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(),
- Instant.now().plusMillis(123)))
- .refreshToken(
- new OAuth2RefreshToken("refresh_token", Instant.now()))
- .principalName("guqing")
- .build();
- }
+public interface OAuth2AuthorizationService {
+ /**
+ * Returns the {@link OAuth2Authorization} containing the provided {@code token},
+ * or {@code null} if not found.
+ *
+ * @param token the token credential
+ * @param tokenType the {@link OAuth2TokenType token type}
+ * @return the {@link OAuth2Authorization} if found, otherwise {@code null}
+ */
+ @Nullable
+ OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType);
+
+ /**
+ * Saves the {@link OAuth2Authorization}.
+ *
+ * @param authorization the {@link OAuth2Authorization}
+ */
+ void save(OAuth2Authorization authorization);
+
+ /**
+ * Removes the {@link OAuth2Authorization}.
+ *
+ * @param authorization the {@link OAuth2Authorization}
+ */
+ void remove(OAuth2Authorization authorization);
+
+ /**
+ * Returns the {@link OAuth2Authorization} identified by the provided {@code id},
+ * or {@code null} if not found.
+ *
+ * @param id the authorization identifier
+ * @return the {@link OAuth2Authorization} if found, otherwise {@code null}
+ */
+ @Nullable
+ OAuth2Authorization findById(String id);
- void save(OAuth2Authorization authorization) {
- // TODO to be implementation
- }
}
diff --git a/src/test/java/run/halo/app/authentication/InMemoryOAuth2AuthorizationServiceTest.java b/src/test/java/run/halo/app/authentication/InMemoryOAuth2AuthorizationServiceTest.java
new file mode 100644
index 000000000..1dfcfe190
--- /dev/null
+++ b/src/test/java/run/halo/app/authentication/InMemoryOAuth2AuthorizationServiceTest.java
@@ -0,0 +1,240 @@
+package run.halo.app.authentication;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Instant;
+import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import run.halo.app.identity.authentication.InMemoryOAuth2AuthorizationService;
+import run.halo.app.identity.authentication.OAuth2Authorization;
+import run.halo.app.identity.authentication.OAuth2AuthorizationService;
+import run.halo.app.identity.authentication.OAuth2TokenType;
+
+/**
+ * An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s in-memory.
+ * NOTE: This implementation should ONLY be used during development/testing.
+ *
+ * @author guqing
+ * @see OAuth2AuthorizationService
+ * @since 2.0.0
+ */
+public class InMemoryOAuth2AuthorizationServiceTest {
+ private static final String ID = "id";
+ private static final String PRINCIPAL_NAME = "principal";
+ private static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE =
+ AuthorizationGrantType.PASSWORD;
+ private static final OAuth2AccessToken TOKEN =
+ new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access_token",
+ Instant.now(), Instant.now().plusSeconds(256));
+ private InMemoryOAuth2AuthorizationService authorizationService;
+
+ @BeforeEach
+ public void setup() {
+ this.authorizationService = new InMemoryOAuth2AuthorizationService();
+ }
+
+ @Test
+ public void constructorVarargsWhenAuthorizationNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(() -> new InMemoryOAuth2AuthorizationService((OAuth2Authorization) null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("authorization cannot be null");
+ }
+
+ @Test
+ public void constructorListWhenAuthorizationsNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(
+ () -> new InMemoryOAuth2AuthorizationService((List) null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("authorizations cannot be null");
+ }
+
+ @Test
+ public void constructorWhenDuplicateAuthorizationsThenThrowIllegalArgumentException() {
+ OAuth2Authorization authorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .token(TOKEN)
+ .build();
+
+ assertThatThrownBy(
+ () -> new InMemoryOAuth2AuthorizationService(authorization, authorization))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("The authorization must be unique. Found duplicate identifier: id");
+ }
+
+ @Test
+ public void saveWhenAuthorizationNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(() -> this.authorizationService.save(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("authorization cannot be null");
+ }
+
+ @Test
+ public void saveWhenAuthorizationNewThenSaved() {
+ OAuth2Authorization expectedAuthorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .token(TOKEN)
+ .build();
+ this.authorizationService.save(expectedAuthorization);
+
+ OAuth2Authorization authorization = this.authorizationService.findByToken(
+ TOKEN.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(authorization).isEqualTo(expectedAuthorization);
+ }
+
+ @Test
+ public void saveWhenAuthorizationExistsThenUpdated() {
+ OAuth2Authorization originalAuthorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .token(TOKEN)
+ .build();
+ this.authorizationService.save(originalAuthorization);
+
+ OAuth2Authorization authorization = this.authorizationService.findById(
+ originalAuthorization.getId());
+ assertThat(authorization).isEqualTo(originalAuthorization);
+
+ OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)
+ .attribute("custom-name-1", "custom-value-1")
+ .build();
+ this.authorizationService.save(updatedAuthorization);
+
+ authorization = this.authorizationService.findById(
+ updatedAuthorization.getId());
+ assertThat(authorization).isEqualTo(updatedAuthorization);
+ assertThat(authorization).isNotEqualTo(originalAuthorization);
+ }
+
+ @Test
+ public void saveWhenInitializedAuthorizationsReachMaxThenOldestRemoved() {
+ int maxInitializedAuthorizations = 5;
+ InMemoryOAuth2AuthorizationService authorizationService =
+ new InMemoryOAuth2AuthorizationService(maxInitializedAuthorizations);
+
+ OAuth2Authorization initialAuthorization = new OAuth2Authorization.Builder()
+ .id(ID + "-initial")
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .attribute(OAuth2ParameterNames.STATE, "state-initial")
+ .build();
+ authorizationService.save(initialAuthorization);
+
+ OAuth2Authorization authorization =
+ authorizationService.findById(initialAuthorization.getId());
+ assertThat(authorization).isEqualTo(initialAuthorization);
+ }
+
+ @Test
+ public void removeWhenAuthorizationNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(() -> this.authorizationService.remove(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("authorization cannot be null");
+ }
+
+ @Test
+ public void removeWhenAuthorizationProvidedThenRemoved() {
+ OAuth2Authorization expectedAuthorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .token(TOKEN)
+ .build();
+
+ this.authorizationService.save(expectedAuthorization);
+ OAuth2Authorization authorization = this.authorizationService.findByToken(
+ TOKEN.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(authorization).isEqualTo(expectedAuthorization);
+
+ this.authorizationService.remove(expectedAuthorization);
+ authorization = this.authorizationService.findByToken(
+ TOKEN.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(authorization).isNull();
+ }
+
+ @Test
+ public void findByIdWhenIdNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(() -> this.authorizationService.findById(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("id cannot be empty");
+ }
+
+ @Test
+ public void findByTokenWhenTokenNullThenThrowIllegalArgumentException() {
+ assertThatThrownBy(
+ () -> this.authorizationService.findByToken(null, OAuth2TokenType.ACCESS_TOKEN))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("token cannot be empty");
+ }
+
+ @Test
+ public void findByTokenWhenAccessTokenExistsThenFound() {
+ OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
+ "access-token", Instant.now().minusSeconds(60), Instant.now());
+ OAuth2Authorization authorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .token(TOKEN)
+ .accessToken(accessToken)
+ .build();
+ this.authorizationService.save(authorization);
+
+ OAuth2Authorization result = this.authorizationService.findByToken(
+ accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(authorization).isEqualTo(result);
+ result = this.authorizationService.findByToken(accessToken.getTokenValue(), null);
+ assertThat(authorization).isEqualTo(result);
+ }
+
+ @Test
+ public void findByTokenWhenRefreshTokenExistsThenFound() {
+ OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now());
+ OAuth2Authorization authorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .refreshToken(refreshToken)
+ .build();
+ this.authorizationService.save(authorization);
+
+ OAuth2Authorization result = this.authorizationService.findByToken(
+ refreshToken.getTokenValue(), OAuth2TokenType.REFRESH_TOKEN);
+ assertThat(authorization).isEqualTo(result);
+ result = this.authorizationService.findByToken(refreshToken.getTokenValue(), null);
+ assertThat(authorization).isEqualTo(result);
+ }
+
+ @Test
+ public void findByTokenWhenWrongTokenTypeThenNotFound() {
+ OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now());
+ OAuth2Authorization authorization = new OAuth2Authorization.Builder()
+ .id(ID)
+ .principalName(PRINCIPAL_NAME)
+ .authorizationGrantType(AUTHORIZATION_GRANT_TYPE)
+ .refreshToken(refreshToken)
+ .build();
+ this.authorizationService.save(authorization);
+
+ OAuth2Authorization result = this.authorizationService.findByToken(
+ refreshToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void findByTokenWhenTokenDoesNotExistThenNull() {
+ OAuth2Authorization result = this.authorizationService.findByToken(
+ "access-token", OAuth2TokenType.ACCESS_TOKEN);
+ assertThat(result).isNull();
+ }
+}