Skip to content

Commit c367378

Browse files
committed
Add OAuth2Client MockMvc Test Support
Fixes gh-7886
1 parent 0694b62 commit c367378

File tree

2 files changed

+287
-6
lines changed

2 files changed

+287
-6
lines changed

test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -432,6 +432,39 @@ public static OidcLoginRequestPostProcessor oidcLogin() {
432432
return new OidcLoginRequestPostProcessor(accessToken);
433433
}
434434

435+
/**
436+
* Establish an {@link OAuth2AuthorizedClient} in the session. All details are
437+
* declarative and do not require associated tokens to be valid.
438+
*
439+
* <p>
440+
* The support works by associating the authorized client to the HttpServletRequest
441+
* via the {@link HttpSessionOAuth2AuthorizedClientRepository}
442+
* </p>
443+
*
444+
* @return the {@link OAuth2ClientRequestPostProcessor} for additional customization
445+
* @since 5.3
446+
*/
447+
public static OAuth2ClientRequestPostProcessor oauth2Client() {
448+
return new OAuth2ClientRequestPostProcessor();
449+
}
450+
451+
/**
452+
* Establish an {@link OAuth2AuthorizedClient} in the session. All details are
453+
* declarative and do not require associated tokens to be valid.
454+
*
455+
* <p>
456+
* The support works by associating the authorized client to the HttpServletRequest
457+
* via the {@link HttpSessionOAuth2AuthorizedClientRepository}
458+
* </p>
459+
*
460+
* @param registrationId The registration id for the {@link OAuth2AuthorizedClient}
461+
* @return the {@link OAuth2ClientRequestPostProcessor} for additional customization
462+
* @since 5.3
463+
*/
464+
public static OAuth2ClientRequestPostProcessor oauth2Client(String registrationId) {
465+
return new OAuth2ClientRequestPostProcessor(registrationId);
466+
}
467+
435468
/**
436469
* Populates the X509Certificate instances onto the request
437470
*/
@@ -1389,12 +1422,12 @@ public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request)
13891422
OAuth2User oauth2User = this.oauth2User.get();
13901423
OAuth2AuthenticationToken token = new OAuth2AuthenticationToken
13911424
(oauth2User, oauth2User.getAuthorities(), this.clientRegistration.getRegistrationId());
1392-
OAuth2AuthorizedClient client = new OAuth2AuthorizedClient
1393-
(this.clientRegistration, token.getName(), this.accessToken);
1394-
OAuth2AuthorizedClientRepository authorizedClientRepository = new HttpSessionOAuth2AuthorizedClientRepository();
1395-
authorizedClientRepository.saveAuthorizedClient(client, token, request, new MockHttpServletResponse());
13961425

1397-
return new AuthenticationRequestPostProcessor(token).postProcessRequest(request);
1426+
request = new AuthenticationRequestPostProcessor(token).postProcessRequest(request);
1427+
return new OAuth2ClientRequestPostProcessor()
1428+
.clientRegistration(this.clientRegistration)
1429+
.accessToken(this.accessToken)
1430+
.postProcessRequest(request);
13981431
}
13991432

14001433
private ClientRegistration.Builder clientRegistrationBuilder() {
@@ -1570,6 +1603,84 @@ private OidcUser defaultPrincipal() {
15701603
}
15711604
}
15721605

