/* * The MIT License * * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk) * * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk) * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $HeadURL$ * $Id$ */ package com.trifork.sosigw.ws; import com.trifork.sosigw.Factory; import com.trifork.sosigw.api.Constants; import com.trifork.sosigw.api.IDCardCache; import com.trifork.sosigw.api.IDCardCacheEntry; import com.trifork.sosigw.api.IDCardState; import com.trifork.sosigw.auditlog.AuditLog; import com.trifork.sosigw.browser.BrowserBasedSigning; import com.trifork.sosigw.cluster.IDCardClusterCacheEntry; import com.trifork.sosigw.config.ConfigurationException; import com.trifork.sosigw.config.RuntimeConfig; import com.trifork.sosigw.util.HttpHeaderException; import com.trifork.sosigw.util.SosiSeal; import com.trifork.sosigw.util.Util; import com.trifork.sosigw.util.timing.Timer; import dk.sosi.gw._2007_09.DGWSFault; import dk.sosi.gw._2007_09.RequestIdCardDigestForSigningResponse; import dk.sosi.gw._2007_09.SignIdCardRequestBody; import dk.sosi.seal.model.IDCard; import dk.sosi.seal.model.SecurityTokenRequest; import dk.sosi.seal.model.UserIDCard; import dk.sosi.seal.modelbuilders.ModelBuildException; import oasis.names.tc.saml._2_0.assertion.AssertionType; import org.apache.log4j.Logger; import org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.Security; import org.w3._2000._09.xmldsig_.KeyInfo; import org.w3._2000._09.xmldsig_.X509Data; import org.w3c.dom.Document; import javax.servlet.http.HttpServletRequest; /** * * * @author Jeppe Sommer * @author Morten Grouleff */ public class SosiGWFacadeBusiness { private static final Logger log = Logger.getLogger(SosiGWFacadeBusiness.class); public static RequestIdCardDigestForSigningResponse requestIdCardDigestForSigningImpl(Security sosigwRequestIdCardDigestForSigningRequestHeader) throws DGWSFault { UserIDCard incomingUserIDCard = parseIntoIDCard(sosigwRequestIdCardDigestForSigningRequestHeader); String cacheKey = computeCacheKey(sosigwRequestIdCardDigestForSigningRequestHeader); return requestIdCardDigestForSigningImpl(incomingUserIDCard, cacheKey); } private static String computeCacheKey(Security securityHeader) throws DGWSFault { String nameID; try { nameID = securityHeader.getAssertion().getSubject().getNameID().getValue(); } catch (NullPointerException e) { throw new DGWSFault(Constants.MISSING_USER_IDCARD_IN_REQUEST_MSG, Constants.FAULTCODE_MISSING_USER_IDCARD_IN_REQUEST, e); } try { return Util.computeCacheKey(nameID); } catch (ConfigurationException e) { throw new DGWSFault(e.getMessage(), Constants.FAULTCODE_INTERNAL_ERROR); } catch (HttpHeaderException e) { throw new DGWSFault(e.getMessage(), Constants.FAULTCODE_SYNTAX_ERROR); } } public static RequestIdCardDigestForSigningResponse requestIdCardDigestForSigningImpl(UserIDCard incomingUserIDCard, String cacheKey) throws DGWSFault { Timer.start(SosiGWFacadeBusiness.class, "requestIdCardDigestForSigningImpl"); log.debug("requestIdCardDigestForSigningImpl " + incomingUserIDCard + ", " + cacheKey); try { final IDCardCache idCardCache = Factory.getIDCardCache(); final IDCardClusterCacheEntry cachedIDCardEntry = idCardCache.ensureIDCard(cacheKey); SecurityTokenRequest securityTokenRequest = SosiSeal.getSosiSealFactory().createNewSecurityTokenRequest(); securityTokenRequest.setIDCard(incomingUserIDCard); Document doc = securityTokenRequest.serialize2DOMDocument(); byte[] bytesForSigning = incomingUserIDCard.getBytesForSigning(doc); synchronized (cachedIDCardEntry) { // Note: this must happen after the incomingUserIDCard.getBytesForSigning call, as this has a side effect in the id card object's underlying DOM cachedIDCardEntry.setIdCard(incomingUserIDCard); idCardCache.updateEntryState(cachedIDCardEntry, IDCardState.AWAITING_SIGNED_BYTES); } String browserUrl = BrowserBasedSigning.prepareBrowserSigning(cachedIDCardEntry, bytesForSigning); log.debug("Before entryUpdatedLocally " + cachedIDCardEntry); idCardCache.entryUpdatedLocally(cachedIDCardEntry); log.debug("After entryUpdatedLocally " + cachedIDCardEntry); auditLog(cachedIDCardEntry); RequestIdCardDigestForSigningResponse resp = new RequestIdCardDigestForSigningResponse(); resp.setBrowserUrl(browserUrl); resp.setDigestValue(bytesForSigning); return resp; } catch (ModelBuildException e) { log.debug(e.getMessage(), e); throw new DGWSFault(e.getMessage(), Constants.FAULTCODE_SYNTAX_ERROR, e); } finally{ Timer.stop(SosiGWFacadeBusiness.class, "requestIdCardDigestForSigningImpl"); } } private static UserIDCard parseIntoIDCard(Security sosigwSignIdCardRequestHeader) throws DGWSFault { if (sosigwSignIdCardRequestHeader == null) { throw new DGWSFault("Request has no SOAP Header", Constants.FAULTCODE_SYNTAX_ERROR); } IDCard idCard = IDCardFromSamlAssertion.build(sosigwSignIdCardRequestHeader.getAssertion()); if (!(idCard instanceof UserIDCard)) { throw new DGWSFault("IDCard in request is not a userIDCard", Constants.FAULTCODE_MISSING_USER_IDCARD_IN_REQUEST); } UserIDCard incomingUserIDCard = (UserIDCard)idCard; return SosiSeal.MakeNewIdenticalLevel4Card(incomingUserIDCard); } public static AssertionType getValidIdCardImpl(Security sosigwGetValidIdCardRequestHeader) throws DGWSFault { log.debug("getValidIdCard"); String cacheKey = computeCacheKey(sosigwGetValidIdCardRequestHeader); final IDCardCache idCardCache = Factory.getIDCardCache(); IDCardCacheEntry cacheEntry = idCardCache.findIDCardOrNull(cacheKey); if (cacheEntry != null) { final long now = System.currentTimeMillis(); if (cacheEntry.getState().equals(IDCardState.SIGNED)) { if (cacheEntry.getIdCard().getExpiryDate().getTime() < now) { log.debug("getValidIdCardImpl: idcard expired for " + cacheEntry.getCacheKey()); throw new DGWSFault("IDCard expired", Constants.FAULTCODE_NO_VALID_IDCARD_IN_CACHE); } return IDCardToSamlAssertion.build(cacheEntry.getIdCard()); } if (cacheEntry.getState().equals(IDCardState.AWAITING_BROWSER_RESPONSE) || cacheEntry.getState().equals(IDCardState.AWAITING_SIGNED_BYTES) || cacheEntry.getState().equals(IDCardState.AWAITING_STS_RESPONSE)) { if (now - cacheEntry.getLastmodified() < RuntimeConfig.getIdCardSigningTimeout()) { throw new DGWSFault("Matching IDCard awaits signing", Constants.FAULTCODE_AWAITING_SIGNING); } } } throw new DGWSFault("No matching IDCard", Constants.FAULTCODE_NO_VALID_IDCARD_IN_CACHE); } public static void logoutImpl(Security sosigwLogoutRequestHeader) throws DGWSFault { log.debug("logout"); final IDCardCache idCardCache = Factory.getIDCardCache(); String cacheKey = computeCacheKey(sosigwLogoutRequestHeader); IDCardClusterCacheEntry cacheEntry = idCardCache.findIDCardOrNull(cacheKey); // NULL or REVOKED either not logged in or already logged out if (cacheEntry != null && cacheEntry.getState() != IDCardState.REVOKED) { idCardCache.updateEntryState(cacheEntry, IDCardState.REVOKED); idCardCache.entryUpdatedLocally(cacheEntry); auditLog(cacheEntry); } else { throw new DGWSFault("No matching IDCard", Constants.FAULTCODE_NO_VALID_IDCARD_IN_CACHE); } } private static void auditLog(IDCardCacheEntry cacheEntry) { HttpServletRequest req = Util.getHttpServletRequest(); AuditLog.logIdcardChange(cacheEntry, req); } public static String signIdCardImpl(Security sosigwSignIdCardRequestHeader, SignIdCardRequestBody body) throws DGWSFault { try { //UserIDCard incomingUserIDCard = parseIntoIDCard(sosigwSignIdCardRequestHeader); String cacheKey = computeCacheKey(sosigwSignIdCardRequestHeader); byte[] signedBytes = body.getSignatureValue(); KeyInfo keyInfo = body.getKeyInfo(); if (keyInfo == null) { throw new DGWSFault("Body has no KeyInfo", Constants.FAULTCODE_MISSING_SIGNINGINFO_IN_REQUEST); } X509Data data = keyInfo.getX509Data(); if (data == null) { throw new DGWSFault("KeyInfo has no X509 data", Constants.FAULTCODE_MISSING_SIGNINGINFO_IN_REQUEST); } byte[] certificate = data.getX509Certificate(); new IdCardSignerImpl().signIdCard(cacheKey, signedBytes, certificate); final IDCardCache idCardCache = Factory.getIDCardCache(); auditLog(idCardCache.findIDCardOrNull(cacheKey)); return "ok"; } catch (ModelBuildException e) { log.debug(e.getMessage(), e); throw new DGWSFault(e.getMessage(), Constants.FAULTCODE_SYNTAX_ERROR, e); } } }