package me.prettyprint.cassandra.connection.client;
import java.net.Socket;
import java.net.SocketException;
import javax.security.auth.Subject;
import me.prettyprint.cassandra.connection.security.KerberosHelper;
import me.prettyprint.cassandra.service.CassandraHost;
import me.prettyprint.hector.api.exceptions.HectorTransportException;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSSLTransportFactory;
import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransportException;
import org.ietf.jgss.GSSContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Hector client that authenticate against kerberos.
*
* @author patricioe (Patricio Echague - patricioe@gmail.com)
*/
public class HKerberosThriftClient extends HThriftClient implements HClient {
private static Logger log = LoggerFactory.getLogger(HKerberosThriftClient.class);
private Subject kerberosTicket;
private String servicePrincipalName;
private TSSLTransportParameters params;
/**
* Constructor
* @param kerberosTicket
* @param cassandraHost
*/
public HKerberosThriftClient(Subject kerberosTicket, CassandraHost cassandraHost, String servicePrincipalName) {
super(cassandraHost);
this.kerberosTicket = kerberosTicket;
this.servicePrincipalName = servicePrincipalName;
}
/**
* Constructor
* @param kerberosTicket
* @param cassandraHost
* @param params
*/
public HKerberosThriftClient(Subject kerberosTicket, CassandraHost cassandraHost, String servicePrincipalName, TSSLTransportParameters params) {
super(cassandraHost);
this.kerberosTicket = kerberosTicket;
this.servicePrincipalName = servicePrincipalName;
this.params = params;
}
/**
* {@inheritDoc}
*/
public HKerberosThriftClient open() {
if ( isOpen() ) {
throw new IllegalStateException("Open called on already open connection. You should not have gotten here.");
}
if ( log.isDebugEnabled() ) {
log.debug("Creating a new thrift connection to {}", cassandraHost);
}
TSocket socket;
try {
socket = params == null ?
new TSocket(cassandraHost.getHost(), cassandraHost.getPort(), timeout)
: TSSLTransportFactory.getClientSocket(cassandraHost.getHost(), cassandraHost.getPort(), timeout, params);
} catch (TTransportException e) {
throw new HectorTransportException("Could not get client socket: ", e);
}
if ( cassandraHost.getUseSocketKeepalive() ) {
try {
socket.getSocket().setKeepAlive(true);
} catch (SocketException se) {
throw new HectorTransportException("Could not set SO_KEEPALIVE on socket: ", se);
}
}
// TODO (patricioe) What should I do with it ?
// KerberosHelper.getSourcePrinciple(clientContext));
transport = maybeWrapWithTFramedTransport(socket);
try {
transport.open();
} catch (TTransportException e) {
// Thrift exceptions aren't very good in reporting, so we have to catch the exception here and
// add details to it.
log.debug("Unable to open transport to " + cassandraHost.getName());
//clientMonitor.incCounter(Counter.CONNECT_ERROR);
throw new HectorTransportException("Unable to open transport to " + cassandraHost.getName() +" , " +
e.getLocalizedMessage(), e);
}
// Kerberos authentication
Socket internalSocket = socket.getSocket();
final GSSContext clientContext = KerberosHelper.authenticateClient(internalSocket, kerberosTicket, servicePrincipalName);
if (clientContext == null) {
close();
throw new HectorTransportException("Kerberos context couldn't be established with client.");
}
return this;
}
}