/* * Atricore IDBus * * Copyright (c) 2009, Atricore Inc. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.atricore.idbus.capabilities.josso.test; import oasis.names.tc.saml._2_0.assertion.*; import oasis.names.tc.saml._2_0.protocol.AuthnRequestType; import oasis.names.tc.saml._2_0.protocol.ResponseType; import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.converter.jaxp.StringSource; import org.apache.camel.impl.JndiRegistry; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xbean.spring.context.ClassPathXmlApplicationContext; import org.atricore.idbus.capabilities.josso.DateUtils; import org.atricore.idbus.capabilities.josso.UUIDGenerator; import org.junit.After; import javax.jws.WebMethod; import javax.jws.WebService; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; import java.io.*; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; /** * * @author <a href="mailto:gbrigand@josso.org">Gianluca Brigandi</a> * @version $Id: JOSSO11WebSSORouteTest.java 1153 2009-04-10 05:21:39Z gbrigand $ */ public class JOSSO11WebSSORouteTest extends ContextTestSupport { private static Log log = LogFactory.getLog(JOSSO11WebSSORouteTest.class); private static final String BASE64_SAMLR2_AUTHNREQUEST = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI%2FPjxuczM6QXV0aG5SZXF1ZXN0" + "IFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUmVkaXJlY3QiIElzUG" + "Fzc2l2ZT0iZmFsc2UiIEZvcmNlQXV0aG49ImZhbHNlIiBJc3N1ZUluc3RhbnQ9IjIwMDktMDQtMDJUMTg6NDU6MDYuMDAwWiIg" + "VmVyc2lvbj0iMi4wIiBJRD0iOTNGMjY5QjgwQzgyMDVCNSIgeG1sbnM6bnM0PSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3" + "htbGVuYyMiIHhtbG5zOm5zMz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpuczI9Imh0dHA6" + "Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW" + "9uIj48SXNzdWVyLz48L25zMzpBdXRoblJlcXVlc3Q%2B"; protected ClassPathXmlApplicationContext applicationContext; protected void setUp() throws Exception { applicationContext = new ClassPathXmlApplicationContext( "/org/atricore/idbus/capabilities/josso/test/josso-test-component.xml" ); super.setUp(); //To change body of overridden methods use File | Settings | File Templates. } public void testJOSSO11AuthNRequestToSAMLR2() throws Exception { String location = null; URL spUrl = new URL("http://localhost:8181/JOSSO11/BIND/CH1?cmd=login"); HttpURLConnection urlConn = (HttpURLConnection) spUrl.openConnection(); urlConn.setInstanceFollowRedirects(false); urlConn.connect(); String headerName=null; String jossoSession = null; for (int i=1; (headerName = urlConn.getHeaderFieldKey(i))!=null; i++) { if (headerName.equals("Location")) { location = urlConn.getHeaderField(i); } } assert location != null; } public void testSAMLR2AuthnToJOSSO11() throws Exception { String location = null; URL spUrl = new URL("http://localhost:8181/JOSSO11/BIND/CH2?SAMLRequest=" + BASE64_SAMLR2_AUTHNREQUEST); HttpURLConnection urlConn = (HttpURLConnection) spUrl.openConnection(); urlConn.setInstanceFollowRedirects(false); urlConn.connect(); String headerName=null; String jossoSession = null; for (int i=1; (headerName = urlConn.getHeaderFieldKey(i))!=null; i++) { if (headerName.equals("Location")) { location = urlConn.getHeaderField(i); } } assert location != null; } public void testSAMLR2ResponseToJOSSO11() throws Exception { String location = null; ResponseType r = buildAuthnResponse(null, createAssertion()); String marshalledResponse; marshalledResponse = marshal( oasis.names.tc.saml._2_0.wsdl.SAMLRequestPortType.class, r, "urn:oasis:names:tc:SAML:2.0:protocol", "Response", new String[]{} ); String base64AuthnResponse; base64AuthnResponse = new String(new Base64().encode(marshalledResponse.getBytes())); String postResponse = executePost("http://localhost:8181/JOSSO11/BIND/ACS", "SAMLResponse=" + URLEncoder.encode(base64AuthnResponse) ); } protected JndiRegistry createRegistry() throws Exception { JndiRegistry jndi = super.createRegistry(); jndi.bind("applicationContext", applicationContext); return jndi; } protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { from("jetty:http://localhost:8181/JOSSO11/BIND/CH1") .to("josso-binding:JOSSO11AuthnRequestToSAMLR2?channelRef=binding_channel_1"); from("jetty:http://localhost:8181/JOSSO11/BIND/CH2") .to("josso-binding:RequestAuthnToJOSSO11IDP?channelRef=binding_channel_2"); from("jetty:http://localhost:8181/JOSSO11/BIND/ACS") .to("josso-binding:SAMLR2ResponseToJOSSO11?channelRef=binding_channel_2" + "&josso11ACSUrl=http://localhost:8282/josso_security_check/"); } }; } @After public void tearDown() throws Exception { super.tearDown(); if (applicationContext != null) { applicationContext.close(); } } private String executePost(String targetURL, String urlParameters) { URL url; HttpURLConnection connection = null; try { //Create connection url = new URL(targetURL); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length)); connection.setRequestProperty("Content-Language", "en-US"); connection.setUseCaches(false); connection.setDoInput(true); connection.setDoOutput(true); //Send request DataOutputStream wr = new DataOutputStream( connection.getOutputStream()); wr.writeBytes(urlParameters); wr.flush(); wr.close(); //Get Response InputStream is = connection.getInputStream(); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer response = new StringBuffer(); while ((line = rd.readLine()) != null) { response.append(line); response.append('\r'); } rd.close(); return response.toString(); } catch (Exception e) { e.printStackTrace(); return null; } finally { if (connection != null) { connection.disconnect(); } } } protected ResponseType buildAuthnResponse(AuthnRequestType authnRequest, AssertionType assertion) { ResponseType authnResponse = new ResponseType(); UUIDGenerator uuidGenerator = new UUIDGenerator(); ///TODO: String acs = authnRequest.getAssertionConsumerServiceURL(); Date dateNow = new java.util.Date(); Date dateOneWeekAhead = new java.util.Date(dateNow.getTime() + (1000 * 60 * 60 * 24 * 7)); authnResponse.setID(uuidGenerator.generateId()); authnResponse.setVersion("2.0"); authnResponse.setIssueInstant(DateUtils.toXMLGregorianCalendar(dateNow)); authnResponse.getAssertionOrEncryptedAssertion().add(assertion); return authnResponse; } public AssertionType createAssertion() { oasis.names.tc.saml._2_0.assertion.ObjectFactory samlObjectFactory; samlObjectFactory = new oasis.names.tc.saml._2_0.assertion.ObjectFactory(); AssertionType assertion = samlObjectFactory.createAssertionType(); UUIDGenerator uuidGenerator = new UUIDGenerator(); // Prepare time stuff Date dateNow = new java.util.Date(); Date dateOneWeekAhead = new java.util.Date(dateNow.getTime() + (1000 * 60 * 60 * 24 * 7)); assertion.setID(uuidGenerator.generateId()); assertion.setIssueInstant(DateUtils.toXMLGregorianCalendar(dateNow)); assertion.setVersion("2.0"); NameIDType issuer = new NameIDType(); issuer.setValue("idp_atricore"); assertion.setIssuer(issuer); SubjectType subject = new SubjectType(); NameIDType subjectNameID = new NameIDType(); subjectNameID.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"); // TODO: VP subjectNameID.setNameQualifier("idp_atricore"); // TODO: VP subjectNameID.setSPNameQualifier("sp_atricore"); // TODO: VP subjectNameID.setValue("user1"); subject.getContent().add(samlObjectFactory.createNameID(subjectNameID)); assertion.setSubject(subject); SubjectConfirmationType subjectConfirmation = new SubjectConfirmationType(); subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer"); // TODO: VP SubjectConfirmationDataType subjectConfirmationData = new SubjectConfirmationDataType(); subjectConfirmationData.setNotBefore(DateUtils.toXMLGregorianCalendar(dateNow)); subjectConfirmationData.setNotOnOrAfter(DateUtils.toXMLGregorianCalendar(dateOneWeekAhead)); subjectConfirmationData.setRecipient("sp_atricore"); // TODO: VP subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); subject.getContent().add(samlObjectFactory.createSubjectConfirmation(subjectConfirmation)); ConditionsType conditions = new ConditionsType(); conditions.setNotBefore(DateUtils.toXMLGregorianCalendar(dateNow)); conditions.setNotOnOrAfter(DateUtils.toXMLGregorianCalendar(dateOneWeekAhead)); assertion.setConditions(conditions); AudienceRestrictionType audienceRestriction = new AudienceRestrictionType(); audienceRestriction.getAudience().add("sp_test"); // TODO: VP audienceRestriction.getAudience().add("sp_atricore"); // TODO: VP conditions.getConditionOrAudienceRestrictionOrOneTimeUse().add(audienceRestriction); AuthnStatementType authnStatement; authnStatement = new AuthnStatementType(); authnStatement.setAuthnInstant(DateUtils.toXMLGregorianCalendar(dateNow)); authnStatement.setSessionIndex(uuidGenerator.generateId()); authnStatement.setSessionNotOnOrAfter(DateUtils.toXMLGregorianCalendar(dateOneWeekAhead)); AuthnContextType authnContext = new AuthnContextType(); JAXBElement<String> passwordProtectedAuthnContext = samlObjectFactory.createAuthnContextClassRef( "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" ); // TODO: VP authnContext.getContent().add(passwordProtectedAuthnContext); authnStatement.setAuthnContext(authnContext); assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement().add(authnStatement); // Compose attribute statements with roles AttributeStatementType groupMembershipStatement; groupMembershipStatement = new AttributeStatementType(); AttributeType groupsAttr = new AttributeType(); groupsAttr.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); groupsAttr.setName("urn:oasis:names:tc:SAML:2.0:profiles:attribute:DCE:groups"); groupsAttr.getAttributeValue().add("GROUP_A"); groupMembershipStatement.getAttributeOrEncryptedAttribute().add(groupsAttr); assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement().add(groupMembershipStatement); // Compose attribute statements with permissions AttributeStatementType privilegeStatement; privilegeStatement = new AttributeStatementType(); AttributeType privAttr = new AttributeType(); privAttr.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); privAttr.setName("urn:att:names:csp:privileges"); privAttr.getAttributeValue().add("PRIV_1"); privilegeStatement.getAttributeOrEncryptedAttribute().add(privAttr); assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement().add(privilegeStatement); return assertion; } protected String marshal(Class endpointInterface, Object msg, String msgQName, String msgLocalName, String[] userPackages) throws Exception { WebService ws = getWebServiceAnnotation(endpointInterface); JAXBContext jaxbContext = createJAXBContext(endpointInterface, userPackages); JAXBElement jaxbRequest = new JAXBElement(new QName(msgQName, msgLocalName), msg.getClass(), msg ); StringWriter writer = new StringWriter(); jaxbContext.createMarshaller().marshal(jaxbRequest, writer); return writer.toString(); } protected String marshal(Object msg, String msgQName, String msgLocalName, String[] userPackages) throws Exception { JAXBContext jaxbContext = createJAXBContext(userPackages); JAXBElement jaxbRequest = new JAXBElement(new QName(msgQName, msgLocalName), msg.getClass(), msg ); StringWriter writer = new StringWriter(); jaxbContext.createMarshaller().marshal(jaxbRequest, writer); return writer.toString(); } protected Object unmarshal(String msg, Class endpointInterface, String userPackages[]) throws Exception { JAXBContext jaxbContext = createJAXBContext(endpointInterface, userPackages); return jaxbContext.createUnmarshaller().unmarshal(new StringSource(msg)); } protected JAXBContext createJAXBContext(Class interfaceClass, String[] userPackages) throws JAXBException { List<Class> classes = new ArrayList<Class>(); //List<String> packages = new ArrayList<String>(); StringBuilder packages = new StringBuilder(); for (int i = 0; i < userPackages.length; i++) { String userPackage = userPackages[i]; packages.append(userPackage + ":"); } //classes.add(JbiFault.class); for (Method mth : interfaceClass.getMethods()) { WebMethod wm = (WebMethod) mth.getAnnotation(WebMethod.class); if (wm != null) { classes.add(mth.getReturnType()); classes.addAll(Arrays.asList(mth.getParameterTypes())); packages.append(getPackages(mth.getParameterTypes())); packages.append(":"); } } return JAXBContext.newInstance(packages.toString()); } protected JAXBContext createJAXBContext(String[] userPackages) throws JAXBException { List<Class> classes = new ArrayList<Class>(); //List<String> packages = new ArrayList<String>(); StringBuilder packages = new StringBuilder(); for (int i = 0; i < userPackages.length; i++) { String userPackage = userPackages[i]; packages.append(userPackage + ":"); } return JAXBContext.newInstance(packages.toString()); } @SuppressWarnings("unchecked") protected WebService getWebServiceAnnotation(Class clazz) { for (Class cl = clazz; cl != null; cl = cl.getSuperclass()) { WebService ws = (WebService) cl.getAnnotation(WebService.class); if (ws != null) { return ws; } } return null; } private static String getPackages(Class[] classes) { StringBuilder pkgs = new StringBuilder(); boolean first = true; for (int i = 0; i < classes.length; i++) { Class aClass = classes[i]; if (!first) pkgs.append(":"); else first = false; pkgs.append(getPackage(aClass.getName())); } return pkgs.toString(); } private static String getPackage(String pckg) { int i = pckg.lastIndexOf("."); if (i != -1) pckg = pckg.substring(0, i); return pckg; } }