[OLINGO-362] Now supporting refresh token

This commit is contained in:
Francesco Chicchiriccò 2014-07-21 13:33:34 +02:00
parent df7cba6909
commit c155238d9c
14 changed files with 358 additions and 259 deletions

View File

@ -18,16 +18,12 @@
*/ */
package org.apache.olingo.fit; package org.apache.olingo.fit;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.olingo.fit.rest.OAuth2InInterceptor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import java.io.IOException; import java.io.IOException;
@Service @Service
@Path("/V40/OAuth2.svc") @Path("/V40/OAuth2.svc")
@InInterceptors(classes = {OAuth2InInterceptor.class})
public class V4OAuth2 extends V4Services { public class V4OAuth2 extends V4Services {
public V4OAuth2() throws IOException { public V4OAuth2() throws IOException {

View File

@ -1,98 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.apache.olingo.fit.rest;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant;
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import java.net.URI;
import java.util.List;
import java.util.Map;
public class OAuth2InInterceptor extends AbstractPhaseInterceptor<Message> {
private static final OAuthClientUtils.Consumer OAUTH2_CONSUMER =
new OAuthClientUtils.Consumer(OAuth2Provider.CLIENT_ID, OAuth2Provider.CLIENT_SECRET);
public OAuth2InInterceptor() {
super(Phase.PRE_INVOKE);
}
@Override
public void handleMessage(final Message message) throws Fault {
final String requestURL = (String) message.get(Message.REQUEST_URL);
if (requestURL.contains("V40/OAuth2.svc")) {
@SuppressWarnings("unchecked")
final Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
final List<String> oauth2CodeHeader = headers.get(OAuth2Provider.OAUTH2_CODE_HEADER);
if (oauth2CodeHeader == null || oauth2CodeHeader.isEmpty()) {
message.put(AbstractHTTPDestination.REQUEST_REDIRECTED, Boolean.TRUE);
final HttpServletResponse response = (HttpServletResponse) message.get(AbstractHTTPDestination.HTTP_RESPONSE);
try {
final String authorizationServiceURI =
StringUtils.substringBefore(requestURL, "V40/OAuth2.svc") + "oauth/authorize";
final URI authorizationURI = OAuthClientUtils.getAuthorizationURI(
authorizationServiceURI,
OAuth2Provider.CLIENT_ID,
OAuth2Provider.REDIRECT_URI,
null,
null);
response.addHeader("Location", authorizationURI.toASCIIString());
response.sendError(303);
} catch (Exception e) {
throw new Fault(e);
}
} else {
try {
final JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(StringUtils.substringBefore(requestURL, "V40/OAuth2.svc") + "oauth/token");
bean.setUsername("odatajclient");
bean.setPassword("odatajclient");
final WebClient accessTokenService = bean.createWebClient().
type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).
accept(MediaType.APPLICATION_JSON_TYPE);
final AuthorizationCodeGrant codeGrant = new AuthorizationCodeGrant(oauth2CodeHeader.get(0));
final ClientAccessToken accessToken =
OAuthClientUtils.getAccessToken(accessTokenService, OAUTH2_CONSUMER, codeGrant);
if (accessToken == null) {
throw new WebApplicationException("No OAuth2 access token");
}
} catch (OAuthServiceException e) {
throw new Fault(e);
}
}
}
}
}

View File

@ -40,8 +40,6 @@ public class OAuth2Provider implements AuthorizationCodeDataProvider {
public static final String REDIRECT_URI = "/stub/StaticService/V40/OAuth2.svc/"; public static final String REDIRECT_URI = "/stub/StaticService/V40/OAuth2.svc/";
public static final String OAUTH2_CODE_HEADER = "oauth2.token";
private Client client; private Client client;
private ServerAuthorizationCodeGrant grant; private ServerAuthorizationCodeGrant grant;

View File

@ -16,38 +16,23 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.olingo.client.core.http; package org.apache.olingo.fit.rest;
import org.apache.http.client.methods.HttpUriRequest; import javax.ws.rs.container.ContainerRequestContext;
import org.apache.olingo.client.api.http.HttpMethod; import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter;
import java.net.URI; @Provider
public class OAuth2RequestFilter extends OAuthRequestFilter implements ContainerRequestFilter {
public abstract class AbstractOAuth2HttpUriRequestFactory extends DefaultHttpUriRequestFactory {
protected final URI redirectURI;
public AbstractOAuth2HttpUriRequestFactory(final URI redirectURI) {
this.redirectURI = redirectURI;
}
protected abstract boolean isInited();
protected abstract void init() throws OAuth2Exception;
protected abstract void sign(HttpUriRequest request);
@Override @Override
public HttpUriRequest create(final HttpMethod method, final URI uri) { public void filter(final ContainerRequestContext context) {
if (!isInited()) { final String svcName =
init(); StringUtils.substringBefore(StringUtils.substringAfter(context.getUriInfo().getPath(), "/"), "/");
if ("OAuth2.svc".equals(svcName)) {
super.filter(context);
} }
final HttpUriRequest request = super.create(method, uri);
sign(request);
return request;
} }
} }

View File

@ -40,6 +40,9 @@
<jaxrs:server id="services" address="/" basePackages="org.apache.olingo.fit"> <jaxrs:server id="services" address="/" basePackages="org.apache.olingo.fit">
<jaxrs:providers> <jaxrs:providers>
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/> <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
<bean class="org.apache.olingo.fit.rest.OAuth2RequestFilter">
<property name="dataProvider" ref="oauthProvider"/>
</bean>
<bean class="org.apache.olingo.fit.rest.ServiceNameResponseFilter"/> <bean class="org.apache.olingo.fit.rest.ServiceNameResponseFilter"/>
</jaxrs:providers> </jaxrs:providers>
</jaxrs:server> </jaxrs:server>
@ -52,7 +55,7 @@
<property name="dataProvider" ref="oauthProvider"/> <property name="dataProvider" ref="oauthProvider"/>
</bean> </bean>
<bean id="oauthSecurityInterceptor" class="org.apache.olingo.fit.rest.StaticSecurityInterceptor"/> <bean id="oauthSecurityInterceptor" class="org.apache.olingo.fit.rest.StaticSecurityInterceptor"/>
<jaxrs:server id="oauthServer" address="/oauth"> <jaxrs:server id="oauthServer" address="/oauth2">
<jaxrs:serviceBeans> <jaxrs:serviceBeans>
<ref bean="authorizationService"/> <ref bean="authorizationService"/>
<ref bean="accessTokenService"/> <ref bean="accessTokenService"/>

View File

@ -20,70 +20,84 @@ package org.apache.olingo.fit;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant;
import org.apache.cxf.rs.security.oauth2.grants.refresh.RefreshTokenGrant;
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams; import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.apache.olingo.client.core.http.AbstractOAuth2HttpUriRequestFactory; import org.apache.olingo.client.core.http.AbstractOAuth2HttpClientFactory;
import org.apache.olingo.client.core.http.OAuth2Exception; import org.apache.olingo.client.core.http.OAuth2Exception;
import org.apache.olingo.fit.rest.OAuth2Provider; import org.apache.olingo.fit.rest.OAuth2Provider;
import java.net.URI; public class CXFOAuth2HttpClientFactory extends AbstractOAuth2HttpClientFactory {
public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequestFactory { private static final OAuthClientUtils.Consumer OAUTH2_CONSUMER =
new OAuthClientUtils.Consumer(OAuth2Provider.CLIENT_ID, OAuth2Provider.CLIENT_SECRET);
private String code; private ClientAccessToken accessToken;
public CXFOAuth2HttpUriRequestFactory(final URI redirectURI) { public CXFOAuth2HttpClientFactory(final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
super(redirectURI); super(oauth2GrantServiceURI, oauth2TokenServiceURI);
}
private WebClient getAccessTokenService() {
final JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(oauth2TokenServiceURI.toASCIIString());
bean.setUsername("odatajclient");
bean.setPassword("odatajclient");
return bean.createWebClient().
type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).accept(MediaType.APPLICATION_JSON_TYPE);
} }
@Override @Override
protected boolean isInited() { protected boolean isInited() throws OAuth2Exception {
return code != null; return accessToken != null;
} }
@Override @Override
protected void init() throws OAuth2Exception { protected void init() throws OAuth2Exception {
// 1. Disable automatic redirects handling final URI authURI = OAuthClientUtils.getAuthorizationURI(
oauth2GrantServiceURI.toASCIIString(),
OAuth2Provider.CLIENT_ID,
OAuth2Provider.REDIRECT_URI,
null,
null);
// Disable automatic redirects handling
final HttpParams params = new BasicHttpParams(); final HttpParams params = new BasicHttpParams();
params.setParameter(ClientPNames.HANDLE_REDIRECTS, false); params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
final DefaultHttpClient httpClient = new DefaultHttpClient(params); final DefaultHttpClient httpClient = new DefaultHttpClient(params);
// 2. Try to access the redirect URI without any special header: get redirected to the OAuth2 service
URI location = null;
try {
final HttpResponse response = httpClient.execute(new HttpGet(redirectURI));
final Header locationHeader = response.getFirstHeader("Location");
if (response.getStatusLine().getStatusCode() != 303 || locationHeader == null) {
throw new IllegalStateException("OAuth flow is broken");
}
location = new URI(locationHeader.getValue());
EntityUtils.consumeQuietly(response.getEntity());
} catch (Exception e) {
throw new OAuth2Exception(e);
}
JsonNode oAuthAuthorizationData = null; JsonNode oAuthAuthorizationData = null;
String authenticityCookie = null; String authenticityCookie = null;
try { try {
// 3. Need to (basic) authenticate against the OAuth2 service // 1. Need to (basic) authenticate against the OAuth2 service
final HttpGet method = new HttpGet(location); final HttpGet method = new HttpGet(authURI);
method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes())); method.addHeader("Authorization", "Basic " + Base64.encodeBase64String("odatajclient:odatajclient".getBytes()));
final HttpResponse response = httpClient.execute(method); final HttpResponse response = httpClient.execute(method);
// 4. Pull out OAuth2 authorization data and "authenticity" cookie (CXF specific) // 2. Pull out OAuth2 authorization data and "authenticity" cookie (CXF specific)
oAuthAuthorizationData = new XmlMapper().readTree(EntityUtils.toString(response.getEntity())); oAuthAuthorizationData = new XmlMapper().readTree(EntityUtils.toString(response.getEntity()));
final Header setCookieHeader = response.getFirstHeader("Set-Cookie"); final Header setCookieHeader = response.getFirstHeader("Set-Cookie");
@ -95,9 +109,10 @@ public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequest
throw new OAuth2Exception(e); throw new OAuth2Exception(e);
} }
String code = null;
try { try {
// 5. Submit the HTTP form for allowing access to the application // 3. Submit the HTTP form for allowing access to the application
location = new URIBuilder(oAuthAuthorizationData.get("replyTo").asText()). final URI location = new URIBuilder(oAuthAuthorizationData.get("replyTo").asText()).
addParameter("session_authenticity_token", oAuthAuthorizationData.get("authenticityToken").asText()). addParameter("session_authenticity_token", oAuthAuthorizationData.get("authenticityToken").asText()).
addParameter("client_id", oAuthAuthorizationData.get("clientId").asText()). addParameter("client_id", oAuthAuthorizationData.get("clientId").asText()).
addParameter("redirect_uri", oAuthAuthorizationData.get("redirectUri").asText()). addParameter("redirect_uri", oAuthAuthorizationData.get("redirectUri").asText()).
@ -114,18 +129,53 @@ public class CXFOAuth2HttpUriRequestFactory extends AbstractOAuth2HttpUriRequest
throw new IllegalStateException("OAuth flow is broken"); throw new IllegalStateException("OAuth flow is broken");
} }
// 6. Finally get the code value out of this last redirect // 4. Get the authorization code value out of this last redirect
code = StringUtils.substringAfterLast(locationHeader.getValue(), "="); code = StringUtils.substringAfterLast(locationHeader.getValue(), "=");
EntityUtils.consumeQuietly(response.getEntity()); EntityUtils.consumeQuietly(response.getEntity());
} catch (Exception e) { } catch (Exception e) {
throw new OAuth2Exception(e); throw new OAuth2Exception(e);
} }
// 5. Obtain the access token
try {
accessToken = OAuthClientUtils.getAccessToken(
getAccessTokenService(), OAUTH2_CONSUMER, new AuthorizationCodeGrant(code));
} catch (OAuthServiceException e) {
throw new OAuth2Exception(e);
}
if (accessToken == null) {
throw new OAuth2Exception("No OAuth2 access token");
}
} }
@Override @Override
protected void sign(final HttpUriRequest request) { protected void accessToken(final DefaultHttpClient client) throws OAuth2Exception {
request.addHeader(OAuth2Provider.OAUTH2_CODE_HEADER, code); client.addRequestInterceptor(new HttpRequestInterceptor() {
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
request.removeHeaders(HttpHeaders.AUTHORIZATION);
request.addHeader(HttpHeaders.AUTHORIZATION, OAuthClientUtils.createAuthorizationHeader(accessToken));
}
});
}
@Override
protected void refreshToken(final DefaultHttpClient client) throws OAuth2Exception {
final String refreshToken = accessToken.getRefreshToken();
if (refreshToken == null) {
throw new OAuth2Exception("No OAuth2 refresh token");
}
// refresh the token
try {
accessToken = OAuthClientUtils.getAccessToken(
getAccessTokenService(), OAUTH2_CONSUMER, new RefreshTokenGrant(refreshToken));
} catch (OAuthServiceException e) {
throw new OAuth2Exception(e);
}
} }
} }

