Keycloak認証を使うリソースサーバーのGroovy Spockテスト
Created: 2018-01-03
概要
Keycloakによる認証を使う、リソースサーバー(Spring Bootクライアント)でのGroovy Spockテスト実装メモ。
この手順で使用したコードは、以下に公開しているので、こちらも参考にしてください。
https://github.com/yo1000/kc-resource/ac9914ae02#try-testing-with-only-kc-resource-server
また、テストについては、既に過去のポストで触れているため、ここでは、Groovy Spockを適用するにあたって、変更が必要となる部分について書いていきます。
要件
環境
今回の作業環境は以下のとおりです。
- Java 1.8.0_131
- Spring Boot 1.5.9.RELEASE
- Keycloak 3.4.1.Final
- Groovy 2.4.11
- Spock 1.1-groovy-2.4
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.12.5
BuildVersion: 16F2073
$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
pom.xmlの変更
依存関係の追加
以下の依存を追加します。
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
ビルド構成の変更
以下のようにビルドプラグインを追加します。
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>addTestSources</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<testSources>
<fileset>
<directory>${pom.basedir}/src/test/groovy</directory>
<includes>
<include>**/*.groovy</include>
</includes>
</fileset>
</testSources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
<include>**/*Spec.java</include>
<include>**/*Specs.java</include>
</includes>
</configuration>
</plugin>
テストの実装
テストコード
過去のポストで書いたテストと、内容は同じものです。Groovy Spock用に書き改めています。
package com.yo1000.keycloak.resource.server
import org.junit.Before
import org.keycloak.KeycloakPrincipal
import org.keycloak.adapters.RefreshableKeycloakSecurityContext
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
@Unroll
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
class KcResourceServerControllerSpecs extends Specification {
@Autowired
WebApplicationContext context
@Shared
MockMvc mockMvc
@Before
def beforeTestEach() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build()
}
/**
* When the user has Admin and User roles,
* then can access endpoints that require Admin role or User role.
*/
def when_the_user_has_Admin_and_User_roles_then_can_access_endpoints_that_require_Admin_role_or_User_role() {
given:
def token = new KeycloakAuthenticationToken(
new SimpleKeycloakAccount(
Mockito.mock(KeycloakPrincipal.class),
["admin", "user"].toSet(),
Mockito.mock(RefreshableKeycloakSecurityContext.class)),
false)
expect:
mockMvc.perform(MockMvcRequestBuilders.get("/kc/resource/server/admin")
.with(SecurityMockMvcRequestPostProcessors.authentication(token)))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
mockMvc.perform(MockMvcRequestBuilders.get("/kc/resource/server/user")
.with(SecurityMockMvcRequestPostProcessors.authentication(token)))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
}
/**
* When the user has only User role,
* then can access endpoints that require User role,
* but can't access endpoints that require Admin role.
*/
def when_the_user_has_only_User_role_then_can_access_endpoints_that_require_User_role_but_cant_access_endpoints_that_require_Admin_role() {
given:
def token = new KeycloakAuthenticationToken(
new SimpleKeycloakAccount(
Mockito.mock(KeycloakPrincipal.class),
["user"].toSet(),
Mockito.mock(RefreshableKeycloakSecurityContext.class)),
false)
expect:
mockMvc.perform(MockMvcRequestBuilders.get("/kc/resource/server/admin")
.with(SecurityMockMvcRequestPostProcessors.authentication(token)))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isForbidden())
mockMvc.perform(MockMvcRequestBuilders.get("/kc/resource/server/user")
.with(SecurityMockMvcRequestPostProcessors.authentication(token)))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
}
}
デモ
参考までに実際に動かした結果の一部を、以下に残しておきます。
$ ./mvnw clean test
..
2018-01-04 02:34:09.638 INFO 74374 --- [ main] .y.k.r.s.KcResourceServerControllerSpecs : Started KcResourceServerControllerSpecs in 3.334 seconds (JVM running for 13.218)
2018-01-04 02:34:09.685 INFO 74374 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet ''
2018-01-04 02:34:09.685 INFO 74374 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
2018-01-04 02:34:09.698 INFO 74374 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization completed in 12 ms
MockHttpServletRequest:
HTTP Method = GET
Request URI = /kc/resource/server/admin
Parameters = {}
Headers = {}
Handler:
Type = com.yo1000.keycloak.resource.server.KcResourceServerController
Method = public java.lang.String com.yo1000.keycloak.resource.server.KcResourceServerController.getAdminResource()
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY], Content-Type=[text/plain;charset=UTF-8], Content-Length=[16]}
Content type = text/plain;charset=UTF-8
Body = ADMIN Resource!!
Forwarded URL = null
Redirected URL = null
Cookies = []
..