package org.simbasecurity.dwclient.gateway;
import java.net.SocketException;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.thrift.TException;
import org.apache.thrift.transport.THttpClient;
import org.apache.thrift.transport.TTransportException;
import org.simbasecurity.api.service.thrift.ActionDescriptor;
import org.simbasecurity.api.service.thrift.ActionType;
import org.simbasecurity.api.service.thrift.AuthenticationFilterService;
import org.simbasecurity.api.service.thrift.AuthenticationFilterService.Client;
import org.simbasecurity.api.service.thrift.RequestData;
import org.simbasecurity.dwclient.dropwizard.credentials.SimbaCredentials;
import org.simbasecurity.dwclient.dropwizard.credentials.SimbaCredentialsFactory;
import org.simbasecurity.dwclient.dropwizard.credentials.SimbaPrincipal;
import org.simbasecurity.dwclient.exception.InternalServerErrorDWSimbaException;
import org.simbasecurity.dwclient.exception.SimbaUnavailableException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.sun.jersey.spi.container.ContainerRequest;
public class SimbaGateway {
public static final String SIMBA_AUTHENTICATION_SERVICE = "authenticationService";
public static final String SESSION_AUTHENTICATE_CHAIN = "sessionChain";
public static final String LOGIN_AUTHENTICATE_CHAIN = "loginAuthChain";
public static final String LOGOUT_CHAIN = "logoutChain";
private Logger logger = LoggerFactory.getLogger(this.getClass());
private String simbaWebURL;
private THttpClient tHttpClient;
private Client authenticationFilterService;
private SimbaServiceFactory simbaServiceFactory;
private SimbaCredentialsFactory simbaCredentialsFactory;
@Inject
public SimbaGateway(@Named("simbaWebURL") String simbaWebURL, SimbaServiceFactory simbaServiceFactory, SimbaCredentialsFactory simbaCredentialsFactory) {
this.simbaWebURL = simbaWebURL;
this.simbaServiceFactory = simbaServiceFactory;
this.simbaCredentialsFactory = simbaCredentialsFactory;
}
public Optional<String> login(String appUser, String appPassword) throws SimbaUnavailableException {
RequestData requestData = simbaCredentialsFactory.createForLogin(appUser, appPassword).asRequestData();
return loginInSimba(requestData);
}
public boolean checkIfPasswordMatches(String username, String password) {
try {
Optional<String> login = login(username, password);
return login.isPresent();
} catch (SimbaUnavailableException e) {
throw new InternalServerErrorDWSimbaException();
}
}
/**
* @param request
* @return Optional<String> that contains the SSOToken as a String on successful login, and null when unsuccessful
* @throws SimbaUnavailableException
*/
public Optional<String> login(ContainerRequest request) throws SimbaUnavailableException {
RequestData requestData = simbaCredentialsFactory.create(request, false).asRequestData();
return loginInSimba(requestData);
}
/**
*
* @param request
* @return true on successful logout
* @throws SimbaUnavailableException
*/
public void logout(ContainerRequest request) throws SimbaUnavailableException {
SimbaCredentials credentials = simbaCredentialsFactory.create(request, true);
processRequestInSimba(credentials.asRequestData(), LOGOUT_CHAIN);
}
/**
* On successful authentication returns a present SimbaPrincipal
* On failed authentication returns either an absent SimbaPrincipal, or throws a SimbaUnavailableException
*
* @param credentials
* @return an absent SimbaPrincipal when the ActionDescriptor does not contain DO_FILTER_AND_SET_PRINCIPAL (means authentication failed)
* a present SimbaPrincipal when the ActionDescriptor does contain DO_FILTER_AND_SET_PRINCIPAL (means authentication was successful)
* @throws SimbaUnavailableException
*/
public Optional<SimbaPrincipal> authenticate(SimbaCredentials credentials) throws SimbaUnavailableException {
SimbaPrincipal principal = null;
ActionDescriptor actionDescriptor = processRequestInSimba(credentials.asRequestData(), SESSION_AUTHENTICATE_CHAIN);
if (isValidActionDescriptor(actionDescriptor) && actionDescriptor.getActionTypes().contains(ActionType.DO_FILTER_AND_SET_PRINCIPAL)) {
String token = actionDescriptor.getSsoToken() != null ? actionDescriptor.getSsoToken().getToken() : null;
principal = new SimbaPrincipal(actionDescriptor.getPrincipal(), token);
}
return Optional.fromNullable(principal);
}
/**
* @return always creates a new client
* @throws SimbaUnavailableException
*/
AuthenticationFilterService.Client createAuthenticationService() throws SimbaUnavailableException {
try {
tHttpClient = getTHttpClient();
authenticationFilterService = simbaServiceFactory.createJSONAuthenticationFilterService(tHttpClient);
} catch (TTransportException | RuntimeException e) {
if (tHttpClient != null) {
tHttpClient.close();
}
logger.error("Simba is down?", e);
throw new SimbaUnavailableException(e);
}
return authenticationFilterService;
}
public boolean isSimbaAlive() {
THttpClient tHttpClient = null;
try {
tHttpClient = getTHttpClient();
tHttpClient.flush();
} catch (TTransportException e) {
if (e.getCause() != null && e.getCause().getClass().isAssignableFrom(SocketException.class)) {
if (tHttpClient != null) {
tHttpClient.close();
}
return false;
}
} finally {
if (tHttpClient != null) {
tHttpClient.close();
}
}
return true;
}
private Optional<String> loginInSimba(RequestData requestData) throws SimbaUnavailableException {
ActionDescriptor actionDescriptor = processRequestInSimba(requestData, LOGIN_AUTHENTICATE_CHAIN);
if (isValidActionDescriptor(actionDescriptor) && actionDescriptor.getActionTypes().contains(ActionType.MAKE_COOKIE)) {
return Optional.fromNullable(actionDescriptor.getSsoToken().getToken());
}
return Optional.absent();
}
private boolean isValidActionDescriptor(ActionDescriptor actionDescriptor) {
return actionDescriptor != null && actionDescriptor.getActionTypes() != null;
}
private ActionDescriptor processRequestInSimba(RequestData requestData, String chain) throws SimbaUnavailableException {
try {
logger.debug("processRequest in simba with requestdata: {}", requestData);
ActionDescriptor actionDescriptor = createAuthenticationService().processRequest(requestData, chain);
return actionDescriptor;
} catch (TException | RuntimeException e) {
logger.error("Simba is down?", e);
throw new SimbaUnavailableException(e);
} finally {
if (tHttpClient != null) {
tHttpClient.close();
}
}
}
private THttpClient getTHttpClient() throws TTransportException {
return simbaServiceFactory.createTHttpClient(simbaWebURL + "/" + SIMBA_AUTHENTICATION_SERVICE);
}
}