Skip to content

Commit

Permalink
javaswift#133 Enable support for keystone v3 authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin Conaway committed Oct 10, 2017
1 parent 290dcd9 commit dc47b18
Show file tree
Hide file tree
Showing 21 changed files with 650 additions and 52 deletions.
@@ -1,6 +1,6 @@
package org.javaswift.joss.client.factory;

import static org.javaswift.joss.client.factory.AuthenticationMethod.*;
import static org.javaswift.joss.client.factory.AuthenticationMethod.KEYSTONE;

public class AccountConfig {

Expand Down Expand Up @@ -217,6 +217,11 @@ public class AccountConfig {

private AuthenticationMethod.AccessProvider accessProvider = null ;

/**
* Keystone domain. Used by the V3 Identity API
*/
private String domain = "Default";

public void setTenantName(String tenantName) {
this.tenantName = tenantName;
}
Expand Down Expand Up @@ -472,4 +477,12 @@ public boolean isAllowSynchronizeWithServer() {
public void setAllowSynchronizeWithServer(boolean allowSynchronizeWithServer) {
this.allowSynchronizeWithServer = allowSynchronizeWithServer;
}

public String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}
}
Expand Up @@ -159,4 +159,8 @@ public AccountFactory setMockClasspath(Class mockClasspath) {
return this;
}

public AccountFactory setDomain(String domain) {
this.config.setDomain(domain);
return this;
}
}
Expand Up @@ -16,6 +16,7 @@
public enum AuthenticationMethod {
BASIC,
KEYSTONE,
KEYSTONE_V3,
TEMPAUTH,
EXTERNAL;

Expand Down
15 changes: 1 addition & 14 deletions src/main/java/org/javaswift/joss/client/impl/ClientImpl.java
@@ -1,13 +1,8 @@
package org.javaswift.joss.client.impl;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.javaswift.joss.client.core.AbstractClient;
Expand Down Expand Up @@ -106,15 +101,7 @@ protected AuthenticationCommandFactory createFactory() {

@Override
protected AccountImpl createAccount() {
AuthenticationCommand command = this.factory.createAuthenticationCommand(
httpClient,
accountConfig.getAuthenticationMethod(),
accountConfig.getAuthUrl(),
accountConfig.getTenantName(),
accountConfig.getTenantId(),
accountConfig.getUsername(),
accountConfig.getPassword(),
accountConfig.getAccessProvider());
AuthenticationCommand command = this.factory.createAuthenticationCommand(httpClient, accountConfig);
LOG.info(
"JOSS / Attempting authentication with tenant name: " + accountConfig.getTenantName()+
", tenant ID: "+accountConfig.getTenantId()+
Expand Down
10 changes: 1 addition & 9 deletions src/main/java/org/javaswift/joss/client/mock/ClientMock.java
Expand Up @@ -60,15 +60,7 @@ protected AccountMock createAccount() {
", tenant ID: "+accountConfig.getTenantId()+
", username: " +accountConfig.getUsername()+
", Auth URL: " +accountConfig.getAuthUrl());
this.factory.createAuthenticationCommand(
null,
accountConfig.getAuthenticationMethod(),
null,
accountConfig.getTenantName(),
accountConfig.getTenantId(),
accountConfig.getUsername(),
accountConfig.getPassword(),
accountConfig.getAccessProvider()).call();
this.factory.createAuthenticationCommand(null, accountConfig).call();
}
return new AccountMock(swift);
}
Expand Down
@@ -1,30 +1,39 @@
package org.javaswift.joss.command.impl.factory;

import org.apache.http.client.HttpClient;
import org.javaswift.joss.client.factory.AuthenticationMethod;
import org.javaswift.joss.client.factory.AccountConfig;
import org.javaswift.joss.command.impl.identity.BasicAuthenticationCommandImpl;
import org.javaswift.joss.command.impl.identity.ExternalAuthenticationCommandImpl;
import org.javaswift.joss.command.impl.identity.KeystoneAuthenticationCommandImpl;
import org.javaswift.joss.command.impl.identity.KeystoneAuthenticationV3CommandImpl;
import org.javaswift.joss.command.impl.identity.TempAuthAuthenticationCommandImpl;
import org.javaswift.joss.command.shared.factory.AuthenticationCommandFactory;
import org.javaswift.joss.command.shared.identity.AuthenticationCommand;

import static org.javaswift.joss.client.factory.AuthenticationMethod.*;
import static org.javaswift.joss.client.factory.AuthenticationMethod.BASIC;
import static org.javaswift.joss.client.factory.AuthenticationMethod.EXTERNAL;
import static org.javaswift.joss.client.factory.AuthenticationMethod.KEYSTONE_V3;
import static org.javaswift.joss.client.factory.AuthenticationMethod.TEMPAUTH;

public class AuthenticationCommandFactoryImpl implements AuthenticationCommandFactory {

@Override
public AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AuthenticationMethod authenticationMethod,
String url, String tenantName, String tenantId,
String username, String password, AuthenticationMethod.AccessProvider accessProvier) {
if (authenticationMethod == BASIC) {
public AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AccountConfig config) {
String url = config.getAuthUrl();
String tenantName = config.getTenantName();
String username = config.getUsername();
String password = config.getPassword();

if (config.getAuthenticationMethod() == BASIC) {
return new BasicAuthenticationCommandImpl(httpClient, url, username, password, tenantName);
} else if (authenticationMethod == TEMPAUTH) {
} else if (config.getAuthenticationMethod() == TEMPAUTH) {
return new TempAuthAuthenticationCommandImpl(httpClient, url, username, password, tenantName);
} else if (authenticationMethod == EXTERNAL) {
return new ExternalAuthenticationCommandImpl (httpClient, url, accessProvier) ;
} else if (config.getAuthenticationMethod() == EXTERNAL) {
return new ExternalAuthenticationCommandImpl (httpClient, url, config.getAccessProvider()) ;
} else if (config.getAuthenticationMethod() == KEYSTONE_V3) {
return new KeystoneAuthenticationV3CommandImpl(httpClient, config);
} else { // KEYSTONE
return new KeystoneAuthenticationCommandImpl(httpClient, url, tenantName, tenantId, username, password);
return new KeystoneAuthenticationCommandImpl(httpClient, url, tenantName, config.getTenantId(), username, password);
}
}

Expand Down
@@ -0,0 +1,79 @@
package org.javaswift.joss.command.impl.identity;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.javaswift.joss.client.factory.AccountConfig;
import org.javaswift.joss.command.impl.core.AbstractCommand;
import org.javaswift.joss.command.impl.core.httpstatus.HttpStatusChecker;
import org.javaswift.joss.command.impl.core.httpstatus.HttpStatusRange;
import org.javaswift.joss.command.impl.core.httpstatus.HttpStatusSuccessCondition;
import org.javaswift.joss.command.shared.identity.AuthenticationCommand;
import org.javaswift.joss.command.shared.identity.access.KeystoneV3Access;
import org.javaswift.joss.command.shared.identity.authentication.KeystoneV3Authentication;
import org.javaswift.joss.exception.CommandException;
import org.javaswift.joss.headers.Accept;
import org.javaswift.joss.model.Access;

import java.io.IOException;

public class KeystoneAuthenticationV3CommandImpl extends AbstractCommand<HttpPost, Access> implements AuthenticationCommand {

private final String url;
private final ObjectMapper requestMapper;
private final ObjectMapper responseMapper;

public KeystoneAuthenticationV3CommandImpl(HttpClient httpClient, AccountConfig config) {
super(httpClient, config.getAuthUrl());

this.url = config.getAuthUrl();
this.requestMapper = createObjectMapper(true);
this.responseMapper = createObjectMapper(false);

setHeader(new Accept(ContentType.APPLICATION_JSON.getMimeType()));
setRequestBody(config.getUsername(), config.getPassword(), config.getDomain());
}

private void setRequestBody(String username, String password, String domain) {
try {
String jsonString = requestMapper.writeValueAsString(
new KeystoneV3Authentication(username, password, domain)
);
request.setEntity(
new StringEntity(jsonString, ContentType.APPLICATION_JSON)
);
} catch (IOException ex) {
throw new CommandException("Unable to set the JSON body on the request", ex);
}
}

@Override
public Access getReturnObject(HttpResponse response) throws IOException {
String tokenValue = response.getFirstHeader("X-Subject-Token").getValue();

JsonNode responseBody = responseMapper.readTree(response.getEntity().getContent());

return new KeystoneV3Access(tokenValue, responseBody);
}

@Override
protected HttpPost createRequest(String url) {
return new HttpPost(url);
}

@Override
public HttpStatusChecker[] getStatusCheckers() {
return new HttpStatusChecker[] {
new HttpStatusSuccessCondition(new HttpStatusRange(200, 299))
};
}

@Override
public String getUrl() {
return this.url;
}
}
@@ -1,7 +1,7 @@
package org.javaswift.joss.command.mock.factory;

import org.apache.http.client.HttpClient;
import org.javaswift.joss.client.factory.AuthenticationMethod;
import org.javaswift.joss.client.factory.AccountConfig;
import org.javaswift.joss.command.mock.identity.AuthenticationCommandMock;
import org.javaswift.joss.command.shared.factory.AuthenticationCommandFactory;
import org.javaswift.joss.command.shared.identity.AuthenticationCommand;
Expand All @@ -16,10 +16,15 @@ public AuthenticationCommandFactoryMock(Swift swift) {
}

@Override
public AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AuthenticationMethod authenticationMethod,
String url, String tenantName, String tenantId,
String username, String password, AuthenticationMethod.AccessProvider accessProvier) {
return new AuthenticationCommandMock(swift, url, tenantName, tenantId, username, password);
public AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AccountConfig config) {
return new AuthenticationCommandMock(
swift,
config.getAuthUrl(),
config.getTenantName(),
config.getTenantId(),
config.getUsername(),
config.getPassword()
);
}

}
@@ -1,13 +1,11 @@
package org.javaswift.joss.command.shared.factory;

import org.apache.http.client.HttpClient;
import org.javaswift.joss.client.factory.AuthenticationMethod;
import org.javaswift.joss.client.factory.AccountConfig;
import org.javaswift.joss.command.shared.identity.AuthenticationCommand;

public interface AuthenticationCommandFactory {

AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AuthenticationMethod authenticationMethod,
String url, String tenantName, String tenantId,
String username, String password, AuthenticationMethod.AccessProvider accessProvier);
AuthenticationCommand createAuthenticationCommand(HttpClient httpClient, AccountConfig accountConfig);

}
@@ -0,0 +1,117 @@
package org.javaswift.joss.command.shared.identity.access;

