package org.dcache.auth;
import com.google.common.base.Charsets;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
public class OpenIdCredentialRefreshable extends WrappingOpenIdCredential
{
private static final Logger LOG =
LoggerFactory.getLogger(OpenIdCredentialRefreshable.class);
private final HttpClient client;
public OpenIdCredentialRefreshable(OpenIdCredential credential, HttpClient client) {
super(checkNotNull(credential, "OpenId Credential can't be null"));
this.client = checkNotNull(client, "Http Client can't be null");
}
@Override
public String getBearerToken()
{
if (timeToRefresh()) {
try {
refreshOpenIdCredentials();
} catch (IOException | AuthenticationException e) {
LOG.warn("Error Refreshing OpenId Bearer Token with {}: {}",
credential.getOpenidProvider(), e.getMessage());
}
}
return credential.getBearerToken();
}
private synchronized void refreshOpenIdCredentials() throws IOException, AuthenticationException
{
HttpPost post = new HttpPost(credential.getOpenidProvider());
BasicScheme scheme = new BasicScheme(Charsets.UTF_8);
UsernamePasswordCredentials clientCreds = new UsernamePasswordCredentials(
credential.getClientCredential().getId(),
credential.getClientCredential().getSecret());
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("client_id", credential.getClientCredential().getId()));
params.add(new BasicNameValuePair("client_secret", credential.getClientCredential().getSecret()));
params.add(new BasicNameValuePair("grant_type", "refresh_token"));
params.add(new BasicNameValuePair("refresh_token", credential.getRefreshToken()));
params.add(new BasicNameValuePair("scope", credential.getScope()));
post.setEntity(new UrlEncodedFormEntity(params));
post.addHeader(scheme.authenticate(clientCreds, post, new BasicHttpContext()) );
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
updateCredential(parseResponseToJson(response));
} else {
throw new IOException(String.format("Error Refreshing OpenId Bearer Token [%s]: %s",
response.getStatusLine().getStatusCode(),
credential.getOpenidProvider()));
}
}
private boolean timeToRefresh() {
return (credential.getExpiresAt() - System.currentTimeMillis()) < 60*1000L;
}
private JSONObject parseResponseToJson(HttpResponse response) throws IOException
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
response.getEntity().writeTo(os);
return new JSONObject(new String(os.toByteArray(), Charsets.UTF_8));
}
private void updateCredential(JSONObject json) throws IOException
{
try {
this.credential = StaticOpenIdCredential.copyOf(credential)
.accessToken(json.getString("access_token"))
.expiry(json.getLong("expires_in"))
.build();
} catch (JSONException je) {
throw new IOException("Error Parsing response of OpenId Bearer Token Refresh: " + je.getMessage());
}
}
}