From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../idp/auth/AbstractAuthenticationManager.java | 341 +++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java') diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java new file mode 100644 index 00000000..e52a7884 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java @@ -0,0 +1,341 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.auth; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IRequestStorage; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.auth.IAuthenticationManager; +import at.gv.egiz.eaaf.core.api.idp.auth.ISSOManager; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.process.ProcessEngine; +import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EAAFSSOException; +import at.gv.egiz.eaaf.core.exceptions.NoPassivAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; +import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; +import at.gv.egiz.eaaf.core.impl.idp.auth.modules.ModuleRegistration; +import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; +import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl; +import at.gv.egiz.eaaf.core.impl.utils.TransactionIDUtils; + +public abstract class AbstractAuthenticationManager implements IAuthenticationManager { + private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationManager.class); + + private static List reqParameterWhiteListeForModules = new ArrayList(); + private static List reqHeaderWhiteListeForModules = new ArrayList(); + + public static final String MOA_SESSION = "MoaAuthenticationSession"; + public static final String MOA_AUTHENTICATED = "MoaAuthenticated"; + + public static final int SLOTIMEOUT = 30 * 1000; //30 sec + + @Autowired(required=true) protected IConfiguration authConfig; + @Autowired(required=true) private ProcessEngine processEngine; + @Autowired(required=true) private IRequestStorage requestStoreage; + @Autowired(required=true) protected IRevisionLogger revisionsLogger; + @Autowired(required=false) protected ISSOManager ssoManager; + + /* (non-Javadoc) + * @see at.gv.egiz.eaaf.core.impl.idp.auth.IAuthenticationManager#addParameterNameToWhiteList(java.lang.String) + */ + @Override + public final void addParameterNameToWhiteList(String httpReqParam) { + if (StringUtils.isNotEmpty(httpReqParam)) + reqParameterWhiteListeForModules.add(httpReqParam); + + } + + /* (non-Javadoc) + * @see at.gv.egiz.eaaf.core.impl.idp.auth.IAuthenticationManager#addHeaderNameToWhiteList(java.lang.String) + */ + @Override + public final void addHeaderNameToWhiteList(String httpReqParam) { + if (StringUtils.isNotEmpty(httpReqParam)) + reqHeaderWhiteListeForModules.add(httpReqParam.toLowerCase()); + + } + + /* (non-Javadoc) + * @see at.gv.egiz.eaaf.core.impl.idp.auth.IAuthenticationManager#addHeaderNameToWhiteList(java.lang.String) + */ + @Override + public final boolean doAuthentication(HttpServletRequest httpReq, HttpServletResponse httpResp, + IRequest pendingReq) throws EAAFException { + + if (!(pendingReq instanceof RequestImpl)) { + log.error("Requests that need authentication MUST be of type 'RequestImpl'"); + throw new RuntimeException("Requests that need authentication HAS TO BE of type 'RequestImpl'"); + + } + + //load OA configuration from pending request + ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + //set logging context and log unique OA identifier to revision log + TransactionIDUtils.setServiceProviderId(oaParam.getUniqueIdentifier()); + revisionsLogger.logEvent(pendingReq, EVENT_AUTHENTICATION_PROCESS_FOR_SP, pendingReq.getSPEntityId()); + + //generic authentication request validation + if (pendingReq.isPassiv() && pendingReq.forceAuth()) { + // conflict! + throw new NoPassivAuthenticationException(); + } + + + //check Single Sign-On functionality if SSOManager is available + boolean isValidSSOSession = false; + if (ssoManager != null) { + log.trace("SSOManager is loaded. Starting SSO session validation ... "); + //check if SSO is allowed for this service provider + ssoManager.isSSOAllowedForSP(pendingReq, httpReq); + + //check if SSO session is active and valid + isValidSSOSession = ssoManager.checkAndValidateSSOSession(pendingReq, httpReq, httpResp); + + } + + //check if session is already authenticated + //boolean isSessionAuthenticated = tryPerformAuthentication((RequestImpl) pendingReq, isValidSSOSession); + //boolean isSessionAuthenticated = isValidSSOSession && StringUtils.isNotEmpty(pendingReq.getSSOSessionIdentifier()); + + + //force new authentication authentication process + if (pendingReq.forceAuth()) { + startAuthenticationProcess(httpReq, httpResp, (RequestImpl) pendingReq); + return false; + + //perform SSO-Consents evaluation if it it required + } else if (isValidSSOSession && pendingReq.isNeedUserConsent()) { + sendSingleSignOnConsentsEvaluation(httpReq, httpResp, (RequestImpl) pendingReq); + return false; + + + } else if (pendingReq.isPassiv()) { + if (isValidSSOSession && + StringUtils.isNotEmpty(pendingReq.getSSOSessionIdentifier()) ) { + // Passive authentication ok! --> Populate pending request from SSO session + ssoManager.populatePendingRequestWithSSOInformation(pendingReq); + revisionsLogger.logEvent(pendingReq, EVENT_AUTHENTICATION_PROCESS_FINISHED); + return true; + + } else { + throw new NoPassivAuthenticationException(); + + } + + } else { + if (isValidSSOSession && + StringUtils.isNotEmpty(pendingReq.getSSOSessionIdentifier())) { + // Is authenticated .. proceed + ssoManager.populatePendingRequestWithSSOInformation(pendingReq); + revisionsLogger.logEvent(pendingReq, EVENT_AUTHENTICATION_PROCESS_FINISHED); + return true; + + } else { + // Start authentication! + startAuthenticationProcess(httpReq, httpResp, (RequestImpl) pendingReq); + return false; + + } + } + } + + public final void performOnlyIDPLogOut(HttpServletRequest request, HttpServletResponse response, IRequest pendingReq) { + + log.debug("Close session. Remove pending request ... "); + requestStoreage.removePendingRequest(pendingReq.getPendingRequestId()); + + + if (ssoManager != null) { + try { + log.trace("'SSOManager' active. Search for active SSO sessions ... "); + if (ssoManager.destroySSOSessionOnIDPOnly(request, response, pendingReq)) + log.info("SSO session successfully closed"); + else + log.info("Closing SSO session NOT successfully"); + + } catch (EAAFSSOException e) { + log.warn("Destroying of SSO session FAILED. Reason: " + e.getMessage(), e); + + } + + } + + } + + /** + * Populate process execution context and start process engine + * + * @param httpReq + * @param httpResp + * @param pendingReq + * @throws ServletException + * @throws IOException + * @throws EAAFException + */ + private void startAuthenticationProcess(HttpServletRequest httpReq, + HttpServletResponse httpResp, RequestImpl pendingReq) + throws EAAFException { + + log.info("Starting authentication ..."); + revisionsLogger.logEvent(pendingReq, EVENT_AUTHENTICATION_PROCESS_STARTED); + + //create authentication process execution context + ExecutionContext executionContext = new ExecutionContextImpl(); + + //set oaIdentifeir + executionContext.put(EAAFConstants.PROCESS_ENGINE_SERVICE_PROVIDER_ENTITYID, + pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); + + //add X509 SSL client certificate if exist + if (httpReq.getAttribute("javax.servlet.request.X509Certificate") != null) { + log.debug("Find SSL-client-certificate on request --> Add it to context"); + executionContext.put(EAAFConstants.PROCESS_ENGINE_SSL_CLIENT_CERTIFICATE, + ((X509Certificate[])httpReq.getAttribute("javax.servlet.request.X509Certificate"))); + pendingReq.setGenericDataToSession(EAAFConstants.PROCESS_ENGINE_SSL_CLIENT_CERTIFICATE, + ((X509Certificate[])httpReq.getAttribute("javax.servlet.request.X509Certificate"))); + + } + + //add additional http request parameter to context + if (!reqParameterWhiteListeForModules.isEmpty()) { + Enumeration reqParamNames = httpReq.getParameterNames(); + while(reqParamNames.hasMoreElements()) { + String paramName = reqParamNames.nextElement(); + if (StringUtils.isNotEmpty(paramName) && reqParameterWhiteListeForModules.contains(paramName) ) + executionContext.put(paramName, StringEscapeUtils.escapeHtml4(httpReq.getParameter(paramName))); + } + } + + //add additional http request parameter to context + if (!reqHeaderWhiteListeForModules.isEmpty()) { + Enumeration reqHeaderNames = httpReq.getHeaderNames(); + while(reqHeaderNames.hasMoreElements()) { + String paramName = reqHeaderNames.nextElement(); + if (StringUtils.isNotEmpty(paramName) && reqHeaderWhiteListeForModules.contains(paramName.toLowerCase()) ) + executionContext.put(paramName, StringEscapeUtils.escapeHtml4(httpReq.getHeader(paramName))); + + } + } + + //populate more IDP specific information to execution context + populateExecutionContext(executionContext, pendingReq, httpReq); + + //start process engine + startProcessEngine(pendingReq, executionContext); + + } + + /** + * + * + * @throws EAAFException + */ + abstract protected void populateExecutionContext(ExecutionContext executionContext, + RequestImpl pendingReq, HttpServletRequest httpReq) throws EAAFException; + + /** + * Starting a user consent evaluation + * + * @param request + * @param response + * @param pendingReq + * @throws ServletException + * @throws IOException + * @throws EAAFException + */ + private void sendSingleSignOnConsentsEvaluation(HttpServletRequest request, + HttpServletResponse response, RequestImpl pendingReq) + throws EAAFException { + + log.debug("Starting SSO user-consents evaluation ..."); + + //set authenticated flag to false, because user consents is required + pendingReq.setAuthenticated(false); + + //create execution context + ExecutionContext executionContext = new ExecutionContextImpl(); + executionContext.put(ISSOManager.PROCESS_ENGINE_SSO_CONSENTS_EVALUATION, true); + + //start process engine + startProcessEngine(pendingReq, executionContext); + + } + + + /** + * Select a specific process and starting process engine + * + * @param pendingReq + * @param executionContext + * @throws EAAFException + */ + private void startProcessEngine(RequestImpl pendingReq, ExecutionContext executionContext) throws EAAFException { + try { + //put pending-request ID on execurtionContext + executionContext.put(EAAFConstants.PROCESS_ENGINE_PENDINGREQUESTID, pendingReq.getPendingRequestId()); + + // create process instance + String processDefinitionId = ModuleRegistration.getInstance().selectProcess(executionContext); + + if (processDefinitionId == null) { + log.warn("No suitable process found for PendingReqId " + pendingReq.getPendingRequestId() ); + throw new EAAFException( + "process.02", + new Object[] {pendingReq.getPendingRequestId()} + ,"No suitable process found for PendingReqId " + pendingReq.getPendingRequestId()); + + } + + String processInstanceId = processEngine.createProcessInstance(processDefinitionId, executionContext); + + // keep process instance id in protocol pending-request + pendingReq.setProcessInstanceId(processInstanceId); + + //store pending-request + requestStoreage.storePendingRequest(pendingReq); + + // start process + processEngine.start(pendingReq); + + } catch (ProcessExecutionException e) { + Throwable cause = e.getCause(); + if (cause != null && cause instanceof TaskExecutionException) { + Throwable taskCause = cause.getCause(); + if (taskCause != null && taskCause instanceof EAAFException) { + EAAFException moaTaskCause = (EAAFException) taskCause; + log.warn(taskCause.getMessage(), taskCause); + throw moaTaskCause; + + } + } + + throw new EAAFException( + "process.01", + new Object[] { pendingReq.getProcessInstanceId(), pendingReq.getPendingRequestId() }, + "Authentication process execution FAILED", + e); + } + + } +} -- cgit v1.2.3