/**
* Copyright (C) 2015 Zalando SE (http://tech.zalando.com)
*
* 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
*
* 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.zalando.stups.tokens;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
public class CloseableHttpProvider extends AbstractHttpProvider {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final CloseableHttpClient client;
private final HttpClientContext localContext;
private UserCredentials userCredentials;
private URI accessTokenUri;
private final HttpHost host;
private final RequestConfig requestConfig;
//@formatter:off
public CloseableHttpProvider(ClientCredentials clientCredentials, UserCredentials userCredentials,
URI accessTokenUri, HttpConfig httpConfig) {
this.userCredentials = userCredentials;
this.accessTokenUri = accessTokenUri;
requestConfig = RequestConfig.custom()
.setSocketTimeout(httpConfig.getSocketTimeout())
.setConnectTimeout(httpConfig.getConnectTimeout())
.setConnectionRequestTimeout(httpConfig.getConnectionRequestTimeout())
.setStaleConnectionCheckEnabled(httpConfig.isStaleConnectionCheckEnabled())
.build();
// prepare basic auth credentials
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(accessTokenUri.getHost(), accessTokenUri.getPort()),
new UsernamePasswordCredentials(clientCredentials.getId(), clientCredentials.getSecret()));
client = HttpClients.custom()
.setUserAgent(USER_AGENT.get())
.useSystemProperties()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
host = new HttpHost(accessTokenUri.getHost(), accessTokenUri.getPort(), accessTokenUri.getScheme());
// enable basic auth for the request
final AuthCache authCache = new BasicAuthCache();
final BasicScheme basicAuth = new BasicScheme();
authCache.put(host, basicAuth);
localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
}
//@formatter:on
@Override
public AccessToken createToken(final AccessTokenConfiguration tokenConfig) throws UnsupportedEncodingException {
final List<NameValuePair> values = buildParameterList(tokenConfig);
final HttpPost request = new HttpPost(accessTokenUri);
request.setEntity(new UrlEncodedFormEntity(values));
request.setConfig(requestConfig);
try (final CloseableHttpResponse response = client.execute(host, request, localContext)) {
// success status code?
final int status = response.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
throw AccessTokenEndpointException.from(response);
}
// get json response
final HttpEntity entity = response.getEntity();
final AccessTokenResponse accessTokenResponse = OBJECT_MAPPER.readValue(EntityUtils.toByteArray(entity),
AccessTokenResponse.class);
// create new access token object
final Date validUntil = calculateValidUntil(accessTokenResponse);
return new AccessToken(accessTokenResponse.getAccessToken(), accessTokenResponse.getTokenType(),
accessTokenResponse.getExpiresInSeconds(), validUntil);
} catch (Throwable t) {
throw new AccessTokenEndpointException(t.getMessage(), t);
}
}
private List<NameValuePair> buildParameterList(final AccessTokenConfiguration tokenConfig) {
Map<String, String> parameterList = buildParameterMap(tokenConfig, userCredentials);
List<NameValuePair> nameValuePairs = new ArrayList<>();
for (Map.Entry<String, String> entry : parameterList.entrySet()) {
nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
return nameValuePairs;
}
@Override
public void close() throws IOException {
client.close();
}
}