Skip to content

Commit

Permalink
Add integration tests for How-to: Implement core services with Redis
Browse files Browse the repository at this point in the history
Issue gh-1019
  • Loading branch information
jgrandja committed Sep 20, 2024
1 parent 20f1048 commit 00f3874
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/spring-authorization-server-docs.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {
runtimeOnly "com.h2database:h2"
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation "org.springframework.security:spring-security-test"
testImplementation "com.github.codemonstur:embedded-redis:1.4.3"
}

tasks.named("test") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,26 @@
public interface OAuth2AuthorizationGrantAuthorizationRepository
extends CrudRepository<OAuth2AuthorizationGrantAuthorization, String> {

<T extends OAuth2AuthorizationCodeGrantAuthorization> T findByState(String token);
<T extends OAuth2AuthorizationCodeGrantAuthorization> T findByState(String state);

<T extends OAuth2AuthorizationCodeGrantAuthorization> T findByAuthorizationCode_TokenValue(String token);
<T extends OAuth2AuthorizationCodeGrantAuthorization> T findByAuthorizationCode_TokenValue(String authorizationCode);

<T extends OAuth2AuthorizationGrantAuthorization> T findByAccessToken_TokenValue(String token);
<T extends OAuth2AuthorizationCodeGrantAuthorization> T findByStateOrAuthorizationCode_TokenValue(String state, String authorizationCode);

<T extends OAuth2AuthorizationGrantAuthorization> T findByRefreshToken_TokenValue(String token);
<T extends OAuth2AuthorizationGrantAuthorization> T findByAccessToken_TokenValue(String accessToken);

<T extends OidcAuthorizationCodeGrantAuthorization> T findByIdToken_TokenValue(String token);
<T extends OAuth2AuthorizationGrantAuthorization> T findByRefreshToken_TokenValue(String refreshToken);

<T extends OAuth2DeviceCodeGrantAuthorization> T findByDeviceState(String token);
<T extends OAuth2AuthorizationGrantAuthorization> T findByAccessToken_TokenValueOrRefreshToken_TokenValue(String accessToken, String refreshToken);

<T extends OAuth2DeviceCodeGrantAuthorization> T findByDeviceCode_TokenValue(String token);
<T extends OidcAuthorizationCodeGrantAuthorization> T findByIdToken_TokenValue(String idToken);

<T extends OAuth2DeviceCodeGrantAuthorization> T findByUserCode_TokenValue(String token);
<T extends OAuth2DeviceCodeGrantAuthorization> T findByDeviceState(String deviceState);

<T extends OAuth2AuthorizationGrantAuthorization> T findByStateOrAuthorizationCode_TokenValueOrAccessToken_TokenValueOrRefreshToken_TokenValueOrIdToken_TokenValueOrDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(
String state, String authorizationCode, String accessToken, String refreshToken, String idToken,
String deviceState, String deviceCode, String userCode);
<T extends OAuth2DeviceCodeGrantAuthorization> T findByDeviceCode_TokenValue(String deviceCode);

<T extends OAuth2DeviceCodeGrantAuthorization> T findByUserCode_TokenValue(String userCode);

<T extends OAuth2DeviceCodeGrantAuthorization> T findByDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(String deviceState, String deviceCode, String userCode);

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,19 @@ public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType)
OAuth2AuthorizationGrantAuthorization authorizationGrantAuthorization = null;
if (tokenType == null) {
authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository
.findByStateOrAuthorizationCode_TokenValueOrAccessToken_TokenValueOrRefreshToken_TokenValueOrIdToken_TokenValueOrDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(
token, token, token, token, token, token, token, token);
.findByStateOrAuthorizationCode_TokenValue(token, token);
if (authorizationGrantAuthorization == null) {
authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository
.findByAccessToken_TokenValueOrRefreshToken_TokenValue(token, token);
}
if (authorizationGrantAuthorization == null) {
authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository
.findByIdToken_TokenValue(token);
}
if (authorizationGrantAuthorization == null) {
authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository
.findByDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(token, token, token);
}
}
else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByState(token);
Expand Down
206 changes: 206 additions & 0 deletions docs/src/test/java/sample/redis/RedisTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* Copyright 2020-2024 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 sample.redis;

import java.io.IOException;
import java.util.Map;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.assertj.core.api.ObjectAssert;
import org.junit.jupiter.api.Test;
import redis.embedded.RedisServer;
import sample.AuthorizationCodeGrantFlow;
import sample.DeviceAuthorizationGrantFlow;
import sample.redis.service.RedisOAuth2AuthorizationConsentService;
import sample.redis.service.RedisOAuth2AuthorizationService;
import sample.redis.service.RedisRegisteredClientRepository;
import sample.util.RegisteredClients;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.util.StringUtils;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for the guide How-to: Implement core services with Redis.
*
* @author Joe Grandja
*/
@SpringBootTest(classes = {RedisTests.AuthorizationServerConfig.class})
@AutoConfigureMockMvc
public class RedisTests {
private static final RegisteredClient TEST_MESSAGING_CLIENT = RegisteredClients.messagingClient();

@Autowired
private MockMvc mockMvc;

@Autowired
private RegisteredClientRepository registeredClientRepository;

@Autowired
private OAuth2AuthorizationService authorizationService;

@Autowired
private OAuth2AuthorizationConsentService authorizationConsentService;

@Test
public void oidcLoginWhenRedisCoreServicesAutowiredThenUsed() throws Exception {
assertThat(this.registeredClientRepository).isInstanceOf(RedisRegisteredClientRepository.class);
assertThat(this.authorizationService).isInstanceOf(RedisOAuth2AuthorizationService.class);
assertThat(this.authorizationConsentService).isInstanceOf(RedisOAuth2AuthorizationConsentService.class);

RegisteredClient registeredClient = TEST_MESSAGING_CLIENT;

AuthorizationCodeGrantFlow authorizationCodeGrantFlow = new AuthorizationCodeGrantFlow(this.mockMvc);
authorizationCodeGrantFlow.setUsername("user");
authorizationCodeGrantFlow.addScope("message.read");
authorizationCodeGrantFlow.addScope("message.write");

String state = authorizationCodeGrantFlow.authorize(registeredClient);
assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull();
assertThatAuthorization(state, null).isNotNull();

String authorizationCode = authorizationCodeGrantFlow.submitConsent(registeredClient, state);
assertThatAuthorization(authorizationCode, OAuth2ParameterNames.CODE).isNotNull();
assertThatAuthorization(authorizationCode, null).isNotNull();

Map<String, Object> tokenResponse = authorizationCodeGrantFlow.getTokenResponse(registeredClient, authorizationCode);
String accessToken = (String) tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN);
assertThatAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN).isNotNull();
assertThatAuthorization(accessToken, null).isNotNull();

