package org.openslx.dozmod.authentication;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openslx.bwlp.thrift.iface.AuthorizationError;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.dozmod.Branding;
import org.openslx.dozmod.util.ProxyConfigurator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import edu.kit.scc.dei.ecplean.ECPAuthenticationException;
import edu.kit.scc.dei.ecplean.ECPAuthenticator;
public class ShibbolethEcp {
/**
* Logger instance for this class
*/
private final static Logger LOGGER = LogManager.getLogger(ShibbolethEcp.class);
/**
* Static gson object for (de)serialization
*/
private static final Gson GSON = new GsonBuilder().create();
/**
* ServiceProviderResponse Object representing the last response we received
*/
private static ServiceProviderResponse lastResponse = null;
/**
* URL for service registration
*/
private static URL registrationUrl = null;
/**
* Return codes
*/
public static enum ReturnCode {
// TODO rework this...
NO_ERROR(0,
"Authentication against the identity provider and request of the service provider resource worked."),
IDENTITY_PROVIDER_ERROR(1, "Authentication against the identity provider failed."),
UNREGISTERED_ERROR(2, "User not registered to use " + Branding.getServiceName() + "."),
SERVICE_PROVIDER_ERROR(3, "Invalid resource of the service provider."),
INVALID_URL_ERROR(4, "Invalid URL received from master server."),
GENERIC_ERROR(5, "Internal error.");
private final int id;
private final String msg;
ReturnCode(int id, String msg) {
this.id = id;
this.msg = msg;
}
public int getId() {
return this.id;
}
public String getMsg() {
return this.msg;
}
}
/**
* Static URI to the SP.
*/
public final static URI BWLP_SP;
static {
URI tmp;
try {
tmp = new URI("https://" + Branding.getMasterServerAddress() + "/webif/shib/api.php");
} catch (URISyntaxException e) {
// should never happen!
LOGGER.error("Bad URI syntax of the service provider, see trace: ", e);
tmp = null;
}
BWLP_SP = tmp;
}
public static ServiceProviderResponse getResponse() {
return lastResponse;
}
/**
* Fetches the resource
*
* @param idpUrl
* URL of the identity provider to authenticate against, as
* String.
* @param user
* Username as String.
* @param pass
* Password as String.
* @return
* true if login worked, false otherwise.
* @throws TAuthorizationException
*/
public static ReturnCode doLogin(final String idpUrl, final String user, final String pass)
throws TAuthorizationException, URISyntaxException, ClientProtocolException, IOException,
ParseException, JsonSyntaxException, MalformedURLException {
// first lets do some sanity checks
if (BWLP_SP == null) {
LOGGER.error("URI to service provider is not set. Check the initialization of 'BWLP_SP'.");
return ReturnCode.GENERIC_ERROR;
}
if (idpUrl == null) {
LOGGER.error("Identity provider is not set, did you initialize this class correctly?");
return ReturnCode.GENERIC_ERROR;
}
if (user == null) {
LOGGER.error("No username given, aborting...");
return ReturnCode.GENERIC_ERROR;
}
if (pass == null) {
LOGGER.error("No password given, aborting...");
return ReturnCode.GENERIC_ERROR;
}
// now init the authenticator for that idp and our static sp
final ECPAuthenticator auth = new ECPAuthenticator(ProxyConfigurator.getClient(), user, pass, new URI(idpUrl), BWLP_SP);
auth.setRetryWithoutAt(true);
CloseableHttpResponse spResponse;
try {
spResponse = auth.authenticate();
} catch (ECPAuthenticationException e) {
LOGGER.error("ECP Authentication Exception, see trace: ", e);
throw new TAuthorizationException(AuthorizationError.GENERIC_ERROR, e.getMessage());
}
if (spResponse.getCode() != 200) {
LOGGER.error("SP does not return HTTP status code 200");
throw new TAuthorizationException(AuthorizationError.GENERIC_ERROR, "SP says: "
+ spResponse.getReasonPhrase());
}
LOGGER.debug("Login complete, getting body");
final String responseBody = EntityUtils.toString(spResponse.getEntity());
try {
lastResponse = GSON.fromJson(responseBody, ServiceProviderResponse.class);
} catch (JsonSyntaxException e) {
LOGGER.warn("Json data from Service Provider malformed", e);
LOGGER.warn("Response was:\n" + responseBody);
throw e;
}
// TODO: here we will need to parse the answer accordingly.
// no errors, meaning everything worked fine.
if (lastResponse.status.equals("unregistered")) {
registrationUrl = new URL(lastResponse.url);
return ReturnCode.UNREGISTERED_ERROR;
}
// TODO the rest of the cases...
if (lastResponse.status.equals("error")) {
LOGGER.error("Server side error: " + lastResponse.error);
return ReturnCode.GENERIC_ERROR;
}
if (lastResponse.status.equals("anonymous")) {
LOGGER.error("IdP did not forward user account information to SP. Contact developer.");
lastResponse.error = "Ihr Identity-Provider hat dem " + Branding.getServiceName() + "-System Ihre E-Mail oder Ihren Namen nicht mitgeteilt";
return ReturnCode.GENERIC_ERROR;
}
if (lastResponse.status.equals("ok")) {
return ReturnCode.NO_ERROR;
}
// still here? then something else went wrong
return ReturnCode.GENERIC_ERROR;
}
/**
* @return Registration URL given by the SP.
*/
public static URL getRegistrationUrl() {
return registrationUrl;
}
}