1606+
/**
1607+
* @author Josh Cummings
1608+
* @since 5.3
1609+
*/
1610+
public final static class OAuth2ClientRequestPostProcessor implements RequestPostProcessor {
1611+
private String registrationId = "test";
1612+
private ClientRegistration clientRegistration;
1613+
private OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
1614+
"access-token", null, null, Collections.singleton("user"));
1615+
1616+
private OAuth2ClientRequestPostProcessor() {
1617+
}
1618+
1619+
private OAuth2ClientRequestPostProcessor(String registrationId) {
1620+
this.registrationId = registrationId;
1621+
clientRegistration(c -> {});
1622+
}
1623+
1624+
/**
1625+
* Use this {@link ClientRegistration}
1626+
*
1627+
* @param clientRegistration
1628+
* @return the {@link OAuth2ClientRequestPostProcessor} for further configuration
1629+
*/
1630+
public OAuth2ClientRequestPostProcessor clientRegistration(ClientRegistration clientRegistration) {
1631+
this.clientRegistration = clientRegistration;
1632+
return this;
1633+
}
1634+
1635+
/**
1636+
* Use this {@link Consumer} to configure a {@link ClientRegistration}
1637+
*
1638+
* @param clientRegistrationConfigurer the {@link ClientRegistration} configurer
1639+
* @return the {@link OAuth2ClientRequestPostProcessor} for further configuration
1640+
*/
1641+
public OAuth2ClientRequestPostProcessor clientRegistration
1642+
(Consumer<ClientRegistration.Builder> clientRegistrationConfigurer) {
1643+
1644+
ClientRegistration.Builder builder = clientRegistrationBuilder();
1645+
clientRegistrationConfigurer.accept(builder);
1646+
this.clientRegistration = builder.build();
1647+
return this;
1648+
}
1649+
1650+
/**
1651+
* Use this {@link OAuth2AccessToken}
1652+
*
1653+
* @param accessToken the {@link OAuth2AccessToken} to use
1654+
* @return the {@link OAuth2ClientRequestPostProcessor} for further configuration
1655+
*/
1656+
public OAuth2ClientRequestPostProcessor accessToken(OAuth2AccessToken accessToken) {
1657+
this.accessToken = accessToken;
1658+
return this;
1659+
}
1660+
1661+
@Override
1662+
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
1663+
if (this.clientRegistration == null) {
1664+
throw new IllegalArgumentException("Please specify a ClientRegistration via one " +
1665+
"of the clientRegistration methods");
1666+
}
1667+
OAuth2AuthorizedClient client = new OAuth2AuthorizedClient
1668+
(this.clientRegistration, "test-subject", this.accessToken);
1669+
OAuth2AuthorizedClientRepository authorizedClientRepository =
1670+
new HttpSessionOAuth2AuthorizedClientRepository();
1671+
authorizedClientRepository.saveAuthorizedClient(client, null, request, new MockHttpServletResponse());
1672+
return request;
1673+
}
1674+
1675+
private ClientRegistration.Builder clientRegistrationBuilder() {
1676+
return ClientRegistration.withRegistrationId(this.registrationId)
1677+
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
1678+
.clientId("test-client")
1679+
.clientSecret("test-secret")
1680+
.tokenUri("https://idp.example.org/oauth/token");
1681+
}
1682+
}
1683+
15731684
private SecurityMockMvcRequestPostProcessors() {
15741685
}
15751686
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.test.web.servlet.request;
17+
18+
import org.junit.After;
19+
import org.junit.Before;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.mock.web.MockHttpServletRequest;
26+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
28+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
29+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
30+
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
31+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
32+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
33+
import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository;
34+
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
35+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
36+
import org.springframework.security.test.context.TestSecurityContextHolder;
37+
import org.springframework.test.context.ContextConfiguration;
38+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
39+
import org.springframework.test.context.web.WebAppConfiguration;
40+
import org.springframework.test.web.servlet.MockMvc;
41+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
42+
import org.springframework.web.bind.annotation.GetMapping;
43+
import org.springframework.web.bind.annotation.RestController;
44+
import org.springframework.web.context.WebApplicationContext;
45+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
46+
47+
import static org.assertj.core.api.Assertions.assertThatCode;
48+
import static org.mockito.Mockito.mock;
49+
import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientRegistration;
50+
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
51+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Client;
52+
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
53+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
54+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
55+
56+
/**
57+
* Tests for {@link SecurityMockMvcRequestPostProcessors#oidcLogin()}
58+
*
59+
* @author Josh Cummings
60+
* @since 5.3
61+
*/
62+
@RunWith(SpringJUnit4ClassRunner.class)
63+
@ContextConfiguration
64+
@WebAppConfiguration
65+
public class SecurityMockMvcRequestPostProcessorsOAuth2ClientTests {
66+
@Autowired
67+
WebApplicationContext context;
68+
69+
MockMvc mvc;
70+
71+
@Before
72+
public void setup() {
73+
// @formatter:off
74+
this.mvc = MockMvcBuilders
75+
.webAppContextSetup(this.context)
76+
.apply(springSecurity())
77+
.build();
78+
// @formatter:on
79+
}
80+
81+
@After
82+
public void cleanup() {
83+
TestSecurityContextHolder.clearContext();
84+
}
85+
86+
87+
@Test
88+
public void oauth2ClientWhenUsingDefaultsThenException()
89+
throws Exception {
90+
91+
assertThatCode(() -> oauth2Client().postProcessRequest(new MockHttpServletRequest()))
92+
.isInstanceOf(IllegalArgumentException.class)
93+
.hasMessageContaining("ClientRegistration");
94+
}
95+
96+
@Test
97+
public void oauth2ClientWhenUsingDefaultsThenProducesDefaultAuthorizedClient()
98+
throws Exception {
99+
100+
this.mvc.perform(get("/access-token").with(oauth2Client("registration-id")))
101+
.andExpect(content().string("access-token"));
102+
this.mvc.perform(get("/client-id").with(oauth2Client("registration-id")))
103+
.andExpect(content().string("test-client"));
104+
}
105+
106+
@Test
107+
public void oauth2ClientWhenClientRegistrationThenUses()
108+
throws Exception {
109+
110+
ClientRegistration clientRegistration = clientRegistration()
111+
.registrationId("registration-id").clientId("client-id").build();
112+
this.mvc.perform(get("/client-id")
113+
.with(oauth2Client().clientRegistration(clientRegistration)))
114+
.andExpect(content().string("client-id"));
115+
}
116+
117+
@Test
118+
public void oauth2ClientWhenClientRegistrationConsumerThenUses()
119+
throws Exception {
120+
121+
this.mvc.perform(get("/client-id")
122+
.with(oauth2Client("registration-id").clientRegistration(c -> c.clientId("client-id"))))
123+
.andExpect(content().string("client-id"));
124+
}
125+
126+
@Test
127+
public void oauth2ClientWhenAccessTokenThenUses() throws Exception {
128+
OAuth2AccessToken accessToken = noScopes();
129+
this.mvc.perform(get("/access-token")
130+
.with(oauth2Client("registration-id").accessToken(accessToken)))
131+
.andExpect(content().string("no-scopes"));
132+
}
133+
134+
@EnableWebSecurity
135+
@EnableWebMvc
136+
static class OAuth2ClientConfig extends WebSecurityConfigurerAdapter {
137+
@Override
138+
protected void configure(HttpSecurity http) throws Exception {
139+
http
140+
.authorizeRequests(authz -> authz
141+
.anyRequest().permitAll()
142+
)
143+
.oauth2Client();
144+
}
145+
146+
@Bean
147+
ClientRegistrationRepository clientRegistrationRepository() {
148+
return mock(ClientRegistrationRepository.class);
149+
}
150+
151+
152+
@Bean
153+
OAuth2AuthorizedClientRepository authorizedClientRepository() {
154+
return new HttpSessionOAuth2AuthorizedClientRepository();
155+
}
156+
157+
@RestController
158+
static class PrincipalController {
159+
@GetMapping("/access-token")
160+
String accessToken(@RegisteredOAuth2AuthorizedClient("registration-id") OAuth2AuthorizedClient authorizedClient) {
161+
return authorizedClient.getAccessToken().getTokenValue();
162+
}
163+
164+
@GetMapping("/client-id")
165+
String clientId(@RegisteredOAuth2AuthorizedClient("registration-id") OAuth2AuthorizedClient authorizedClient) {
166+
return authorizedClient.getClientRegistration().getClientId();
167+
}
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)