package org.ngbw.web.actions; import java.security.NoSuchAlgorithmException; import java.util.Properties; import org.apache.log4j.Logger; import org.ngbw.sdk.UserAuthenticationException; import org.ngbw.sdk.WorkbenchSession; import org.ngbw.sdk.common.util.StringUtils; import org.ngbw.sdk.common.util.ValidationResult; import org.ngbw.sdk.database.User; import org.ngbw.sdk.database.SSO; /** * Struts action class to process login to CIPRES with CyVerse/iPlant * credentials. * * @author Tony Chen */ @SuppressWarnings("serial") public class CASCredentialManager extends SessionManager { private static final Logger logger = Logger.getLogger(CASCredentialManager.class); private void storeIplantHeaders () { logger.debug("BEGIN: storeIplantHeaders()::void"); //deleteIplantHeaders(); clearSessionAttribute(SHIB_HEADERS); logger.debug(String.format("Session attribute [%s] cleared.", SHIB_HEADERS)); String eppn, uid, mail, givenName, sn; eppn = request.getHeader("PRINCIPAL"); logger.debug(String.format("EPPN from 'PRINCIPAL' Headers = [%s]", eppn)); if (!StringUtils.isNullOrEmpty(eppn, true)) { eppn = eppn.trim() + "@iplantcollaborative.org"; uid = request.getHeader("uid"); mail = request.getHeader("CAS_email"); givenName = request.getHeader("CAS_firstName"); sn = request.getHeader("CAS_lastName"); if (uid == null) { uid = ""; } if (mail == null) { mail = ""; } if (givenName == null || givenName.trim().isEmpty()) { givenName = eppn; } if (sn == null || sn.trim().isEmpty()) { sn = eppn; } Properties shibHeaders = new Properties(); shibHeaders.setProperty("eppn", eppn); shibHeaders.setProperty("uid", uid); shibHeaders.setProperty("mail", mail); shibHeaders.setProperty("givenName", givenName); shibHeaders.setProperty("sn", sn); setSessionAttribute(SHIB_HEADERS, shibHeaders); logger.debug( String.format( "Stored iPlant Headers: eppn=[%s] uid=[%s] mail=[%s] givenName=[%s] sn=[%s]", shibHeaders.getProperty("eppn"), shibHeaders.getProperty("uid"), shibHeaders.getProperty("mail"), shibHeaders.getProperty("givenName"), shibHeaders.getProperty("sn"))); } logger.debug("END: storeIplantHeaders()::void"); } // If we've stored iplant Headers in the session, this returns them. private Properties getIplantHeaders () { // Properties shibHeaders = (Properties) getSessionAttribute(SHIB_HEADERS); // // if (shibHeaders != null) // { // String eppn = shibHeaders.getProperty("eppn"); // // if (!StringUtils.isNullOrEmpty(eppn, true)) // { // return shibHeaders; // } // } // // return null; return (Properties) getSessionAttribute(SHIB_HEADERS); } /** * Returns value of eppn header if present and not empty; null otherwise. * @return */ public String getIplantName () { Properties shibHeaders = getIplantHeaders(); return (shibHeaders == null)? null : shibHeaders.getProperty("eppn"); } @Override public String input () { logger.debug("BEGIN: input()::String"); String retVal = INPUT; // Get the value of variable "portal.enableIplant" in properties file. if (!iplantEnabled()) { retVal = INPUT; logger.warn("iplantLogin is NOT enabled on this server."); } else { logger.debug("iplantLogin is enabled on this server."); // Store the needed iplant request headers, if present, for use in the next action(s). logger.debug("Storing iPlant Headers ..."); storeIplantHeaders(); logger.debug("iPlant Headers stored."); // Clear the attribute that indicates that we've logged a user in via iplant. clearSessionAttribute(IPLANT_USER); logger.debug("iPlant user info in Session cleared."); // check to see if user is logged in via iplant, by checking request headers. String iplantName = getIplantName(); logger.debug(String.format("iPlantName (eppn) in Headers=[%s]", iplantName)); // If not, display the login page. if (iplantName == null) { retVal = INPUT; } else { logger.debug("Dumping HttpRequest Headers:"); logger.debug("=============================="); dumpRequestHeaders("iplantLogin"); logger.debug("=============================="); setSessionAttribute(IPLANT_USER, "1"); //reportUserMessage("Logged in via sso as iplant user: " + iplantName); User cipresUser = null; try { // Does iplant user already have a cipres account linked to his iplant id? cipresUser = getController().getCorrespondingCipresUser(iplantName); if (cipresUser != null) { logger.debug("This iPlant/CyVerse user has a linked CIPRES account."); retVal = cyverseToCipresLogin(cipresUser, iplantName); } else { logger.debug("This iPlant user doesn't have a linked CIPRES account."); super.reportUserError( "Starting Dec 1, 2020, CIPRES does not accept CyVerse credentials. " + "Please login with CIPRES credentials."); retVal = INPUT; // IPLANT_REGISTER; } } catch ( Exception e ) { logger.error(e.getMessage(), e); logger.error(reportUserError("Sorry, there was an internal error looking up your account information: " + e.getMessage())); retVal = INPUT; } } } logger.debug(String.format("RetVal=[%s]", retVal)); logger.debug("END: input()::String"); return retVal; } /** * @deprecated * * We get here after we've detected that the needed shibboleth headers are * present and that the user doesn't already have a CIPRES account linked * to his Shibboleth/CAS account. The iplantRegister.jsp form has him enter * his CIPRES username and password if he has a CIPRES account. If he leaves * them blank, we create a new CIPRES account for him. * * @return */ public String iplantRegister_v01 () { logger.debug("BEGIN: iplantRegister()::String"); String retVal = SUCCESS; String username = getUsername(); if (username == null || username.trim().isEmpty()) { logger.debug("User says he does NOT have a CIPRES account so a new one is being created."); try { // Create the cipres User table account, then create a sso record to link the iplant username to the ciprs account. User user = iplant2CipresAccount(); if (user == null) { retVal = INPUT; } else { SSO.addAccount(user, getIplantName()); // Now log the user in. retVal = cyverseToCipresLogin(user, getIplantName()); } } catch ( Exception e ) { logger.error(e.getMessage(), e); logger.error(reportUserError( "Sorry, there was an internal error linking your iplant and CIPRES accounts: " + e.getMessage())); logger.debug("SessionManager.iplantRegister returning " + INPUT); retVal = INPUT; } } else { logger.debug("User says he DOES have a CIPRES account so we're logging him in."); // User said he does have a CIPRES account, so make sure we can log // him in (i.e. don't let him hijack someone else's account). retVal = login(); if (retVal.equals(INPUT)) { // Invalid username and/or password, redisplay iplantRegister.jsp with error messages. logger.debug("Login failed."); logger.debug("SessionManager.iplantRegister returning " + INPUT); retVal = INPUT; } else { // OK, the user is logged in, we just need to update the db tables to link his iplant and cipres // user IDs. logger.debug("Login ok."); try { SSO.addAccount(getAuthenticatedUser(), getIplantName()); } catch ( Exception e ) { logger.error(e.getMessage(), e); logger.error(reportUserError("Sorry, there was an internal error linking your iplant and CIPRES accounts: " + e.getMessage())); logger.debug("SessionManager.iplantRegister returning " + INPUT); retVal = INPUT; } } } logger.debug(String.format("RetVal=[%s]", retVal)); logger.debug("END: iplantRegister()::String"); return retVal; } public String iplantRegister () { logger.debug("BEGIN: iplantRegister()::String"); String retVal = INPUT; super.reportUserError( "Starting Dec 1, 2020, CIPRES does not accept CyVerse credentials. " + "Please login with CIPRES credentials."); logger.debug(String.format("RetVal=[%s]", retVal)); logger.debug("END: iplantRegister()::String"); return retVal; } /** * This is exactly like the Register() method, except that its to * automatically create a CIPRES User account for someone who is already an * iplant user. * * @return * @throws java.security.NoSuchAlgorithmException */ public User iplant2CipresAccount () throws NoSuchAlgorithmException { logger.debug("BEGIN: iplant2CipresAccount()::User"); User user = null; Properties shibHeaders = getIplantHeaders(); if (shibHeaders == null) { logger.error(reportUserError("Sorry, there was an error creating your account: iplant Headers not found in session.")); } else { // else for first, last and institution. String eppn = shibHeaders.getProperty("eppn"); ValidationResult result = getController().registerUser( eppn, shibHeaders.getProperty("mail"), shibHeaders.getProperty("givenName"), shibHeaders.getProperty("sn"), "", "US", super.getHttpSession()); if (result == null) { addActionError("Sorry, there was an error creating your account."); } else if (!result.isValid()) { logger.error(reportUserError("Sorry, there was an error creating your account:")); for (String error : result.getErrors()) { logger.error(reportUserError(error)); } } else { setWorkbenchSession(getController().getWorkbenchSession()); addActionMessage(String.format("User account \"%s\" successfully created.", getUsername())); user = getAuthenticatedUser(); } } logger.debug("END: iplant2CipresAccount()::User"); return user; } private String cyverseToCipresLogin ( User user, String cyverseUsername ) { logger.debug("BEGIN: cyverseToCipresLogin(User, String)::String"); WorkbenchSession session = null; String retVal = INPUT; try { session = getController().loginWithCyVerseCredential( user.getUsername(), cyverseUsername, super.getHttpSession()); if (session == null) { logger.error(reportUserError("There was an error authenticating your information.")); retVal = INPUT; } if (finalizeLogin()) { retVal = SUCCESS; } } catch ( UserAuthenticationException error ) { logger.error(reportUserError(error.getMessage())); retVal = INPUT; } logger.debug(String.format("RetVal=[%s]", retVal)); logger.debug("END: cyverseToCipresLogin(User, String)::String"); return retVal; } /** * User clicked "Login via Iplant" on login.jsp. Because that goes to * "iplantLogin" action which is protected via Shibboleth the user was * redirected to iplant to login before this method was called. * Shibboleth headers should be present. * * @return */ public String cyverseLogin () { logger.debug("BEGIN: cyverseLogin()::String"); // TL: added 6_2017, working on CAS login. // logger.debug("Dumping HttpRequest Headers:"); // logger.debug("=============================="); // dumpRequestHeaders("iplantLogin"); // logger.debug("=============================="); //String retval = input(); String retval = INPUT; // Starting 2020-12-01, CIPRES does not support CyVerse login for 2 reasons: // 1. New version of Apache HTTPD is not compatible with current CASAuth module. // The new version of CASAuth is nowhere to be found. // 2. CIPRES introduces a new pay-per-use model for non-US users // starting 2021-01-01. super.reportUserError( "Starting Dec 1, 2020, CIPRES does not accept CyVerse credentials. " + "Please login with CIPRES credentials."); logger.debug(String.format("RetVal from input() = [%s]", retval)); logger.debug("END: cyverseLogin()::String"); return retval; } }