import org.codehaus.jackson.JsonNode;
import org.javaswift.joss.client.factory.TempUrlHashPrefixSource;
import org.javaswift.joss.model.Access;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

public class KeystoneV3Access implements Access {

private final String token;
private final Collection<EndPoint> endPoints;

private EndPoint currentEndpoint;

public KeystoneV3Access(String token, JsonNode response) {
this.token = token;
this.endPoints = getEndPoints(response);
this.currentEndpoint = getEndPoint(null);
}

@Override
public void setPreferredRegion(String preferredRegion) {
this.currentEndpoint = getEndPoint(preferredRegion);
}

@Override
public String getToken() {
return token;
}

@Override
public String getInternalURL() {
return currentEndpoint.internalURL;
}

@Override
public String getPublicURL() {
return currentEndpoint.publicURL;
}

/**
* @return Always true, v3 doesn't use the tenant ID for auth
*/
@Override
public boolean isTenantSupplied() {
return true;
}

@Override
public String getTempUrlPrefix(TempUrlHashPrefixSource tempUrlHashPrefixSource) {
final String tempUrlPrefix;
if (tempUrlHashPrefixSource == null) {
tempUrlPrefix = "";
} else if (tempUrlHashPrefixSource == TempUrlHashPrefixSource.PUBLIC_URL_PATH) {
tempUrlPrefix = TempUrlHashPrefixSource.getPath(currentEndpoint.publicURL);
} else if (tempUrlHashPrefixSource == TempUrlHashPrefixSource.INTERNAL_URL_PATH) {
tempUrlPrefix = TempUrlHashPrefixSource.getPath(currentEndpoint.internalURL);
} else {
tempUrlPrefix = TempUrlHashPrefixSource.getPath(currentEndpoint.adminURL);
}
return tempUrlPrefix.endsWith("/") ? tempUrlPrefix.substring(0, tempUrlPrefix.length()-1) : tempUrlPrefix;
}

private EndPoint getEndPoint(String region) {
if (endPoints.isEmpty()) {
throw new IllegalStateException("No endpoints available");
}

if (region == null) {
return endPoints.iterator().next();
}

for (EndPoint endPoint : endPoints) {
if (region.equals(endPoint.region)) {
return endPoint;
}
}

throw new IllegalArgumentException("No endpoint for region: " + region);
}

private Collection<EndPoint> getEndPoints(JsonNode response) {
Map<String, EndPoint> result = new LinkedHashMap<String, EndPoint>();

JsonNode token = response.get("token");
for (JsonNode catalog : token.get("catalog")) {
if ("object-store".equals(catalog.get("type").asText())) {
for (JsonNode endpointNode : catalog.get("endpoints")) {
String interfaceType = endpointNode.get("interface").asText();
String url = endpointNode.get("url").asText();
String region = endpointNode.get("region").asText();;

EndPoint endPoint = result.get(region);

if (endPoint == null) {
endPoint = new EndPoint();
endPoint.region = region;
result.put(region, endPoint);
}

if ("public".equals(interfaceType)) {
endPoint.publicURL = url;
} else if ("admin".equals(interfaceType)) {
endPoint.adminURL = url;
} else if ("internal".equals(interfaceType)) {
endPoint.internalURL = url;
}
}
}
}

return result.values();
}
}

0 comments on commit dc47b18

Please sign in to comment.