monasca-common/java/monasca-common-middleware/src/main/java/monasca/common/middleware/HttpAuthClient.java

342 lines
11 KiB
Java

/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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 monasca.common.middleware;
import static monasca.common.middleware.AuthConstants.TOKEN;
import static monasca.common.middleware.AuthConstants.AUTH_SUBJECT_TOKEN;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public class HttpAuthClient implements AuthClient {
private static final Logger logger = LoggerFactory.getLogger(HttpAuthClient.class);
private static final int DELTA_TIME_IN_SEC = 30;
private static final String APPLICATION_JSON = "application/json";
private static SimpleDateFormat expiryFormat;
static {
expiryFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
expiryFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
private final Config appConfig = Config.getInstance();
private HttpClient client;
private String adminToken;
private String adminTokenExpiry;
private URI uri;
public HttpAuthClient(HttpClient client, URI uri) {
this.client = client;
this.uri = uri;
}
@Override
public String validateTokenForServiceEndpointV3(String token) throws ClientProtocolException {
String newUri = uri.toString() + "/v3/auth/tokens/";
Header[] header = new Header[]{new BasicHeader(AUTH_SUBJECT_TOKEN, token)};
return verifyUUIDToken(token, newUri, header);
}
private String verifyUUIDToken(String token, String newUri,
Header[] headers)
throws ClientProtocolException {
HttpGet httpGet = new HttpGet(newUri);
try {
httpGet.setHeader("Accept", APPLICATION_JSON);
httpGet.setHeader("Content-Type", APPLICATION_JSON);
if (headers != null) {
for (Header header : headers) {
httpGet.setHeader(header);
}
}
HttpResponse response = sendGet(httpGet);
HttpEntity entity = response.getEntity();
int code = response.getStatusLine().getStatusCode();
InputStream instream;
try {
if (code == 404) {
instream = entity.getContent();
instream.close();
throw new AuthException("Authorization failed for user token: " + token);
}
if (code != 200) {
adminToken = null;
instream = entity.getContent();
instream.close();
String reasonPhrase = response.getStatusLine().getReasonPhrase();
throw new AuthException("Failed to validate via HTTP " + code + " " + reasonPhrase);
}
} catch (IOException e) {
throw new ClientProtocolException("IO Exception: problem closing stream ", e);
}
return parseResponse(response);
} finally {
httpGet.releaseConnection();
}
}
private HttpResponse sendPost(HttpPost httpPost)
throws ClientProtocolException {
HttpResponse response;
try {
response = client.execute(httpPost);
int code = response.getStatusLine().getStatusCode();
if (!(code == 201 || code == 200 || code == 203)) {
adminToken = null;
throw new AdminAuthException(
"Failed to authenticate admin credentials " + code
+ response.getStatusLine().getReasonPhrase());
}
} catch (IOException e) {
final String message;
if ((e.getMessage() == null) && (e.getCause() != null)) {
message = e.getCause().getMessage();
} else {
message = e.getMessage();
}
logger.error("Failure authenticating adminUser: {}", message);
httpPost.abort();
throw new AdminAuthException(
"Failure authenticating adminUser :" + message, e);
}
return response;
}
private HttpResponse sendGet(HttpGet httpGet)
throws ClientProtocolException {
HttpResponse response;
if (appConfig.getAdminAuthMethod().equalsIgnoreCase(Config.TOKEN)) {
httpGet.setHeader(new BasicHeader(TOKEN, appConfig.getAdminToken()));
} else {
httpGet.setHeader(new BasicHeader(TOKEN, getAdminToken()));
}
try {
response = client.execute(httpGet);
} catch (ConnectException c) {
httpGet.abort();
throw new ServiceUnavailableException(c.getMessage());
} catch (IOException e) {
httpGet.abort();
throw new ClientProtocolException(
"IO Exception during GET request ", e);
}
return response;
}
private String parseResponse(HttpResponse response) {
StringBuffer json = new StringBuffer();
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream;
try {
instream = entity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(instream));
String line = reader.readLine();
while (line != null) {
json.append(line);
line = reader.readLine();
}
instream.close();
reader.close();
} catch (Exception e) {
throw new AuthException("Failed to parse Http Response ", e);
}
}
return json.toString();
}
private String getAdminToken() throws ClientProtocolException {
if (adminTokenExpiry != null) {
if (isExpired(adminTokenExpiry)) {
adminToken = null;
}
}
if (adminToken == null) {
String authUri = uri + "/v3/auth/tokens";
HttpPost httpPost = new HttpPost(authUri);
try {
StringEntity params = getUnscopedV3AdminTokenRequest();
httpPost.setHeader("Accept", APPLICATION_JSON);
httpPost.setHeader("Content-Type", APPLICATION_JSON);
httpPost.setEntity(params);
HttpResponse response = sendPost(httpPost);
adminToken = response.getFirstHeader(AUTH_SUBJECT_TOKEN).getValue();
String json = parseResponse(response);
JsonObject
token =
new JsonParser().parse(json).getAsJsonObject().get("token").getAsJsonObject();
adminTokenExpiry = token.get("expires_at").getAsString();
} finally {
httpPost.releaseConnection();
}
}
return adminToken;
}
private String buildAuth(final String userName, final String password,
final String projectId, final String projectName,
final String userDomainName, final String projectDomainName) {
final JsonObject UserDomain = new JsonObject();
if (!userDomainName.isEmpty()) {
UserDomain.addProperty("name", userDomainName);
} else {
UserDomain.addProperty("id", "default");
}
final JsonObject user = new JsonObject();
user.addProperty("name", userName);
user.addProperty("password", password);
user.add("domain", UserDomain);
final JsonObject passwordHolder = new JsonObject();
passwordHolder.add("user", user);
final JsonArray methods = new JsonArray();
methods.add(new JsonPrimitive("password"));
final JsonObject identity = new JsonObject();
identity.add("methods", methods);
identity.add("password", passwordHolder);
boolean scopeDefined = false;
final JsonObject project = new JsonObject();
// If project id is available, it is preferred
if (!projectId.isEmpty()) {
project.addProperty("id", projectId);
scopeDefined = true;
} else if (!projectName.isEmpty()) {
final JsonObject ProjectDomain = new JsonObject();
if (!projectDomainName.isEmpty()) {
ProjectDomain.addProperty("name", projectDomainName);
} else {
ProjectDomain.addProperty("id", "default");
}
project.add("domain", ProjectDomain);
project.addProperty("name", projectName);
scopeDefined = true;
}
final JsonObject auth = new JsonObject();
auth.add("identity", identity);
if (scopeDefined) {
final JsonObject scope = new JsonObject();
scope.add("project", project);
auth.add("scope", scope);
}
final JsonObject outer = new JsonObject();
outer.add("auth", auth);
return outer.toString();
}
private StringEntity getUnscopedV3AdminTokenRequest() {
final String body;
if (appConfig.getAdminAuthMethod().equalsIgnoreCase(Config.PASSWORD)) {
body = buildAuth(appConfig.getAdminUser(), appConfig.getAdminPassword(),
appConfig.getAdminProjectId(), appConfig.getAdminProjectName(),
appConfig.getAdminUserDomainName(), appConfig.getAdminProjectDomainName());
} else {
String
msg =
String.format("Admin auth method %s not supported", appConfig.getAdminAuthMethod());
throw new AdminAuthException(msg);
}
try {
return new StringEntity(body);
} catch (UnsupportedEncodingException e) {
throw new AdminAuthException("Invalid V3 authentication request " + e);
}
}
private boolean isExpired(String expires) {
Date tokenExpiryDate;
try {
// The date looks like: 2014-11-13T02:34:59.953729Z
// SimpleDateFormat can't handle the microseconds so take them off
final String tmp = expires.replaceAll("\\.[\\d]+Z", "Z");
tokenExpiryDate = expiryFormat.parse(tmp);
} catch (ParseException e) {
logger.warn("Failure parsing Admin Token expiration date: {}", e.getMessage());
return true;
}
Date current = new Date();
return tokenExpiryDate.getTime() < (current.getTime() + DELTA_TIME_IN_SEC * 1000);
}
public void reset() {
}
}