Java tutorial
/* * Open Source Business Intelligence Tools - http://www.osbitools.com/ * * Copyright 2014-2016 IvaLab Inc. and by respective contributors (see below). * * Released under the LGPL v3 or higher * See http://www.gnu.org/licenses/lgpl.txt * * Date: 2015-10-26 * * Contributors: * * Igor Peonte <igor.144@gmail.com> * */ package com.osbitools.ws.shared.auth; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.opensaml.DefaultBootstrap; import org.opensaml.common.SAMLVersion; import org.opensaml.common.SignableSAMLObject; import org.opensaml.common.SAMLObjectBuilder; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeStatement; import org.opensaml.saml2.core.AuthnRequest; import org.opensaml.saml2.core.AuthnStatement; import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameIDPolicy; import org.opensaml.saml2.core.RequestedAuthnContext; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.SessionIndex; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusMessage; import org.opensaml.saml2.core.impl.AuthnRequestMarshaller; import org.opensaml.saml2.core.impl.LogoutRequestMarshaller; import org.opensaml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml2.metadata.KeyDescriptor; import org.opensaml.saml2.metadata.NameIDFormat; import org.opensaml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.saml2.metadata.SingleSignOnService; import org.opensaml.saml2.metadata.impl.EntityDescriptorMarshaller; import org.opensaml.saml2.metadata.provider.DOMMetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.opensaml.security.MetadataCredentialResolver; import org.opensaml.security.MetadataCredentialResolverFactory; import org.opensaml.security.MetadataCriteria; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.ws.soap.client.BasicSOAPMessageContext; import org.opensaml.ws.soap.client.http.HttpClientBuilder; import org.opensaml.ws.soap.client.http.HttpSOAPClient; import org.opensaml.ws.soap.common.SOAPException; import org.opensaml.ws.soap.soap11.Body; import org.opensaml.ws.soap.soap11.Envelope; import org.opensaml.xml.Configuration; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.XMLObject; import org.opensaml.xml.XMLObjectBuilderFactory; import org.opensaml.xml.io.Marshaller; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.Unmarshaller; import org.opensaml.xml.io.UnmarshallerFactory; import org.opensaml.xml.io.UnmarshallingException; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.parse.XMLParserException; import org.opensaml.xml.security.Criteria; import org.opensaml.xml.security.CriteriaSet; import org.opensaml.xml.security.credential.KeyStoreCredentialResolver; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.criteria.EntityIDCriteria; import org.opensaml.xml.security.criteria.UsageCriteria; import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory; import org.opensaml.xml.signature.KeyInfo; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureConstants; import org.opensaml.xml.signature.SignatureException; import org.opensaml.xml.signature.SignatureValidator; import org.opensaml.xml.signature.Signer; import org.opensaml.xml.signature.impl.SignatureBuilder; import org.opensaml.xml.util.Base64; import org.opensaml.xml.util.XMLHelper; import org.opensaml.xml.validation.ValidationException; import org.opensaml.xml.security.SecurityException; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.osbitools.ws.shared.Constants; import com.osbitools.ws.shared.Utils; import com.osbitools.ws.shared.WsSrvException; /** * Implementation for SAML Security Provider * * @author Igor Peonte <igor.144@gmail.com> * */ public class SamlSecurityProvider extends SessionSecurityProvider { // Pool with parsers private BasicParserPool _pmgr; // Generic builder factory private XMLObjectBuilderFactory _bf; // IDP Id private String _idp; // IDP Signing credentials private X509Credential _cred; // POST Redirect URL for SSO private String _login; // Single Logout URL private String _logout; // SP credentials that build out of local keystore private X509Credential _scred; // Private key PrivateKey _key; // Service provider name. Taking parameter saml.<context_name>.sp_name from // configuration file or taking default servlet context path private String _sname = null; //Service location. Taking parameter saml.<context_name>sp_loc from // configuration file or using default // protocol://host_name:host_port/servlet_context_path private String _sloc = null; // List of issued SAML AuthnRequest for further validation and redirection private static final ConcurrentHashMap<String, SamlAbstractRequest> _rmap = new ConcurrentHashMap<String, SamlAbstractRequest>(); // Cross reference table between SAML sessions and session objects private static final ConcurrentHashMap<String, Session> _smap = new ConcurrentHashMap<String, Session>(); // Pattern to extract SAMLResponse our of html form private static final Pattern SAML_RESP = Pattern .compile(".*<input type=\"hidden\" name=\"SAMLResponse\" value=\"(.*?)\" />.*", Pattern.DOTALL); // Pattern to find JavaScript encoded Hex codes private static final Pattern HP = Pattern.compile("&#x([0-9A-Fa-f]{1,2});"); @Override public void init(ServletContext ctx, Properties properties) throws RuntimeException { super.init(ctx, properties); // Read keystore password String kpwd = properties.getProperty("keystore.pwd"); if (Utils.isEmpty(kpwd)) throw new RuntimeException("Keystore password is not found"); byte[] bkpwd = Base64.decode(kpwd); // Read default service provider name which is same as servlet context String cpath = properties.getProperty("cpath").substring(1); // Read actual service provider name (if defined) _sname = properties.getProperty("saml." + cpath + ".sp_name", cpath); // Read service location _sloc = properties.getProperty("saml." + cpath + ".sp_loc"); // Read service provider keystore password String spwd = properties.getProperty("keystore." + cpath + ".pwd"); if (Utils.isEmpty(spwd)) throw new RuntimeException("Keystore password for '" + cpath + "' key is not found"); byte[] bspwd = Base64.decode(spwd); // Initialize internal variables try { DefaultBootstrap.bootstrap(); } catch (ConfigurationException e) { throw new RuntimeException(e); } _bf = Configuration.getBuilderFactory(); // Read servlet config directory String cdir = properties.getProperty("cdir"); // Load service provider certificates KeyStore ks; try { ks = KeyStore.getInstance(KeyStore.getDefaultType()); } catch (KeyStoreException e) { throw new RuntimeException(e); } FileInputStream fis; try { fis = new FileInputStream(cdir + File.separator + Constants.KEYSTORE_FILE); } catch (FileNotFoundException e) { throw new RuntimeException(e); } try { ks.load(fis, new String(bkpwd).toCharArray()); } catch (NoSuchAlgorithmException | CertificateException | IOException e) { throw new RuntimeException(e); } // Remember private key for sign request try { _key = (PrivateKey) ks.getKey(cpath, new String(bspwd).toCharArray()); } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) { throw new RuntimeException("Error loading key for alias '" + cpath + "'. ERROR: " + e.getMessage()); } if (_key == null) throw new RuntimeException("Key for alias '" + cpath + "' not found in keystore "); try { fis.close(); } catch (IOException e) { throw new RuntimeException(e); } Map<String, String> passwordMap = new HashMap<String, String>(); passwordMap.put(cpath, new String(bspwd)); KeyStoreCredentialResolver resolver = new KeyStoreCredentialResolver(ks, passwordMap); Criteria criteria = new EntityIDCriteria(cpath); CriteriaSet criteriaSet = new CriteriaSet(criteria); try { _scred = (X509Credential) resolver.resolveSingle(criteriaSet); } catch (SecurityException e) { throw new RuntimeException(e); } // Load IDP Metadata // Get parser pool manager _pmgr = new BasicParserPool(); _pmgr.setNamespaceAware(true); // Parse metadata file InputStream in; try { in = new FileInputStream(cdir + File.separator + Constants.IDP_METADATA_FILE); } catch (FileNotFoundException e) { throw new RuntimeException(e); } Document doc; try { doc = _pmgr.parse(in); } catch (XMLParserException e) { throw new RuntimeException(e); } try { in.close(); } catch (IOException e) { throw new RuntimeException(e); } Element root = doc.getDocumentElement(); UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(root); EntityDescriptor eds; try { eds = (EntityDescriptor) unmarshaller.unmarshall(root); } catch (UnmarshallingException e) { throw new RuntimeException(e); } _idp = eds.getEntityID(); DOMMetadataProvider mp = new DOMMetadataProvider(root); mp.setRequireValidMetadata(true); // mp.setParserPool(new BasicParserPool()); try { mp.initialize(); } catch (MetadataProviderException e) { throw new RuntimeException(e); } MetadataCredentialResolverFactory crf = MetadataCredentialResolverFactory.getFactory(); MetadataCredentialResolver cr = crf.getInstance(mp); // Look for signing key CriteriaSet cs = new CriteriaSet(); cs.add((Criteria) new MetadataCriteria(IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS)); cs.add(new EntityIDCriteria(_idp)); cs.add(new UsageCriteria(UsageType.SIGNING)); try { _cred = (X509Credential) cr.resolveSingle(cs); } catch (SecurityException e) { throw new RuntimeException(e); } if (_cred == null) throw new RuntimeException("Signing Key Descriptors " + "not found in IDP Entity Descriptor"); IDPSSODescriptor idps = eds.getIDPSSODescriptor(SAMLConstants.SAML20P_NS); for (SingleSignOnService sss : idps.getSingleSignOnServices()) { if (sss.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) { _login = sss.getLocation(); break; } } if (_login == null) throw new RuntimeException("IDP SSO POST Redirecting " + "Location not found in IDP Entity Descriptor"); // Get Single Logout Service for (SingleLogoutService slo : idps.getSingleLogoutServices()) { if (slo.getBinding().equals(SAMLConstants.SAML2_SOAP11_BINDING_URI)) _logout = slo.getLocation(); } if (_logout == null) throw new RuntimeException("IDP SLO SOAP " + "Location not found in IDP Entity Descriptor"); } @Override public String authenticate(HttpServletRequest req, String user, String password) throws WsSrvException { // Create AuthRequest try { return _login + "?" + URLEncoder.encode(createAuthnRequest(getServiceLocation(req), getRefererUrl(req)), "UTF-8"); } catch (MarshallingException | IOException | SignatureException e) { //-- 48 throw new WsSrvException(48, e); } } /** * Look for a session and if it's completed or not found try * re-validate existing security token */ @Override public void validate(HttpServletRequest req, String stoken) throws WsSrvException { Session session = activeSessions.get(stoken); if (!(session == null || session.isFinished())) // Everything fine return; getLogger(req).debug("Local session validation faied." + " Sending POST request to validate SAML session"); // Revalidate session PostMethod post = new PostMethod(_login); try { String ars = createAuthnRequest(getServiceLocation(req), getRefererUrl(req)); post.setParameter("SAMLRequest", ars); } catch (MarshallingException | SignatureException | IOException e) { //-- 63 throw new WsSrvException(63, e); } HttpClient hc = (new HttpClientBuilder()).buildClient(); post.setRequestHeader("Cookie", (String) req.getSession().getServletContext().getAttribute("scookie_name") + "=" + stoken); int result; try { result = hc.executeMethod(post); } catch (IOException e) { //-- 66 throw new WsSrvException(66, e); } // Expecting 200 if cookie valid and 302 if not, rest are errors if (result == HttpStatus.SC_OK) { // Extract end process SAML response from form String rb; try { rb = new String(post.getResponseBody()); } catch (IOException e) { //-- 67 throw new WsSrvException(67, e); } Matcher m = SAML_RESP.matcher(rb); if (m.matches() && m.groupCount() == 1) { String gs = m.group(1); // Convert hex decoded javascript variable String msg = ""; int start = 0; Matcher m1 = HP.matcher(gs); while (m1.find()) { String dc = m1.group(1); int i = Integer.decode("#" + dc); int st = m1.start(); int ed = m1.end(); msg += gs.substring(start, st) + (char) i; start = ed; } try { procAuthnResponse(req, msg, stoken); } catch (Exception e) { //-- 62 throw new WsSrvException(62, e); } } } else if (result == HttpStatus.SC_MOVED_TEMPORARILY) { //-- 64 throw new WsSrvException(64, "Redirect received"); } else { //-- 65 throw new WsSrvException(65, "Unexpected http return code " + result); } } @Override public synchronized void logout(HttpServletRequest req, String stoken) throws WsSrvException { super.logout(req, stoken); LogoutRequest lreq; try { lreq = createLogoutRequest(activeSessions.get(stoken)); } catch (MarshallingException | SignatureException | IOException e) { //-- 70 throw new WsSrvException(70, e); } Body body = getSoapBody(); body.getUnknownXMLObjects().add(lreq); BasicSOAPMessageContext ctx = new BasicSOAPMessageContext(); ctx.setOutboundMessage(getSoapEnvelope(body)); HttpClientBuilder cb = new HttpClientBuilder(); HttpSOAPClient soapClient = new HttpSOAPClient(cb.buildClient(), new BasicParserPool()); try { soapClient.send(_logout, ctx); } catch (SOAPException | SecurityException e) { //-- 75 throw new WsSrvException(75, e); } Envelope renv = (Envelope) ctx.getInboundMessage(); LogoutResponse lresp; List<XMLObject> lobj = renv.getBody().getUnknownXMLObjects(); // Expecting only one object with LogoutResponse if (lobj.size() != 1) //-- 66 throw new WsSrvException(66, "Invalid SOAP envelope." + " Expected 1 return message but found " + lobj.size()); try { lresp = (LogoutResponse) lobj.get(0); } catch (ClassCastException e) { //-- 72 throw new WsSrvException(72, e, "Invalid type on logout response"); } // Validate response id if (!lresp.getInResponseTo().equals(lreq.getID())) //-- 73 throw new WsSrvException(73, lresp.getInResponseTo() + " doesn't match initial id " + lreq.getID()); // Check status code Status status = lresp.getStatus(); String scode = status.getStatusCode().getValue(); if (!scode.equals(StatusCode.SUCCESS_URI)) { String smsg = status.getStatusMessage().getMessage(); //-- 74 throw new WsSrvException(74, "Invalid logout response status code " + scode + (smsg != null ? "; " + smsg + ";" : "")); } getLogger(req).debug("Single Logout successful with response " + scode); } @SuppressWarnings("unchecked") public String getMetadata(String url) throws WsSrvException { EntityDescriptor ed = ((SAMLObjectBuilder<EntityDescriptor>) _bf .getBuilder(EntityDescriptor.DEFAULT_ELEMENT_NAME)).buildObject(); ed.setEntityID(_sname); SPSSODescriptor sd = ((SAMLObjectBuilder<SPSSODescriptor>) _bf .getBuilder(SPSSODescriptor.DEFAULT_ELEMENT_NAME)).buildObject(); sd.setAuthnRequestsSigned(false); sd.setWantAssertionsSigned(true); // Get key info for signing/encryption descriptors KeyInfo ki; X509KeyInfoGeneratorFactory kigf = new X509KeyInfoGeneratorFactory(); kigf.setEmitEntityCertificate(true); KeyInfoGenerator kig = kigf.newInstance(); try { ki = kig.generate(_scred); } catch (SecurityException e) { //-- 68 throw new WsSrvException(68, e); } // Skip encryption keys /* KeyDescriptor ekd = ((SAMLObjectBuilder<KeyDescriptor>) _sh.getBuilderFactory().getBuilder( KeyDescriptor.DEFAULT_ELEMENT_NAME)).buildObject(); ekd.setUse(UsageType.ENCRYPTION); ekd.setKeyInfo(ki); sd.getKeyDescriptors().add(ekd); */ KeyDescriptor skd = ((SAMLObjectBuilder<KeyDescriptor>) _bf.getBuilder(KeyDescriptor.DEFAULT_ELEMENT_NAME)) .buildObject(); skd.setUse(UsageType.SIGNING); // Set usage skd.setKeyInfo(ki); sd.getKeyDescriptors().add(skd); NameIDFormat nid = ((SAMLObjectBuilder<NameIDFormat>) _bf.getBuilder(NameIDFormat.DEFAULT_ELEMENT_NAME)) .buildObject(); nid.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient"); sd.getNameIDFormats().add(nid); // Setting AssertionConsumerService AssertionConsumerService acs = ((SAMLObjectBuilder<AssertionConsumerService>) _bf .getBuilder(AssertionConsumerService.DEFAULT_ELEMENT_NAME)).buildObject(); acs.setIndex(0); acs.setBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI); acs.setLocation(url + "/saml2/consumer"); sd.getAssertionConsumerServices().add(acs); SingleLogoutService slo = ((SAMLObjectBuilder<SingleLogoutService>) _bf .getBuilder(SingleLogoutService.DEFAULT_ELEMENT_NAME)).buildObject(); slo.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI); slo.setLocation(url + "/saml2/logout"); sd.getSingleLogoutServices().add(slo); sd.addSupportedProtocol(SAMLConstants.SAML20P_NS); ed.getRoleDescriptors().add(sd); EntityDescriptorMarshaller marshaller = new EntityDescriptorMarshaller(); Element plaintextElement; try { plaintextElement = marshaller.marshall(ed); } catch (MarshallingException e) { //-- 69 throw new WsSrvException(69, e); } return XMLHelper.nodeToString(plaintextElement); } private String getRefererUrl(HttpServletRequest req) throws WsSrvException { // Check if referrer present to redirect after SSO authentication String rurl = req.getHeader("Referer"); if (Utils.isEmpty(rurl)) //-- 50 throw new WsSrvException(50, "Missing HTTP referer"); return rurl; } @Override public boolean hasSingleSignOn() { return true; } private String createAuthnRequest(String surl, String referer) throws MarshallingException, IOException, SignatureException { return createAuthnRequest(surl, false, referer); } private String createAuthnRequest(String surl, boolean fdeflate, String referer) throws MarshallingException, IOException, SignatureException { AuthnRequest ar = createAuthnRequest(surl + "/saml2/consumer", false, false, SAMLConstants.SAML2_POST_BINDING_URI, null, null); // Create signature and add to auth Request Signature sig = getSignature(); ar.setSignature(sig); AuthnRequestMarshaller marshaller = new AuthnRequestMarshaller(); Element arn = marshaller.marshall(ar); Signer.signObject(sig); byte[] res = XMLHelper.nodeToString(arn).getBytes(); // System.out.println(new String(res)); // Remember authentication request been sent String rid = ar.getID(); SamlAbstractRequest sar = new SamlAbstractRequest(rid, referer); _rmap.put(rid, sar); return fdeflate ? deflate(res) : encode(res); } @SuppressWarnings("unchecked") private AuthnRequest createAuthnRequest(String surl, boolean fauth, boolean isp, String proto, NameIDPolicy npolicy, RequestedAuthnContext actx) { AuthnRequest ar = ((SAMLObjectBuilder<AuthnRequest>) _bf.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME)) .buildObject(); ar.setAssertionConsumerServiceURL(surl); ar.setForceAuthn(fauth); String uid = UUID.randomUUID().toString(); ar.setID(uid); ar.setIsPassive(isp); ar.setIssueInstant(new DateTime()); ar.setProtocolBinding(proto); ar.setVersion(SAMLVersion.VERSION_20); ar.setIssuer(getIssuer()); // ar.setNameIDPolicy(npolicy); // ar.setRequestedAuthnContext(actx); return ar; } public LogoutRequest createLogoutRequest(Session session) throws IOException, SignatureException, MarshallingException { if (_logout == null) return null; // Retrieve initial authn response Response resp = (Response) session.getCustomParams(); LogoutRequest lr = createLogoutRequest(resp); // Create signature and add to auth Request LogoutRequestMarshaller marshaller = new LogoutRequestMarshaller(); Signature sig = getSignature(); lr.setSignature(sig); // Marshall object to prepare for signature marshaller.marshall(lr); Signer.signObject(sig); return lr; } @SuppressWarnings("unchecked") public LogoutRequest createLogoutRequest(Response resp) { LogoutRequest lr = ((SAMLObjectBuilder<LogoutRequest>) _bf.getBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME)) .buildObject(); String uid = UUID.randomUUID().toString(); lr.setID(uid); lr.setIssueInstant(new DateTime()); lr.setVersion(SAMLVersion.VERSION_20); lr.setIssuer(getIssuer()); // Get NameID and SessionIndex from first assertion from // Authentication Response object Assertion asr = resp.getAssertions().get(0); NameID nid = ((SAMLObjectBuilder<NameID>) _bf.getBuilder(NameID.DEFAULT_ELEMENT_NAME)).buildObject(); nid.setValue(asr.getSubject().getNameID().getValue()); lr.setNameID(nid); // Set session index(es) List<AuthnStatement> ausl = asr.getAuthnStatements(); if (ausl != null) { for (AuthnStatement aus : ausl) { SessionIndex sindex = ((SAMLObjectBuilder<SessionIndex>) _bf .getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME)).buildObject(); sindex.setSessionIndex(aus.getSessionIndex()); lr.getSessionIndexes().add(sindex); } } return lr; } public String procAuthnResponse(HttpServletRequest req, String msg, String stoken) throws WsSrvException { Document doc; try { doc = _pmgr.parse(new ByteArrayInputStream(Base64.decode(msg))); } catch (XMLParserException e) { //-- 51 throw new WsSrvException(51, e); } // Get Response Element element = doc.getDocumentElement(); UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); Response rs; try { rs = (Response) unmarshaller.unmarshall(element); } catch (UnmarshallingException e) { //-- 52 throw new WsSrvException(52, e); } // Check status code String scode = rs.getStatus().getStatusCode().getValue(); if (!scode.equals(StatusCode.SUCCESS_URI)) //-- 71 throw new WsSrvException(71, "Received Invalid Status Code '" + scode + "'"); // Check if response has corresponded request String rid = rs.getInResponseTo(); SamlAbstractRequest sar = _rmap.get(rid); if (sar == null) //-- 59 throw new WsSrvException(59, "Unknown Authn Response"); // Delete initiated request _rmap.remove(rid); // Validate signature of each Assertion List<Assertion> asl = rs.getAssertions(); if (asl == null || asl.size() == 0) //-- 53 throw new WsSrvException(53, "No assertions found in Authn Response"); for (Assertion as : asl) { Signature sig = as.getSignature(); if (sig == null) //-- 54 throw new WsSrvException(54, "Signature not found"); SAMLSignatureProfileValidator pvalidator = new SAMLSignatureProfileValidator(); try { pvalidator.validate(sig); } catch (ValidationException e) { //-- 55 throw new WsSrvException(55, e); } SignatureValidator svalidator = new SignatureValidator(_cred); try { svalidator.validate(sig); } catch (ValidationException e) { //-- 56 throw new WsSrvException(56, e); } } String uid = null; Assertion asr = asl.get(0); for (AttributeStatement ast : asr.getAttributeStatements()) { for (Attribute at : ast.getAttributes()) // Look for UserId value if ("UserId".equals(at.getName())) { uid = ""; for (XMLObject atv : at.getAttributeValues()) uid += atv.getDOM().getTextContent(); } } if (uid == null) //-- 60 throw new WsSrvException(60, "User Id not found"); // Register user session createUserSession(req, asr, uid, stoken, rs); return sar.getReqOriginUrl(); } private synchronized void createUserSession(HttpServletRequest req, Assertion asr, String user, String stoken, Object params) { Session session = createUserSession(req, user, stoken, params); List<AuthnStatement> ausl = asr.getAuthnStatements(); if (ausl != null) for (AuthnStatement aus : ausl) _smap.put(aus.getSessionIndex(), session); } public byte[] procLogoutRequest(HttpServletRequest req) throws WsSrvException { LogoutResponse lresp; try { lresp = procLogoutRequest(req.getInputStream()); } catch (XMLParserException | UnmarshallingException | IOException | ValidationException e) { //-- 76 throw new WsSrvException(76, e); } // Return soap logout response Envelope evp = makeSoapEnvelope(lresp); Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(evp); ByteArrayOutputStream out = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(out, Charset.forName("UTF-8")); try { XMLHelper.writeNode(marshaller.marshall(evp), writer); } catch (MarshallingException e) { //-- 77 throw new WsSrvException(77, e); } return out.toByteArray(); } private LogoutResponse procLogoutRequest(InputStream in) throws XMLParserException, UnmarshallingException, WsSrvException, ValidationException { Document d = _pmgr.parse(in); Element rel = d.getDocumentElement(); Unmarshaller um = Configuration.getUnmarshallerFactory().getUnmarshaller(rel); Envelope req; try { req = (Envelope) um.unmarshall(rel); } catch (ClassCastException e) { throw e; } LogoutRequest lreq; List<XMLObject> lobj = req.getBody().getUnknownXMLObjects(); // Expecting only one object with LogoutResponse if (lobj.size() != 1) //-- 78 throw new WsSrvException(78, "Invalid SOAP envelope." + " Expected 1 included element but found " + lobj.size()); try { lreq = (LogoutRequest) lobj.get(0); } catch (ClassCastException e) { //-- 79 throw new WsSrvException(79, e); } // Check signature validateSignature(lreq); // After logout request validated than close local session String smsg = ""; String scode = StatusCode.SUCCESS_URI; for (SessionIndex sidx : lreq.getSessionIndexes()) { String sindex = sidx.getSessionIndex(); Session session = _smap.get(sindex); if (session == null) { scode = StatusCode.RESPONDER_URI; smsg += "Session " + sindex + " not found;"; } else if (session.isFinished()) { scode = StatusCode.RESPONDER_URI; smsg += "Session " + sindex + " already terminated;"; } else { session.logout(); } } return createLogoutResponse(lreq.getID(), scode, smsg); } @SuppressWarnings("unchecked") private LogoutResponse createLogoutResponse(String id, String code, String msg) { // Consume LogoutResponse LogoutResponse lresp = ((SAMLObjectBuilder<LogoutResponse>) _bf .getBuilder(LogoutResponse.DEFAULT_ELEMENT_NAME)).buildObject(); String uid = UUID.randomUUID().toString(); lresp.setID(uid); lresp.setInResponseTo(id); lresp.setIssueInstant(new DateTime()); lresp.setVersion(SAMLVersion.VERSION_20); lresp.setIssuer(getIssuer()); // Set status code Status status = ((SAMLObjectBuilder<Status>) _bf.getBuilder(Status.DEFAULT_ELEMENT_NAME)).buildObject(); StatusCode scode = ((SAMLObjectBuilder<StatusCode>) _bf.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME)) .buildObject(); scode.setValue(code); status.setStatusCode(scode); if (!msg.equals("")) { StatusMessage smsg = ((SAMLObjectBuilder<StatusMessage>) _bf .getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME)).buildObject(); smsg.setMessage(msg); status.setStatusMessage(smsg); } lresp.setStatus(status); return lresp; } private void validateSignature(SignableSAMLObject obj) throws WsSrvException, ValidationException { Signature sig = obj.getSignature(); if (sig == null) throw new WsSrvException(80, "Signature not found"); SAMLSignatureProfileValidator pvalidator = new SAMLSignatureProfileValidator(); pvalidator.validate(sig); SignatureValidator svalidator = new SignatureValidator(_cred); svalidator.validate(sig); } private Envelope makeSoapEnvelope(XMLObject obj) { Body body = getSoapBody(); body.getUnknownXMLObjects().add(obj); Envelope evp = (Envelope) _bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME) .buildObject(Envelope.DEFAULT_ELEMENT_NAME); evp.setBody(body); return evp; } private String deflate(byte[] msg) throws IOException { Deflater dfl = new Deflater(Deflater.DEFLATED, true); ByteArrayOutputStream out = new ByteArrayOutputStream(); DeflaterOutputStream dos = new DeflaterOutputStream(out, dfl); dos.write(msg); dos.close(); return encode(out.toByteArray(), true); } private String encode(byte[] msg) throws UnsupportedEncodingException { return encode(msg, false); } private String encode(byte[] msg, boolean fdeflate) throws UnsupportedEncodingException { return fdeflate ? URLEncoder.encode(Base64.encodeBytes(msg, Base64.DONT_BREAK_LINES), "UTF-8") : Base64.encodeBytes(msg); } // Signature disabled private Signature getSignature() { Signature sig = (new SignatureBuilder()).buildObject(); sig.setSigningCredential(_scred); sig.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); sig.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); return sig; } /* public X509Credential getX509Credentials() { return _scred; } public XMLObjectBuilderFactory getBuilderFactory() { return _bf; } public String getServiceName() { return _sname; } */ /* public X509Credential getX509Credential() { return _scred; } */ public String getServiceLocation(HttpServletRequest req) { return _sloc == null ? getDefServiceLocationUrl(req) : _sloc; } public String getDefServiceLocationUrl(HttpServletRequest req) { return "http:" + (req.isSecure() ? "s" : "") + "//" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath(); } public String getConfigServiceLocation() { return _sloc; } private Issuer getIssuer() { @SuppressWarnings("unchecked") Issuer issuer = ((SAMLObjectBuilder<Issuer>) _bf.getBuilder(Issuer.DEFAULT_ELEMENT_NAME)).buildObject(); issuer.setValue(_sname); return issuer; } private Logger getLogger(HttpServletRequest req) { return (Logger) req.getSession().getServletContext().getAttribute("log"); } public Body getSoapBody() { return (Body) _bf.getBuilder(Body.DEFAULT_ELEMENT_NAME).buildObject(Body.DEFAULT_ELEMENT_NAME); } public Envelope getSoapEnvelope(Body body) { Envelope evp = (Envelope) _bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME) .buildObject(Envelope.DEFAULT_ELEMENT_NAME); evp.setBody(body); return evp; } class SamlAbstractRequest { private String _id; private String _url; public SamlAbstractRequest(String id) { this(id, null); } public SamlAbstractRequest(String id, String url) { _id = id; _url = url; } public String getAuthId() { return _id; } public String getReqOriginUrl() { return _url; } } }