Merge pull request #445 from spring-cloud/issues_#444
Moved the code from Spring Cloud Security
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2015-2015 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 org.springframework.cloud.openfeign.security;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.resource.UserApprovalRequiredException;
|
||||
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* Mocks the access token provider
|
||||
*
|
||||
* @author Mihhail Verhovtsov
|
||||
*/
|
||||
public class MockAccessTokenProvider implements AccessTokenProvider {
|
||||
|
||||
private OAuth2AccessToken token;
|
||||
|
||||
public MockAccessTokenProvider(OAuth2AccessToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
|
||||
AccessTokenRequest accessTokenRequest)
|
||||
throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsResource(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
|
||||
OAuth2RefreshToken oAuth2RefreshToken, AccessTokenRequest accessTokenRequest)
|
||||
throws UserRedirectRequiredException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRefresh(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2015-2015 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 org.springframework.cloud.openfeign.security;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* Mocks the OAuth2 access token
|
||||
*
|
||||
* @author Mihhail Verhovtsov
|
||||
*/
|
||||
public class MockOAuth2AccessToken implements OAuth2AccessToken {
|
||||
|
||||
private String value;
|
||||
|
||||
public MockOAuth2AccessToken(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2RefreshToken getRefreshToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiration() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpiresIn() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2015-2015 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 org.springframework.cloud.openfeign.security;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* Mocks the current client context
|
||||
*
|
||||
* @author João Pedro Evangelista
|
||||
*/
|
||||
final class MockOAuth2ClientContext implements OAuth2ClientContext {
|
||||
|
||||
private final String value;
|
||||
|
||||
MockOAuth2ClientContext(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken getAccessToken() {
|
||||
return new DefaultOAuth2AccessToken(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessToken(OAuth2AccessToken accessToken) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTokenRequest getAccessTokenRequest() {
|
||||
DefaultAccessTokenRequest tokenRequest = new DefaultAccessTokenRequest(new HashMap<String, String[]>());
|
||||
tokenRequest.setExistingToken(new DefaultOAuth2AccessToken(value));
|
||||
return tokenRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreservedState(String stateKey, Object preservedState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removePreservedState(String stateKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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 org.springframework.cloud.openfeign.security;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Request.HttpMethod;
|
||||
import feign.RequestTemplate;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author João Pedro Evangelista
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class OAuth2FeignRequestInterceptorTests {
|
||||
|
||||
private OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor;
|
||||
|
||||
private RequestTemplate requestTemplate;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(new MockOAuth2ClientContext("Fancy"),
|
||||
new BaseOAuth2ProtectedResourceDetails());
|
||||
requestTemplate = new RequestTemplate().method(HttpMethod.GET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyAuthorizationHeader() {
|
||||
oAuth2FeignRequestInterceptor.apply(requestTemplate);
|
||||
Map<String, Collection<String>> headers = requestTemplate.headers();
|
||||
Assert.assertTrue("RequestTemplate must have a Authorization header", headers.containsKey("Authorization"));
|
||||
Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"),
|
||||
contains("Bearer Fancy"));
|
||||
}
|
||||
|
||||
@Test(expected = OAuth2AccessDeniedException.class)
|
||||
public void tryToAcquireToken() {
|
||||
oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(),
|
||||
new BaseOAuth2ProtectedResourceDetails());
|
||||
OAuth2AccessToken oAuth2AccessToken = oAuth2FeignRequestInterceptor.getToken();
|
||||
Assert.assertTrue(oAuth2AccessToken.getValue() + " Must be null", oAuth2AccessToken.getValue() == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureAccessTokenProvider() {
|
||||
OAuth2AccessToken mockedToken = new MockOAuth2AccessToken("MOCKED_TOKEN");
|
||||
oAuth2FeignRequestInterceptor.setAccessTokenProvider(new MockAccessTokenProvider(mockedToken));
|
||||
Assert.assertEquals("Should return same mocked token instance", mockedToken,
|
||||
oAuth2FeignRequestInterceptor.acquireAccessToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyAuthorizationHeaderOnlyOnce() {
|
||||
OAuth2ClientContext oAuth2ClientContext = mock(OAuth2ClientContext.class);
|
||||
when(oAuth2ClientContext.getAccessToken()).thenReturn(new MockOAuth2AccessToken("MOCKED_TOKEN"));
|
||||
|
||||
OAuth2FeignRequestInterceptor oAuth2FeignRequestInterceptor = new OAuth2FeignRequestInterceptor(
|
||||
oAuth2ClientContext, new BaseOAuth2ProtectedResourceDetails());
|
||||
|
||||
oAuth2FeignRequestInterceptor.apply(requestTemplate);
|
||||
|
||||
// First idempotent call failed, retry mechanism kicks in, and token has expired
|
||||
// in the meantime
|
||||
|
||||
OAuth2AccessToken expiredAccessToken = mock(OAuth2AccessToken.class);
|
||||
when(expiredAccessToken.isExpired()).thenReturn(true);
|
||||
when(oAuth2ClientContext.getAccessToken()).thenReturn(expiredAccessToken);
|
||||
AccessTokenRequest accessTokenRequest = mock(AccessTokenRequest.class);
|
||||
when(oAuth2ClientContext.getAccessTokenRequest()).thenReturn(accessTokenRequest);
|
||||
OAuth2AccessToken newToken = new MockOAuth2AccessToken("Fancy");
|
||||
oAuth2FeignRequestInterceptor.setAccessTokenProvider(new MockAccessTokenProvider(newToken));
|
||||
|
||||
oAuth2FeignRequestInterceptor.apply(requestTemplate);
|
||||
|
||||
Map<String, Collection<String>> headers = requestTemplate.headers();
|
||||
Assert.assertTrue("RequestTemplate must have a Authorization header", headers.containsKey("Authorization"));
|
||||
Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"), hasSize(1));
|
||||
Assert.assertThat("Authorization must have a extract of Fancy", headers.get("Authorization"),
|
||||
contains("Bearer Fancy"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user