String refreshToken = (String) tokenResponse.get(OAuth2ParameterNames.REFRESH_TOKEN);
assertThatAuthorization(refreshToken, OAuth2ParameterNames.REFRESH_TOKEN).isNotNull();
assertThatAuthorization(refreshToken, null).isNotNull();

String idToken = (String) tokenResponse.get(OidcParameterNames.ID_TOKEN);
assertThatAuthorization(idToken, OidcParameterNames.ID_TOKEN).isNotNull();
assertThatAuthorization(idToken, null).isNotNull();

OAuth2Authorization authorization = findAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN);
assertThat(authorization.getToken(idToken)).isNotNull();

String scopes = (String) tokenResponse.get(OAuth2ParameterNames.SCOPE);
OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById(
registeredClient.getId(), "user");
assertThat(authorizationConsent).isNotNull();
assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrder(
StringUtils.delimitedListToStringArray(scopes, " "));
}

@Test
public void deviceAuthorizationWhenRedisCoreServicesAutowiredThenUsed() throws Exception {
assertThat(this.registeredClientRepository).isInstanceOf(RedisRegisteredClientRepository.class);
assertThat(this.authorizationService).isInstanceOf(RedisOAuth2AuthorizationService.class);
assertThat(this.authorizationConsentService).isInstanceOf(RedisOAuth2AuthorizationConsentService.class);

RegisteredClient registeredClient = TEST_MESSAGING_CLIENT;

DeviceAuthorizationGrantFlow deviceAuthorizationGrantFlow = new DeviceAuthorizationGrantFlow(this.mockMvc);
deviceAuthorizationGrantFlow.setUsername("user");
deviceAuthorizationGrantFlow.addScope("message.read");
deviceAuthorizationGrantFlow.addScope("message.write");

Map<String, Object> deviceAuthorizationResponse = deviceAuthorizationGrantFlow.authorize(registeredClient);
String userCode = (String) deviceAuthorizationResponse.get(OAuth2ParameterNames.USER_CODE);
assertThatAuthorization(userCode, OAuth2ParameterNames.USER_CODE).isNotNull();
assertThatAuthorization(userCode, null).isNotNull();

String deviceCode = (String) deviceAuthorizationResponse.get(OAuth2ParameterNames.DEVICE_CODE);
assertThatAuthorization(deviceCode, OAuth2ParameterNames.DEVICE_CODE).isNotNull();
assertThatAuthorization(deviceCode, null).isNotNull();

String state = deviceAuthorizationGrantFlow.submitCode(userCode);
assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull();
assertThatAuthorization(state, null).isNotNull();

deviceAuthorizationGrantFlow.submitConsent(registeredClient, state, userCode);

Map<String, Object> tokenResponse = deviceAuthorizationGrantFlow.getTokenResponse(registeredClient, deviceCode);
String accessToken = (String) tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN);
assertThatAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN).isNotNull();
assertThatAuthorization(accessToken, null).isNotNull();

String refreshToken = (String) tokenResponse.get(OAuth2ParameterNames.REFRESH_TOKEN);
assertThatAuthorization(refreshToken, OAuth2ParameterNames.REFRESH_TOKEN).isNotNull();
assertThatAuthorization(refreshToken, null).isNotNull();

String scopes = (String) tokenResponse.get(OAuth2ParameterNames.SCOPE);
OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById(
registeredClient.getId(), "user");
assertThat(authorizationConsent).isNotNull();
assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrder(
StringUtils.delimitedListToStringArray(scopes, " "));
}

private ObjectAssert<OAuth2Authorization> assertThatAuthorization(String token, String tokenType) {
return assertThat(findAuthorization(token, tokenType));
}

private OAuth2Authorization findAuthorization(String token, String tokenType) {
return this.authorizationService.findByToken(token, tokenType == null ? null : new OAuth2TokenType(tokenType));
}

@EnableWebSecurity
@EnableAutoConfiguration(exclude = {JpaRepositoriesAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@ComponentScan
static class AuthorizationServerConfig {
}

@TestConfiguration
static class RedisServerConfig {
private final RedisServer redisServer;

@Autowired
private RegisteredClientRepository registeredClientRepository;

RedisServerConfig() throws IOException {
this.redisServer = new RedisServer();
}

@PostConstruct
void postConstruct() throws IOException {
this.redisServer.start();
this.registeredClientRepository.save(TEST_MESSAGING_CLIENT);
}

@PreDestroy
void preDestroy() throws IOException {
this.redisServer.stop();
}

}

}

0 comments on commit 00f3874

Please sign in to comment.