View File

@ -18,6 +18,9 @@
*/ */
package org.apache.olingo.fit.v4; package org.apache.olingo.fit.v4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest; import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
@ -25,39 +28,41 @@ import org.apache.olingo.client.api.uri.v4.URIBuilder;
import org.apache.olingo.client.api.v4.EdmEnabledODataClient; import org.apache.olingo.client.api.v4.EdmEnabledODataClient;
import org.apache.olingo.client.api.v4.ODataClient; import org.apache.olingo.client.api.v4.ODataClient;
import org.apache.olingo.client.core.ODataClientFactory; import org.apache.olingo.client.core.ODataClientFactory;
import org.apache.olingo.client.core.http.DefaultHttpUriRequestFactory;
import org.apache.olingo.commons.api.domain.v4.ODataEntity; import org.apache.olingo.commons.api.domain.v4.ODataEntity;
import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.fit.CXFOAuth2HttpUriRequestFactory;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.net.URI; import java.net.URI;
import org.apache.olingo.client.core.http.DefaultHttpClientFactory;
import static org.junit.Assert.assertEquals; import org.apache.olingo.fit.CXFOAuth2HttpClientFactory;
import static org.junit.Assert.assertTrue;
public class OAuth2TestITCase extends AbstractTestITCase { public class OAuth2TestITCase extends AbstractTestITCase {
private static final URI OAUTH2_GRANT_SERVICE_URI =
URI.create("http://localhost:9080/stub/StaticService/oauth2/authorize");
private static final URI OAUTH2_TOKEN_SERVICE_URI =
URI.create("http://localhost:9080/stub/StaticService/oauth2/token");
private EdmEnabledODataClient _edmClient; private EdmEnabledODataClient _edmClient;
@BeforeClass @BeforeClass
public static void enableOAuth2() { public static void enableOAuth2() {
client.getConfiguration().setHttpUriRequestFactory( client.getConfiguration().setHttpClientFactory(
new CXFOAuth2HttpUriRequestFactory(URI.create(testOAuth2ServiceRootURL))); new CXFOAuth2HttpClientFactory(OAUTH2_GRANT_SERVICE_URI, OAUTH2_TOKEN_SERVICE_URI));
} }
@AfterClass @AfterClass
public static void disableOAuth2() { public static void disableOAuth2() {
client.getConfiguration().setHttpUriRequestFactory(new DefaultHttpUriRequestFactory()); client.getConfiguration().setHttpClientFactory(new DefaultHttpClientFactory());
} }
protected EdmEnabledODataClient getEdmClient() { protected EdmEnabledODataClient getEdmClient() {
if (_edmClient == null) { if (_edmClient == null) {
_edmClient = ODataClientFactory.getEdmEnabledV4(testOAuth2ServiceRootURL); _edmClient = ODataClientFactory.getEdmEnabledV4(testOAuth2ServiceRootURL);
_edmClient.getConfiguration().setHttpUriRequestFactory( _edmClient.getConfiguration().setHttpClientFactory(
new CXFOAuth2HttpUriRequestFactory(URI.create(testOAuth2ServiceRootURL))); new CXFOAuth2HttpClientFactory(OAUTH2_GRANT_SERVICE_URI, OAUTH2_TOKEN_SERVICE_URI));
} }
return _edmClient; return _edmClient;

View File

@ -0,0 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.apache.olingo.client.api.http;
public interface WrappingHttpClientFactory extends HttpClientFactory {
HttpClientFactory getWrappedHttpClientFactory();
}

View File

@ -0,0 +1,116 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.apache.olingo.client.core.http;
import java.io.IOException;
import java.net.URI;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
import org.apache.olingo.client.api.http.HttpClientFactory;
import org.apache.olingo.client.api.http.HttpMethod;
import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
public abstract class AbstractOAuth2HttpClientFactory
extends AbstractHttpClientFactory implements WrappingHttpClientFactory {
protected final DefaultHttpClientFactory wrapped;
protected final URI oauth2GrantServiceURI;
protected final URI oauth2TokenServiceURI;
protected HttpUriRequest currentRequest;
public AbstractOAuth2HttpClientFactory(final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
this(new DefaultHttpClientFactory(), oauth2GrantServiceURI, oauth2TokenServiceURI);
}
public AbstractOAuth2HttpClientFactory(final DefaultHttpClientFactory wrapped,
final URI oauth2GrantServiceURI, final URI oauth2TokenServiceURI) {
super();
this.wrapped = wrapped;
this.oauth2GrantServiceURI = oauth2GrantServiceURI;
this.oauth2TokenServiceURI = oauth2TokenServiceURI;
}
@Override
public HttpClientFactory getWrappedHttpClientFactory() {
return wrapped;
}
protected abstract boolean isInited() throws OAuth2Exception;
protected abstract void init() throws OAuth2Exception;
protected abstract void accessToken(DefaultHttpClient client) throws OAuth2Exception;
protected abstract void refreshToken(DefaultHttpClient client) throws OAuth2Exception;
@Override
public HttpClient create(final HttpMethod method, final URI uri) {
if (!isInited()) {
init();
}
final DefaultHttpClient httpClient = wrapped.create(method, uri);
accessToken(httpClient);
httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if (request instanceof HttpUriRequest) {
currentRequest = (HttpUriRequest) request;
} else {
currentRequest = null;
}
}
});
httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
@Override
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
refreshToken(httpClient);
if (currentRequest != null) {
httpClient.execute(currentRequest);
}
}
}
});
return httpClient;
}
@Override
public void close(final HttpClient httpClient) {
wrapped.close(httpClient);
}
}

