mirror of https://github.com/halo-dev/halo
feat: Add InMemoryOAuth2AuthorizationService for development or testing (#1854)
parent
eee58989fe
commit
ad562b4917
|
@ -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;
|
||||
|
|
|
@ -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.<p>
|
||||
* <b>NOTE:</b> 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<String, OAuth2Authorization> initializedAuthorizations =
|
||||
Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
|
||||
|
||||
/*
|
||||
* Stores "completed" authorizations, where an access token has been granted.
|
||||
*/
|
||||
private final Map<String, OAuth2Authorization> 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<OAuth2Authorization> 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<OAuth2AccessToken> accessToken =
|
||||
authorization.getToken(OAuth2AccessToken.class);
|
||||
return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken =
|
||||
authorization.getToken(OAuth2RefreshToken.class);
|
||||
return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static final class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {
|
||||
private final int maxSize;
|
||||
|
||||
private MaxSizeHashMap(int maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() > this.maxSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<OAuth2RefreshToken> 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<String> 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);
|
||||
|
|
|
@ -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<Class<? extends OAuth2Token>, Token<?>> tokens;
|
||||
private Map<String, Object> 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<Class<? extends OAuth2Token>, Token<?>> tokens = new HashMap<>();
|
||||
private final Map<String, Object> 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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.<p>
|
||||
* <b>NOTE:</b> 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<OAuth2Authorization>) 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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue