blob: 050cdd812af2993faa2a6e5fc0dc50d82f31a5f4 [file] [log] [blame]
package net.sf.taverna.t2.activities.rest;
import java.net.URI;
import java.net.URLEncoder;
import java.security.Principal;
import javax.management.remote.JMXPrincipal;
import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
//import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.log4j.Logger;
/**
* This CredentialsProvider acts as a mediator between the Apache HttpClient and
* Taverna's CredentialManager that stores all user's credentials.
*
* The only role of it is to retrieve stored details from CredentialManager when
* they are required for HTTP authentication.
*
* @author Sergejs Aleksejevs
* @author Alex Nenadic
*/
public class RESTActivityCredentialsProvider extends BasicCredentialsProvider {
private static Logger logger = Logger.getLogger(RESTActivityCredentialsProvider.class);
private static final int DEFAULT_HTTP_PORT = 80;
private static final int DEFAULT_HTTPS_PORT = 443;
private static final String HTTP_PROTOCOL = "http";
private static final String HTTPS_PROTOCOL = "https";
private CredentialManager credentialManager;
public RESTActivityCredentialsProvider(CredentialManager credentialManager) {
this.credentialManager = credentialManager;
}
@Override
public Credentials getCredentials(AuthScope authscope) {
logger.info("Looking for credentials for: Host - " + authscope.getHost() + ";" + "Port - "
+ authscope.getPort() + ";" + "Realm - " + authscope.getRealm() + ";"
+ "Authentication scheme - " + authscope.getScheme());
// Ask the superclass first
Credentials creds = super.getCredentials(authscope);
if (creds != null) {
/*
* We have used setCredentials() on this class (for proxy host,
* port, username,password) just before we invoked the http request,
* which will then pick the proxy credentials up from here.
*/
return creds;
}
// Otherwise, ask Credential Manager if is can provide the credential
String AUTHENTICATION_REQUEST_MSG = "This REST service requires authentication in "
+ authscope.getRealm();
try {
UsernamePassword credentials = null;
/*
* if port is 80 - use HTTP, don't append port if port is 443 - use
* HTTPS, don't append port any other port - append port + do 2
* tests:
*
* --- test HTTPS first has...()
* --- if not there, do get...() for HTTP (which will save the thing)
*
* (save both these entries for HTTP + HTTPS if not there)
*/
// build the service URI back to front
StringBuilder serviceURI = new StringBuilder();
serviceURI.insert(0, "/#" + URLEncoder.encode(authscope.getRealm(), "UTF-16"));
if (authscope.getPort() != DEFAULT_HTTP_PORT
&& authscope.getPort() != DEFAULT_HTTPS_PORT) {
// non-default port - add port name to the URI
serviceURI.insert(0, ":" + authscope.getPort());
}
serviceURI.insert(0, authscope.getHost());
serviceURI.insert(0, "://");
// now the URI is complete, apart from the protocol name
if (authscope.getPort() == DEFAULT_HTTP_PORT
|| authscope.getPort() == DEFAULT_HTTPS_PORT) {
// definitely HTTP or HTTPS
serviceURI.insert(0, (authscope.getPort() == DEFAULT_HTTP_PORT ? HTTP_PROTOCOL
: HTTPS_PROTOCOL));
// request credentials from CrendentialManager
credentials = credentialManager.getUsernameAndPasswordForService(
URI.create(serviceURI.toString()), true, AUTHENTICATION_REQUEST_MSG);
} else {
/*
* non-default port - will need to try both HTTP and HTTPS; just
* check (no pop-up will be shown) if credentials are there -
* one protocol that matched will be used; if
*/
if (credentialManager.hasUsernamePasswordForService(URI.create(HTTPS_PROTOCOL
+ serviceURI.toString()))) {
credentials = credentialManager.getUsernameAndPasswordForService(
URI.create(HTTPS_PROTOCOL + serviceURI.toString()), true,
AUTHENTICATION_REQUEST_MSG);
} else if (credentialManager.hasUsernamePasswordForService(URI.create(HTTP_PROTOCOL
+ serviceURI.toString()))) {
credentials = credentialManager.getUsernameAndPasswordForService(
URI.create(HTTP_PROTOCOL + serviceURI.toString()), true,
AUTHENTICATION_REQUEST_MSG);
} else {
/*
* Neither of the two options succeeded, request details with a
* popup for HTTP...
*/
credentials = credentialManager.getUsernameAndPasswordForService(
URI.create(HTTP_PROTOCOL + serviceURI.toString()), true,
AUTHENTICATION_REQUEST_MSG);
/*
* ...then save a second entry with HTTPS protocol (if the
* user has chosen to save the credentials)
*/
if (credentials != null && credentials.isShouldSave()) {
credentialManager.addUsernameAndPasswordForService(credentials,
URI.create(HTTPS_PROTOCOL + serviceURI.toString()));
}
}
}
if (credentials != null) {
logger.info("Credentials obtained successfully");
return new RESTActivityCredentials(credentials.getUsername(),
credentials.getPasswordAsString());
}
} catch (Exception e) {
logger.error(
"Unexpected error while trying to obtain user's credential from CredentialManager",
e);
}
// error or nothing was found
logger.info("Credentials not found - the user must have refused to enter them.");
return null;
}
/**
* This class encapsulates user's credentials that this CredentialsProvider
* can pass to Apache HttpClient.
*
* @author Sergejs Aleksejevs
*/
public class RESTActivityCredentials implements Credentials {
// this seems to be the simplest existing standard implementation of
// Principal interface
private final JMXPrincipal user;
private final String password;
public RESTActivityCredentials(String username, String password) {
this.user = new JMXPrincipal(username);
this.password = password;
}
@Override
public String getPassword() {
return password;
}
@Override
public Principal getUserPrincipal() {
return user;
}
}
}