package servlets;
import com.onelogin.saml2.Auth;
import com.onelogin.saml2.exception.Error;
import com.onelogin.saml2.exception.SettingsException;
import dbProcs.Getter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import utils.Hash;
import utils.ShepherdLogManager;
import utils.UserKicker;
/**
* Control class for the authentication procedure.
*
* This file is part of the Security Shepherd Project.
*
*
The Security Shepherd project is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
*
The Security Shepherd project is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
*
You should have received a copy of the GNU General Public License along with the Security
* Shepherd project. If not, see .
*
* @author Mark Denihan
*/
public class ACS extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger log = LogManager.getLogger(ACS.class);
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ShepherdLogManager.setRequestIp(request.getRemoteAddr(), request.getHeader("X-Forwarded-For"));
log.debug("**** servlets.ACS ***");
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
HttpSession ses = request.getSession(true);
Object language = ses.getAttribute("lang");
String errorMessage = new String();
request.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
log.debug("Getting ApplicationRoot");
String ApplicationRoot = getServletContext().getRealPath("");
log.debug("Servlet root = " + ApplicationRoot);
boolean mustRedirect = false;
boolean ssoValid = false;
// Start SAML processing
log.debug("Processing SAML data...");
Auth auth;
try {
auth = new Auth(request, response);
} catch (SettingsException e) {
throw new RuntimeException("SAML not configured: " + e.toString());
} catch (Error e) {
throw new RuntimeException("SAML error : " + e.toString());
}
try {
auth.processResponse();
} catch (Exception e) {
throw new RuntimeException("SAML error when processing response: " + e.toString());
}
log.debug("SAML data processed");
if (!auth.isAuthenticated()) {
log.debug("User not authenticated");
errorMessage += "Not authenticated";
ses.setAttribute("loginFailed", errorMessage);
response.sendRedirect("../login.jsp");
} else {
log.debug("User authenticated");
List errors = auth.getErrors();
if (!errors.isEmpty()) {
errorMessage += StringUtils.join(errors, ", ");
log.debug("SAML errors found: " + StringUtils.join(errors, ", "));
} else {
Map> attributes = auth.getAttributes();
String nameId = auth.getNameId();
String nameIdFormat = auth.getNameIdFormat();
String sessionIndex = auth.getSessionIndex();
String nameidNameQualifier = auth.getNameIdNameQualifier();
String nameidSPNameQualifier = auth.getNameIdSPNameQualifier();
ses.setAttribute("attributes", attributes);
ses.setAttribute("nameId", nameId);
ses.setAttribute("nameIdFormat", nameIdFormat);
ses.setAttribute("sessionIndex", sessionIndex);
ses.setAttribute("nameidNameQualifier", nameidNameQualifier);
ses.setAttribute("nameidSPNameQualifier", nameidSPNameQualifier);
if (attributes.isEmpty()) {
errorMessage += "You don't have any attributes";
log.debug("No SAML attributes found");
} else {
log.debug("Unpacking SAML attributes...");
String ssoName = null;
String userName = null;
String userRole = null;
ClassLoader classLoader = getClass().getClassLoader();
log.debug("Loading saml unpack properties file");
String unpackFileName = "sso.properties";
try (InputStream inputStream = classLoader.getResourceAsStream(unpackFileName)) {
if (inputStream != null) {
Properties prop = new Properties();
prop.load(inputStream);
if (prop != null) {
log.debug("Saml unpack properties file loaded, unpacking saml data");
// Get id and name from SAML data
String ssoNameKey = prop.getProperty("sso.saml.ssoName");
ssoName = attributes.get(ssoNameKey).get(0);
log.debug("ssoName = " + ssoName);
String userNameKey = prop.getProperty("sso.saml.userName");
userName = attributes.get(userNameKey).get(0);
log.debug("userName = " + userName);
String affiliationKey = prop.getProperty("sso.saml.affiliation");
List affiliations = attributes.get(affiliationKey);
String adminAffiliation = prop.getProperty("sso.saml.adminAffiliation");
String playerAffiliation = prop.getProperty("sso.saml.playerAffiliation");
List adminAffiliations = Arrays.asList(adminAffiliation.split(",[ ]*"));
List playerAffiliations = Arrays.asList(playerAffiliation.split(",[ ]*"));
boolean foundAdmin = false;
boolean foundPlayer = false;
for (String affiliation : adminAffiliations) {
if (affiliations.contains(affiliation)) {
foundAdmin = true;
}
}
if (!foundAdmin) {
for (String affiliation : playerAffiliations) {
if (affiliations.contains(affiliation)) {
foundPlayer = true;
}
}
}
if (foundAdmin) {
userRole = "admin";
ssoValid = true;
} else if (foundPlayer) {
userRole = "player";
ssoValid = true;
} else {
ssoValid = false;
errorMessage +=
"Authorization failed. Please ensure that you are a member of one of the"
+ " following groups: ";
for (String affiliation : adminAffiliations) {
errorMessage += affiliation + ", ";
}
for (String affiliation : playerAffiliations) {
errorMessage += affiliation + ", ";
}
}
log.debug("userRole = " + userRole);
}
} else {
String errorMsg =
"SAML unpack properties file '" + unpackFileName + "' not found in the classpath";
log.error(errorMsg);
throw new RuntimeException(errorMsg);
}
} catch (IOException e) {
String errorMsg =
"SAML unpack properties file '" + unpackFileName + "' cannot be loaded";
log.error(errorMsg);
throw new RuntimeException(errorMsg);
}
if (ssoValid) {
if (ssoName == null || userName == null || userRole == null) {
String errorMsg = "Unknown error occurred when unpacking SAML properties";
log.error(errorMsg);
throw new RuntimeException(errorMsg);
}
log.debug("Saml userdata loaded, calling authUserSSO");
String user[] = Getter.authUserSSO(ApplicationRoot, null, userName, ssoName, userRole);
if (user != null && !user[0].isEmpty()) {
// Kill Session and Create a new one with user logged in
log.debug("Creating new session for " + user[2] + " " + user[1]);
ses.invalidate();
ses = request.getSession(true);
ses.setAttribute("userStamp", user[0]);
ses.setAttribute("userName", user[1]);
ses.setAttribute("userRole", user[2]);
ses.setAttribute("lang", language);
log.debug("userClassId = " + user[4]);
ses.setAttribute("userClass", user[4]);
if (user[5].equalsIgnoreCase("true")) {
log.debug("Temporary Username Detected, user will be prompted to change");
ses.setAttribute("ChangeUsername", "true");
}
log.debug("Setting CSRF cookie");
Cookie token = new Cookie("token", Hash.randomString());
if (request.getRequestURL().toString().startsWith("https")) // If Requested over HTTPs
{
token.setSecure(true);
}
// We must set the path because the ACS servlet is in a subdir...
token.setPath("/");
response.addCookie(token);
mustRedirect = true;
// Removing user from kick list. If they were on it before, their suspension
// must have ended if their authentication Succeeded
UserKicker.removeFromKicklist(user[1]);
}
if (mustRedirect) {
response.sendRedirect("../index.jsp");
} else {
ssoValid = false;
}
}
if (!ssoValid) {
log.debug("Could not authenticate");
errorMessage += "SSO login failed.";
ses.setAttribute("loginFailed", errorMessage);
// Lagging Response
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
response.sendRedirect("../login.jsp");
}
}
}
}
log.debug("**** End servlets.ACS ***");
}
/** Redirects user to index.jsp */
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.sendRedirect("../index.jsp");
}
}