diff options
Diffstat (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth')
11 files changed, 3188 insertions, 3077 deletions
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 index 4cefcd8d..7a967d3f 100644 --- 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 @@ -1,29 +1,22 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.auth; import java.io.IOException; @@ -31,340 +24,371 @@ 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.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.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.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; +import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils; +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; public abstract class AbstractAuthenticationManager implements IAuthenticationManager { - private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationManager.class); - - private static List<String> reqParameterWhiteListeForModules = new ArrayList<String>(); - private static List<String> reqHeaderWhiteListeForModules = new ArrayList<String>(); - - 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 - final 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) && - pendingReq.needSingleSignOnFunctionality(); - - - } - - //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.getInternalSSOSessionIdentifier()) ) { - // 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.getInternalSSOSessionIdentifier())) { - // 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; - - } - } - } - - @Override - 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 (final 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 - final 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.setRawDataToTransaction(EAAFConstants.PROCESS_ENGINE_SSL_CLIENT_CERTIFICATE, - (httpReq.getAttribute("javax.servlet.request.X509Certificate"))); - - } - - //add additional http request parameter to context - if (!reqParameterWhiteListeForModules.isEmpty()) { - final Enumeration<String> reqParamNames = httpReq.getParameterNames(); - while(reqParamNames.hasMoreElements()) { - final 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()) { - final Enumeration<String> reqHeaderNames = httpReq.getHeaderNames(); - while(reqHeaderNames.hasMoreElements()) { - final String paramName = reqHeaderNames.nextElement(); - if (StringUtils.isNotEmpty(paramName) - && at.gv.egiz.eaaf.core.impl.utils.ArrayUtils.containsCaseInsensitive(paramName, reqHeaderWhiteListeForModules) - //reqHeaderWhiteListeForModules.contains(paramName.toLowerCase()) - ) - executionContext.put(paramName.toLowerCase(), 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 - final 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 - final String processDefinitionId = ModuleRegistration.getInstance().selectProcess(executionContext, pendingReq); - - if (processDefinitionId == null) { - log.warn("No suitable process found for PendingReqId " + pendingReq.getPendingRequestId() ); - throw new EAAFException( - "process.02", - new Object[] {pendingReq.getPendingRequestId()}); - - } - - final 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 (final ProcessExecutionException e) { - final Throwable cause = e.getCause(); - if (cause != null && cause instanceof TaskExecutionException) { - final Throwable taskCause = cause.getCause(); - if (taskCause != null && taskCause instanceof EAAFException) { - final EAAFException moaTaskCause = (EAAFException) taskCause; - log.warn(taskCause.getMessage(), taskCause); - throw moaTaskCause; - - } - } - - throw new EAAFException( - "process.01", - new Object[] { pendingReq.getProcessInstanceId(), pendingReq.getPendingRequestId() }, e); - } - - } + private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationManager.class); + + private static List<String> reqParameterWhiteListeForModules = new ArrayList<>(); + private static List<String> 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; + @Autowired ModuleRegistration moduleRegistration; + + /* + * (non-Javadoc) + * + * @see + * at.gv.egiz.eaaf.core.impl.idp.auth.IAuthenticationManager#addParameterNameToWhiteList(java.lang + * .String) + */ + @Override + public final void addParameterNameToWhiteList(final 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(final 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(final HttpServletRequest httpReq, + final HttpServletResponse httpResp, final 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 + final 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) + && pendingReq.needSingleSignOnFunctionality(); + + + } + + // 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.getInternalSsoSessionIdentifier())) { + // 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.getInternalSsoSessionIdentifier())) { + // 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; + + } + } + } + + @Override + public final void performOnlyIdpLogOut(final HttpServletRequest request, + final HttpServletResponse response, final 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 (final EaafSsoException e) { + log.warn("Destroying of SSO session FAILED. Reason: " + e.getMessage(), e); + + } + + } + + } + + /** + * Populate process execution context and start process engine. + * + * @param httpReq http request + * @param httpResp http response + * @param pendingReq current pending request + * @throws ServletException In case of a servlet error + * @throws IOException In case of an IO error + * @throws EaafException In case of EAAF processing error + */ + private void startAuthenticationProcess(final HttpServletRequest httpReq, + final HttpServletResponse httpResp, final RequestImpl pendingReq) throws EaafException { + + log.info("Starting authentication ..."); + revisionsLogger.logEvent(pendingReq, EVENT_AUTHENTICATION_PROCESS_STARTED); + + // create authentication process execution context + final 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.setRawDataToTransaction(EAAFConstants.PROCESS_ENGINE_SSL_CLIENT_CERTIFICATE, + (httpReq.getAttribute("javax.servlet.request.X509Certificate"))); + + } + + // add additional http request parameter to context + if (!reqParameterWhiteListeForModules.isEmpty()) { + final Enumeration<String> reqParamNames = httpReq.getParameterNames(); + while (reqParamNames.hasMoreElements()) { + final 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()) { + final Enumeration<String> reqHeaderNames = httpReq.getHeaderNames(); + while (reqHeaderNames.hasMoreElements()) { + final String paramName = reqHeaderNames.nextElement(); + if (StringUtils.isNotEmpty(paramName) && at.gv.egiz.eaaf.core.impl.utils.ArrayUtils + .containsCaseInsensitive(paramName, reqHeaderWhiteListeForModules) + // reqHeaderWhiteListeForModules.contains(paramName.toLowerCase()) + ) { + executionContext.put(paramName.toLowerCase(), + StringEscapeUtils.escapeHtml4(httpReq.getHeader(paramName))); + } + + } + } + + + + // populate more IDP specific information to execution context + populateExecutionContext(executionContext, pendingReq, httpReq); + + // start process engine + startProcessEngine(pendingReq, executionContext); + + } + + /** + * Add additional parameters into context of process-engine. + * + * @param executionContext Process-engine context + * @param pendingReq Current pending request + * @param httpReq http request + * + * @throws EaafException In case of an error + */ + protected abstract void populateExecutionContext(ExecutionContext executionContext, + RequestImpl pendingReq, HttpServletRequest httpReq) throws EaafException; + + /** + * Starting a user consent evaluation. + * + * @param request http request + * @param response http response + * @param pendingReq current pending request + * @throws ServletException In case of a servlet error + * @throws IOException In case of an IO error + * @throws EaafException In case of a EAAF processing error + */ + private void sendSingleSignOnConsentsEvaluation(final HttpServletRequest request, + final HttpServletResponse response, final 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 + final 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 current pending request + * @param executionContext current context for process-engine + * @throws EaafException In case of an process-engine error + */ + private void startProcessEngine(final RequestImpl pendingReq, + final ExecutionContext executionContext) throws EaafException { + try { + // put pending-request ID on execurtionContext + executionContext.put(EAAFConstants.PROCESS_ENGINE_PENDINGREQUESTID, + pendingReq.getPendingRequestId()); + + // create process instance + final String processDefinitionId = + moduleRegistration.selectProcess(executionContext, pendingReq); + + if (processDefinitionId == null) { + log.warn("No suitable process found for PendingReqId " + pendingReq.getPendingRequestId()); + throw new EaafException("process.02", new Object[] {pendingReq.getPendingRequestId()}); + + } + + final 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 (final ProcessExecutionException e) { + final Throwable cause = e.getCause(); + if (cause != null && cause instanceof TaskExecutionException) { + final Throwable taskCause = cause.getCause(); + if (taskCause != null && taskCause instanceof EaafException) { + final EaafException moaTaskCause = (EaafException) taskCause; + log.warn(taskCause.getMessage(), taskCause); + throw moaTaskCause; + + } + } + + throw new EaafException("process.01", + new Object[] {pendingReq.getProcessInstanceId(), pendingReq.getPendingRequestId()}, e); + } + + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/RequestStorage.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/RequestStorage.java index e1598b8f..1afa879f 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/RequestStorage.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/RequestStorage.java @@ -1,218 +1,224 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -package at.gv.egiz.eaaf.core.impl.idp.auth; + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. + */ -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; +package at.gv.egiz.eaaf.core.impl.idp.auth; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.IRequestStorage; -import at.gv.egiz.eaaf.core.api.idp.process.ProcessInstanceStoreDAO; +import at.gv.egiz.eaaf.core.api.idp.process.ProcessInstanceStoreDao; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; -import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; -import at.gv.egiz.eaaf.core.impl.utils.TransactionIDUtils; +import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; @Service("RequestStorage") -public class RequestStorage implements IRequestStorage{ - private static final Logger log = LoggerFactory.getLogger(RequestStorage.class); - - @Autowired(required=true) ITransactionStorage transactionStorage; - @Autowired(required=true) ProcessInstanceStoreDAO processInstanceStore; - @Autowired(required=true) IPendingRequestIdGenerationStrategy pendingReqIdGenerationStrategy; - - @Override - public IRequest getPendingRequest(String pendingReqID) throws PendingReqIdValidationException { - - try { - final String internalPendingReqId = - pendingReqIdGenerationStrategy.validateAndGetPendingRequestId(pendingReqID); - log.debug("PendingReqId is valid"); - - //get pending-request from storage - final IRequest pendingRequest = getInternalPendingRequest(internalPendingReqId); - - //set transactionID and sessionID to Logger - TransactionIDUtils.setAllLoggingVariables(pendingRequest); - - return pendingRequest; - - } catch (final PendingReqIdValidationException e) { - log.info("PendingRequestId is invalid. Reason: {} ", e.getMessage()); - - // search invalid pending-request for errorHandling - IRequest invalidPendingRequest = null; - try { - if (StringUtils.isNotEmpty(e.getInvalidInternalPendingReqId())) - invalidPendingRequest = transactionStorage.get(e.getInvalidInternalPendingReqId(), IRequest.class); - - } catch (final EAAFException e1) { - log.info("No PendingRequst found with pendingRequestID " + pendingReqID); - return null; - - } - - e.setInvalidPendingReq(invalidPendingRequest); - throw e; - - } catch (EAAFException | NullPointerException e) { - log.info("No PendingRequst found with pendingRequestID " + pendingReqID); - return null; - - } - } - - @Override - public void storePendingRequest(IRequest pendingRequest) throws EAAFException { - try { - if (pendingRequest instanceof IRequest) { - try { - //validate pending-requestId - final String internalPendingRequestId = pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(pendingRequest.getPendingRequestId()); - - //store pending request - transactionStorage.put(internalPendingRequestId, pendingRequest, -1); - - } catch (final PendingReqIdValidationException e) { - log.warn("Invalid pending-request-Id. Reason: {}", e.getMessage()); - log.warn("Do NOT store pending-request with invalid pending-request-Id. The process will break soon!"); - - } - - } else - throw new EAAFException("PendigRequest is NOT of type 'IRequest'", null); - - } catch (final EAAFException e) { - log.warn("PendingRequest with ID=" + pendingRequest.getPendingRequestId() + - " can not stored.", e); - throw new EAAFStorageException("PendingRequest with Id: " + pendingRequest.getPendingRequestId() - + " can not be stored", e); - - } - - } - - @Override - public void removePendingRequest(String pendingReqID) { - - if (pendingReqID != null) { - String internalPendingReqId = null; - try { - internalPendingReqId = pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(pendingReqID); - - } catch (final PendingReqIdValidationException e) { - internalPendingReqId = e.getInvalidInternalPendingReqId(); - - } - - try { - //remove process-management execution instance# - if (internalPendingReqId != null) { - final IRequest pendingReq = getInternalPendingRequest(internalPendingReqId); - if (pendingReq != null && - pendingReq.getProcessInstanceId() != null) - processInstanceStore.remove(pendingReq.getProcessInstanceId()); - - //remove pending-request - transactionStorage.remove(internalPendingReqId); - } - - } catch (final EAAFException e) { - log.warn("Removing process associated with pending-request:" + pendingReqID + " FAILED.", e); - - } - - } - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.storage.IRequestStorage#changePendingRequestID(at.gv.egovernment.moa.id.moduls.IRequest) - */ - @Override - public String changePendingRequestID(IRequest pendingRequest) throws EAAFException { - if (pendingRequest instanceof RequestImpl) { - - //get old internal pendingReqId - String oldInternalRequestID = null; - try { - oldInternalRequestID = - pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(pendingRequest.getPendingRequestId()); - - } catch (final PendingReqIdValidationException e) { - //it's no problem, because it must be valid before when pending-request was loaded and we change it now - oldInternalRequestID = e.getInvalidInternalPendingReqId(); - - } - - - //generate new pendingReqId and get internalPendingReqId - final String newRequestID = pendingReqIdGenerationStrategy.generateExternalPendingRequestId(); - log.debug("Change pendingRequestID from " + pendingRequest.getPendingRequestId() + " to " + newRequestID); - ((RequestImpl)pendingRequest).setPendingRequestId(newRequestID); - - String newInternalPendingRequestId = null; - try { - newInternalPendingRequestId = pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(newRequestID); - - } catch (final PendingReqIdValidationException e) { - throw new EAAFException("internal.99", new Object[]{"Generate invalid pendingRequestId. Something looks WRONG"}, e); - - } - - - //change Key in cache - transactionStorage.changeKey(oldInternalRequestID, newInternalPendingRequestId, pendingRequest); - - //only delete oldRequestID, no change. - return newRequestID; - - } else { - log.error("PendingRequest object is not of type 'RequestImpl.class'"); - throw new EAAFException("PendingRequest object is not of type 'RequestImpl.class'", null); - - } - - } - - private IRequest getInternalPendingRequest(String internalPendingReqId) throws EAAFException { - final IRequest pendingRequest = transactionStorage.get(internalPendingReqId, IRequest.class); - if (pendingRequest == null) { - log.info("No PendingRequst found with pendingRequestID " + internalPendingReqId); - return null; - - } - - return pendingRequest; - - } +public class RequestStorage implements IRequestStorage { + private static final Logger log = LoggerFactory.getLogger(RequestStorage.class); + + @Autowired(required = true) + ITransactionStorage transactionStorage; + @Autowired(required = true) + ProcessInstanceStoreDao processInstanceStore; + @Autowired(required = true) + IPendingRequestIdGenerationStrategy pendingReqIdGenerationStrategy; + + @Override + public IRequest getPendingRequest(final String pendingReqID) + throws PendingReqIdValidationException { + + try { + final String internalPendingReqId = + pendingReqIdGenerationStrategy.validateAndGetPendingRequestId(pendingReqID); + log.debug("PendingReqId is valid"); + + // get pending-request from storage + final IRequest pendingRequest = getInternalPendingRequest(internalPendingReqId); + + // set transactionID and sessionID to Logger + TransactionIdUtils.setAllLoggingVariables(pendingRequest); + + return pendingRequest; + + } catch (final PendingReqIdValidationException e) { + log.info("PendingRequestId is invalid. Reason: {} ", e.getMessage()); + + // search invalid pending-request for errorHandling + IRequest invalidPendingRequest = null; + try { + if (StringUtils.isNotEmpty(e.getInvalidInternalPendingReqId())) { + invalidPendingRequest = + transactionStorage.get(e.getInvalidInternalPendingReqId(), IRequest.class); + } + + } catch (final EaafException e1) { + log.info("No PendingRequst found with pendingRequestID " + pendingReqID); + return null; + + } + + e.setInvalidPendingReq(invalidPendingRequest); + throw e; + + } catch (EaafException | NullPointerException e) { + log.info("No PendingRequst found with pendingRequestID " + pendingReqID); + return null; + + } + } + + @Override + public void storePendingRequest(final IRequest pendingRequest) throws EaafException { + try { + // validate pending-requestId + final String internalPendingRequestId = pendingReqIdGenerationStrategy + .getPendingRequestIdWithOutChecks(pendingRequest.getPendingRequestId()); + + // store pending request + transactionStorage.put(internalPendingRequestId, pendingRequest, -1); + + } catch (final PendingReqIdValidationException e) { + log.warn("Invalid pending-request-Id. Reason: {}", e.getMessage()); + log.warn( + "Do NOT store pending-request with invalid pending-request-Id. The process will break soon!"); + + } catch (final EaafException e) { + log.warn( + "PendingRequest with ID=" + pendingRequest.getPendingRequestId() + " can not stored.", e); + throw new EaafStorageException( + "PendingRequest with Id: " + pendingRequest.getPendingRequestId() + " can not be stored", + e); + + } + + } + + @Override + public void removePendingRequest(final String pendingReqID) { + + if (pendingReqID != null) { + String internalPendingReqId = null; + try { + internalPendingReqId = + pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(pendingReqID); + + } catch (final PendingReqIdValidationException e) { + internalPendingReqId = e.getInvalidInternalPendingReqId(); + + } + + try { + // remove process-management execution instance# + if (internalPendingReqId != null) { + final IRequest pendingReq = getInternalPendingRequest(internalPendingReqId); + if (pendingReq != null && pendingReq.getProcessInstanceId() != null) { + processInstanceStore.remove(pendingReq.getProcessInstanceId()); + } + + // remove pending-request + transactionStorage.remove(internalPendingReqId); + } + + } catch (final EaafException e) { + log.warn("Removing process associated with pending-request:" + pendingReqID + " FAILED.", + e); + + } + + } + } + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.storage.IRequestStorage#changePendingRequestID(at.gv.egovernment.moa. + * id.moduls.IRequest) + */ + @Override + public String changePendingRequestID(final IRequest pendingRequest) throws EaafException { + if (pendingRequest instanceof RequestImpl) { + + // get old internal pendingReqId + String oldInternalRequestID = null; + try { + oldInternalRequestID = pendingReqIdGenerationStrategy + .getPendingRequestIdWithOutChecks(pendingRequest.getPendingRequestId()); + + } catch (final PendingReqIdValidationException e) { + // it's no problem, because it must be valid before when pending-request was loaded and we + // change it now + oldInternalRequestID = e.getInvalidInternalPendingReqId(); + + } + + + // generate new pendingReqId and get internalPendingReqId + final String newRequestID = pendingReqIdGenerationStrategy.generateExternalPendingRequestId(); + log.debug("Change pendingRequestID from " + pendingRequest.getPendingRequestId() + " to " + + newRequestID); + ((RequestImpl) pendingRequest).setPendingRequestId(newRequestID); + + String newInternalPendingRequestId = null; + try { + newInternalPendingRequestId = + pendingReqIdGenerationStrategy.getPendingRequestIdWithOutChecks(newRequestID); + + } catch (final PendingReqIdValidationException e) { + throw new EaafException("internal.99", + new Object[] {"Generate invalid pendingRequestId. Something looks WRONG"}, e); + + } + + + // change Key in cache + transactionStorage.changeKey(oldInternalRequestID, newInternalPendingRequestId, + pendingRequest); + + // only delete oldRequestID, no change. + return newRequestID; + + } else { + log.error("PendingRequest object is not of type 'RequestImpl.class'"); + throw new EaafException("PendingRequest object is not of type 'RequestImpl.class'", null); + + } + + } + + private IRequest getInternalPendingRequest(final String internalPendingReqId) + throws EaafException { + final IRequest pendingRequest = transactionStorage.get(internalPendingReqId, IRequest.class); + if (pendingRequest == null) { + log.info("No PendingRequst found with pendingRequestID " + internalPendingReqId); + return null; + + } + + return pendingRequest; + + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java index 2108e041..491fdf4a 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java @@ -1,29 +1,22 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.auth.builder; import java.io.ByteArrayInputStream; @@ -32,18 +25,6 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Map.Entry; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.lang.NonNull; -import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; -import org.w3c.dom.DOMException; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.data.ExtendedPVPAttributeDefinitions; @@ -51,674 +32,748 @@ import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions; import at.gv.egiz.eaaf.core.api.idp.IAuthData; import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder; import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP; -import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.IspConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; -import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; -import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException; -import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; -import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.exceptions.EAAFParserException; -import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; +import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafParserException; +import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import at.gv.egiz.eaaf.core.exceptions.XPathException; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser; import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; - +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + public abstract class AbstractAuthenticationDataBuilder implements IAuthenticationDataBuilder { - private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationDataBuilder.class); - - /** - * Identify authProcessData that should be directly mapped into authData - */ - public static final String GENERIC_AUTHDATA_IDENTIFIER = "authData_"; - - public static final String CONFIG_PROP_ENABLE_IDL_ATTRIBUTE_ESCAPEING = "configuration.bugfix.enable.idl.escaping"; - - protected Collection<String> includedToGenericAuthData = null; - @Autowired protected IConfigurationWithSP basicConfig; - - @Override - public IAuthData buildAuthenticationData(IRequest pendingReq) throws EAAFAuthenticationException { - IAuthData authData = null; - final IAuthProcessDataContainer authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); - - try { - if (authProcessData.isEIDProcess()) { - log.debug("Building AuthData from new E-ID information ... "); - authData = getAuthDataInstance(pendingReq); - Assert.notNull(authData, "AuthData is null"); - - log.trace("Adding generic AuthData information ... "); - buildInternalAuthDataGeneric(authData, authProcessData, pendingReq); - - log.trace("Build service-specific AuthData information ... "); - buildServiceSpecificAuthenticationData(authData, pendingReq); - - } else { - log.info("User authentication uses the deprecated. Building AuthData from deprecated information ... "); - authData = buildDeprecatedAuthData(pendingReq); - Assert.notNull(authData, "AuthData is null"); - - } - - } catch ( final EAAFAuthenticationException e) { - throw e; - - } catch (XPathException | DOMException | EAAFException e) { - log.warn("Can not build authentication data from auth. process information"); - throw new EAAFAuthenticationException("builder.11", new Object[]{e.getMessage()}, e); - - } - - log.trace("AuthData generation finished"); - return authData; - - } - - /** - * * @param pendingReq current pendingRequest - * - * @param pendingReq current pendingRequest - * @return {@link IAuthData} but never <code>null</code> - * @throws EAAFException - */ - @NonNull - abstract protected IAuthData getAuthDataInstance(IRequest pendingReq) throws EAAFException; - - /** - * Build service-specific AuthData by using information from E-ID - * This builder uses vSZ, MDS and Consent as input information - * - * @param pendingReq current pendingRequest - * @return {@link IAuthData} but never <code>null</code> - * @throws EAAFException - */ - abstract protected void buildServiceSpecificAuthenticationData(IAuthData authData, IRequest pendingReq) throws EAAFException; - - - /** - * Add generic E-ID information into already existing AuthData - * - * @param authData - * @param authProcessData - * @param pendingReq - */ - private void buildInternalAuthDataGeneric(@NonNull IAuthData authData, - @NonNull IAuthProcessDataContainer authProcessData, @NonNull IRequest pendingReq) { - Assert.notNull(pendingReq, "PendingRequest is null"); - Assert.notNull(authData, "AuthData is null"); - Assert.notNull(authProcessData, "AuthProcessData is null"); - - if (!(authData instanceof AuthenticationData)) { - log.error("AuthData has no suitable type! Requires: {}", AuthenticationData.class.getName()); - throw new RuntimeException("AuthData has no suitable type! Requires: " + AuthenticationData.class.getName()); - - } - - final AuthenticationData internalAuthData = (AuthenticationData)authData; - - //TODO: check if it is needed -// if (authProcessData.getGenericSessionDataStorage() != null && -// !authProcessData.getGenericSessionDataStorage().isEmpty()) -// includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet(); -// else - includedToGenericAuthData = new ArrayList<String>(); - - //#################################################### - //set general authData info's - internalAuthData.setAuthenticationIssuer(pendingReq.getAuthURL()); - internalAuthData.setSsoSession(pendingReq.needSingleSignOnFunctionality()); - internalAuthData.setBaseIDTransferRestrication(pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction()); - - //#################################################### - //set MDS and vSZ - internalAuthData.setFamilyName(authProcessData.getGenericDataFromSession(ExtendedPVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class)); - internalAuthData.setGivenName(authProcessData.getGenericDataFromSession(ExtendedPVPAttributeDefinitions.GIVEN_NAME_NAME, String.class)); - internalAuthData.setDateOfBirth(authProcessData.getGenericDataFromSession(ExtendedPVPAttributeDefinitions.BIRTHDATE_NAME, String.class)); - internalAuthData.setEncSourceId(authProcessData.getGenericDataFromSession(ExtendedPVPAttributeDefinitions.EID_ENCRYPTED_SOURCEID_NAME, String.class)); - internalAuthData.setEncSourceIdType(authProcessData.getGenericDataFromSession(ExtendedPVPAttributeDefinitions.EID_ENCRYPTED_SOURCEID_TYPE_NAME, String.class)); - - //#################################################### - //set QAA level - setQAALevel(internalAuthData, authProcessData, pendingReq); - - - //#################################################### - //set isForeigner flag - setFlagForeigner(internalAuthData, authProcessData, pendingReq); - - - //#################################################### - //set citizen country-code - setCitizenCountryCode(internalAuthData, authProcessData, pendingReq); - - - //set generic authProcessData to authdata - for (final Entry<String, Object> el : authProcessData.getGenericSessionDataStorage().entrySet()) { - if (el.getKey().startsWith(GENERIC_AUTHDATA_IDENTIFIER)) { - log.trace("Find generic authProcessData {}. Map it directly to authData", el.getKey()); - try { - internalAuthData.setGenericData(el.getKey(), el.getValue()); - - } catch (final EAAFStorageException e) { - log.warn("Can NOT set authData with key: {}", el.getKey(), null, e); - - } - - } - - } - - - } - - /** - * Parse citzen country-code into AuthData - * - * @param internalAuthData - * @param authProcessData - * @param pendingReq - */ - private void setCitizenCountryCode(AuthenticationData authData, IAuthProcessDataContainer authProcessData, - IRequest pendingReq) { - includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME); - final String pvpCCCAttr = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME, String.class); - if (StringUtils.isNotEmpty(pvpCCCAttr)) { - authData.setCiticenCountryCode(pvpCCCAttr); - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_ISSUING_NATION_FRIENDLY_NAME); - - } else { - if (authData.isForeigner()) { - //TODO!!!! - - } else { - authData.setCiticenCountryCode(basicConfig.getBasicConfiguration( - IConfigurationWithSP.CONFIG_PROPS_AUTH_DEFAULT_COUNTRYCODE, - EAAFConstants.COUNTRYCODE_AUSTRIA)); - - } - } - - } - - /** - * parse QAA Level into AuthData - * - * @param authData - * @param authProcessData - * @param pendingReq - */ - private void setQAALevel(@NonNull AuthenticationData authData, - @NonNull IAuthProcessDataContainer authProcessData, @NonNull IRequest pendingReq) { - includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME); - String currentLoA = null; - if (StringUtils.isNotEmpty(authProcessData.getQAALevel())) - currentLoA = authProcessData.getQAALevel(); - else { - currentLoA = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME, String.class); - if (StringUtils.isNotEmpty(currentLoA)) { - log.debug("Find PVP-Attr '" + PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_FRIENDLY_NAME + "':" + currentLoA - + " --> Parse QAA-Level from that attribute."); - - } - } - if (StringUtils.isNotEmpty(currentLoA)) { - if (currentLoA.startsWith(EAAFConstants.EIDAS_LOA_PREFIX)) { - authData.seteIDASLoA(currentLoA); - - } else - log.info("Only eIDAS LoAs are supported by this implementation"); - - } else { - log.info("No QAA level found. Set to default level " + EAAFConstants.EIDAS_LOA_LOW); - authData.seteIDASLoA(EAAFConstants.EIDAS_LOA_LOW); - - } - - } - - /** - * Parse Foreigner information into AuthData - * - * @param authData - * @param authProcessData - * @param pendingReq - */ - private void setFlagForeigner(AuthenticationData authData, IAuthProcessDataContainer authProcessData, IRequest pendingReq) { - //TODO: change to new eIDAS-token attribute identifier - if (authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_STORK_TOKEN_NAME) != null) { - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_STORK_TOKEN_FRIENDLY_NAME - + " --> Set 'isForeigner' flag to TRUE"); - authData.setForeigner(true); - - } else { - authData.setForeigner(authProcessData.isForeigner()); - - } - } - - /** - * Build authentication data by using information from citizen-card or mobile-phone signature - * This builder uses IdentityLink, AuthBlock, full MIS mandate as input information - * - * @param pendingReq current pendingRequest - * @return {@link IAuthData} but never <code>null</code> - * @throws EAAFException - */ - @Deprecated - @NonNull - abstract protected IAuthData buildDeprecatedAuthData(IRequest pendingReq) throws EAAFException; - - @Deprecated - protected void generateDeprecatedBasicAuthData(AuthenticationData authData, IRequest pendingReq, - IAuthProcessDataContainer authProcessData) throws EAAFBuilderException, EAAFConfigurationException, XPathException, DOMException, EAAFParserException { - - if (authProcessData.getGenericSessionDataStorage() != null && - !authProcessData.getGenericSessionDataStorage().isEmpty()) - includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet(); - else - includedToGenericAuthData = new ArrayList<String>(); - - //#################################################### - //set general authData info's - authData.setAuthenticationIssuer(pendingReq.getAuthURL()); - authData.setSsoSession(pendingReq.needSingleSignOnFunctionality()); - authData.setBaseIDTransferRestrication(pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction()); - - - //#################################################### - //parse user info's from identityLink - IIdentityLink idlFromPVPAttr = null; - final IIdentityLink identityLink = authProcessData.getIdentityLink(); - if (identityLink != null) { - parseBasicUserInfosFromIDL(authData, identityLink, includedToGenericAuthData); - - } else { - // identityLink is not direct in MOASession - final String pvpAttrIDL = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME, String.class); - //find PVP-Attr. which contains the IdentityLink - if (StringUtils.isNotEmpty(pvpAttrIDL)) { - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_IDENTITY_LINK_FRIENDLY_NAME - + " --> Parse basic user info's from that attribute."); - InputStream idlStream = null; - try { - idlStream = new ByteArrayInputStream(Base64Utils.decodeFromString(pvpAttrIDL)); - idlFromPVPAttr = new SimpleIdentityLinkAssertionParser(idlStream).parseIdentityLink(); - parseBasicUserInfosFromIDL(authData, idlFromPVPAttr, includedToGenericAuthData); - - //set identitylink into AuthProcessData - authProcessData.setIdentityLink(idlFromPVPAttr);; - - } catch (final EAAFParserException e) { - log.warn("Received IdentityLink is not valid", e); - - } catch (final Exception e) { - log.warn("Received IdentityLink is not valid", e); - - } finally { - try { - includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME); - if (idlStream != null) - idlStream.close(); - - } catch (final IOException e) { - log.warn("Close InputStream FAILED.", e); - - } - } - } - - //if no basic user info's are set yet, parse info's single PVP-Attributes - if (StringUtils.isEmpty(authData.getFamilyName())) { - log.debug("No IdentityLink found or not parseable --> Parse basic user info's from single PVP-Attributes."); - authData.setFamilyName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class)); - authData.setGivenName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.GIVEN_NAME_NAME, String.class)); - authData.setDateOfBirth(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.BIRTHDATE_NAME, String.class)); - authData.setIdentificationValue(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME, String.class)); - authData.setIdentificationType(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME, String.class)); - - //remove corresponding keys from genericSessionData if exists - includedToGenericAuthData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); - includedToGenericAuthData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); - includedToGenericAuthData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); - includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); - includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); - } - - } - - if (authData.getIdentificationType() != null && - !authData.getIdentificationType().equals(EAAFConstants.URN_PREFIX_BASEID)) { - log.trace("IdentificationType is not a baseID --> clear it. "); - authData.setBPK(authData.getIdentificationValue()); - authData.setBPKType(authData.getIdentificationType()); - - authData.setIdentificationValue(null); - authData.setIdentificationType(null); - } - - - //#################################################### - //set QAA level - setQAALevel(authData, authProcessData, pendingReq); - - - //#################################################### - //set isForeigner flag - setFlagForeigner(authData, authProcessData, pendingReq); - - - //#################################################### - //set citizen country-code - setCitizenCountryCode(authData, authProcessData, pendingReq); - - - //#################################################### - // set bPK and IdentityLink - final String pvpbPKValue = getbPKValueFromPVPAttribute(authProcessData); - final String pvpbPKTypeAttr = getbPKTypeFromPVPAttribute(authProcessData); - final Pair<String, String> pvpEncbPKAttr = getEncryptedbPKFromPVPAttribute(authProcessData, authData, pendingReq.getServiceProviderConfiguration()); - - //check if a unique ID for this citizen exists - if (StringUtils.isEmpty(authData.getIdentificationValue()) && - StringUtils.isEmpty(pvpbPKValue) && StringUtils.isEmpty(authData.getBPK()) && - pvpEncbPKAttr == null) { - log.info("Can not build authData, because moaSession include no bPK, encrypted bPK or baseID"); - throw new EAAFBuilderException("builder.08", new Object[]{"No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME}, - "No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); - - } - - //check if bPK already added to AuthData matches OA - if (StringUtils.isNotEmpty(authData.getBPK()) - && matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), authData.getBPKType()) ) { - log.debug("Correct bPK is already included in AuthData."); - - //check if bPK received by PVP-Attribute matches OA - } else if (StringUtils.isNotEmpty(pvpbPKValue) && - matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), pvpbPKTypeAttr)) { - log.debug("Receive correct bPK from PVP-Attribute"); - authData.setBPK(pvpbPKValue); - authData.setBPKType(pvpbPKTypeAttr); - - // baseID is in AuthSesson --> calculate bPK directly - } else if (StringUtils.isNotEmpty(authData.getIdentificationValue())) { - log.debug("Citizen baseID is in MOASession --> calculate bPK from this."); - final Pair<String, String> result = buildOAspecificbPK(pendingReq, authData); - authData.setBPK(result.getFirst()); - authData.setBPKType(result.getSecond()); - - //check if decrypted bPK exists - } else if (pvpEncbPKAttr != null) { - log.debug("Receive bPK as encrypted bPK and decryption was possible."); - authData.setBPK(pvpEncbPKAttr.getFirst()); - authData.setBPKType(pvpEncbPKAttr.getSecond()); - - //ask SZR to get bPK - } else { - String notValidbPK = authData.getBPK(); - String notValidbPKType = authData.getBPKType(); - if (StringUtils.isEmpty(notValidbPK) && - StringUtils.isEmpty(notValidbPKType)) { - notValidbPK = pvpbPKValue; - notValidbPKType = pvpbPKTypeAttr; - - if (StringUtils.isEmpty(notValidbPK) && - StringUtils.isEmpty(notValidbPKType)) { - log.error("No bPK in MOASession. THIS error should not occur any more."); - throw new NullPointerException("No bPK in MOASession. THIS error should not occur any more."); - } - } - - final Pair<String, String> baseIDFromSZR = getbaseIDFromSZR(authData, notValidbPK, notValidbPKType); - if (baseIDFromSZR != null) { - log.info("Receive citizen baseID from SRZ. Authentication can be completed"); - authData.setIdentificationValue(baseIDFromSZR.getFirst()); - authData.setIdentificationType(baseIDFromSZR.getSecond()); - final Pair<String, String> result = buildOAspecificbPK(pendingReq, authData); - authData.setBPK(result.getFirst()); - authData.setBPKType(result.getSecond()); - - } else { - log.warn("Can not build authData, because moaSession include no valid bPK, encrypted bPK or sourceID"); - throw new EAAFBuilderException("builder.13", new Object[]{pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()}, - "No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME - + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); - - } - } - - //build IdentityLink - if (authProcessData.getIdentityLink() != null) - authData.setIdentityLink(buildOAspecificIdentityLink( - pendingReq.getServiceProviderConfiguration(), - authProcessData.getIdentityLink(), - authData.getBPK(), - authData.getBPKType())); - else - log.info("Can NOT set IdentityLink. Msg: No IdentityLink found"); - - } - - //extract a encrypted bPK from PVP attrobute - @Deprecated - protected abstract Pair<String, String> getEncryptedbPKFromPVPAttribute(IAuthProcessDataContainer authProcessDataContainer, - AuthenticationData authData, ISPConfiguration spConfig) throws EAAFBuilderException; - - //request baseId from SRZ - @Deprecated - protected abstract Pair<String, String> getbaseIDFromSZR(AuthenticationData authData, String notValidbPK, - String notValidbPKType); - - @Deprecated - protected Pair<String, String> buildOAspecificbPK(IRequest pendingReq, AuthenticationData authData) throws EAAFBuilderException { - final ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); - - final String baseID = authData.getIdentificationValue(); - final String baseIDType = authData.getIdentificationType(); - Pair<String, String> sectorSpecId = null; - - if (EAAFConstants.URN_PREFIX_BASEID.equals(baseIDType)) { - //SAML1 legacy target parameter work-around - final String spTargetId = oaParam.getAreaSpecificTargetIdentifier(); - log.debug("Use OA target identifier '" + spTargetId + "' from configuration"); - - //calculate sector specific unique identifier - sectorSpecId = new BPKBuilder().generateAreaSpecificPersonIdentifier(baseID, spTargetId); - - } else { - log.error("!!!baseID-element does not include a baseID. This should not be happen any more!!!"); - sectorSpecId = Pair.newInstance(baseID, baseIDType); - - } - - log.trace("Authenticate user with bPK:" + sectorSpecId.getFirst() + " Type:" + sectorSpecId.getSecond()); - return sectorSpecId; - - } - - @Deprecated - protected IIdentityLink buildOAspecificIdentityLink(ISPConfiguration spConfig, IIdentityLink idl, String bPK, String bPKType) throws EAAFConfigurationException, XPathException, DOMException, EAAFParserException { - if (spConfig.hasBaseIdTransferRestriction()) { - log.debug("SP: " + spConfig.getUniqueIdentifier() + " has baseId transfer restriction. Remove baseId from IDL ..."); - final Element idlassertion = idl.getSamlAssertion(); - //set bpk/wpbk; - final Node prIdentification = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); - prIdentification.getFirstChild().setNodeValue(bPK); - //set bkp/wpbk type - final Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH); - prIdentificationType.getFirstChild().setNodeValue(bPKType); - - final SimpleIdentityLinkAssertionParser idlparser = new SimpleIdentityLinkAssertionParser(idlassertion); - return idlparser.parseIdentityLink(); - - } else - return idl; - - } - - /** - * Check a bPK-Type against a Service-Provider configuration <br> - * If bPK-Type is <code>null</code> the result is <code>false</code>. - * - * @param oaParam Service-Provider configuration, never null - * @param bPKType bPK-Type to check - * @return true, if bPK-Type matchs to Service-Provider configuration, otherwise false - */ - @Deprecated - protected boolean matchsReceivedbPKToOnlineApplication(ISPConfiguration oaParam, String bPKType) { - return oaParam.getAreaSpecificTargetIdentifier().equals(bPKType); - - } - - /** - * Parse information from an IdentityLink into AuthData object - * - * @param authData - * @param identityLink - * @param includedGenericSessionData - */ - @Deprecated - private void parseBasicUserInfosFromIDL(AuthenticationData authData, IIdentityLink identityLink, Collection<String> includedGenericSessionData) { - authData.setIdentificationValue(identityLink.getIdentificationValue()); - authData.setIdentificationType(identityLink.getIdentificationType()); - - /* GivenNames and FamilyNames with simple Apostrophe were escaped with ' - * in IdentityLinkParser since 5 years. This feature was bug-fix for an SL1.0 AuthBlock problem. - * However, the authentication attributes (SAML2, eIDAS, OpenID-Connect) also includes this escaped values, - * but there it is not neccesary. We fix this problem in 3.4.3, but the fix can be deactivated - * for dependency reasons. - */ - if (basicConfig.getBasicConfigurationBoolean(CONFIG_PROP_ENABLE_IDL_ATTRIBUTE_ESCAPEING, false)) { - authData.setGivenName(identityLink.getGivenName().replaceAll("'", "'")); - authData.setFamilyName(identityLink.getFamilyName().replaceAll("'", "'")); - - } else { - authData.setGivenName(identityLink.getGivenName()); - authData.setFamilyName(identityLink.getFamilyName()); - - } - - authData.setDateOfBirth(identityLink.getDateOfBirth()); - - - //remove corresponding keys from genericSessionData if exists - includedGenericSessionData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); - includedGenericSessionData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); - includedGenericSessionData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); - includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); - includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); - - } - - /** - * Get bPK from PVP Attribute 'BPK_NAME', which could be exist in - * MOASession as 'GenericData' <br> <pre><code>session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class)</code></pre> - * - * @param session MOASession, but never null - * @return bPK, which was received by PVP-Attribute, or <code>null</code> if no attribute exists - */ - @Deprecated - private String getbPKValueFromPVPAttribute(IAuthProcessDataContainer session) { - String pvpbPKValueAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.BPK_NAME, String.class); - if (StringUtils.isNotEmpty(pvpbPKValueAttr)) { - - //fix a wrong bPK-value prefix, which was used in some PVP Standardportal implementations - if (pvpbPKValueAttr.startsWith("bPK:")) { - log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME - + " contains a not standardize prefix! Staring attribute value correction process ..."); - pvpbPKValueAttr = pvpbPKValueAttr.substring("bPK:".length()); - - } - - final String[] spitted = pvpbPKValueAttr.split(":"); - if (spitted.length == 2) { - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME); - return spitted[1]; - - - - } else if (spitted.length > 2) { - log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + " has a wrong encoding and can NOT be USED!" - + " Value:" + pvpbPKValueAttr); - return null; - - } else { - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + " without prefix. Use it as it is"); - return spitted[0]; - - } - - } - - return null; - } - - /** - * Get bPK-Type from PVP Attribute 'EID_SECTOR_FOR_IDENTIFIER_NAME', which could be exist in - * MOASession as 'GenericData' <br> <pre><code>session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class)</code></pre> - * - * @param session MOASession, but never null - * @return bPKType, which was received by PVP-Attribute, or <code>null</code> if no attribute exists - */ - @Deprecated - private String getbPKTypeFromPVPAttribute(IAuthProcessDataContainer session) { - final String pvpbPKTypeAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class); - - if (StringUtils.isNotEmpty(pvpbPKTypeAttr)) { -// //fix a wrong bPK-Type encoding, which was used in some PVP Standardportal implementations -// if (pvpbPKTypeAttr.startsWith(EAAFConstants.URN_PREFIX_CDID) && -// !pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length(), -// EAAFConstants.URN_PREFIX_CDID.length() + 1).equals("+")) { -// log.warn("Receive uncorrect encoded bBKType attribute " + pvpbPKTypeAttr + " Starting attribute value correction ... "); -// pvpbPKTypeAttr = EAAFConstants.URN_PREFIX_CDID + "+" + pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length() + 1); -// -// } - log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME); - return pvpbPKTypeAttr; - } - - return null; - - - /* - * INFO: This code could be used to extract the bPKType from 'PVPConstants.BPK_NAME', - * because the prefix of BPK_NAME attribute contains the postfix of the bPKType - * - * Now, all PVP Standardportals should be able to send 'EID_SECTOR_FOR_IDENTIFIER' - * PVP attributes - */ -// String pvpbPKValueAttr = session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class); -// String[] spitted = pvpbPKValueAttr.split(":"); -// if (MiscUtil.isEmpty(authData.getBPKType())) { -// Logger.debug("PVP assertion contains NO bPK/wbPK target attribute. " + -// "Starting target extraction from bPK/wbPK prefix ..."); -// //exract bPK/wbPK type from bpk attribute value prefix if type is -// //not transmitted as single attribute -// Pattern pattern = Pattern.compile("[a-zA-Z]{2}(-[a-zA-Z]+)?"); -// Matcher matcher = pattern.matcher(spitted[0]); -// if (matcher.matches()) { -// //find public service bPK -// authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + spitted[0]); -// Logger.debug("Found bPK prefix. Set target to " + authData.getBPKType()); -// -// } else { -// //find business service wbPK -// authData.setBPKType(Constants.URN_PREFIX_WBPK+ "+" + spitted[0]); -// Logger.debug("Found wbPK prefix. Set target to " + authData.getBPKType()); -// -// } -// } - - } + private static final Logger log = + LoggerFactory.getLogger(AbstractAuthenticationDataBuilder.class); + + /** + * Identify authProcessData that should be directly mapped into authData. + */ + public static final String GENERIC_AUTHDATA_IDENTIFIER = "authData_"; + + public static final String CONFIG_PROP_ENABLE_IDL_ATTRIBUTE_ESCAPEING = + "configuration.bugfix.enable.idl.escaping"; + + protected Collection<String> includedToGenericAuthData = null; + @Autowired + protected IConfigurationWithSP basicConfig; + + @Override + public IAuthData buildAuthenticationData(final IRequest pendingReq) + throws EaafAuthenticationException { + IAuthData authData = null; + final IAuthProcessDataContainer authProcessData = + pendingReq.getSessionData(AuthProcessDataWrapper.class); + + try { + if (authProcessData.isEIDProcess()) { + log.debug("Building AuthData from new E-ID information ... "); + authData = getAuthDataInstance(pendingReq); + Assert.notNull(authData, "AuthData is null"); + + log.trace("Adding generic AuthData information ... "); + buildInternalAuthDataGeneric(authData, authProcessData, pendingReq); + + log.trace("Build service-specific AuthData information ... "); + buildServiceSpecificAuthenticationData(authData, pendingReq); + + } else { + log.info( + "User authentication uses the deprecated. Building AuthData from deprecated information ... "); + authData = buildDeprecatedAuthData(pendingReq); + Assert.notNull(authData, "AuthData is null"); + + } + + } catch (final EaafAuthenticationException e) { + throw e; + + } catch (XPathException | DOMException | EaafException e) { + log.warn("Can not build authentication data from auth. process information"); + throw new EaafAuthenticationException("builder.11", new Object[] {e.getMessage()}, e); + + } + + log.trace("AuthData generation finished"); + return authData; + + } + + /** + * * @param pendingReq current pendingRequest. + * + * @param pendingReq current pendingRequest + * @return {@link IAuthData} but never <code>null</code> + * @throws EaafException In case of an error + */ + @NonNull + protected abstract IAuthData getAuthDataInstance(IRequest pendingReq) throws EaafException; + + /** + * Build service-specific AuthData by using information from E-ID This builder uses vSZ, MDS and + * Consent as input information. + * + * @param pendingReq current pendingRequest + * @return {@link IAuthData} but never <code>null</code> + * @throws EaafException In case of an error + */ + protected abstract void buildServiceSpecificAuthenticationData(IAuthData authData, + IRequest pendingReq) throws EaafException; + + + /** + * Add generic E-ID information into already existing AuthData. + * + * @param authData AuthData object + * @param authProcessData Authentication information holder from current pending request + * @param pendingReq current pending request + */ + private void buildInternalAuthDataGeneric(@NonNull final IAuthData authData, + @NonNull final IAuthProcessDataContainer authProcessData, + @NonNull final IRequest pendingReq) { + Assert.notNull(pendingReq, "PendingRequest is null"); + Assert.notNull(authData, "AuthData is null"); + Assert.notNull(authProcessData, "AuthProcessData is null"); + + if (!(authData instanceof AuthenticationData)) { + log.error("AuthData has no suitable type! Requires: {}", AuthenticationData.class.getName()); + throw new RuntimeException( + "AuthData has no suitable type! Requires: " + AuthenticationData.class.getName()); + + } + + final AuthenticationData internalAuthData = (AuthenticationData) authData; + + // TODO: check if it is needed + // if (authProcessData.getGenericSessionDataStorage() != null && + // !authProcessData.getGenericSessionDataStorage().isEmpty()) + // includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet(); + // else + includedToGenericAuthData = new ArrayList<>(); + + // #################################################### + // set general authData info's + internalAuthData.setAuthenticationIssuer(pendingReq.getAuthUrl()); + internalAuthData.setSsoSession(pendingReq.needSingleSignOnFunctionality()); + internalAuthData.setBaseIdTransferRestrication( + pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction()); + + // #################################################### + // set MDS and vSZ + internalAuthData.setFamilyName(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class)); + internalAuthData.setGivenName(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.GIVEN_NAME_NAME, String.class)); + internalAuthData.setDateOfBirth(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.BIRTHDATE_NAME, String.class)); + internalAuthData.setEncSourceId(authProcessData.getGenericDataFromSession( + ExtendedPVPAttributeDefinitions.EID_ENCRYPTED_SOURCEID_NAME, String.class)); + internalAuthData.setEncSourceIdType(authProcessData.getGenericDataFromSession( + ExtendedPVPAttributeDefinitions.EID_ENCRYPTED_SOURCEID_TYPE_NAME, String.class)); + + // #################################################### + // set QAA level + setQaaLevel(internalAuthData, authProcessData, pendingReq); + + + // #################################################### + // set isForeigner flag + setFlagForeigner(internalAuthData, authProcessData, pendingReq); + + + // #################################################### + // set citizen country-code + setCitizenCountryCode(internalAuthData, authProcessData, pendingReq); + + + // set generic authProcessData to authdata + for (final Entry<String, Object> el : authProcessData.getGenericSessionDataStorage() + .entrySet()) { + if (el.getKey().startsWith(GENERIC_AUTHDATA_IDENTIFIER)) { + log.trace("Find generic authProcessData {}. Map it directly to authData", el.getKey()); + try { + internalAuthData.setGenericData(el.getKey(), el.getValue()); + + } catch (final EaafStorageException e) { + log.warn("Can NOT set authData with key: {}", el.getKey(), null, e); + + } + + } + + } + + + } + + /** + * Parse citzen country-code into AuthData. + * + * @param authData Current authentication data + * @param authProcessData Authentication information holder from current pending request + * @param pendingReq Current pending request + */ + private void setCitizenCountryCode(final AuthenticationData authData, + final IAuthProcessDataContainer authProcessData, final IRequest pendingReq) { + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME); + final String pvpCccAttr = authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME, String.class); + if (StringUtils.isNotEmpty(pvpCccAttr)) { + authData.setCiticenCountryCode(pvpCccAttr); + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_ISSUING_NATION_FRIENDLY_NAME); + + } else { + if (authData.isForeigner()) { + // TODO!!!! + + } else { + authData.setCiticenCountryCode(basicConfig.getBasicConfiguration( + IConfigurationWithSP.CONFIG_PROPS_AUTH_DEFAULT_COUNTRYCODE, + EAAFConstants.COUNTRYCODE_AUSTRIA)); + + } + } + + } + + /** + * parse QAA Level into AuthData. + * + * @param authData current authentication data + * @param authProcessData Authentication information holder from current pending request + * @param pendingReq current pending request + */ + private void setQaaLevel(@NonNull final AuthenticationData authData, + @NonNull final IAuthProcessDataContainer authProcessData, + @NonNull final IRequest pendingReq) { + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME); + String currentLoA = null; + if (StringUtils.isNotEmpty(authProcessData.getQAALevel())) { + currentLoA = authProcessData.getQAALevel(); + } else { + currentLoA = authProcessData.getGenericDataFromSession( + PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME, String.class); + if (StringUtils.isNotEmpty(currentLoA)) { + log.debug( + "Find PVP-Attr '" + PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_FRIENDLY_NAME + + "':" + currentLoA + " --> Parse QAA-Level from that attribute."); + + } + } + if (StringUtils.isNotEmpty(currentLoA)) { + if (currentLoA.startsWith(EAAFConstants.EIDAS_LOA_PREFIX)) { + authData.setEidasLoa(currentLoA); + + } else { + log.info("Only eIDAS LoAs are supported by this implementation"); + } + + } else { + log.info("No QAA level found. Set to default level " + EAAFConstants.EIDAS_LOA_LOW); + authData.setEidasLoa(EAAFConstants.EIDAS_LOA_LOW); + + } + + } + + + private void setFlagForeigner(final AuthenticationData authData, + final IAuthProcessDataContainer authProcessData, final IRequest pendingReq) { + // TODO: change to new eIDAS-token attribute identifier + if (authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.EID_STORK_TOKEN_NAME) != null) { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_STORK_TOKEN_FRIENDLY_NAME + + " --> Set 'isForeigner' flag to TRUE"); + authData.setForeigner(true); + + } else { + authData.setForeigner(authProcessData.isForeigner()); + + } + } + + /** + * Build authentication data by using information from citizen-card or mobile-phone signature This + * builder uses IdentityLink, AuthBlock, full MIS mandate as input information. + * + * @param pendingReq current pendingRequest + * @return {@link IAuthData} but never <code>null</code> + * @throws EaafException In case of an error + */ + @Deprecated + @NonNull + protected abstract IAuthData buildDeprecatedAuthData(IRequest pendingReq) throws EaafException; + + @Deprecated + protected void generateDeprecatedBasicAuthData(final AuthenticationData authData, + final IRequest pendingReq, final IAuthProcessDataContainer authProcessData) + throws EaafBuilderException, EaafConfigurationException, XPathException, DOMException, + EaafParserException { + + if (authProcessData.getGenericSessionDataStorage() != null + && !authProcessData.getGenericSessionDataStorage().isEmpty()) { + includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet(); + } else { + includedToGenericAuthData = new ArrayList<>(); + } + + // #################################################### + // set general authData info's + authData.setAuthenticationIssuer(pendingReq.getAuthUrl()); + authData.setSsoSession(pendingReq.needSingleSignOnFunctionality()); + authData.setBaseIdTransferRestrication( + pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction()); + + + // #################################################### + // parse user info's from identityLink + IIdentityLink idlFromPvpAttr = null; + final IIdentityLink identityLink = authProcessData.getIdentityLink(); + if (identityLink != null) { + parseBasicUserInfosFromIdl(authData, identityLink, includedToGenericAuthData); + + } else { + // identityLink is not direct in MOASession + final String pvpAttrIdl = authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME, String.class); + // find PVP-Attr. which contains the IdentityLink + if (StringUtils.isNotEmpty(pvpAttrIdl)) { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_IDENTITY_LINK_FRIENDLY_NAME + + " --> Parse basic user info's from that attribute."); + InputStream idlStream = null; + try { + idlStream = new ByteArrayInputStream(Base64Utils.decodeFromString(pvpAttrIdl)); + idlFromPvpAttr = new SimpleIdentityLinkAssertionParser(idlStream).parseIdentityLink(); + parseBasicUserInfosFromIdl(authData, idlFromPvpAttr, includedToGenericAuthData); + + // set identitylink into AuthProcessData + authProcessData.setIdentityLink(idlFromPvpAttr); + + } catch (final EaafParserException e) { + log.warn("Received IdentityLink is not valid", e); + + } catch (final Exception e) { + log.warn("Received IdentityLink is not valid", e); + + } finally { + try { + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME); + if (idlStream != null) { + idlStream.close(); + } + + } catch (final IOException e) { + log.warn("Close InputStream FAILED.", e); + + } + } + } + + // if no basic user info's are set yet, parse info's single PVP-Attributes + if (StringUtils.isEmpty(authData.getFamilyName())) { + log.debug( + "No IdentityLink found or not parseable --> Parse basic user info's from single PVP-Attributes."); + authData.setFamilyName(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class)); + authData.setGivenName(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.GIVEN_NAME_NAME, String.class)); + authData.setDateOfBirth(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.BIRTHDATE_NAME, String.class)); + authData.setIdentificationValue(authProcessData + .getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME, String.class)); + authData.setIdentificationType(authProcessData.getGenericDataFromSession( + PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME, String.class)); + + // remove corresponding keys from genericSessionData if exists + includedToGenericAuthData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); + } + + } + + if (authData.getIdentificationType() != null + && !authData.getIdentificationType().equals(EAAFConstants.URN_PREFIX_BASEID)) { + log.trace("IdentificationType is not a baseID --> clear it. "); + authData.setBpk(authData.getIdentificationValue()); + authData.setBpkType(authData.getIdentificationType()); + + authData.setIdentificationValue(null); + authData.setIdentificationType(null); + } + + + // #################################################### + // set QAA level + setQaaLevel(authData, authProcessData, pendingReq); + + + // #################################################### + // set isForeigner flag + setFlagForeigner(authData, authProcessData, pendingReq); + + + // #################################################### + // set citizen country-code + setCitizenCountryCode(authData, authProcessData, pendingReq); + + + // #################################################### + // set bPK and IdentityLink + final String pvpBpkValue = getBpkValueFromPvpAttribute(authProcessData); + final String pvpBpkTypeAttr = getBpkTypeFromPvpAttribute(authProcessData); + final Pair<String, String> pvpEncBpkAttr = getEncryptedBpkFromPvpAttribute(authProcessData, + authData, pendingReq.getServiceProviderConfiguration()); + + // check if a unique ID for this citizen exists + if (StringUtils.isEmpty(authData.getIdentificationValue()) && StringUtils.isEmpty(pvpBpkValue) + && StringUtils.isEmpty(authData.getBpk()) && pvpEncBpkAttr == null) { + log.info( + "Can not build authData, because moaSession include no bPK, encrypted bPK or baseID"); + throw new EaafBuilderException("builder.08", + new Object[] {"No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME}, + "No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); + + } + + // check if bPK already added to AuthData matches OA + if (StringUtils.isNotEmpty(authData.getBpk()) && matchsReceivedBpkToOnlineApplication( + pendingReq.getServiceProviderConfiguration(), authData.getBpkType())) { + log.debug("Correct bPK is already included in AuthData."); + + // check if bPK received by PVP-Attribute matches OA + } else if (StringUtils.isNotEmpty(pvpBpkValue) && matchsReceivedBpkToOnlineApplication( + pendingReq.getServiceProviderConfiguration(), pvpBpkTypeAttr)) { + log.debug("Receive correct bPK from PVP-Attribute"); + authData.setBpk(pvpBpkValue); + authData.setBpkType(pvpBpkTypeAttr); + + // baseID is in AuthSesson --> calculate bPK directly + } else if (StringUtils.isNotEmpty(authData.getIdentificationValue())) { + log.debug("Citizen baseID is in MOASession --> calculate bPK from this."); + final Pair<String, String> result = buildOAspecificbPK(pendingReq, authData); + authData.setBpk(result.getFirst()); + authData.setBpkType(result.getSecond()); + + // check if decrypted bPK exists + } else if (pvpEncBpkAttr != null) { + log.debug("Receive bPK as encrypted bPK and decryption was possible."); + authData.setBpk(pvpEncBpkAttr.getFirst()); + authData.setBpkType(pvpEncBpkAttr.getSecond()); + + // ask SZR to get bPK + } else { + String notValidbPK = authData.getBpk(); + String notValidBpkType = authData.getBpkType(); + if (StringUtils.isEmpty(notValidbPK) && StringUtils.isEmpty(notValidBpkType)) { + notValidbPK = pvpBpkValue; + notValidBpkType = pvpBpkTypeAttr; + + if (StringUtils.isEmpty(notValidbPK) && StringUtils.isEmpty(notValidBpkType)) { + log.error("No bPK in MOASession. THIS error should not occur any more."); + throw new NullPointerException( + "No bPK in MOASession. THIS error should not occur any more."); + } + } + + final Pair<String, String> baseIdFromSzr = + getbaseIdFromSzr(authData, notValidbPK, notValidBpkType); + if (baseIdFromSzr != null) { + log.info("Receive citizen baseID from SRZ. Authentication can be completed"); + authData.setIdentificationValue(baseIdFromSzr.getFirst()); + authData.setIdentificationType(baseIdFromSzr.getSecond()); + final Pair<String, String> result = buildOAspecificbPK(pendingReq, authData); + authData.setBpk(result.getFirst()); + authData.setBpkType(result.getSecond()); + + } else { + log.warn( + "Can not build authData, because moaSession include no valid bPK, encrypted bPK or sourceID"); + throw new EaafBuilderException("builder.13", + new Object[] { + pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()}, + "No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + " or " + + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); + + } + } + + // build IdentityLink + if (authProcessData.getIdentityLink() != null) { + authData + .setIdentityLink(buildOAspecificIdentityLink(pendingReq.getServiceProviderConfiguration(), + authProcessData.getIdentityLink(), authData.getBpk(), authData.getBpkType())); + } else { + log.info("Can NOT set IdentityLink. Msg: No IdentityLink found"); + } + + } + + // extract a encrypted bPK from PVP attrobute + @Deprecated + protected abstract Pair<String, String> getEncryptedBpkFromPvpAttribute( + IAuthProcessDataContainer authProcessDataContainer, AuthenticationData authData, + IspConfiguration spConfig) throws EaafBuilderException; + + // request baseId from SRZ + @Deprecated + protected abstract Pair<String, String> getbaseIdFromSzr(AuthenticationData authData, + String notValidBpk, String notValidBpkType); + + @Deprecated + protected Pair<String, String> buildOAspecificbPK(final IRequest pendingReq, + final AuthenticationData authData) throws EaafBuilderException { + final IspConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + final String baseID = authData.getIdentificationValue(); + final String baseIdType = authData.getIdentificationType(); + Pair<String, String> sectorSpecId = null; + + if (EAAFConstants.URN_PREFIX_BASEID.equals(baseIdType)) { + // SAML1 legacy target parameter work-around + final String spTargetId = oaParam.getAreaSpecificTargetIdentifier(); + log.debug("Use OA target identifier '" + spTargetId + "' from configuration"); + + new BpkBuilder(); + // calculate sector specific unique identifier + sectorSpecId = BpkBuilder.generateAreaSpecificPersonIdentifier(baseID, spTargetId); + + } else { + log.error( + "!!!baseID-element does not include a baseID. This should not be happen any more!!!"); + sectorSpecId = Pair.newInstance(baseID, baseIdType); + + } + + log.trace("Authenticate user with bPK:" + sectorSpecId.getFirst() + " Type:" + + sectorSpecId.getSecond()); + return sectorSpecId; + + } + + @Deprecated + protected IIdentityLink buildOAspecificIdentityLink(final IspConfiguration spConfig, + final IIdentityLink idl, final String bpk, final String bpkType) + throws EaafConfigurationException, XPathException, DOMException, EaafParserException { + if (spConfig.hasBaseIdTransferRestriction()) { + log.debug("SP: " + spConfig.getUniqueIdentifier() + + " has baseId transfer restriction. Remove baseId from IDL ..."); + final Element idlassertion = idl.getSamlAssertion(); + // set bpk/wpbk; + final Node prIdentification = XPathUtils.selectSingleNode(idlassertion, + SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); + prIdentification.getFirstChild().setNodeValue(bpk); + // set bkp/wpbk type + final Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, + SimpleIdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH); + prIdentificationType.getFirstChild().setNodeValue(bpkType); + + final SimpleIdentityLinkAssertionParser idlparser = + new SimpleIdentityLinkAssertionParser(idlassertion); + return idlparser.parseIdentityLink(); + + } else { + return idl; + } + + } + + /** + * Check a bPK-Type against a Service-Provider configuration <br> + * If bPK-Type is <code>null</code> the result is <code>false</code>. + * + * @param oaParam Service-Provider configuration, never null + * @param bpkType bPK-Type to check + * @return true, if bPK-Type matchs to Service-Provider configuration, otherwise false + */ + @Deprecated + protected boolean matchsReceivedBpkToOnlineApplication(final IspConfiguration oaParam, + final String bpkType) { + return oaParam.getAreaSpecificTargetIdentifier().equals(bpkType); + + } + + /** + * Parse information from an IdentityLink into AuthData object. + * + * @param authData current authentication data + * @param identityLink User's identityLink + * @param includedGenericSessionData Generic AuthSession Data from PVP attributes + */ + @Deprecated + private void parseBasicUserInfosFromIdl(final AuthenticationData authData, + final IIdentityLink identityLink, final Collection<String> includedGenericSessionData) { + authData.setIdentificationValue(identityLink.getIdentificationValue()); + authData.setIdentificationType(identityLink.getIdentificationType()); + + /* + * GivenNames and FamilyNames with simple Apostrophe were escaped with ' in + * IdentityLinkParser since 5 years. This feature was bug-fix for an SL1.0 AuthBlock problem. + * However, the authentication attributes (SAML2, eIDAS, OpenID-Connect) also includes this + * escaped values, but there it is not neccesary. We fix this problem in 3.4.3, but the fix can + * be deactivated for dependency reasons. + */ + if (basicConfig.getBasicConfigurationBoolean(CONFIG_PROP_ENABLE_IDL_ATTRIBUTE_ESCAPEING, + false)) { + authData.setGivenName(identityLink.getGivenName().replaceAll("'", "'")); + authData.setFamilyName(identityLink.getFamilyName().replaceAll("'", "'")); + + } else { + authData.setGivenName(identityLink.getGivenName()); + authData.setFamilyName(identityLink.getFamilyName()); + + } + + authData.setDateOfBirth(identityLink.getDateOfBirth()); + + + // remove corresponding keys from genericSessionData if exists + includedGenericSessionData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); + + } + + /** + * Get bPK from PVP Attribute 'BPK_NAME', which could be exist in MOASession as 'GenericData'. <br> + * + * <pre> + * <code>session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class)</code> + * </pre> + * + * @param session MOASession, but never null + * @return bPK, which was received by PVP-Attribute, or <code>null</code> if no attribute exists + */ + @Deprecated + private String getBpkValueFromPvpAttribute(final IAuthProcessDataContainer session) { + String pvpBpkValueAttr = + session.getGenericDataFromSession(PVPAttributeDefinitions.BPK_NAME, String.class); + if (StringUtils.isNotEmpty(pvpBpkValueAttr)) { + + // fix a wrong bPK-value prefix, which was used in some PVP Standardportal implementations + if (pvpBpkValueAttr.startsWith("bPK:")) { + log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + + " contains a not standardize prefix! Staring attribute value correction process ..."); + pvpBpkValueAttr = pvpBpkValueAttr.substring("bPK:".length()); + + } + + final String[] spitted = pvpBpkValueAttr.split(":"); + if (spitted.length == 2) { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME); + return spitted[1]; + + + + } else if (spitted.length > 2) { + log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + + " has a wrong encoding and can NOT be USED!" + " Value:" + pvpBpkValueAttr); + return null; + + } else { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + + " without prefix. Use it as it is"); + return spitted[0]; + + } + + } + + return null; + } + + /** + * Get bPK-Type from PVP Attribute 'EID_SECTOR_FOR_IDENTIFIER_NAME', which could be exist in + * MOASession as 'GenericData'. <br> + * + * <pre> + * <code>session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class)</code> + * </pre> + * + * @param session MOASession, but never null + * @return bPKType, which was received by PVP-Attribute, or <code>null</code> if no attribute + * exists + */ + @Deprecated + private String getBpkTypeFromPvpAttribute(final IAuthProcessDataContainer session) { + final String pvpBpkTypeAttr = session.getGenericDataFromSession( + PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class); + + if (StringUtils.isNotEmpty(pvpBpkTypeAttr)) { + // //fix a wrong bPK-Type encoding, which was used in some PVP Standardportal implementations + // if (pvpbPKTypeAttr.startsWith(EAAFConstants.URN_PREFIX_CDID) && + // !pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length(), + // EAAFConstants.URN_PREFIX_CDID.length() + 1).equals("+")) { + // log.warn("Receive uncorrect encoded bBKType attribute " + pvpbPKTypeAttr + " Starting + // attribute value correction ... "); + // pvpbPKTypeAttr = EAAFConstants.URN_PREFIX_CDID + "+" + + // pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length() + 1); + // + // } + log.debug( + "Find PVP-Attr: " + PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME); + return pvpBpkTypeAttr; + } + + return null; + + + /* + * INFO: This code could be used to extract the bPKType from 'PVPConstants.BPK_NAME', because + * the prefix of BPK_NAME attribute contains the postfix of the bPKType + * + * Now, all PVP Standardportals should be able to send 'EID_SECTOR_FOR_IDENTIFIER' PVP + * attributes + */ + // String pvpbPKValueAttr = session.getGenericDataFromSession(PVPConstants.BPK_NAME, + // String.class); + // String[] spitted = pvpbPKValueAttr.split(":"); + // if (MiscUtil.isEmpty(authData.getBPKType())) { + // Logger.debug("PVP assertion contains NO bPK/wbPK target attribute. " + + // "Starting target extraction from bPK/wbPK prefix ..."); + // //exract bPK/wbPK type from bpk attribute value prefix if type is + // //not transmitted as single attribute + // Pattern pattern = Pattern.compile("[a-zA-Z]{2}(-[a-zA-Z]+)?"); + // Matcher matcher = pattern.matcher(spitted[0]); + // if (matcher.matches()) { + // //find public service bPK + // authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + spitted[0]); + // Logger.debug("Found bPK prefix. Set target to " + authData.getBPKType()); + // + // } else { + // //find business service wbPK + // authData.setBPKType(Constants.URN_PREFIX_WBPK+ "+" + spitted[0]); + // Logger.debug("Found wbPK prefix. Set target to " + authData.getBPKType()); + // + // } + // } + + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java deleted file mode 100644 index 602546a2..00000000 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java +++ /dev/null @@ -1,302 +0,0 @@ -/******************************************************************************* - * Copyright 2014 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - ******************************************************************************/ -/* - * Copyright 2003 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - */ - - -package at.gv.egiz.eaaf.core.impl.idp.auth.builder; - -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.Base64Utils; - -import at.gv.egiz.eaaf.core.api.data.EAAFConstants; -import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException; -import at.gv.egiz.eaaf.core.impl.data.Pair; - -/** - * Builder for the bPK, as defined in - * <code>"Ableitung f¨r die bereichsspezifische Personenkennzeichnung"</code> - * version <code>1.0.1</code> from <code>"reference.e-government.gv.at"</code>. - * - */ -public class BPKBuilder { - private static final Logger log = LoggerFactory.getLogger(BPKBuilder.class); - - /** - * Calculates an area specific unique person-identifier from a baseID - * - * @param baseID baseId from user but never null - * @param targetIdentifier target identifier for area specific identifier calculation but never null - * @return Pair consists of (unique person identifier for this target, targetArea) but never null - * @throws EAAFBuilderException if some input data are not valid - */ - public static Pair<String, String> generateAreaSpecificPersonIdentifier(String baseID, String targetIdentifier) throws EAAFBuilderException { - return generateAreaSpecificPersonIdentifier(baseID, EAAFConstants.URN_PREFIX_BASEID, targetIdentifier); - - } - - /** - * Calculates an area specific unique person-identifier from an unique identifier with a specific type - * - * @param baseID baseId from user but never null - * @param baseIdType Type of the baseID but never null - * @param targetIdentifier target identifier for area specific identifier calculation but never null - * @return Pair consists of (unique person identifier for this target, targetArea) but never null - * @throws EAAFBuilderException if some input data are not valid - */ - public static Pair<String, String> generateAreaSpecificPersonIdentifier(String baseID, String baseIdType, String targetIdentifier) throws EAAFBuilderException{ - if (StringUtils.isEmpty(baseID)) - throw new EAAFBuilderException("builder.00", new Object[]{"baseID is empty or null"}, - "BaseId is empty or null"); - - if (StringUtils.isEmpty(baseIdType)) - throw new EAAFBuilderException("builder.00", new Object[]{"the type of baseID is empty or null"}, - "Type of baseId is empty or null"); - - if (StringUtils.isEmpty(targetIdentifier)) - throw new EAAFBuilderException("builder.00", new Object[]{"SP specific target identifier is empty or null"}, - "SP specific target identifier is empty or null"); - - if (baseIdType.equals(EAAFConstants.URN_PREFIX_BASEID)) { - log.trace("Find baseID. Starting unique identifier caluclation for this target"); - - if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_CDID) || - targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_WBPK)) { - log.trace("Calculate bPK, wbPK, or STORK identifier for target: " + targetIdentifier); - return Pair.newInstance(calculatebPKwbPK(baseID + "+" + targetIdentifier), targetIdentifier); - - } else if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_EIDAS)) { - log.trace("Calculate eIDAS identifier for target: " + targetIdentifier); - final String[] splittedTarget = targetIdentifier.split("\\+"); - final String cititzenCountryCode = splittedTarget[1]; - final String eIDASOutboundCountry = splittedTarget[2]; - - if (cititzenCountryCode.equalsIgnoreCase(eIDASOutboundCountry)) { - log.warn("Suspect configuration FOUND!!! CitizenCountry equals DestinationCountry"); - - } - return buildeIDASIdentifer(baseID, baseIdType, cititzenCountryCode, eIDASOutboundCountry); - - - } else - throw new EAAFBuilderException("builder.00", - new Object[]{"Target identifier: " + targetIdentifier + " is NOT allowed or unknown"}, - "Target identifier: " + targetIdentifier + " is NOT allowed or unknown"); - - } else { - log.trace("BaseID is not of type " + EAAFConstants.URN_PREFIX_BASEID + ". Check type against requested target ..."); - if (baseIdType.equals(targetIdentifier)) { - log.debug("Unique identifier is already area specific. Is nothing todo"); - return Pair.newInstance(baseID, targetIdentifier); - - } else { - log.warn("Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required!"); - throw new EAAFBuilderException("builder.00", - new Object[]{"Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required"}, - "Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required"); - - } - } - } - - - /** - * Builds the eIDAS from the given parameters. - * - * @param baseID baseID of the citizen - * @param baseIDType Type of the baseID - * @param sourceCountry CountryCode of that country, which build the eIDAs ID - * @param destinationCountry CountryCode of that country, which receives the eIDAs ID - * - * @return Pair<eIDAs, bPKType> in a BASE64 encoding - * @throws EAAFBuilderException if some input data are not valid - */ - private static Pair<String, String> buildeIDASIdentifer(String baseID, String baseIDType, String sourceCountry, String destinationCountry) - throws EAAFBuilderException { - String bPK = null; - String bPKType = null; - - // check if we have been called by public sector application - if (baseIDType.startsWith(EAAFConstants.URN_PREFIX_BASEID)) { - bPKType = EAAFConstants.URN_PREFIX_EIDAS + sourceCountry + "+" + destinationCountry; - log.debug("Building eIDAS identification from: [identValue]+" + bPKType); - bPK = calculatebPKwbPK(baseID + "+" + bPKType); - - } else { // if not, sector identification value is already calculated by BKU - log.debug("eIDAS eIdentifier already provided by BKU"); - bPK = baseID; - } - - if ((StringUtils.isEmpty(bPK) || - StringUtils.isEmpty(sourceCountry) || - StringUtils.isEmpty(destinationCountry))) { - throw new EAAFBuilderException("builder.00", - new Object[]{"eIDAS-ID", "Unvollständige Parameterangaben: identificationValue=" + - bPK + ", Zielland=" + destinationCountry + ", Ursprungsland=" + sourceCountry} - ,"eIDAS-ID: Unvollständige Parameterangaben: identificationValue=" + - bPK + ", Zielland=" + destinationCountry + ", Ursprungsland=" + sourceCountry); - } - - log.trace("eIDAS pseudonym generation finished. "); - final String eIdentifier = sourceCountry + "/" + destinationCountry + "/" + bPK; - - return Pair.newInstance(eIdentifier, bPKType); - } - - public static String encryptBPK(String bpk, String target, PublicKey publicKey) throws EAAFBuilderException { - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - if (target.startsWith(EAAFConstants.URN_PREFIX_CDID)) - target = target.substring((EAAFConstants.URN_PREFIX_CDID).length()); - - final String input = "V1::urn:publicid:gv.at:cdid+" + target + "::" - + bpk + "::" - + sdf.format(new Date()); - //System.out.println(input); - byte[] result; - try { - final byte[] inputBytes = input.getBytes("ISO-8859-1"); - result = encrypt(inputBytes, publicKey); - return new String(Base64Utils.encode(result), "ISO-8859-1").replaceAll("\r\n", ""); - //return new String(Base64Utils.encode(result, "ISO-8859-1")).replaceAll("\r\n", ""); - - - } catch (final Exception e) { - throw new EAAFBuilderException("bPK encryption FAILED", null, - e.getMessage(), e); - - } - } - - public static String decryptBPK(String encryptedBpk, String target, PrivateKey privateKey) throws EAAFBuilderException { - String decryptedString; - try { - //byte[] encryptedBytes = Base64Utils.decode(encryptedBpk, false, "ISO-8859-1"); - final byte[] encryptedBytes = Base64Utils.decode(encryptedBpk.getBytes("ISO-8859-1")); - final byte[] decryptedBytes = decrypt(encryptedBytes, privateKey); - decryptedString = new String(decryptedBytes, "ISO-8859-1"); - - } catch (final Exception e) { - throw new EAAFBuilderException("bPK decryption FAILED", null, - e.getMessage(), e); - - } - - String tmp = decryptedString.substring(decryptedString.indexOf('+') + 1); - final String sector = tmp.substring(0, tmp.indexOf("::")); - tmp = tmp.substring(tmp.indexOf("::") + 2); - final String bPK = tmp.substring(0, tmp.indexOf("::")); - - if (target.startsWith(EAAFConstants.URN_PREFIX_CDID + "+")) - target = target.substring((EAAFConstants.URN_PREFIX_CDID + "+").length()); - - if (target.equals(sector)) - return bPK; - - else { - log.error("Decrypted bPK does not match to request bPK target."); - return null; - } - } - - private static String calculatebPKwbPK(String basisbegriff) throws EAAFBuilderException { - try { - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - final byte[] hash = md.digest(basisbegriff.getBytes("ISO-8859-1")); - final String hashBase64 = new String(Base64Utils.encode(hash), "ISO-8859-1").replaceAll("\r\n", ""); //Base64Utils.encode(hash); - return hashBase64; - - } catch (final Exception ex) { - throw new EAAFBuilderException("builder.00", new Object[]{"bPK/wbPK", ex.toString()}, - ex.getMessage(), ex); - - } - - } - - private static byte[] encrypt(byte[] inputBytes, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - byte[] result; - Cipher cipher = null; - try { - cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle - - } catch(final NoSuchAlgorithmException e) { - cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider - } - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - result = cipher.doFinal(inputBytes); - - return result; - } - - private static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey) - throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{ - byte[] result; - Cipher cipher = null; - try { - cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle - - } catch(final NoSuchAlgorithmException e) { - cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider - - } - cipher.init(Cipher.DECRYPT_MODE, privateKey); - result = cipher.doFinal(encryptedBytes); - return result; - - } -} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BpkBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BpkBuilder.java new file mode 100644 index 00000000..765a6669 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BpkBuilder.java @@ -0,0 +1,312 @@ +/* + * Copyright 2014 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + + + +package at.gv.egiz.eaaf.core.impl.idp.auth.builder; + +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Base64Utils; + +/** + * Builder for the bPK, as defined in + * <code>"Ableitung f¨r die bereichsspezifische Personenkennzeichnung"</code> version + * <code>1.0.1</code> from <code>"reference.e-government.gv.at"</code>. + * + */ +public class BpkBuilder { + private static final Logger log = LoggerFactory.getLogger(BpkBuilder.class); + + /** + * Calculates an area specific unique person-identifier from a baseID. + * + * @param baseID baseId from user but never null + * @param targetIdentifier target identifier for area specific identifier calculation but never + * null + * @return Pair consists of (unique person identifier for this target, targetArea) but never null + * @throws EaafBuilderException if some input data are not valid + */ + public static Pair<String, String> generateAreaSpecificPersonIdentifier(final String baseID, + final String targetIdentifier) throws EaafBuilderException { + return generateAreaSpecificPersonIdentifier(baseID, EAAFConstants.URN_PREFIX_BASEID, + targetIdentifier); + + } + + /** + * Calculates an area specific unique person-identifier from an unique identifier with a specific + * type. + * + * @param baseID baseId from user but never null + * @param baseIdType Type of the baseID but never null + * @param targetIdentifier target identifier for area specific identifier calculation but never + * null + * @return Pair consists of (unique person identifier for this target, targetArea) but never null + * @throws EaafBuilderException if some input data are not valid + */ + public static Pair<String, String> generateAreaSpecificPersonIdentifier(final String baseID, + final String baseIdType, final String targetIdentifier) throws EaafBuilderException { + if (StringUtils.isEmpty(baseID)) { + throw new EaafBuilderException("builder.00", new Object[] {"baseID is empty or null"}, + "BaseId is empty or null"); + } + + if (StringUtils.isEmpty(baseIdType)) { + throw new EaafBuilderException("builder.00", + new Object[] {"the type of baseID is empty or null"}, "Type of baseId is empty or null"); + } + + if (StringUtils.isEmpty(targetIdentifier)) { + throw new EaafBuilderException("builder.00", + new Object[] {"SP specific target identifier is empty or null"}, + "SP specific target identifier is empty or null"); + } + + if (baseIdType.equals(EAAFConstants.URN_PREFIX_BASEID)) { + log.trace("Find baseID. Starting unique identifier caluclation for this target"); + + if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_CDID) + || targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_WBPK)) { + log.trace("Calculate bPK, wbPK, or STORK identifier for target: " + targetIdentifier); + return Pair.newInstance(calculatebPKwbPK(baseID + "+" + targetIdentifier), + targetIdentifier); + + } else if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_EIDAS)) { + log.trace("Calculate eIDAS identifier for target: " + targetIdentifier); + final String[] splittedTarget = targetIdentifier.split("\\+"); + final String cititzenCountryCode = splittedTarget[1]; + final String eidasOutboundCountry = splittedTarget[2]; + + if (cititzenCountryCode.equalsIgnoreCase(eidasOutboundCountry)) { + log.warn("Suspect configuration FOUND!!! CitizenCountry equals DestinationCountry"); + + } + return buildEidasIdentifer(baseID, baseIdType, cititzenCountryCode, eidasOutboundCountry); + + + } else { + throw new EaafBuilderException("builder.00", + new Object[] {"Target identifier: " + targetIdentifier + " is NOT allowed or unknown"}, + "Target identifier: " + targetIdentifier + " is NOT allowed or unknown"); + } + + } else { + log.trace("BaseID is not of type " + EAAFConstants.URN_PREFIX_BASEID + + ". Check type against requested target ..."); + if (baseIdType.equals(targetIdentifier)) { + log.debug("Unique identifier is already area specific. Is nothing todo"); + return Pair.newInstance(baseID, targetIdentifier); + + } else { + log.warn("Get unique identifier for target: " + baseIdType + " but target: " + + targetIdentifier + " is required!"); + throw new EaafBuilderException("builder.00", + new Object[] {"Get unique identifier for target: " + baseIdType + " but target: " + + targetIdentifier + " is required"}, + "Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + + " is required"); + + } + } + } + + + /** + * Builds the eIDAS from the given parameters. + * + * @param baseId baseID of the citizen + * @param baseIdType Type of the baseID + * @param sourceCountry CountryCode of that country, which build the eIDAs ID + * @param destinationCountry CountryCode of that country, which receives the eIDAs ID + * + * @return Pair eIDAs/bPKType in a BASE64 encoding + * @throws EaafBuilderException if some input data are not valid + */ + private static Pair<String, String> buildEidasIdentifer(final String baseId, + final String baseIdType, final String sourceCountry, final String destinationCountry) + throws EaafBuilderException { + String bpk = null; + String bpkType = null; + + // check if we have been called by public sector application + if (baseIdType.startsWith(EAAFConstants.URN_PREFIX_BASEID)) { + bpkType = EAAFConstants.URN_PREFIX_EIDAS + sourceCountry + "+" + destinationCountry; + log.debug("Building eIDAS identification from: [identValue]+" + bpkType); + bpk = calculatebPKwbPK(baseId + "+" + bpkType); + + } else { // if not, sector identification value is already calculated by BKU + log.debug("eIDAS eIdentifier already provided by BKU"); + bpk = baseId; + } + + if ((StringUtils.isEmpty(bpk) || StringUtils.isEmpty(sourceCountry) + || StringUtils.isEmpty(destinationCountry))) { + throw new EaafBuilderException("builder.00", + new Object[] {"eIDAS-ID", + "Unvollständige Parameterangaben: identificationValue=" + bpk + ", Zielland=" + + destinationCountry + ", Ursprungsland=" + sourceCountry}, + "eIDAS-ID: Unvollständige Parameterangaben: identificationValue=" + bpk + ", Zielland=" + + destinationCountry + ", Ursprungsland=" + sourceCountry); + } + + log.trace("eIDAS pseudonym generation finished. "); + final String eIdentifier = sourceCountry + "/" + destinationCountry + "/" + bpk; + + return Pair.newInstance(eIdentifier, bpkType); + } + + /** + * Create an encrypted bPK. + * + * @param bpk unencrypted bPK + * @param target bPK target + * @param publicKey Public-Key used for encryption + * @return encrypted bPK + * @throws EaafBuilderException In case of an error + */ + public static String encryptBpk(final String bpk, String target, final PublicKey publicKey) + throws EaafBuilderException { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + if (target.startsWith(EAAFConstants.URN_PREFIX_CDID)) { + target = target.substring((EAAFConstants.URN_PREFIX_CDID).length()); + } + + final String input = + "V1::urn:publicid:gv.at:cdid+" + target + "::" + bpk + "::" + sdf.format(new Date()); + // System.out.println(input); + byte[] result; + try { + final byte[] inputBytes = input.getBytes("ISO-8859-1"); + result = encrypt(inputBytes, publicKey); + return new String(Base64Utils.encode(result), "ISO-8859-1").replaceAll("\r\n", ""); + // return new String(Base64Utils.encode(result, "ISO-8859-1")).replaceAll("\r\n", ""); + + + } catch (final Exception e) { + throw new EaafBuilderException("bPK encryption FAILED", null, e.getMessage(), e); + + } + } + + /** + * Decrypt an encrypted bPK. + * + * @param encryptedBpk encrypted bPK + * @param target bPK target + * @param privateKey private-key for decryption + * @return bPK + * @throws EaafBuilderException In case of an error + */ + public static String decryptBpk(final String encryptedBpk, String target, + final PrivateKey privateKey) throws EaafBuilderException { + String decryptedString; + try { + // byte[] encryptedBytes = Base64Utils.decode(encryptedBpk, false, "ISO-8859-1"); + final byte[] encryptedBytes = Base64Utils.decode(encryptedBpk.getBytes("ISO-8859-1")); + final byte[] decryptedBytes = decrypt(encryptedBytes, privateKey); + decryptedString = new String(decryptedBytes, "ISO-8859-1"); + + } catch (final Exception e) { + throw new EaafBuilderException("bPK decryption FAILED", null, e.getMessage(), e); + + } + + String tmp = decryptedString.substring(decryptedString.indexOf('+') + 1); + final String sector = tmp.substring(0, tmp.indexOf("::")); + tmp = tmp.substring(tmp.indexOf("::") + 2); + final String bPK = tmp.substring(0, tmp.indexOf("::")); + + if (target.startsWith(EAAFConstants.URN_PREFIX_CDID + "+")) { + target = target.substring((EAAFConstants.URN_PREFIX_CDID + "+").length()); + } + + if (target.equals(sector)) { + return bPK; + } else { + log.error("Decrypted bPK does not match to request bPK target."); + return null; + } + } + + private static String calculatebPKwbPK(final String basisbegriff) throws EaafBuilderException { + try { + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + final byte[] hash = md.digest(basisbegriff.getBytes("ISO-8859-1")); + final String hashBase64 = + new String(Base64Utils.encode(hash), "ISO-8859-1").replaceAll("\r\n", ""); // Base64Utils.encode(hash); + return hashBase64; + + } catch (final Exception ex) { + throw new EaafBuilderException("builder.00", new Object[] {"bPK/wbPK", ex.toString()}, + ex.getMessage(), ex); + + } + + } + + private static byte[] encrypt(final byte[] inputBytes, final PublicKey publicKey) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + byte[] result; + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle + + } catch (final NoSuchAlgorithmException e) { + cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider + } + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + result = cipher.doFinal(inputBytes); + + return result; + } + + private static byte[] decrypt(final byte[] encryptedBytes, final PrivateKey privateKey) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + byte[] result; + Cipher cipher = null; + try { + cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle + + } catch (final NoSuchAlgorithmException e) { + cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider + + } + cipher.init(Cipher.DECRYPT_MODE, privateKey); + result = cipher.doFinal(encryptedBytes); + return result; + + } +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/AuthProcessDataWrapper.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/AuthProcessDataWrapper.java index e096b8e6..7c143ca2 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/AuthProcessDataWrapper.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/AuthProcessDataWrapper.java @@ -1,24 +1,20 @@ /******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ /******************************************************************************* *******************************************************************************/ @@ -29,226 +25,276 @@ package at.gv.egiz.eaaf.core.impl.idp.auth.data; import java.util.Date; import java.util.HashMap; import java.util.Map; - +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.idp.EaafAuthProcessDataConstants; +import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; +import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; +import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import at.gv.egiz.eaaf.core.api.data.EAAFConstants; -import at.gv.egiz.eaaf.core.api.idp.EAAFAuthProcessDataConstants; -import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; -import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; -import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; - -public class AuthProcessDataWrapper implements IAuthProcessDataContainer, EAAFAuthProcessDataConstants { - private static final Logger log = LoggerFactory.getLogger(AuthProcessDataWrapper.class); - - protected Map<String, Object> authProcessData; - - public AuthProcessDataWrapper(Map<String, Object> authProcessData) { - this.authProcessData = authProcessData; - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIssueInstant() - */ - @Override - public String getIssueInstant() { - return wrapStringObject(VALUE_ISSUEINSTANT, null, String.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIssueInstant(java.lang.String) - */ - @Override - public void setIssueInstant(String issueInstant) { - authProcessData.put(VALUE_ISSUEINSTANT, issueInstant); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isAuthenticated() - */ - @Override - public boolean isAuthenticated() { - return wrapStringObject(FLAG_IS_AUTHENTICATED, false, Boolean.class); - - } - - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setAuthenticated(boolean) - */ - @Override - public void setAuthenticated(boolean authenticated) { - authProcessData.put(FLAG_IS_AUTHENTICATED, authenticated); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIdentityLink() - */ - @Override - public IIdentityLink getIdentityLink() { - return wrapStringObject(VALUE_IDENTITYLINK, null, IIdentityLink.class); - - } - - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIdentityLink(at.gv.egovernment.moa.id.auth.data.IdentityLink) - */ - @Override - public void setIdentityLink(IIdentityLink identityLink) { - authProcessData.put(VALUE_IDENTITYLINK, identityLink); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isMandateUsed() - */ - @Override - public boolean isMandateUsed() { - return wrapStringObject(FLAG_USE_MANDATE, false, Boolean.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setUseMandates(boolean) - */ - @Override - public void setUseMandates(boolean useMandates) { - authProcessData.put(FLAG_USE_MANDATE, useMandates); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getQAALevel() - */ - @Override - public String getQAALevel() { - return wrapStringObject(VALUE_QAALEVEL, null, String.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setQAALevel(java.lang.String) - */ - @Override - public void setQAALevel(String qAALevel) { - authProcessData.put(VALUE_QAALEVEL, qAALevel); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isForeigner() - */ - @Override - public boolean isForeigner() { - return wrapStringObject(FLAG_IS_FOREIGNER, false, Boolean.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setForeigner(boolean) - */ - @Override - public void setForeigner(boolean isForeigner) { - authProcessData.put(FLAG_IS_FOREIGNER, isForeigner); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isOW() - */ - @Override - public boolean isOW() { - return wrapStringObject(FLAG_IS_ORGANWALTER, false, Boolean.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setOW(boolean) - */ - @Override - public void setOW(boolean isOW) { - authProcessData.put(FLAG_IS_ORGANWALTER, isOW); - - } - - @Override - public boolean isEIDProcess() { - return wrapStringObject(FLAG_IS_NEW_EID_PROCESS, false, Boolean.class); - } - - @Override - public void setEIDProcess(boolean value) { - authProcessData.put(FLAG_IS_NEW_EID_PROCESS, value); - - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getSessionCreated() - */ - @Override - public Date getSessionCreated() { - return wrapStringObject(EAAFConstants.AUTH_DATA_CREATED, null, Date.class); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericSessionDataStorage() - */ - @Override - public Map<String, Object> getGenericSessionDataStorage() { - final Map<String, Object> result = new HashMap<String, Object>(); - for (final String el : authProcessData.keySet()) { - if (el.startsWith(GENERIC_PREFIX)) - result.put(el.substring(GENERIC_PREFIX.length()), authProcessData.get(el)); - - } - - return result; - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericDataFromSession(java.lang.String) - */ - @Override - public Object getGenericDataFromSession(String key) { - return authProcessData.get(GENERIC_PREFIX + key); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericDataFromSession(java.lang.String, java.lang.Class) - */ - @Override - public <T> T getGenericDataFromSession(String key, Class<T> clazz) { - return wrapStringObject(GENERIC_PREFIX + key, null, clazz); - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setGenericDataToSession(java.lang.String, java.lang.Object) - */ - @Override - public void setGenericDataToSession(String key, Object object) throws EAAFStorageException { - authProcessData.put(GENERIC_PREFIX + key, object); - - } - - protected <T> T wrapStringObject(String key, Object defaultValue, Class<T> clazz) { - if (StringUtils.isNotEmpty(key)) { - final Object obj = authProcessData.get(key); - if (obj != null && clazz.isInstance(obj)) - return (T) obj; - } - - if (defaultValue == null) - return null; - - else if (clazz.isInstance(defaultValue)) - return (T)defaultValue; - - else { - log.error("DefaultValue: " + defaultValue.getClass().getName() + " is not of Type:" + clazz.getName()); - throw new IllegalStateException("DefaultValue: " + defaultValue.getClass().getName() + " is not of Type:" + clazz.getName()); - - } - } +public class AuthProcessDataWrapper + implements IAuthProcessDataContainer, EaafAuthProcessDataConstants { + private static final Logger log = LoggerFactory.getLogger(AuthProcessDataWrapper.class); + + protected Map<String, Object> authProcessData; + + public AuthProcessDataWrapper(final Map<String, Object> authProcessData) { + this.authProcessData = authProcessData; + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIssueInstant() + */ + @Override + public String getIssueInstant() { + return wrapStringObject(VALUE_ISSUEINSTANT, null, String.class); + } + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIssueInstant(java.lang.String) + */ + @Override + public void setIssueInstant(final String issueInstant) { + authProcessData.put(VALUE_ISSUEINSTANT, issueInstant); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isAuthenticated() + */ + @Override + public boolean isAuthenticated() { + return wrapStringObject(FLAG_IS_AUTHENTICATED, false, Boolean.class); + + } + + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setAuthenticated(boolean) + */ + @Override + public void setAuthenticated(final boolean authenticated) { + authProcessData.put(FLAG_IS_AUTHENTICATED, authenticated); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getIdentityLink() + */ + @Override + public IIdentityLink getIdentityLink() { + return wrapStringObject(VALUE_IDENTITYLINK, null, IIdentityLink.class); + + } + + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setIdentityLink(at.gv.egovernment.moa + * .id.auth.data.IdentityLink) + */ + @Override + public void setIdentityLink(final IIdentityLink identityLink) { + authProcessData.put(VALUE_IDENTITYLINK, identityLink); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isMandateUsed() + */ + @Override + public boolean isMandateUsed() { + return wrapStringObject(FLAG_USE_MANDATE, false, Boolean.class); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setUseMandates(boolean) + */ + @Override + public void setUseMandates(final boolean useMandates) { + authProcessData.put(FLAG_USE_MANDATE, useMandates); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getQAALevel() + */ + @Override + public String getQAALevel() { + return wrapStringObject(VALUE_QAALEVEL, null, String.class); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setQAALevel(java.lang.String) + */ + @Override + public void setQAALevel(final String qAALevel) { + authProcessData.put(VALUE_QAALEVEL, qAALevel); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isForeigner() + */ + @Override + public boolean isForeigner() { + return wrapStringObject(FLAG_IS_FOREIGNER, false, Boolean.class); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setForeigner(boolean) + */ + @Override + public void setForeigner(final boolean isForeigner) { + authProcessData.put(FLAG_IS_FOREIGNER, isForeigner); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#isOW() + */ + @Override + public boolean isOW() { + return wrapStringObject(FLAG_IS_ORGANWALTER, false, Boolean.class); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setOW(boolean) + */ + @Override + public void setOW(final boolean isOW) { + authProcessData.put(FLAG_IS_ORGANWALTER, isOW); + + } + + @Override + public boolean isEIDProcess() { + return wrapStringObject(FLAG_IS_NEW_EID_PROCESS, false, Boolean.class); + } + + @Override + public void setEIDProcess(final boolean value) { + authProcessData.put(FLAG_IS_NEW_EID_PROCESS, value); + + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getSessionCreated() + */ + @Override + public Date getSessionCreated() { + return wrapStringObject(EAAFConstants.AUTH_DATA_CREATED, null, Date.class); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericSessionDataStorage() + */ + @Override + public Map<String, Object> getGenericSessionDataStorage() { + final Map<String, Object> result = new HashMap<>(); + for (final Map.Entry<String,Object> el : authProcessData.entrySet()) { + if (el.getKey().startsWith(GENERIC_PREFIX)) { + result.put(el.getKey().substring(GENERIC_PREFIX.length()), el.getValue()); + } + + } + + return result; + } + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericDataFromSession(java.lang. + * String) + */ + @Override + public Object getGenericDataFromSession(final String key) { + return authProcessData.get(GENERIC_PREFIX + key); + } + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#getGenericDataFromSession(java.lang. + * String, java.lang.Class) + */ + @Override + public <T> T getGenericDataFromSession(final String key, final Class<T> clazz) { + return wrapStringObject(GENERIC_PREFIX + key, null, clazz); + } + + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IAuthenticationSession#setGenericDataToSession(java.lang. + * String, java.lang.Object) + */ + @Override + public void setGenericDataToSession(final String key, final Object object) + throws EaafStorageException { + authProcessData.put(GENERIC_PREFIX + key, object); + + } + + protected <T> T wrapStringObject(final String key, final Object defaultValue, + final Class<T> clazz) { + if (StringUtils.isNotEmpty(key)) { + final Object obj = authProcessData.get(key); + if (obj != null && clazz.isInstance(obj)) { + return (T) obj; + } + } + + if (defaultValue == null) { + return null; + } else if (clazz.isInstance(defaultValue)) { + return (T) defaultValue; + } else { + log.error("DefaultValue: " + defaultValue.getClass().getName() + " is not of Type:" + + clazz.getName()); + throw new IllegalStateException("DefaultValue: " + defaultValue.getClass().getName() + + " is not of Type:" + clazz.getName()); + + } + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/IdentityLink.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/IdentityLink.java index 367643ec..a1faa0a4 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/IdentityLink.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/IdentityLink.java @@ -1,306 +1,375 @@ /******************************************************************************* - * Copyright 2014 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. + * Copyright 2014 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. ******************************************************************************/ /* - * Copyright 2003 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * Copyright 2003 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.eaaf.core.impl.idp.auth.data; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; import java.security.PublicKey; - import javax.xml.transform.TransformerException; - -import org.w3c.dom.Element; - import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; -import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; +import at.gv.egiz.eaaf.core.impl.utils.DomUtils; +import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; +import org.w3c.dom.Element; /** - * Data contained in an identity link issued by BMI, relevant to the MOA ID component. - * <br><code>"IdentityLink"</code> is the translation of <code>"Personenbindung"</code>. - * + * Data contained in an identity link issued by BMI, relevant to the MOA ID component. <br> + * <code>"IdentityLink"</code> is the translation of <code>"Personenbindung"</code>. + * * @author Paul Ivancsics * @version $Id$ */ -public class IdentityLink implements Serializable, IIdentityLink{ - - private static final long serialVersionUID = 1L; - - /** - * <code>"identificationValue"</code> is the translation of <code>"Stammzahl"</code>. - */ - private String identificationValue; - /** - * <code>"identificationType"</code> type of the identificationValue in the IdentityLink. - */ - private String identificationType; - /** - * first name - */ - private String givenName; - /** - * family name - */ - private String familyName; - - /** - * date of birth - */ - private String dateOfBirth; +public class IdentityLink implements Serializable, IIdentityLink { + + private static final long serialVersionUID = 1L; + + /** + * <code>"identificationValue"</code> is the translation of <code>"Stammzahl"</code>. + */ + private String identificationValue; + /** + * <code>"identificationType"</code> type of the identificationValue in the IdentityLink. + */ + private String identificationType; + /** + * first name + */ + private String givenName; + /** + * family name + */ + private String familyName; + + /** + * date of birth + */ + private String dateOfBirth; /** * the original saml:Assertion-Element */ - private Element samlAssertion; + private transient Element samlAssertion; /** * the serializes saml:Assertion */ private String serializedSamlAssertion; - /** - * Element /saml:Assertion/saml:AttributeStatement/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/pr:Person - */ - private Element prPerson; /** - * we need for each dsig:Reference Element all - * transformation elements + * Element + * /saml:Assertion/saml:AttributeStatement/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData/pr:Person + */ + private transient Element prPerson = null; + /** + * we need for each dsig:Reference Element all transformation elements */ - private Element[] dsigReferenceTransforms; - + private transient Element[] dsigReferenceTransforms = null; + /** * The issuing time of the identity link SAML assertion. */ private String issueInstant; /** - * we need all public keys stored in - * the identity link + * we need all public keys stored in the identity link */ private PublicKey[] publicKey; - /** - * Constructor for IdentityLink - */ - public IdentityLink() { - } + /** + * Constructor for IdentityLink + */ + public IdentityLink() {} - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getDateOfBirth() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getDateOfBirth() + */ @Override -public String getDateOfBirth() { + public String getDateOfBirth() { return dateOfBirth; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getFamilyName() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getFamilyName() + */ @Override -public String getFamilyName() { + public String getFamilyName() { return familyName; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getGivenName() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getGivenName() + */ @Override -public String getGivenName() { + public String getGivenName() { return givenName; } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getName() - */ + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getName() + */ @Override @Deprecated public String getName() { return givenName + " " + familyName; - + } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIdentificationValue() - */ + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIdentificationValue() + */ @Override -public String getIdentificationValue() { + public String getIdentificationValue() { return identificationValue; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIdentificationType() - */ - @Override - public String getIdentificationType() { - return identificationType; - } + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIdentificationType() + */ + @Override + public String getIdentificationType() { + return identificationType; + } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setDateOfBirth(java.lang.String) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setDateOfBirth(java.lang.String) + */ @Override -public void setDateOfBirth(String dateOfBirth) { + public void setDateOfBirth(final String dateOfBirth) { this.dateOfBirth = dateOfBirth; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setFamilyName(java.lang.String) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setFamilyName(java.lang.String) + */ @Override -public void setFamilyName(String familyName) { + public void setFamilyName(final String familyName) { this.familyName = familyName; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setGivenName(java.lang.String) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setGivenName(java.lang.String) + */ @Override -public void setGivenName(String givenName) { + public void setGivenName(final String givenName) { this.givenName = givenName; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIdentificationValue(java.lang.String) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIdentificationValue(java.lang.String) + */ @Override -public void setIdentificationValue(String identificationValue) { + public void setIdentificationValue(final String identificationValue) { this.identificationValue = identificationValue; } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIdentificationType(java.lang.String) - */ - @Override - public void setIdentificationType(String identificationType) { - this.identificationType = identificationType; - } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getSamlAssertion() - */ + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIdentificationType(java.lang.String) + */ @Override -public Element getSamlAssertion() { - return samlAssertion; + public void setIdentificationType(final String identificationType) { + this.identificationType = identificationType; } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getSerializedSamlAssertion() - */ + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getSamlAssertion() + */ @Override -public String getSerializedSamlAssertion() { + public Element getSamlAssertion() { + if (this.samlAssertion == null) { + try { + this.samlAssertion = DomUtils.parseXmlNonValidating( + new ByteArrayInputStream(serializedSamlAssertion.getBytes("UTF-8"))); + + } catch (final Exception e) { + throw new RuntimeException(e); + + } + } + + return this.samlAssertion; + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getSerializedSamlAssertion() + */ + @Override + public String getSerializedSamlAssertion() { return serializedSamlAssertion; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setSamlAssertion(org.w3c.dom.Element) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setSamlAssertion(org.w3c.dom.Element) + */ @Override -public void setSamlAssertion(Element samlAssertion) throws TransformerException, IOException { + public void setSamlAssertion(final Element samlAssertion) + throws TransformerException, IOException { this.samlAssertion = samlAssertion; - this.serializedSamlAssertion = DOMUtils.serializeNode(samlAssertion); + this.serializedSamlAssertion = DomUtils.serializeNode(samlAssertion); } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getDsigReferenceTransforms() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getDsigReferenceTransforms() + */ @Override -public Element[] getDsigReferenceTransforms() { - return dsigReferenceTransforms; + public Element[] getDsigReferenceTransforms() { + if (dsigReferenceTransforms != null) { + return dsigReferenceTransforms.clone(); + + } else { + return null; + + } } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setDsigReferenceTransforms(org.w3c.dom.Element[]) - */ + /* + * (non-Javadoc) + * + * @see + * at.gv.egovernment.moa.id.auth.data.IIdentityLink#setDsigReferenceTransforms(org.w3c.dom.Element + * []) + */ @Override -public void setDsigReferenceTransforms(Element[] dsigReferenceTransforms) { - this.dsigReferenceTransforms = dsigReferenceTransforms; + public void setDsigReferenceTransforms(final Element[] dsigReferenceTransforms) { + if (dsigReferenceTransforms != null) { + this.dsigReferenceTransforms = dsigReferenceTransforms.clone(); + + } } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getPublicKey() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getPublicKey() + */ @Override -public PublicKey[] getPublicKey() { - return publicKey; + public PublicKey[] getPublicKey() { + if (publicKey != null) { + return publicKey.clone(); + + } else { + return null; + + } } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setPublicKey(java.security.PublicKey[]) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setPublicKey(java.security.PublicKey[]) + */ @Override -public void setPublicKey(PublicKey[] publicKey) { - this.publicKey = publicKey; + public void setPublicKey(final PublicKey[] publicKey) { + if (publicKey != null) { + this.publicKey = publicKey.clone(); + + } } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getPrPerson() - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getPrPerson() + */ @Override -public Element getPrPerson() { + public Element getPrPerson() { + if (prPerson == null) { + prPerson = (Element) XPathUtils.selectSingleNode( + getSamlAssertion(), SimpleIdentityLinkAssertionParser.PERSON_XPATH); + + } + return prPerson; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setPrPerson(org.w3c.dom.Element) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setPrPerson(org.w3c.dom.Element) + */ @Override -public void setPrPerson(Element prPerson) { + public void setPrPerson(final Element prPerson) { this.prPerson = prPerson; } - - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIssueInstant() - */ + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#getIssueInstant() + */ @Override -public String getIssueInstant() { + public String getIssueInstant() { return issueInstant; } - /* (non-Javadoc) - * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIssueInstant(java.lang.String) - */ + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.auth.data.IIdentityLink#setIssueInstant(java.lang.String) + */ @Override -public void setIssueInstant(String issueInstant) { + public void setIssueInstant(final String issueInstant) { this.issueInstant = issueInstant; } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/SimpleIdentityLinkAssertionParser.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/SimpleIdentityLinkAssertionParser.java index 658e6a42..220469d3 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/SimpleIdentityLinkAssertionParser.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/data/SimpleIdentityLinkAssertionParser.java @@ -1,46 +1,36 @@ /******************************************************************************* - * Copyright 2014 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. + * Copyright 2014 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. ******************************************************************************/ /* - * Copyright 2003 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * Copyright 2003 Federal Chancellery Austria MOA-ID has been developed in a cooperation between + * BRZ, the Federal Chancellery Austria - ICT staff unit, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: http://www.osor.eu/eupl/ * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. */ @@ -50,22 +40,19 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; - -import org.springframework.util.Base64Utils; -import org.w3c.dom.Element; -import org.w3c.dom.traversal.NodeIterator; - import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; -import at.gv.egiz.eaaf.core.exceptions.EAAFParserException; -import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; +import at.gv.egiz.eaaf.core.exceptions.EaafParserException; +import at.gv.egiz.eaaf.core.impl.utils.DomUtils; import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; +import org.springframework.util.Base64Utils; +import org.w3c.dom.Element; +import org.w3c.dom.traversal.NodeIterator; /** - * Parses MDS from an identity link <code><saml:Assertion></code> - * <br> + * Parses MDS from an identity link <code><saml:Assertion></code> <br> * <b>This IDL parser extract NO key information!</b> - + * */ public class SimpleIdentityLinkAssertionParser { @@ -79,246 +66,163 @@ public class SimpleIdentityLinkAssertionParser { private static final String SAML = XMLNamespaceConstants.SAML_PREFIX + ":"; /** Xpath prefix for reaching XML-DSIG Namespaces */ private static final String DSIG = XMLNamespaceConstants.DSIG_PREFIX + ":"; - /** Xpath prefix for reaching ECDS Namespaces */ - private static final String ECDSA = XMLNamespaceConstants.ECDSA_PREFIX + ":"; - /** Xpath expression to the root element */ - private static final String ROOT = ""; - /** Xpath expression to the SAMLSubjectConfirmationData element */ + /** Xpath expression to the root element */ + private static final String ROOT = ""; + /** Xpath expression to the SAMLSubjectConfirmationData element */ private static final String SAML_SUBJECT_CONFIRMATION_DATA_XPATH = - ROOT - + SAML - + "AttributeStatement/" - + SAML - + "Subject/" - + SAML - + "SubjectConfirmation/" - + SAML - + "SubjectConfirmationData"; + ROOT + SAML + "AttributeStatement/" + SAML + "Subject/" + SAML + "SubjectConfirmation/" + SAML + + "SubjectConfirmationData"; /** Xpath expression to the PersonData element */ - private static final String PERSON_XPATH = - SAML_SUBJECT_CONFIRMATION_DATA_XPATH - + "/" - + PDATA - + "Person"; - /** Xpath expression to the PersonData GivenName element */ + public static final String PERSON_XPATH = + SAML_SUBJECT_CONFIRMATION_DATA_XPATH + "/" + PDATA + "Person"; + /** Xpath expression to the PersonData GivenName element */ public static final String PERSON_GIVEN_NAME_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "Name/" - + PDATA - + "GivenName"; + PERSON_XPATH + "/" + PDATA + "Name/" + PDATA + "GivenName"; /** Xpath expression to the PersonData FamilyName element */ public static final String PERSON_FAMILY_NAME_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "Name/" - + PDATA - + "FamilyName"; + PERSON_XPATH + "/" + PDATA + "Name/" + PDATA + "FamilyName"; /** Xpath expression to the PersonData DateOfBirth element */ public static final String PERSON_DATE_OF_BIRTH_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "DateOfBirth"; - /** Xpath expression to the Identification element */ - private static final String PERSON_IDENT_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "Identification"; - - /** Xpath expression to the Identification Value element */ + PERSON_XPATH + "/" + PDATA + "DateOfBirth"; + /** Xpath expression to the Identification Value element */ public static final String PERSON_IDENT_VALUE_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "Identification/" - + PDATA - + "Value"; + PERSON_XPATH + "/" + PDATA + "Identification/" + PDATA + "Value"; - /** Xpath expression to the Identification Value element */ - public static final String PERSON_IDENT_TYPE_XPATH = - PERSON_XPATH - + "/" - + PDATA - + "Identification/" - + PDATA - + "Type"; + /** Xpath expression to the Identification Value element */ + public static final String PERSON_IDENT_TYPE_XPATH = + PERSON_XPATH + "/" + PDATA + "Identification/" + PDATA + "Type"; - /** Xpath expression to the RSAKeyValue element */ - private static final String RSA_KEY_VALUE_XPATH = - ROOT - + SAML - + "AttributeStatement/" - + SAML - + "Attribute/" - + SAML - + "AttributeValue/" - + DSIG - + "RSAKeyValue"; + /** Xpath expression to the DSIG X509Certificate element */ + private static final String DSIG_CERTIFICATES_XPATH = ROOT + DSIG + "Signature/" + DSIG + + "KeyInfo/" + DSIG + "X509Data/" + DSIG + "X509Certificate"; + /** Xpath expression to the DSIG Transforms element */ + private static final String DSIG_REFERENCE_TRANSFORMATION_XPATH = + ROOT + DSIG + "Signature/" + DSIG + "SignedInfo/" + DSIG + "Reference/" + DSIG + "Transforms"; - /** Xpath expression to the ECKeyValue element */ - private static final String ECDSA_KEY_VALUE_XPATH = - ROOT - + SAML - + "AttributeStatement/" - + SAML - + "Attribute/" - + SAML - + "AttributeValue/" - + ECDSA - + "ECDSAKeyValue"; + /** The IssueInstant attribute of the SAML assertion */ + private static final String ISSUE_INSTANT_ATTR = "IssueInstant"; - - /** Xpath expression to the RSA Modulus element */ - private static final String RSA_KEY_MODULUS_XPATH = DSIG + "Modulus"; - /** Xpath expression to the RSA Exponent element */ - private static final String RSA_KEY_EXPONENT_XPATH = DSIG + "Exponent"; - /** Xpath expression to the DSIG X509Certificate element */ - private static final String DSIG_CERTIFICATES_XPATH = - ROOT - + DSIG - + "Signature/" - + DSIG - + "KeyInfo/" - + DSIG - + "X509Data/" - + DSIG - + "X509Certificate"; - /** Xpath expression to the DSIG Transforms element */ - private static final String DSIG_REFERENCE_TRANSFORMATION_XPATH = - ROOT - + DSIG - + "Signature/" - + DSIG - + "SignedInfo/" - + DSIG - + "Reference/" - + DSIG - + "Transforms"; - - /** The IssueInstant attribute of the SAML assertion */ - private static final String ISSUE_INSTANT_ATTR = "IssueInstant"; - - public static final String ASSERTIONID = "AssertionID"; - - /**This is the root element of the XML-Document provided by the Security Layer Card*/ + public static final String ASSERTIONID = "AssertionID"; + + /** This is the root element of the XML-Document provided by the Security Layer Card */ private Element assertionElem; /** - * Constructor for <code>IdentityLinkAssertionParser</code>. - * A DOM-representation of the incoming String will be created + * Constructor for <code>IdentityLinkAssertionParser</code>. A DOM-representation of the incoming + * String will be created + * * @param xmlAssertion <code><saml:Assertion></code> as String - * @throws EAAFParserException on any parsing error + * @throws EaafParserException on any parsing error */ - public SimpleIdentityLinkAssertionParser(String xmlAssertion) throws EAAFParserException { + public SimpleIdentityLinkAssertionParser(final String xmlAssertion) throws EaafParserException { try { - InputStream s = new ByteArrayInputStream(xmlAssertion.getBytes("UTF-8")); - assertionElem = DOMUtils.parseXmlValidating(s); - - } - catch (Throwable t) { - throw new EAAFParserException("parser.01", new Object[] { t.toString()}, t); - + final InputStream s = new ByteArrayInputStream(xmlAssertion.getBytes("UTF-8")); + assertionElem = DomUtils.parseXmlValidating(s); + + } catch (final Throwable t) { + throw new EaafParserException("parser.01", new Object[] {t.toString()}, t); + } } - + /** * Sets the <@link assertionElem>. + * * @param xmlAssertion the assertion element - * @throws EAAFParserException on any parsing error + * @throws EaafParserException on any parsing error */ - public SimpleIdentityLinkAssertionParser(Element xmlAssertion) throws EAAFParserException { + public SimpleIdentityLinkAssertionParser(final Element xmlAssertion) throws EaafParserException { assertionElem = xmlAssertion; } /** - * Constructor for <code>IdentityLinkAssertionParser</code>. - * A DOM-representation of the incoming Inputstream will be created + * Constructor for <code>IdentityLinkAssertionParser</code>. A DOM-representation of the incoming + * Inputstream will be created + * * @param xmlAssertion <code><saml:Assertion></code> as InputStream - * @throws EAAFParserException on any parsing error + * @throws EaafParserException on any parsing error */ - public SimpleIdentityLinkAssertionParser(InputStream xmlAssertion) throws EAAFParserException { + public SimpleIdentityLinkAssertionParser(final InputStream xmlAssertion) + throws EaafParserException { try { - assertionElem = DOMUtils.parseXmlValidating(xmlAssertion); - - } - catch (Throwable t) { - throw new EAAFParserException("parser.01", new Object[] { t.toString() }, t); - + assertionElem = DomUtils.parseXmlValidating(xmlAssertion); + + } catch (final Throwable t) { + throw new EaafParserException("parser.01", new Object[] {t.toString()}, t); + } } /** * Parses the identity link from the <code><saml:Assertion></code> + * * @return Identity link - * @throws EAAFParserException on any parsing error + * @throws EaafParserException on any parsing error */ - public IIdentityLink parseIdentityLink() throws EAAFParserException { + public IIdentityLink parseIdentityLink() throws EaafParserException { IIdentityLink identityLink; try { identityLink = new IdentityLink(); identityLink.setSamlAssertion(assertionElem); identityLink.setIssueInstant(assertionElem.getAttribute(ISSUE_INSTANT_ATTR)); - identityLink.setPrPerson((Element) - XPathUtils.selectSingleNode(assertionElem, PERSON_XPATH)); + identityLink.setPrPerson((Element) XPathUtils.selectSingleNode(assertionElem, PERSON_XPATH)); identityLink.setIdentificationValue( - XPathUtils.getElementValue(assertionElem, PERSON_IDENT_VALUE_XPATH, "")); - identityLink.setIdentificationType( - XPathUtils.getElementValue(assertionElem, PERSON_IDENT_TYPE_XPATH, "")); - - String givenname = XPathUtils.getElementValue(assertionElem, PERSON_GIVEN_NAME_XPATH, ""); - String familyname = XPathUtils.getElementValue(assertionElem, PERSON_FAMILY_NAME_XPATH, ""); + XPathUtils.getElementValue(assertionElem, PERSON_IDENT_VALUE_XPATH, "")); + identityLink.setIdentificationType( + XPathUtils.getElementValue(assertionElem, PERSON_IDENT_TYPE_XPATH, "")); + + final String givenname = + XPathUtils.getElementValue(assertionElem, PERSON_GIVEN_NAME_XPATH, ""); + final String familyname = + XPathUtils.getElementValue(assertionElem, PERSON_FAMILY_NAME_XPATH, ""); + + // replace ' in name with ' + // givenname = givenname.replaceAll("'", "'"); + // familyname = familyname.replaceAll("'", "'"); - // replace ' in name with ' -// givenname = givenname.replaceAll("'", "'"); -// familyname = familyname.replaceAll("'", "'"); - identityLink.setGivenName(givenname); identityLink.setFamilyName(familyname); identityLink.setDateOfBirth( - XPathUtils.getElementValue(assertionElem, PERSON_DATE_OF_BIRTH_XPATH, "")); - NodeIterator dsigRefTransforms = - XPathUtils.selectNodeIterator(assertionElem, DSIG_REFERENCE_TRANSFORMATION_XPATH); - List transElems = new ArrayList(); - Element transformsElem; - while ((transformsElem = (Element) dsigRefTransforms.nextNode()) != null) { - transElems.add(transformsElem); - } - Element[] result = new Element[transElems.size()]; - transElems.toArray(result); - identityLink.setDsigReferenceTransforms(result); - - //identityLink.setPublicKey(getPublicKeys()); - - } - catch (Throwable t) { - throw new EAAFParserException("parser.01", new Object[] { t.toString() }, t); + XPathUtils.getElementValue(assertionElem, PERSON_DATE_OF_BIRTH_XPATH, "")); + final NodeIterator dsigRefTransforms = + XPathUtils.selectNodeIterator(assertionElem, DSIG_REFERENCE_TRANSFORMATION_XPATH); + final List transElems = new ArrayList(); + Element transformsElem; + while ((transformsElem = (Element) dsigRefTransforms.nextNode()) != null) { + transElems.add(transformsElem); + } + final Element[] result = new Element[transElems.size()]; + transElems.toArray(result); + identityLink.setDsigReferenceTransforms(result); + + // identityLink.setPublicKey(getPublicKeys()); + + } catch (final Throwable t) { + throw new EaafParserException("parser.01", new Object[] {t.toString()}, t); } return identityLink; } /** - * Parses a string array of decoded base64 certificates from - * the <code><InfoboxReadResponse></code> found in the dsig-signature - * @return String[] with raw-certificates from the dsig-signature keyinfo - * @throws Exception - */ + * Parses a string array of decoded base64 certificates from the + * <code><InfoboxReadResponse></code> found in the dsig-signature + * + * @return String[] with raw-certificates from the dsig-signature keyinfo + * @throws Exception + */ public String[] getCertificates() throws Exception { - List certs = new ArrayList(); - NodeIterator rsaIter = - XPathUtils.selectNodeIterator(assertionElem, DSIG_CERTIFICATES_XPATH); + final List certs = new ArrayList(); + final NodeIterator rsaIter = + XPathUtils.selectNodeIterator(assertionElem, DSIG_CERTIFICATES_XPATH); Element certElem; while ((certElem = (Element) rsaIter.nextNode()) != null) { - String content = DOMUtils.getText(certElem); - certs.add(new String(Base64Utils.decodeFromString(content))); - + final String content = DomUtils.getText(certElem); + certs.add(new String(Base64Utils.decodeFromString(content), "UTF-8")); + } - String[] result = new String[certs.size()]; + final String[] result = new String[certs.size()]; certs.toArray(result); return result; diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java index ce9ba57c..c785e1cb 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java @@ -1,29 +1,22 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.auth.modules; import java.io.ByteArrayOutputStream; @@ -34,21 +27,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileItemFactory; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.apache.commons.lang3.ArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ResourceLoader; - 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; @@ -56,216 +36,233 @@ import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; -import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.idp.controller.ProtocolFinalizationController; import at.gv.egiz.eaaf.core.impl.idp.process.springweb.AbstractTask; -import at.gv.egiz.eaaf.core.impl.utils.DataURLBuilder; +import at.gv.egiz.eaaf.core.impl.utils.DataUrlBuilder; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ResourceLoader; /** - * Task based counterpart to {@link AuthServlet}, providing the same utility methods (error handling, parameter parsing - * etc.).</p> The code has been taken from {@link AuthServlet}. + * Task based counterpart to {@link AuthServlet}, providing the same utility methods (error + * handling, parameter parsing etc.). + * </p> + * The code has been taken from {@link AuthServlet}. */ public abstract class AbstractAuthServletTask extends AbstractTask { - private static final Logger log = LoggerFactory.getLogger(AbstractAuthServletTask.class); - - @Autowired(required=true) IProtocolAuthenticationService protAuchService; - @Autowired(required=true) protected IRequestStorage requestStoreage; - @Autowired(required=true) protected IConfiguration authConfig; - @Autowired(required=true) protected ResourceLoader resourceLoader; - - @Autowired protected IRevisionLogger revisionsLogger; - - protected static final String ERROR_CODE_PARAM = "errorid"; - - protected IRequest pendingReq = null; - - @Override - public abstract void execute(ExecutionContext executionContext, HttpServletRequest request, - HttpServletResponse response) throws TaskExecutionException; - - - @Override - protected final IRequest internalExecute(IRequest pendingReq, ExecutionContext executionContext, HttpServletRequest request, - HttpServletResponse response) throws TaskExecutionException { - //set pending-request object - this.pendingReq = pendingReq; - - //add latest pendingRequestId on execution context - executionContext.put(EAAFConstants.PROCESS_ENGINE_PENDINGREQUESTID, pendingReq.getPendingRequestId()); - - //execute task specific action - execute(executionContext, request, response); - - //return pending-request object - return this.pendingReq; - } - - /** - * Redirect the authentication process to protocol specific finalization endpoint. - * @param executionContext - * - * @param pendingReq Actually processed protocol specific authentication request - * @param httpResp - * @throws IOException - * @throws EAAFException - */ - protected void performRedirectToProtocolFinialization(ExecutionContext executionContext, IRequest pendingReq, HttpServletRequest httpReq, HttpServletResponse httpResp) throws EAAFException, IOException { - final Object frontChannelRedirectFlagObj = executionContext.get(EAAFConstants.PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT); - if (frontChannelRedirectFlagObj != null && frontChannelRedirectFlagObj instanceof Boolean && - (Boolean)frontChannelRedirectFlagObj) { - log.info("AuthProcess finished. Forward to Protocol finalization."); - protAuchService.finalizeAuthentication(httpReq, httpResp, pendingReq); - - } else { - log.info("AuthProcess finished. Redirect to Protocol Dispatcher."); - requestStoreage.storePendingRequest(pendingReq); - performRedirectToItself(pendingReq, httpResp, ProtocolFinalizationController.ENDPOINT_FINALIZEPROTOCOL); - - } - - - - } - - /** - * Redirect the authentication process to IDP itself - * - * @param pendingReq Actually processed protocol specific authentication request - * @param httpResp - * @param idpEndPoint Servlet EndPoint that should receive the redirect - */ - protected void performRedirectToItself(IRequest pendingReq, HttpServletResponse httpResp, String idpEndPoint) { - final String redirectURL = new DataURLBuilder().buildDataURL(pendingReq.getAuthURL(), - idpEndPoint, pendingReq.getPendingRequestId()); - - httpResp.setContentType("text/html"); - httpResp.setStatus(302); - httpResp.addHeader("Location", redirectURL); - log.debug("REDIRECT TO: " + redirectURL); - - } - - - /** - * Parses the request input stream for parameters, assuming parameters are - * encoded UTF-8 (no standard exists how browsers should encode them). - * - * @param req - * servlet request - * - * @return mapping parameter name -> value - * - * @throws IOException - * if parsing request parameters fails. - * - * @throws FileUploadException - * if parsing request parameters fails. - */ - protected Map<String, String> getParameters(HttpServletRequest req) throws IOException, - FileUploadException { - - final Map<String, String> parameters = new HashMap<String, String>(); - - if (ServletFileUpload.isMultipartContent(req)) { - // request is encoded as mulitpart/form-data - final FileItemFactory factory = new DiskFileItemFactory(); - ServletFileUpload upload = null; - upload = new ServletFileUpload(factory); - List items = null; - items = upload.parseRequest(req); - for (int i = 0; i < items.size(); i++) { - final FileItem item = (FileItem) items.get(i); - if (item.isFormField()) { - // Process only form fields - no file upload items - parameters.put(item.getFieldName(), item.getString("UTF-8")); - - //log requests on trace - if (log.isTraceEnabled()) { - final String logString = item.getString("UTF-8"); - - // TODO use RegExp - final String startS = "<pr:Identification><pr:Value>"; - final String endS = "</pr:Value><pr:Type>urn:publicid:gv.at:baseid</pr:Type>"; - String logWithMaskedBaseid = logString; - final int start = logString.indexOf(startS); - if (start > -1) { - final int end = logString.indexOf(endS); - if (end > -1) { - logWithMaskedBaseid = logString.substring(0, start); - logWithMaskedBaseid += startS; - logWithMaskedBaseid += "xxxxxxxxxxxxxxxxxxxxxxxx"; - logWithMaskedBaseid += logString.substring(end, - logString.length()); - } - } - - log.debug("Processed multipart/form-data request parameter: \nName: " - + item.getFieldName() - + "\nValue: " - + logWithMaskedBaseid); - } - - } - } - } - - else { - final Iterator<Entry<String, String[]>> requestParamIt = req.getParameterMap().entrySet().iterator(); - while (requestParamIt.hasNext()) { - final Entry<String, String[]> entry = requestParamIt.next(); - final String key = entry.getKey(); - final String[] values = entry.getValue(); - // take the last value from the value array since the legacy code above also does it this way - parameters.put(key, ArrayUtils.isEmpty(values) ? null : values[values.length-1]); - } - - } - - return parameters; - } - - /** - * Reads bytes up to a delimiter, consuming the delimiter. - * - * @param in - * input stream - * @param delimiter - * delimiter character - * @return String constructed from the read bytes - * @throws IOException - */ - protected String readBytesUpTo(InputStream in, char delimiter) - throws IOException { - final ByteArrayOutputStream bout = new ByteArrayOutputStream(); - boolean done = false; - int b; - while (!done && (b = in.read()) >= 0) { - if (b == delimiter) - done = true; - else - bout.write(b); - } - return bout.toString(); - } - - /** - * Adds a parameter to a URL. - * - * @param url - * the URL - * @param paramname - * parameter name - * @param paramvalue - * parameter value - * @return the URL with parameter added - */ - protected static String addURLParameter(String url, String paramname, - String paramvalue) { - final String param = paramname + "=" + paramvalue; - if (url.indexOf("?") < 0) - return url + "?" + param; - else - return url + "&" + param; - } + private static final Logger log = LoggerFactory.getLogger(AbstractAuthServletTask.class); + + @Autowired(required = true) + IProtocolAuthenticationService protAuchService; + @Autowired(required = true) + protected IRequestStorage requestStoreage; + @Autowired(required = true) + protected IConfiguration authConfig; + @Autowired(required = true) + protected ResourceLoader resourceLoader; + + @Autowired + protected IRevisionLogger revisionsLogger; + + protected static final String ERROR_CODE_PARAM = "errorid"; + + protected IRequest pendingReq = null; + + @Override + public abstract void execute(ExecutionContext executionContext, HttpServletRequest request, + HttpServletResponse response) throws TaskExecutionException; + + + @Override + protected final IRequest internalExecute(final IRequest pendingReq, + final ExecutionContext executionContext, final HttpServletRequest request, + final HttpServletResponse response) throws TaskExecutionException { + // set pending-request object + this.pendingReq = pendingReq; + + // add latest pendingRequestId on execution context + executionContext.put(EAAFConstants.PROCESS_ENGINE_PENDINGREQUESTID, + pendingReq.getPendingRequestId()); + + // execute task specific action + execute(executionContext, request, response); + + // return pending-request object + return this.pendingReq; + } + + /** + * Redirect the authentication process to protocol specific finalization endpoint. + * + * @param executionContext + * + * @param pendingReq Actually processed protocol specific authentication request + * @param httpResp http response object + * @throws IOException In case of a general error + * @throws EaafException In case of an application error + */ + protected void performRedirectToProtocolFinialization(final ExecutionContext executionContext, + final IRequest pendingReq, final HttpServletRequest httpReq, + final HttpServletResponse httpResp) throws EaafException, IOException { + final Object frontChannelRedirectFlagObj = + executionContext.get(EAAFConstants.PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT); + if (frontChannelRedirectFlagObj != null && frontChannelRedirectFlagObj instanceof Boolean + && (Boolean) frontChannelRedirectFlagObj) { + log.info("AuthProcess finished. Forward to Protocol finalization."); + protAuchService.finalizeAuthentication(httpReq, httpResp, pendingReq); + + } else { + log.info("AuthProcess finished. Redirect to Protocol Dispatcher."); + requestStoreage.storePendingRequest(pendingReq); + performRedirectToItself(pendingReq, httpResp, + ProtocolFinalizationController.ENDPOINT_FINALIZEPROTOCOL); + + } + + + + } + + /** + * Redirect the authentication process to IDP itself. + * + * @param pendingReq Actually processed protocol specific authentication request + * @param httpResp http response + * @param idpEndPoint Servlet EndPoint that should receive the redirect + */ + protected void performRedirectToItself(final IRequest pendingReq, + final HttpServletResponse httpResp, final String idpEndPoint) { + final String redirectUrl = new DataUrlBuilder().buildDataUrl(pendingReq.getAuthUrl(), + idpEndPoint, pendingReq.getPendingRequestId()); + + httpResp.setContentType("text/html"); + httpResp.setStatus(302); + httpResp.addHeader("Location", redirectUrl); + log.debug("REDIRECT TO: " + redirectUrl); + + } + + + /** + * Parses the request input stream for parameters, assuming parameters are encoded UTF-8 (no + * standard exists how browsers should encode them). + * + * @param req servlet request + * + * @return mapping parameter name -> value + * + * @throws IOException if parsing request parameters fails. + * + * @throws FileUploadException if parsing request parameters fails. + */ + protected Map<String, String> getParameters(final HttpServletRequest req) + throws IOException, FileUploadException { + + final Map<String, String> parameters = new HashMap<>(); + + if (ServletFileUpload.isMultipartContent(req)) { + // request is encoded as mulitpart/form-data + final FileItemFactory factory = new DiskFileItemFactory(); + ServletFileUpload upload = null; + upload = new ServletFileUpload(factory); + List items = null; + items = upload.parseRequest(req); + for (int i = 0; i < items.size(); i++) { + final FileItem item = (FileItem) items.get(i); + if (item.isFormField()) { + // Process only form fields - no file upload items + parameters.put(item.getFieldName(), item.getString("UTF-8")); + + // log requests on trace + if (log.isTraceEnabled()) { + final String logString = item.getString("UTF-8"); + + // TODO use RegExp + final String startS = "<pr:Identification><pr:Value>"; + final String endS = "</pr:Value><pr:Type>urn:publicid:gv.at:baseid</pr:Type>"; + String logWithMaskedBaseid = logString; + final int start = logString.indexOf(startS); + if (start > -1) { + final int end = logString.indexOf(endS); + if (end > -1) { + logWithMaskedBaseid = logString.substring(0, start); + logWithMaskedBaseid += startS; + logWithMaskedBaseid += "xxxxxxxxxxxxxxxxxxxxxxxx"; + logWithMaskedBaseid += logString.substring(end, logString.length()); + } + } + + log.debug("Processed multipart/form-data request parameter: \nName: " + + item.getFieldName() + "\nValue: " + logWithMaskedBaseid); + } + + } + } + + } else { + final Iterator<Entry<String, String[]>> requestParamIt = + req.getParameterMap().entrySet().iterator(); + while (requestParamIt.hasNext()) { + final Entry<String, String[]> entry = requestParamIt.next(); + final String key = entry.getKey(); + final String[] values = entry.getValue(); + // take the last value from the value array since the legacy code above also does it this + // way + parameters.put(key, ArrayUtils.isEmpty(values) ? null : values[values.length - 1]); + } + + } + + return parameters; + } + + /** + * Reads bytes up to a delimiter, consuming the delimiter. + * + * @param in input stream + * @param delimiter delimiter character + * @return String constructed from the read bytes + * @throws IOException In case of a general error + */ + protected String readBytesUpTo(final InputStream in, final char delimiter) throws IOException { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + boolean done = false; + int b; + while (!done && (b = in.read()) >= 0) { + if (b == delimiter) { + done = true; + } else { + bout.write(b); + } + } + return bout.toString("UTF-8"); + + } + + /** + * Adds a parameter to a URL. + * + * @param url the URL + * @param paramname parameter name + * @param paramvalue parameter value + * @return the URL with parameter added + */ + protected static String addUrlParameter(final String url, final String paramname, + final String paramvalue) { + final String param = paramname + "=" + paramvalue; + if (url.indexOf("?") < 0) { + return url + "?" + param; + } else { + return url + "&" + param; + } + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/ModuleRegistration.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/ModuleRegistration.java index 6789c802..b04b000e 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/ModuleRegistration.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/ModuleRegistration.java @@ -1,43 +1,38 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.auth.modules; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; - import javax.annotation.PostConstruct; - +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.auth.modules.AuthModule; +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.impl.idp.process.ProcessDefinitionParserException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,135 +40,124 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; -import at.gv.egiz.eaaf.core.api.IRequest; -import at.gv.egiz.eaaf.core.api.idp.auth.modules.AuthModule; -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.impl.idp.process.ProcessDefinitionParserException; - /** - * This class handles registering modules. The modules are detected either with - * the ServiceLoader mechanism or via Spring. All detected modules are ranked - * according to their priority. + * This class handles registering modules. The modules are detected either with the ServiceLoader + * mechanism or via Spring. All detected modules are ranked according to their priority. */ public class ModuleRegistration { - private static ModuleRegistration instance = new ModuleRegistration(); - - private final List<AuthModule> priorizedModules = new ArrayList<>(); - - @Autowired - private ApplicationContext ctx; - - @Autowired - private ProcessEngine processEngine; - - private final Logger log = LoggerFactory.getLogger(getClass()); - - public static ModuleRegistration getInstance() { - return instance; - } - - private ModuleRegistration() { - } - - @PostConstruct - private void init() { - // load modules via the ServiceLoader - initServiceLoaderModules(); - - // load modules via Spring - initSpringModules(); - - // order modules according to their priority - sortModules(); - - instance = this; - } - - /** - * Discovers modules which use the ServiceLoader mechanism. - */ - private void initServiceLoaderModules() { - log.info("Looking for auth modules."); - final ServiceLoader<AuthModule> loader = ServiceLoader.load(AuthModule.class); - final Iterator<AuthModule> modules = loader.iterator(); - while (modules.hasNext()) { - final AuthModule module = modules.next(); - log.info("Detected module {}", module.getClass().getName()); - registerModuleProcessDefinitions(module); - priorizedModules.add(module); - } - } - - /** - * Discovers modules which use Spring. - */ - private void initSpringModules() { - log.debug("Discovering Spring modules."); - final Map<String, AuthModule> modules = ctx.getBeansOfType(AuthModule.class); - for (final AuthModule module : modules.values()) { - registerModuleProcessDefinitions(module); - priorizedModules.add(module); - } - } - - /** - * Registers the resource uris for the module. - * - * @param module - * the module. - */ - private void registerModuleProcessDefinitions(AuthModule module) { - for (final String uri : module.getProcessDefinitions()) { - final Resource resource = ctx.getResource(uri); - if (resource.isReadable()) { - log.info("Registering process definition '{}'.", uri); - try (InputStream processDefinitionInputStream = resource.getInputStream()) { - processEngine.registerProcessDefinition(processDefinitionInputStream); - } catch (final IOException e) { - log.error("Process definition '{}' could NOT be read.", uri, e); - } catch (final ProcessDefinitionParserException e) { - log.error("Error while parsing process definition '{}'", uri, e); - } - } else { - log.error("Process definition '{}' cannot be read.", uri); - } - } - } - - /** - * Order the modules in descending order according to their priority. - */ - private void sortModules() { - Collections.sort(priorizedModules, new Comparator<AuthModule>() { - @Override - public int compare(AuthModule thisAuthModule, AuthModule otherAuthModule) { - final int thisOrder = thisAuthModule.getPriority(); - final int otherOrder = otherAuthModule.getPriority(); - return (thisOrder < otherOrder ? 1 : (thisOrder == otherOrder ? 0 : -1)); - } - }); - } - - /** - * Returns the process description id of the first process, in the highest ranked - * module, which is able to work with the given execution context. - * - * @param context - * the {@link ExecutionContext}. - * @param pendingReq the current processed {@link IRequest} - * @return the process id or {@code null} - */ - public String selectProcess(ExecutionContext context, IRequest pendingReq) { - for (final AuthModule module : priorizedModules) { - final String id = module.selectProcess(context, pendingReq); - if (StringUtils.isNotEmpty(id)) { - log.debug("Process with id '{}' selected, for context '{}'.", id, context); - return id; - } - } - log.info("No process is able to handle context '{}'.", context); - return null; - } + //private static ModuleRegistration instance = new ModuleRegistration(); + + private final List<AuthModule> priorizedModules = new ArrayList<>(); + + @Autowired + private ApplicationContext ctx; + + @Autowired + private ProcessEngine processEngine; + + private final Logger log = LoggerFactory.getLogger(getClass()); + +// public static ModuleRegistration getInstance() { +// return ctx.; +// } + + private ModuleRegistration() { + + } + + @PostConstruct + private void init() { + // load modules via the ServiceLoader + initServiceLoaderModules(); + + // load modules via Spring + initSpringModules(); + + // order modules according to their priority + sortModules(); + + //instance = this; + } + + /** + * Discovers modules which use the ServiceLoader mechanism. + */ + private void initServiceLoaderModules() { + log.info("Looking for auth modules."); + final ServiceLoader<AuthModule> loader = ServiceLoader.load(AuthModule.class); + final Iterator<AuthModule> modules = loader.iterator(); + while (modules.hasNext()) { + final AuthModule module = modules.next(); + log.info("Detected module {}", module.getClass().getName()); + registerModuleProcessDefinitions(module); + priorizedModules.add(module); + } + } + + /** + * Discovers modules which use Spring. + */ + private void initSpringModules() { + log.debug("Discovering Spring modules."); + final Map<String, AuthModule> modules = ctx.getBeansOfType(AuthModule.class); + for (final AuthModule module : modules.values()) { + registerModuleProcessDefinitions(module); + priorizedModules.add(module); + } + } + + /** + * Registers the resource uris for the module. + * + * @param module the module. + */ + private void registerModuleProcessDefinitions(final AuthModule module) { + for (final String uri : module.getProcessDefinitions()) { + final Resource resource = ctx.getResource(uri); + if (resource.isReadable()) { + log.info("Registering process definition '{}'.", uri); + try (InputStream processDefinitionInputStream = resource.getInputStream()) { + processEngine.registerProcessDefinition(processDefinitionInputStream); + } catch (final IOException e) { + log.error("Process definition '{}' could NOT be read.", uri, e); + } catch (final ProcessDefinitionParserException e) { + log.error("Error while parsing process definition '{}'", uri, e); + } + } else { + log.error("Process definition '{}' cannot be read.", uri); + } + } + } + + /** + * Order the modules in descending order according to their priority. + */ + private void sortModules() { + Collections.sort(priorizedModules, (thisAuthModule, otherAuthModule) -> { + final int thisOrder = thisAuthModule.getPriority(); + final int otherOrder = otherAuthModule.getPriority(); + return (thisOrder < otherOrder ? 1 : (thisOrder == otherOrder ? 0 : -1)); + }); + } + + /** + * Returns the process description id of the first process, in the highest ranked module, which is + * able to work with the given execution context. + * + * @param context the {@link ExecutionContext}. + * @param pendingReq the current processed {@link IRequest} + * @return the process id or {@code null} + */ + public String selectProcess(final ExecutionContext context, final IRequest pendingReq) { + for (final AuthModule module : priorizedModules) { + final String id = module.selectProcess(context, pendingReq); + if (StringUtils.isNotEmpty(id)) { + log.debug("Process with id '{}' selected, for context '{}'.", id, context); + return id; + } + } + log.info("No process is able to handle context '{}'.", context); + return null; + } } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java index 2edf8a75..a5030851 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java @@ -1,25 +1,22 @@ -/******************************************************************************* - * Copyright 2019 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2019 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.auth.services; import java.io.IOException; @@ -27,499 +24,518 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.List; - import javax.naming.ConfigurationException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.ArrayUtils; -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 org.springframework.context.ApplicationContext; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Service; - import at.gv.egiz.components.eventlog.api.EventConstants; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.IRequestStorage; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; -import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfiguration; -import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfigurationFactory; -import at.gv.egiz.eaaf.core.api.gui.IGUIFormBuilder; +import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration; +import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfigurationFactory; +import at.gv.egiz.eaaf.core.api.gui.IGuiFormBuilder; import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.idp.IAction; import at.gv.egiz.eaaf.core.api.idp.IAuthData; import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder; import at.gv.egiz.eaaf.core.api.idp.IModulInfo; -import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +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.auth.ISsoManager; import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; -import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; +import at.gv.egiz.eaaf.core.api.idp.slo.SloInformationInterface; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; import at.gv.egiz.eaaf.core.api.logging.IStatisticLogger; import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; import at.gv.egiz.eaaf.core.exceptions.AuthnRequestValidatorException; -import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; -import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.exceptions.EAAFSSOException; -import at.gv.egiz.eaaf.core.exceptions.GUIBuildException; +import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafSsoException; +import at.gv.egiz.eaaf.core.exceptions.GuiBuildException; import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException; -import at.gv.egiz.eaaf.core.impl.gui.AbstractGUIFormBuilderConfiguration; +import at.gv.egiz.eaaf.core.impl.gui.AbstractGuiFormBuilderConfiguration; import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; -import at.gv.egiz.eaaf.core.impl.utils.HTTPUtils; +import at.gv.egiz.eaaf.core.impl.utils.HttpUtils; +import org.apache.commons.lang3.ArrayUtils; +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 org.springframework.context.ApplicationContext; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Service; @Service public class ProtocolAuthenticationService implements IProtocolAuthenticationService { - private static final Logger log = LoggerFactory.getLogger(ProtocolAuthenticationService.class); - - private static final List<String> ERROR_LOGGER_ON_INFO_LEVEL = - Arrays.asList( - IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP - ); - - @Autowired(required=true) private ApplicationContext applicationContext; - @Autowired(required=true) private IAuthenticationManager authmanager; - @Autowired(required=true) private IAuthenticationDataBuilder authDataBuilder; - @Autowired(required=true) private IGUIBuilderConfigurationFactory guiConfigFactory; - @Autowired(required=true) private IStatusMessenger statusMessager; - @Autowired(required=true) private IRequestStorage requestStorage; - @Autowired(required=true) IPendingRequestIdGenerationStrategy pendingReqIdGenerationStrategy; - - @Autowired(required=false) private ISSOManager ssoManager; - @Autowired private IStatisticLogger statisticLogger; - @Autowired private IRevisionLogger revisionsLogger; - - - private IGUIFormBuilder guiBuilder; - - /* (non-Javadoc) - * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService#performAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) - */ - @Override - public void performAuthentication(final HttpServletRequest req, final HttpServletResponse resp, - final IRequest pendingReq) throws IOException, EAAFException { - try { - if (pendingReq.isNeedAuthentication()) { - //request needs authentication --> start authentication process ... - - //set pendingRequestId to support asynchrony message-processing - ((RequestImpl)pendingReq).setPendingRequestId(pendingReqIdGenerationStrategy.generateExternalPendingRequestId()); - - //load Parameters from OnlineApplicationConfiguration - final ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); - - if (oaParam == null) - throw new EAAFAuthenticationException( - IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOSPCONFIG, - new Object[] { pendingReq.getSPEntityId() }); - - if (authmanager.doAuthentication(req, resp, pendingReq)) { - //pending request is already authenticated --> protocol-specific postProcessing can start directly - finalizeAuthentication(req, resp, pendingReq); - - //transaction is finished, log transaction finished event - revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); - - } - - } else { - executeProtocolSpecificAction(req, resp, pendingReq, null); - - } - - } catch (final Exception e) { - buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - } - - /* (non-Javadoc) - * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService#finalizeAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) - */ - @Override - public void finalizeAuthentication(final HttpServletRequest req, final HttpServletResponse resp, final IRequest pendingReq) throws EAAFException, IOException{ - log.debug("Finalize PendingRequest with ID " + pendingReq.getPendingRequestId()); - try { - - //check if pending-request has 'abortedByUser' flag set - if (pendingReq.isAbortedByUser()) { - //send authentication aborted error to Service Provider - buildProtocolSpecificErrorResponse( - new EAAFAuthenticationException( - IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP, - new Object[] {}), - req, resp, pendingReq); - - //do not remove the full active SSO-Session - // in case of only one Service-Provider authentication request is aborted - if ( !pendingReq.needSingleSignOnFunctionality()) { - requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); - - } - - //check if pending-request are authenticated - } else if (pendingReq.isAuthenticated() && !pendingReq.isNeedUserConsent()) { - internalFinalizeAuthenticationProcess(req, resp, pendingReq); - - } else { - //suspect state: pending-request is not aborted but also are not authenticated - log.warn("PendingRequest flag for 'authenticated':{} and 'needConsent':{}", pendingReq.isAuthenticated(), pendingReq.isNeedUserConsent()); - if (pendingReq.isNeedUserConsent()) { - log.error("PendingRequest NEEDS user-consent. Can NOT fininalize authentication --> Abort authentication process!"); - - } else { - log.error("PendingRequest is NOT authenticated --> Abort authentication process!"); - - } - - handleErrorNoRedirect( - new EAAFException( - "auth.20", - null), req, resp, true); - - } - - } catch (final Exception e) { - log.error("Finalize authentication protocol FAILED." , e); - buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); - - } - - //remove pending-request - if (pendingReq != null) { - requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); - revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); - - } - } - - - @Override - public void buildProtocolSpecificErrorResponse(final Throwable throwable, final HttpServletRequest req, - final HttpServletResponse resp, final IRequest protocolRequest) throws EAAFException, IOException { - try { - - final Class<?> clazz = Class.forName(protocolRequest.requestedModule()); - - if (clazz == null || - !IModulInfo.class.isAssignableFrom(clazz)) { - log.error("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); - throw new Exception("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); - - } - - final IModulInfo handlingModule = (IModulInfo) applicationContext.getBean(clazz); - - if (handlingModule.generateErrorMessage( - throwable, req, resp, protocolRequest)) { - - //log Error to technical log - logExceptionToTechnicalLog(throwable); - - //log Error Message - statisticLogger.logErrorOperation(throwable, protocolRequest); - - //write revision log entries - revisionsLogger.logEvent(protocolRequest, EventConstants.TRANSACTION_ERROR, protocolRequest.getUniqueTransactionIdentifier()); - - return; - - } else { - handleErrorNoRedirect(throwable, req, resp, true); - - } - - } catch (final Throwable e) { - handleErrorNoRedirect(throwable, req, resp, true); - - } - - } - - @Override - public void handleErrorNoRedirect(final Throwable throwable, final HttpServletRequest req, - final HttpServletResponse resp, final boolean writeExceptionToStatisticLog) throws IOException, EAAFException { - - //log Exception into statistic database - if (writeExceptionToStatisticLog) - statisticLogger.logErrorOperation(throwable); - - //write errror to console - logExceptionToTechnicalLog(throwable); - - //return error to Web browser - if (throwable instanceof EAAFException || throwable instanceof ProcessExecutionException) - internalMOAIDExceptionHandler(req, resp, (Exception)throwable, false); - - else { - //write generic message for general exceptions - final String msg = statusMessager.getMessage(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC, null); - writeHTMLErrorResponse(req, resp, msg, "9199", null, (Exception) throwable); - - } - - } - - - public void setGuiBuilder(IGUIFormBuilder guiBuilder) { - this.guiBuilder = guiBuilder; - } - - /** - * Finalize the requested protocol operation - * - * @param httpReq HttpServletRequest - * @param httpResp HttpServletResponse - * @param protocolRequest Authentication request which is actually in process - * @param moaSession MOASession object, which is used to generate the protocol specific authentication information - * @throws Exception - */ - protected void internalFinalizeAuthenticationProcess(final HttpServletRequest req, final HttpServletResponse resp, - final IRequest pendingReq) throws Exception { - - String newSSOSessionId = null; - - //if Single Sign-On functionality is enabled for this request - if (pendingReq.needSingleSignOnFunctionality()) { - if (ssoManager != null) { - newSSOSessionId = ssoManager.createNewSSOSessionCookie(req, resp, pendingReq); - if (StringUtils.isEmpty(pendingReq.getInternalSSOSessionIdentifier())) - ssoManager.createNewSSOSession(pendingReq, newSSOSessionId); - - } else - log.warn("SSO is requested but there is not SSO Session-Manager available"); - - } - - //build authenticationdata from session information and OA configuration - final IAuthData authData = authDataBuilder.buildAuthenticationData(pendingReq); - - //execute the protocol-specific action - final SLOInformationInterface sloInformation = executeProtocolSpecificAction(req, resp, pendingReq, authData); - - //Store OA specific SSO session information if an SSO cookie is set - if (StringUtils.isNotEmpty(newSSOSessionId)) { - try { - ssoManager.updateSSOSession(pendingReq, newSSOSessionId, sloInformation); - - } catch (final EAAFSSOException e) { - log.warn("SSO Session information can not be stored -> SSO is not enabled!"); - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - - } else { - //remove MOASession from database - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - - //Advanced statistic logging - statisticLogger.logSuccessOperation(pendingReq, authData, StringUtils.isNotEmpty(newSSOSessionId)); - - } - - /** - * Executes the requested protocol action - * - * @param httpReq HttpServletRequest - * @param httpResp HttpServletResponse - * @param protocolRequest Authentication request which is actually in process - * @param authData Service-provider specific authentication data - * - * @return Return Single LogOut information or null if protocol supports no SSO - * - * @throws Exception - */ - private SLOInformationInterface executeProtocolSpecificAction(final HttpServletRequest httpReq, final HttpServletResponse httpResp, - final IRequest pendingReq, final IAuthData authData) throws Exception { - try { - // request needs no authentication --> start request processing - final Class<?> clazz = Class.forName(pendingReq.requestedAction()); - if (clazz == null || - !IAction.class.isAssignableFrom(clazz)) { - log.error("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); - throw new Exception("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); - - } - - final IAction protocolAction = (IAction) applicationContext.getBean(clazz); - return protocolAction.processRequest(pendingReq, httpReq, httpResp, authData); - - } catch (final ClassNotFoundException e) { - log.error("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); - throw new Exception("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); - } - - } - - /** - * Write a Exception to the MOA-ID-Auth internal technical log - * - * @param loggedException Exception to log - */ - protected void logExceptionToTechnicalLog(final Throwable loggedException) { - if (!( loggedException instanceof EAAFException - || loggedException instanceof ProcessExecutionException )) { - log.error("Receive an internal error: Message=" + loggedException.getMessage(), loggedException); - - } else { - if (loggedException instanceof EAAFAuthenticationException && - ERROR_LOGGER_ON_INFO_LEVEL.contains( - ((EAAFAuthenticationException) loggedException).getErrorId())) { - if (log.isDebugEnabled() || log.isTraceEnabled()) { - log.info(loggedException.getMessage(), loggedException); - - } else { - log.info(loggedException.getMessage()); - - } - - } else { - if (log.isDebugEnabled() || log.isTraceEnabled()) { - log.warn(loggedException.getMessage(), loggedException); - - } else { - log.warn(loggedException.getMessage()); - - } - } - } - } - - private void writeHTMLErrorResponse(@NonNull final HttpServletRequest httpReq, @NonNull final HttpServletResponse httpResp, - @NonNull final String msg, @NonNull final String errorCode, @Nullable final Object[] params, @NonNull final Exception error) throws IOException, EAAFException { - - try { - final IGUIBuilderConfiguration config - = guiConfigFactory.getDefaultErrorGUI(HTTPUtils.extractAuthURLFromRequest(httpReq)); - - - String[] errorCodeParams = null; - if (params == null) - errorCodeParams = new String[] {}; - else { - errorCodeParams = new String[params.length]; - for (int i=0; i<params.length; i++) { - if (params[i] != null) - errorCodeParams[i] = params[i].toString(); - else - errorCodeParams[i] = "null"; - - } - } - - - - //add errorcode and errormessage - if (config instanceof ModifyableGuiBuilderConfiguration) { - ((ModifyableGuiBuilderConfiguration)config) - .putCustomParameter(AbstractGUIFormBuilderConfiguration.PARAM_GROUP_MSG, - PARAM_GUI_ERROMSG, msg); - ((ModifyableGuiBuilderConfiguration)config) - .putCustomParameter(AbstractGUIFormBuilderConfiguration.PARAM_GROUP_MSG, - PARAM_GUI_ERRORCODE, errorCode); - ((ModifyableGuiBuilderConfiguration)config) - .putCustomParameterWithOutEscaption(AbstractGUIFormBuilderConfiguration.PARAM_GROUP_MSG, - PARAM_GUI_ERRORCODEPARAMS, ArrayUtils.toString(errorCodeParams)); - - //add stacktrace if debug is enabled - if (log.isTraceEnabled()) { - ((ModifyableGuiBuilderConfiguration)config) - .putCustomParameter(AbstractGUIFormBuilderConfiguration.PARAM_GROUP_MSG, - PARAM_GUI_ERRORSTACKTRACE, getStacktraceFromException(error)); - - } - - } else - log.info("Can not ADD error message, because 'GUIBuilderConfiguration' is not modifieable "); - - - - guiBuilder.build(httpReq, httpResp, config, "Error-Message"); - - } catch (final GUIBuildException e) { - log.warn("Can not build error-message GUI.", e); - throw new EAAFException("9199", null, e); - - - } - - } - - private String getStacktraceFromException(final Exception ex) { - final StringWriter errors = new StringWriter(); - ex.printStackTrace(new PrintWriter(errors)); - return errors.toString(); - - } - - private void internalMOAIDExceptionHandler(final HttpServletRequest req, final HttpServletResponse resp, final Exception e, final boolean writeExceptionToStatisicLog) throws IOException, EAAFException { - if (e instanceof ProtocolNotActiveException) { - resp.getWriter().write(e.getMessage()); - resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); - resp.sendError(HttpServletResponse.SC_FORBIDDEN, - StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage()))); - - } else if (e instanceof AuthnRequestValidatorException) { - final AuthnRequestValidatorException ex = (AuthnRequestValidatorException)e; - //log Error Message - if (writeExceptionToStatisicLog) - statisticLogger.logErrorOperation(ex, ex.getErrorRequest()); - - //write error message - //writeBadRequestErrorResponse(req, resp, (EAAFException) e); - writeHTMLErrorResponse(req, resp, - e.getMessage(), - statusMessager.getResponseErrorCode(e), - null, - e); - - } else if (e instanceof InvalidProtocolRequestException) { - //send error response - //writeBadRequestErrorResponse(req, resp, (EAAFException) e); - writeHTMLErrorResponse(req, resp, - e.getMessage(), - statusMessager.getResponseErrorCode(e), - null, - e); - - } else if (e instanceof ConfigurationException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, - e.getMessage(), - statusMessager.getResponseErrorCode(e), - null, - e); - - } else if (e instanceof EAAFException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, - e.getMessage(), - statusMessager.getResponseErrorCode(e), - ((EAAFException) e).getParams(), - e); - - } else if (e instanceof ProcessExecutionException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, - e.getMessage(), - statusMessager.getResponseErrorCode(e), - null, - e); - - } - - } - - + private static final Logger log = LoggerFactory.getLogger(ProtocolAuthenticationService.class); + + private static final List<String> ERROR_LOGGER_ON_INFO_LEVEL = + Arrays.asList(IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP); + + @Autowired(required = true) + private ApplicationContext applicationContext; + @Autowired(required = true) + private IAuthenticationManager authmanager; + @Autowired(required = true) + private IAuthenticationDataBuilder authDataBuilder; + @Autowired(required = true) + private IGuiBuilderConfigurationFactory guiConfigFactory; + @Autowired(required = true) + private IStatusMessenger statusMessager; + @Autowired(required = true) + private IRequestStorage requestStorage; + @Autowired(required = true) + IPendingRequestIdGenerationStrategy pendingReqIdGenerationStrategy; + + @Autowired(required = false) + private ISsoManager ssoManager; + @Autowired + private IStatisticLogger statisticLogger; + @Autowired + private IRevisionLogger revisionsLogger; + + + private IGuiFormBuilder guiBuilder; + + /* + * (non-Javadoc) + * + * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService# + * performAuthentication(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) + */ + @Override + public void performAuthentication(final HttpServletRequest req, final HttpServletResponse resp, + final IRequest pendingReq) throws IOException, EaafException { + try { + if (pendingReq.isNeedAuthentication()) { + // request needs authentication --> start authentication process ... + + // set pendingRequestId to support asynchrony message-processing + ((RequestImpl) pendingReq) + .setPendingRequestId(pendingReqIdGenerationStrategy.generateExternalPendingRequestId()); + + // load Parameters from OnlineApplicationConfiguration + final IspConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + if (oaParam == null) { + throw new EaafAuthenticationException( + IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOSPCONFIG, + new Object[] {pendingReq.getSpEntityId()}); + } + + if (authmanager.doAuthentication(req, resp, pendingReq)) { + // pending request is already authenticated --> protocol-specific postProcessing can start + // directly + finalizeAuthentication(req, resp, pendingReq); + + // transaction is finished, log transaction finished event + revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, + pendingReq.getUniqueTransactionIdentifier()); + + } + + } else { + executeProtocolSpecificAction(req, resp, pendingReq, null); + + } + + } catch (final Exception e) { + buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); + authmanager.performOnlyIdpLogOut(req, resp, pendingReq); + + } + } + + /* + * (non-Javadoc) + * + * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService# + * finalizeAuthentication(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) + */ + @Override + public void finalizeAuthentication(final HttpServletRequest req, final HttpServletResponse resp, + final IRequest pendingReq) throws EaafException, IOException { + log.debug("Finalize PendingRequest with ID " + pendingReq.getPendingRequestId()); + try { + + // check if pending-request has 'abortedByUser' flag set + if (pendingReq.isAbortedByUser()) { + // send authentication aborted error to Service Provider + buildProtocolSpecificErrorResponse( + new EaafAuthenticationException(IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP, + new Object[] {}), + req, resp, pendingReq); + + // do not remove the full active SSO-Session + // in case of only one Service-Provider authentication request is aborted + if (!pendingReq.needSingleSignOnFunctionality()) { + requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); + + } + + // check if pending-request are authenticated + } else if (pendingReq.isAuthenticated() && !pendingReq.isNeedUserConsent()) { + internalFinalizeAuthenticationProcess(req, resp, pendingReq); + + } else { + // suspect state: pending-request is not aborted but also are not authenticated + log.warn("PendingRequest flag for 'authenticated':{} and 'needConsent':{}", + pendingReq.isAuthenticated(), pendingReq.isNeedUserConsent()); + if (pendingReq.isNeedUserConsent()) { + log.error( + "PendingRequest NEEDS user-consent. Can NOT fininalize authentication --> Abort authentication process!"); + + } else { + log.error("PendingRequest is NOT authenticated --> Abort authentication process!"); + + } + + handleErrorNoRedirect(new EaafException("auth.20", null), req, resp, true); + + } + + } catch (final Exception e) { + log.error("Finalize authentication protocol FAILED.", e); + buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); + + } + + // remove pending-request + requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); + revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, + pendingReq.getUniqueTransactionIdentifier()); + + } + + + @Override + public void buildProtocolSpecificErrorResponse(final Throwable throwable, + final HttpServletRequest req, final HttpServletResponse resp, final IRequest protocolRequest) + throws EaafException, IOException { + try { + + final Class<?> clazz = Class.forName(protocolRequest.requestedModule()); + + if (clazz == null || !IModulInfo.class.isAssignableFrom(clazz)) { + log.error( + "Requested protocol module Class is NULL or does not implement the IModulInfo interface."); + throw new Exception( + "Requested protocol module Class is NULL or does not implement the IModulInfo interface."); + + } + + final IModulInfo handlingModule = (IModulInfo) applicationContext.getBean(clazz); + + if (handlingModule.generateErrorMessage(throwable, req, resp, protocolRequest)) { + + // log Error to technical log + logExceptionToTechnicalLog(throwable); + + // log Error Message + statisticLogger.logErrorOperation(throwable, protocolRequest); + + // write revision log entries + revisionsLogger.logEvent(protocolRequest, EventConstants.TRANSACTION_ERROR, + protocolRequest.getUniqueTransactionIdentifier()); + + return; + + } else { + handleErrorNoRedirect(throwable, req, resp, true); + + } + + } catch (final Throwable e) { + handleErrorNoRedirect(throwable, req, resp, true); + + } + + } + + @Override + public void handleErrorNoRedirect(final Throwable throwable, final HttpServletRequest req, + final HttpServletResponse resp, final boolean writeExceptionToStatisticLog) + throws IOException, EaafException { + + // log Exception into statistic database + if (writeExceptionToStatisticLog) { + statisticLogger.logErrorOperation(throwable); + } + + // write errror to console + logExceptionToTechnicalLog(throwable); + + // return error to Web browser + if (throwable instanceof EaafException || throwable instanceof ProcessExecutionException) { + internalMoaidExceptionHandler(req, resp, (Exception) throwable, false); + } else { + // write generic message for general exceptions + final String msg = + statusMessager.getMessage(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC, null); + writeHtmlErrorResponse(req, resp, msg, "9199", null, (Exception) throwable); + + } + + } + + + public void setGuiBuilder(final IGuiFormBuilder guiBuilder) { + this.guiBuilder = guiBuilder; + } + + /** + * Finalize the requested protocol operation. + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @param moaSession MOASession object, which is used to generate the protocol specific + * authentication information + * @throws Exception In case of an error + */ + protected void internalFinalizeAuthenticationProcess(final HttpServletRequest req, + final HttpServletResponse resp, final IRequest pendingReq) throws Exception { + + String newSsoSessionId = null; + + // if Single Sign-On functionality is enabled for this request + if (pendingReq.needSingleSignOnFunctionality()) { + if (ssoManager != null) { + newSsoSessionId = ssoManager.createNewSsoSessionCookie(req, resp, pendingReq); + if (StringUtils.isEmpty(pendingReq.getInternalSsoSessionIdentifier())) { + ssoManager.createNewSsoSession(pendingReq, newSsoSessionId); + } + + } else { + log.warn("SSO is requested but there is not SSO Session-Manager available"); + } + + } + + // build authenticationdata from session information and OA configuration + final IAuthData authData = authDataBuilder.buildAuthenticationData(pendingReq); + + // execute the protocol-specific action + final SloInformationInterface sloInformation = + executeProtocolSpecificAction(req, resp, pendingReq, authData); + + // Store OA specific SSO session information if an SSO cookie is set + if (StringUtils.isNotEmpty(newSsoSessionId)) { + try { + ssoManager.updateSsoSession(pendingReq, newSsoSessionId, sloInformation); + + } catch (final EaafSsoException e) { + log.warn("SSO Session information can not be stored -> SSO is not enabled!"); + authmanager.performOnlyIdpLogOut(req, resp, pendingReq); + + } + + } else { + // remove MOASession from database + authmanager.performOnlyIdpLogOut(req, resp, pendingReq); + + } + + // Advanced statistic logging + statisticLogger.logSuccessOperation(pendingReq, authData, + StringUtils.isNotEmpty(newSsoSessionId)); + + } + + /** + * Executes the requested protocol action. + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @param authData Service-provider specific authentication data + * + * @return Return Single LogOut information or null if protocol supports no SSO + * + * @throws Exception in case of an error + */ + private SloInformationInterface executeProtocolSpecificAction(final HttpServletRequest httpReq, + final HttpServletResponse httpResp, final IRequest pendingReq, final IAuthData authData) + throws Exception { + try { + // request needs no authentication --> start request processing + final Class<?> clazz = Class.forName(pendingReq.requestedAction()); + if (clazz == null || !IAction.class.isAssignableFrom(clazz)) { + log.error( + "Requested protocol-action processing Class is NULL or does not implement the IAction interface."); + throw new Exception( + "Requested protocol-action processing Class is NULL or does not implement the IAction interface."); + + } + + final IAction protocolAction = (IAction) applicationContext.getBean(clazz); + return protocolAction.processRequest(pendingReq, httpReq, httpResp, authData); + + } catch (final ClassNotFoundException e) { + log.error( + "Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); + throw new Exception( + "Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); + } + + } + + /** + * Write a Exception to the MOA-ID-Auth internal technical log. + * + * @param loggedException Exception to log + */ + protected void logExceptionToTechnicalLog(final Throwable loggedException) { + if (!(loggedException instanceof EaafException + || loggedException instanceof ProcessExecutionException)) { + log.error("Receive an internal error: Message=" + loggedException.getMessage(), + loggedException); + + } else { + if (loggedException instanceof EaafAuthenticationException && ERROR_LOGGER_ON_INFO_LEVEL + .contains(((EaafAuthenticationException) loggedException).getErrorId())) { + if (log.isDebugEnabled() || log.isTraceEnabled()) { + log.info(loggedException.getMessage(), loggedException); + + } else { + log.info(loggedException.getMessage()); + + } + + } else { + if (log.isDebugEnabled() || log.isTraceEnabled()) { + log.warn(loggedException.getMessage(), loggedException); + + } else { + log.warn(loggedException.getMessage()); + + } + } + } + } + + private void writeHtmlErrorResponse(@NonNull final HttpServletRequest httpReq, + @NonNull final HttpServletResponse httpResp, @NonNull final String msg, + @NonNull final String errorCode, @Nullable final Object[] params, + @NonNull final Exception error) throws IOException, EaafException { + + try { + final IGuiBuilderConfiguration config = + guiConfigFactory.getDefaultErrorGui(HttpUtils.extractAuthUrlFromRequest(httpReq)); + + + String[] errorCodeParams = null; + if (params == null) { + errorCodeParams = new String[] {}; + } else { + errorCodeParams = new String[params.length]; + for (int i = 0; i < params.length; i++) { + if (params[i] != null) { + errorCodeParams[i] = params[i].toString(); + } else { + errorCodeParams[i] = "null"; + } + + } + } + + + + // add errorcode and errormessage + if (config instanceof ModifyableGuiBuilderConfiguration) { + ((ModifyableGuiBuilderConfiguration) config).putCustomParameter( + AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_ERROMSG, msg); + ((ModifyableGuiBuilderConfiguration) config).putCustomParameter( + AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_ERRORCODE, errorCode); + ((ModifyableGuiBuilderConfiguration) config).putCustomParameterWithOutEscaption( + AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_ERRORCODEPARAMS, + ArrayUtils.toString(errorCodeParams)); + + // add stacktrace if debug is enabled + if (log.isTraceEnabled()) { + ((ModifyableGuiBuilderConfiguration) config).putCustomParameter( + AbstractGuiFormBuilderConfiguration.PARAM_GROUP_MSG, PARAM_GUI_ERRORSTACKTRACE, + getStacktraceFromException(error)); + + } + + } else { + log.info( + "Can not ADD error message, because 'GUIBuilderConfiguration' is not modifieable "); + } + + + + guiBuilder.build(httpReq, httpResp, config, "Error-Message"); + + } catch (final GuiBuildException e) { + log.warn("Can not build error-message GUI.", e); + throw new EaafException("9199", null, e); + + + } + + } + + private String getStacktraceFromException(final Exception ex) { + final StringWriter errors = new StringWriter(); + ex.printStackTrace(new PrintWriter(errors)); + return errors.toString(); + + } + + private void internalMoaidExceptionHandler(final HttpServletRequest req, + final HttpServletResponse resp, final Exception e, final boolean writeExceptionToStatisicLog) + throws IOException, EaafException { + if (e instanceof ProtocolNotActiveException) { + resp.getWriter().write(e.getMessage()); + resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, + StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage()))); + + } else if (e instanceof AuthnRequestValidatorException) { + final AuthnRequestValidatorException ex = (AuthnRequestValidatorException) e; + // log Error Message + if (writeExceptionToStatisicLog) { + statisticLogger.logErrorOperation(ex, ex.getErrorRequest()); + } + + // write error message + // writeBadRequestErrorResponse(req, resp, (EAAFException) e); + writeHtmlErrorResponse(req, resp, e.getMessage(), statusMessager.getResponseErrorCode(e), + null, e); + + } else if (e instanceof InvalidProtocolRequestException) { + // send error response + // writeBadRequestErrorResponse(req, resp, (EAAFException) e); + writeHtmlErrorResponse(req, resp, e.getMessage(), statusMessager.getResponseErrorCode(e), + null, e); + + } else if (e instanceof ConfigurationException) { + // send HTML formated error message + writeHtmlErrorResponse(req, resp, e.getMessage(), statusMessager.getResponseErrorCode(e), + null, e); + + } else if (e instanceof EaafException) { + // send HTML formated error message + writeHtmlErrorResponse(req, resp, e.getMessage(), statusMessager.getResponseErrorCode(e), + ((EaafException) e).getParams(), e); + + } else if (e instanceof ProcessExecutionException) { + // send HTML formated error message + writeHtmlErrorResponse(req, resp, e.getMessage(), statusMessager.getResponseErrorCode(e), + null, e); + + } + + } + + } |