View File

@ -22,6 +22,10 @@ public class OAuth2Exception extends RuntimeException {
private static final long serialVersionUID = 5695438980473040134L; private static final long serialVersionUID = 5695438980473040134L;
public OAuth2Exception(final String message) {
super(message);
}
public OAuth2Exception(final Throwable cause) { public OAuth2Exception(final Throwable cause) {
super(cause); super(cause);
} }

View File

@ -28,12 +28,13 @@ import org.apache.olingo.client.api.http.HttpClientFactory;
import org.apache.olingo.client.api.http.HttpMethod; import org.apache.olingo.client.api.http.HttpMethod;
import java.net.URI; import java.net.URI;
import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
/** /**
* Implementation for working behind an HTTP proxy (possibly requiring authentication); requires another concrete * Implementation for working behind an HTTP proxy (possibly requiring authentication); requires another concrete
* {@link HttpClientFactory} implementation acting as real HTTP client factory. * {@link HttpClientFactory} implementation acting as real HTTP client factory.
*/ */
public class ProxyWrapperHttpClientFactory implements HttpClientFactory { public class ProxyWrappingHttpClientFactory implements WrappingHttpClientFactory {
private final URI proxy; private final URI proxy;
@ -43,19 +44,19 @@ public class ProxyWrapperHttpClientFactory implements HttpClientFactory {
private final DefaultHttpClientFactory wrapped; private final DefaultHttpClientFactory wrapped;
public ProxyWrapperHttpClientFactory(final URI proxy) { public ProxyWrappingHttpClientFactory(final URI proxy) {
this(proxy, null, null, new DefaultHttpClientFactory()); this(proxy, null, null, new DefaultHttpClientFactory());
} }
public ProxyWrapperHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) { public ProxyWrappingHttpClientFactory(final URI proxy, final String proxyUsername, final String proxyPassword) {
this(proxy, proxyUsername, proxyPassword, new DefaultHttpClientFactory()); this(proxy, proxyUsername, proxyPassword, new DefaultHttpClientFactory());
} }
public ProxyWrapperHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) { public ProxyWrappingHttpClientFactory(final URI proxy, final DefaultHttpClientFactory wrapped) {
this(proxy, null, null, wrapped); this(proxy, null, null, wrapped);
} }
public ProxyWrapperHttpClientFactory(final URI proxy, public ProxyWrappingHttpClientFactory(final URI proxy,
final String proxyUsername, final String proxyPassword, final DefaultHttpClientFactory wrapped) { final String proxyUsername, final String proxyPassword, final DefaultHttpClientFactory wrapped) {
this.proxy = proxy; this.proxy = proxy;

View File

@ -46,6 +46,7 @@ import java.io.InputStream;
public abstract class AbstractODataDeserializer { public abstract class AbstractODataDeserializer {
protected final ODataServiceVersion version; protected final ODataServiceVersion version;
protected final ODataDeserializer deserializer; protected final ODataDeserializer deserializer;
public AbstractODataDeserializer(final ODataServiceVersion version, final boolean serverMode, public AbstractODataDeserializer(final ODataServiceVersion version, final boolean serverMode,

View File

@ -31,7 +31,6 @@ import org.apache.olingo.client.api.CommonODataClient;
import org.apache.olingo.client.api.http.HttpClientFactory; import org.apache.olingo.client.api.http.HttpClientFactory;
import org.apache.olingo.client.api.uri.SegmentType; import org.apache.olingo.client.api.uri.SegmentType;
import org.apache.olingo.client.core.http.BasicAuthHttpClientFactory; import org.apache.olingo.client.core.http.BasicAuthHttpClientFactory;
import org.apache.olingo.client.core.http.ProxyWrapperHttpClientFactory;
import org.apache.olingo.commons.api.Constants; import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@ -65,6 +64,7 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.olingo.client.api.http.WrappingHttpClientFactory;
/** /**
* URI utilities. * URI utilities.
@ -354,8 +354,8 @@ public final class URIUtils {
HttpClientFactory httpclientFactory = client.getConfiguration().getHttpClientFactory(); HttpClientFactory httpclientFactory = client.getConfiguration().getHttpClientFactory();
if (httpclientFactory instanceof BasicAuthHttpClientFactory) { if (httpclientFactory instanceof BasicAuthHttpClientFactory) {
return true; return true;
} else if (httpclientFactory instanceof ProxyWrapperHttpClientFactory) { } else if (httpclientFactory instanceof WrappingHttpClientFactory) {
ProxyWrapperHttpClientFactory tmp = (ProxyWrapperHttpClientFactory) httpclientFactory; WrappingHttpClientFactory tmp = (WrappingHttpClientFactory) httpclientFactory;
if (tmp.getWrappedHttpClientFactory() instanceof BasicAuthHttpClientFactory) { if (tmp.getWrappedHttpClientFactory() instanceof BasicAuthHttpClientFactory) {
return true; return true;
} }

View File

@ -53,7 +53,6 @@ import org.apache.olingo.commons.core.data.LinkImpl;
import org.apache.olingo.commons.core.data.LinkedComplexValueImpl; import org.apache.olingo.commons.core.data.LinkedComplexValueImpl;
import org.apache.olingo.commons.core.data.PropertyImpl; import org.apache.olingo.commons.core.data.PropertyImpl;
import org.apache.olingo.commons.core.edm.EdmTypeInfo; import org.apache.olingo.commons.core.edm.EdmTypeInfo;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.AbstractMap.SimpleEntry; import java.util.AbstractMap.SimpleEntry;
@ -69,23 +68,39 @@ import java.util.regex.Pattern;
public class JsonDeserializer implements ODataDeserializer { public class JsonDeserializer implements ODataDeserializer {
protected final Pattern CUSTOM_ANNOTATION = Pattern.compile("(.+)@(.+)\\.(.+)"); protected final Pattern CUSTOM_ANNOTATION = Pattern.compile("(.+)@(.+)\\.(.+)");
protected final ODataServiceVersion version; protected final ODataServiceVersion version;
protected final boolean serverMode; protected final boolean serverMode;
protected String jsonType; protected String jsonType;
protected String jsonId; protected String jsonId;
protected String jsonETag; protected String jsonETag;
protected String jsonReadLink; protected String jsonReadLink;
protected String jsonEditLink; protected String jsonEditLink;
protected String jsonMediaEditLink; protected String jsonMediaEditLink;
protected String jsonMediaReadLink; protected String jsonMediaReadLink;
protected String jsonMediaContentType; protected String jsonMediaContentType;
protected String jsonMediaETag; protected String jsonMediaETag;
protected String jsonAssociationLink; protected String jsonAssociationLink;
protected String jsonNavigationLink; protected String jsonNavigationLink;
protected String jsonCount; protected String jsonCount;
protected String jsonNextLink; protected String jsonNextLink;
protected String jsonDeltaLink; protected String jsonDeltaLink;
protected String jsonError; protected String jsonError;
private JsonGeoValueDeserializer geoDeserializer; private JsonGeoValueDeserializer geoDeserializer;
@ -143,11 +158,9 @@ public class JsonDeserializer implements ODataDeserializer {
} else if (inline instanceof ArrayNode) { } else if (inline instanceof ArrayNode) {
link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString()); link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
EntitySet entitySet = new EntitySetImpl(); final EntitySet entitySet = new EntitySetImpl();
Iterator<JsonNode> entries = inline.elements(); for (final Iterator<JsonNode> entries = inline.elements(); entries.hasNext();) {
while (entries.hasNext()) { entitySet.getEntities().add(entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload());
entitySet.getEntities().add(
entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload());
} }
link.setInlineEntitySet(entitySet); link.setInlineEntitySet(entitySet);
@ -313,11 +326,11 @@ public class JsonDeserializer implements ODataDeserializer {
} }
private Object fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) throws EdmPrimitiveTypeException { private Object fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) throws EdmPrimitiveTypeException {
return node.isNull() ? null : return node.isNull() ? null
typeInfo == null ? node.asText() : : typeInfo == null ? node.asText()
typeInfo.getPrimitiveTypeKind().isGeospatial() ? : typeInfo.getPrimitiveTypeKind().isGeospatial()
getGeoDeserializer().deserialize(node, typeInfo) : ? getGeoDeserializer().deserialize(node, typeInfo)
((EdmPrimitiveType) typeInfo.getType()) : ((EdmPrimitiveType) typeInfo.getType())
.valueOfString(node.asText(), true, null, .valueOfString(node.asText(), true, null,
Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true,
((EdmPrimitiveType) typeInfo.getType()).getDefaultType()); ((EdmPrimitiveType) typeInfo.getType()).getDefaultType());
@ -327,11 +340,11 @@ public class JsonDeserializer implements ODataDeserializer {
throws IOException, EdmPrimitiveTypeException { throws IOException, EdmPrimitiveTypeException {
if (version.compareTo(ODataServiceVersion.V40) < 0) { if (version.compareTo(ODataServiceVersion.V40) < 0) {
List<Property> properties = new ArrayList<Property>(); final List<Property> properties = new ArrayList<Property>();
populate(null, properties, node, codec); populate(null, properties, node, codec);
return properties; return properties;
} else { } else {
LinkedComplexValue linkComplexValue = new LinkedComplexValueImpl(); final LinkedComplexValue linkComplexValue = new LinkedComplexValueImpl();
final Set<String> toRemove = new HashSet<String>(); final Set<String> toRemove = new HashSet<String>();
for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) { for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
final Map.Entry<String, JsonNode> field = itor.next(); final Map.Entry<String, JsonNode> field = itor.next();
@ -348,11 +361,11 @@ public class JsonDeserializer implements ODataDeserializer {
private void fromCollection(final Valuable valuable, final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo, private void fromCollection(final Valuable valuable, final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo,
final ObjectCodec codec) throws IOException, EdmPrimitiveTypeException { final ObjectCodec codec) throws IOException, EdmPrimitiveTypeException {
List<Object> values = new ArrayList<Object>(); final List<Object> values = new ArrayList<Object>();
ValueType valueType = ValueType.COLLECTION_PRIMITIVE; ValueType valueType = ValueType.COLLECTION_PRIMITIVE;
final EdmTypeInfo type = typeInfo == null ? null : final EdmTypeInfo type = typeInfo == null ? null
new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build(); : new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
while (nodeItor.hasNext()) { while (nodeItor.hasNext()) {
final JsonNode child = nodeItor.next(); final JsonNode child = nodeItor.next();
@ -371,8 +384,8 @@ public class JsonDeserializer implements ODataDeserializer {
((ObjectNode) child).remove(jsonType); ((ObjectNode) child).remove(jsonType);
} }
final Object value = fromComplex((ObjectNode) child, codec); final Object value = fromComplex((ObjectNode) child, codec);
valueType = value instanceof LinkedComplexValue ? ValueType.COLLECTION_LINKED_COMPLEX : valueType = value instanceof LinkedComplexValue ? ValueType.COLLECTION_LINKED_COMPLEX
ValueType.COLLECTION_COMPLEX; : ValueType.COLLECTION_COMPLEX;
values.add(value); values.add(value);
} }
} }
@ -381,18 +394,19 @@ public class JsonDeserializer implements ODataDeserializer {
protected void value(final Valuable valuable, final JsonNode node, final ObjectCodec codec) protected void value(final Valuable valuable, final JsonNode node, final ObjectCodec codec)
throws IOException, EdmPrimitiveTypeException { throws IOException, EdmPrimitiveTypeException {
EdmTypeInfo typeInfo = StringUtils.isBlank(valuable.getType()) ? null :
new EdmTypeInfo.Builder().setTypeExpression(valuable.getType()).build(); EdmTypeInfo typeInfo = StringUtils.isBlank(valuable.getType()) ? null
: new EdmTypeInfo.Builder().setTypeExpression(valuable.getType()).build();
final Map.Entry<ODataPropertyType, EdmTypeInfo> guessed = guessPropertyType(node); final Map.Entry<ODataPropertyType, EdmTypeInfo> guessed = guessPropertyType(node);
if (typeInfo == null) { if (typeInfo == null) {
typeInfo = guessed.getValue(); typeInfo = guessed.getValue();
} }
final ODataPropertyType propType = typeInfo == null ? guessed.getKey() : final ODataPropertyType propType = typeInfo == null ? guessed.getKey()
typeInfo.isCollection() ? ODataPropertyType.COLLECTION : : typeInfo.isCollection() ? ODataPropertyType.COLLECTION
typeInfo.isPrimitiveType() ? ODataPropertyType.PRIMITIVE : : typeInfo.isPrimitiveType() ? ODataPropertyType.PRIMITIVE
node.isValueNode() ? ODataPropertyType.ENUM : ODataPropertyType.COMPLEX; : node.isValueNode() ? ODataPropertyType.ENUM : ODataPropertyType.COMPLEX;
switch (propType) { switch (propType) {
case COLLECTION: case COLLECTION: