Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E87AB17892 for ; Wed, 4 Mar 2015 16:00:07 +0000 (UTC) Received: (qmail 13027 invoked by uid 500); 4 Mar 2015 16:00:07 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 12970 invoked by uid 500); 4 Mar 2015 16:00:07 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 12960 invoked by uid 99); 4 Mar 2015 16:00:07 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 04 Mar 2015 16:00:07 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 7C912E0EE5; Wed, 4 Mar 2015 16:00:07 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: coheigea@apache.org To: commits@cxf.apache.org Message-Id: <2c02fcfca2214f3e82cf1eb9900a5307@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: cxf-fediz git commit: Adding a mock SAML SSO IdP to the systests to be used in the federation scenario with the Fediz IdP Date: Wed, 4 Mar 2015 16:00:07 +0000 (UTC) Repository: cxf-fediz Updated Branches: refs/heads/master 4e44b9aaf -> 1858cb9e3 Adding a mock SAML SSO IdP to the systests to be used in the federation scenario with the Fediz IdP Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/1858cb9e Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/1858cb9e Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/1858cb9e Branch: refs/heads/master Commit: 1858cb9e357f0829f013df52bb265b2e883d1543 Parents: 4e44b9a Author: Colm O hEigeartaigh Authored: Wed Mar 4 15:56:15 2015 +0000 Committer: Colm O hEigeartaigh Committed: Wed Mar 4 15:56:15 2015 +0000 ---------------------------------------------------------------------- systests/federation/pom.xml | 1 + systests/federation/samlIdpWebapp/pom.xml | 73 ++++++++ .../fediz/samlsso/example/BasicAuthFilter.java | 122 +++++++++++++ .../samlsso/example/CommonCallbackHandler.java | 46 +++++ .../samlsso/example/SAML2CallbackHandler.java | 147 ++++++++++++++++ .../example/SAML2PResponseComponentBuilder.java | 126 ++++++++++++++ .../cxf/fediz/samlsso/example/SamlSso.java | 174 +++++++++++++++++++ .../src/main/resources/log4j.properties | 17 ++ .../src/main/resources/logging.properties | 52 ++++++ .../src/main/resources/stsKeystoreB.properties | 6 + .../src/main/resources/stsrealm_b.jks | Bin 0 -> 2061 bytes .../src/main/resources/ststrust.jks | Bin 0 -> 2561 bytes .../main/webapp/WEB-INF/applicationContext.xml | 47 +++++ .../src/main/webapp/WEB-INF/cxf-service.xml | 68 ++++++++ .../src/main/webapp/WEB-INF/web.xml | 29 ++++ .../samlIdpWebapp/src/main/webapp/index.html | 25 +++ .../src/main/webapp/secure/test.html | 25 +++ systests/federation/samlsso/pom.xml | 10 ++ .../cxf/fediz/integrationtests/SAMLSSOTest.java | 89 +++++++++- .../src/test/resources/entities-realma.xml | 2 +- .../cxf/fediz/integrationtests/WSFedTest.java | 2 +- 21 files changed, 1054 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/pom.xml ---------------------------------------------------------------------- diff --git a/systests/federation/pom.xml b/systests/federation/pom.xml index 870a9b4..0cd60d3 100644 --- a/systests/federation/pom.xml +++ b/systests/federation/pom.xml @@ -31,6 +31,7 @@ pom + samlIdpWebapp samlsso wsfed http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/pom.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/pom.xml b/systests/federation/samlIdpWebapp/pom.xml new file mode 100644 index 0000000..10b6cdf --- /dev/null +++ b/systests/federation/samlIdpWebapp/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + + org.apache.cxf.fediz.systests + fediz-systests-federation + 1.2.0-SNAPSHOT + ../pom.xml + + org.apache.cxf.fediz.systests.federation + fediz-systests-federation-samlIdpWebapp + Apache Fediz Federation Systests SAML SSO Mock Idp Webapp + war + + UTF-8 + + + + javax.servlet + servlet-api + ${servlet.version} + provided + + + org.apache.commons + commons-lang3 + ${commons.lang.version} + + + org.apache.cxf + cxf-rt-frontend-jaxrs + ${cxf.version} + + + org.apache.cxf + cxf-rt-rs-security-sso-saml + ${cxf.version} + + + org.springframework + spring-web + ${spring.version} + + + org.slf4j + slf4j-jdk14 + ${slf4j.version} + + + + + + samlssoidp + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/BasicAuthFilter.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/BasicAuthFilter.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/BasicAuthFilter.java new file mode 100644 index 0000000..29cf4df --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/BasicAuthFilter.java @@ -0,0 +1,122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.fediz.samlsso.example; + +import java.io.IOException; +import java.security.Principal; + +import javax.security.auth.callback.CallbackHandler; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Response; + +import org.w3c.dom.Document; +import org.apache.cxf.configuration.security.AuthorizationPolicy; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.security.SecurityContext; +import org.apache.wss4j.common.principal.WSUsernameTokenPrincipalImpl; +import org.apache.wss4j.dom.WSConstants; +import org.apache.wss4j.dom.WSSConfig; +import org.apache.wss4j.dom.handler.RequestData; +import org.apache.wss4j.dom.message.token.UsernameToken; +import org.apache.wss4j.dom.validate.Credential; +import org.apache.wss4j.dom.validate.UsernameTokenValidator; + +/** + * A simple filter to validate a Basic Auth username/password via a CallbackHandler + */ +public class BasicAuthFilter implements ContainerRequestFilter { + + static { + WSSConfig.init(); + } + + private CallbackHandler callbackHandler; + + public void filter(ContainerRequestContext requestContext) throws IOException { + Message message = JAXRSUtils.getCurrentMessage(); + AuthorizationPolicy policy = message.get(AuthorizationPolicy.class); + + if (policy == null || policy.getUserName() == null || policy.getPassword() == null) { + requestContext.abortWith( + Response.status(401).header("WWW-Authenticate", "Basic realm=\"IdP\"").build()); + return; + } + + try { + UsernameToken token = convertPolicyToToken(policy); + Credential credential = new Credential(); + credential.setUsernametoken(token); + + RequestData data = new RequestData(); + data.setMsgContext(message); + data.setCallbackHandler(callbackHandler); + UsernameTokenValidator validator = new UsernameTokenValidator(); + credential = validator.validate(credential, data); + + // Create a Principal/SecurityContext + Principal p = null; + if (credential != null && credential.getPrincipal() != null) { + p = credential.getPrincipal(); + } else { + p = new WSUsernameTokenPrincipalImpl(policy.getUserName(), false); + ((WSUsernameTokenPrincipalImpl)p).setPassword(policy.getPassword()); + } + message.put(SecurityContext.class, createSecurityContext(p)); + } catch (Exception ex) { + requestContext.abortWith( + Response.status(401).header("WWW-Authenticate", "Basic realm=\"IdP\"").build()); + } + } + + protected UsernameToken convertPolicyToToken(AuthorizationPolicy policy) + throws Exception { + + Document doc = DOMUtils.createDocument(); + UsernameToken token = new UsernameToken(false, doc, + WSConstants.PASSWORD_TEXT); + token.setName(policy.getUserName()); + token.setPassword(policy.getPassword()); + return token; + } + + protected SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + + public Principal getUserPrincipal() { + return p; + } + + public boolean isUserInRole(String arg0) { + return false; + } + }; + } + + public CallbackHandler getCallbackHandler() { + return callbackHandler; + } + + public void setCallbackHandler(CallbackHandler callbackHandler) { + this.callbackHandler = callbackHandler; + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/CommonCallbackHandler.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/CommonCallbackHandler.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/CommonCallbackHandler.java new file mode 100644 index 0000000..1576304 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/CommonCallbackHandler.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.fediz.samlsso.example; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.wss4j.common.ext.WSPasswordCallback; + +public class CommonCallbackHandler implements CallbackHandler { + + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof WSPasswordCallback) { // CXF + WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; + if ("ALICE".equals(pc.getIdentifier())) { + pc.setPassword("ECILA"); + break; + } else if ("realmb".equals(pc.getIdentifier())) { + pc.setPassword("realmb"); + break; + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2CallbackHandler.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2CallbackHandler.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2CallbackHandler.java new file mode 100644 index 0000000..39065b6 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2CallbackHandler.java @@ -0,0 +1,147 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.samlsso.example; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.bean.AttributeBean; +import org.apache.wss4j.common.saml.bean.AttributeStatementBean; +import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean; +import org.apache.wss4j.common.saml.bean.ConditionsBean; +import org.apache.wss4j.common.saml.bean.SubjectBean; +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.opensaml.common.SAMLVersion; + +/** + * A Callback Handler implementation for a SAML 2 assertion for use by the SAML SSO IdP. By + * default it creates a SAML 2.0 Assertion with an AuthenticationStatement. If a list of roles + * are also supplied, it will insert them as part of an AttributeStatement. + */ +public class SAML2CallbackHandler implements CallbackHandler { + + private String subjectName; + private String subjectQualifier; + private String confirmationMethod = SAML2Constants.CONF_BEARER; + private String issuer; + private String subjectNameIDFormat; + private ConditionsBean conditions; + private SubjectConfirmationDataBean subjectConfirmationData; + + private void createAndSetStatement(SAMLCallback callback) { + AuthenticationStatementBean authBean = new AuthenticationStatementBean(); + authBean.setAuthenticationMethod("Password"); + callback.setAuthenticationStatementData(Collections.singletonList(authBean)); + + // Add roles for certain users + List roles = new ArrayList(); + if ("alice".equals(subjectName)) { + roles.add("boss"); + roles.add("employee"); + roles.add("User"); + } else if ("bob".equals(subjectName)) { + roles.add("employee"); + } + + if (!roles.isEmpty()) { + AttributeStatementBean attrBean = new AttributeStatementBean(); + AttributeBean attributeBean = new AttributeBean(); + attributeBean.setQualifiedName("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); + attributeBean.setNameFormat(SAML2Constants.ATTRNAME_FORMAT_UNSPECIFIED); + attributeBean.setAttributeValues(roles); + + attrBean.setSamlAttributes(Collections.singletonList(attributeBean)); + callback.setAttributeStatementData(Collections.singletonList(attrBean)); + } + } + + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof SAMLCallback) { + SAMLCallback callback = (SAMLCallback) callbacks[i]; + callback.setSamlVersion(SAMLVersion.VERSION_20); + callback.setIssuer(issuer); + if (conditions != null) { + callback.setConditions(conditions); + } + + SubjectBean subjectBean = + new SubjectBean( + subjectName, subjectQualifier, confirmationMethod + ); + if (subjectNameIDFormat != null) { + subjectBean.setSubjectNameIDFormat(subjectNameIDFormat); + } + subjectBean.setSubjectConfirmationData(subjectConfirmationData); + + callback.setSubject(subjectBean); + createAndSetStatement(callback); + } else { + throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); + } + } + } + + public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData) { + this.subjectConfirmationData = subjectConfirmationData; + } + + public void setConditions(ConditionsBean conditionsBean) { + this.conditions = conditionsBean; + } + + public void setConfirmationMethod(String confMethod) { + confirmationMethod = confMethod; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public void setSubjectNameIDFormat(String subjectNameIDFormat) { + this.subjectNameIDFormat = subjectNameIDFormat; + } + + public String getSubjectName() { + return subjectName; + } + + public void setSubjectName(String subjectName) { + this.subjectName = subjectName; + } + + public String getSubjectQualifier() { + return subjectQualifier; + } + + public void setSubjectQualifier(String subjectQualifier) { + this.subjectQualifier = subjectQualifier; + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2PResponseComponentBuilder.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2PResponseComponentBuilder.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2PResponseComponentBuilder.java new file mode 100644 index 0000000..e53f443 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SAML2PResponseComponentBuilder.java @@ -0,0 +1,126 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.samlsso.example; + +import java.util.UUID; + +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.common.SAMLObjectBuilder; +import org.opensaml.common.SAMLVersion; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.Status; +import org.opensaml.saml2.core.StatusCode; +import org.opensaml.saml2.core.StatusMessage; +import org.opensaml.xml.XMLObjectBuilderFactory; + +/** +* A (basic) set of utility methods to construct SAML 2.0 Protocol Response statements +*/ +public final class SAML2PResponseComponentBuilder { + + private static SAMLObjectBuilder responseBuilder; + + private static SAMLObjectBuilder issuerBuilder; + + private static SAMLObjectBuilder statusBuilder; + + private static SAMLObjectBuilder statusCodeBuilder; + + private static SAMLObjectBuilder statusMessageBuilder; + + private static XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + + private SAML2PResponseComponentBuilder() { + + } + + @SuppressWarnings("unchecked") + public static Response createSAMLResponse( + String inResponseTo, + String issuer, + Status status + ) { + if (responseBuilder == null) { + responseBuilder = (SAMLObjectBuilder) + builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME); + } + Response response = responseBuilder.buildObject(); + + response.setID(UUID.randomUUID().toString()); + response.setIssueInstant(new DateTime()); + response.setInResponseTo(inResponseTo); + response.setIssuer(createIssuer(issuer)); + response.setStatus(status); + response.setVersion(SAMLVersion.VERSION_20); + + return response; + } + + @SuppressWarnings("unchecked") + public static Issuer createIssuer( + String issuerValue + ) { + if (issuerBuilder == null) { + issuerBuilder = (SAMLObjectBuilder) + builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + } + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(issuerValue); + + return issuer; + } + + @SuppressWarnings("unchecked") + public static Status createStatus( + String statusCodeValue, + String statusMessage + ) { + if (statusBuilder == null) { + statusBuilder = (SAMLObjectBuilder) + builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME); + } + if (statusCodeBuilder == null) { + statusCodeBuilder = (SAMLObjectBuilder) + builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME); + } + if (statusMessageBuilder == null) { + statusMessageBuilder = (SAMLObjectBuilder) + builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME); + } + + Status status = statusBuilder.buildObject(); + + StatusCode statusCode = statusCodeBuilder.buildObject(); + statusCode.setValue(statusCodeValue); + status.setStatusCode(statusCode); + + if (statusMessage != null) { + StatusMessage statusMessageObject = statusMessageBuilder.buildObject(); + statusMessageObject.setMessage(statusMessage); + status.setStatusMessage(statusMessageObject); + } + + return status; + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java new file mode 100644 index 0000000..d65172f --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/java/org/apache/cxf/fediz/samlsso/example/SamlSso.java @@ -0,0 +1,174 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.samlsso.example; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Collections; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriBuilder; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; +import org.apache.cxf.staxutils.StaxUtils; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.SAMLUtil; +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean; +import org.apache.wss4j.common.saml.bean.ConditionsBean; +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean; +import org.apache.wss4j.common.util.DOM2Writer; +import org.joda.time.DateTime; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.Status; + +/** + * A mock IdP for SAML SSO. The user is already authenticated via HTTP/BA. + */ +@Path("/samlsso") +public class SamlSso { + + static { + OpenSAMLUtil.initSamlEngine(); + } + + private final DocumentBuilderFactory docBuilderFactory; + private MessageContext messageContext; + + public SamlSso() { + docBuilderFactory = DocumentBuilderFactory.newInstance(); + docBuilderFactory.setNamespaceAware(true); + } + + @GET + public javax.ws.rs.core.Response login(@QueryParam("SAMLRequest") String samlRequest, + @QueryParam("RelayState") String relayState) throws Exception { + + byte[] deflatedToken = Base64Utility.decode(samlRequest); + InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); + + Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + AuthnRequest request = + (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + System.out.println(DOM2Writer.nodeToString(responseDoc)); + + String racs = request.getAssertionConsumerServiceURL(); + String requestIssuer = request.getIssuer().getValue(); + + // Create the response + Element response = createResponse(request.getID(), racs, requestIssuer); + String responseStr = encodeResponse(response); + + // Perform Redirect to RACS + UriBuilder ub = UriBuilder.fromUri(racs); + ub.queryParam("SAMLResponse", responseStr); + ub.queryParam("RelayState", relayState); + + // TODO hack just to keep the Fediz IdP happy + ub.queryParam("wtrealm", "blah"); + ub.queryParam("wresult", "blah"); + + return javax.ws.rs.core.Response.seeOther(ub.build()).build(); + } + + @Context + public void setMessageContext(MessageContext mc) { + this.messageContext = mc; + } + + private Element createResponse(String requestID, String racs, String requestIssuer) throws Exception { + DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + + Status status = + SAML2PResponseComponentBuilder.createStatus( + "urn:oasis:names:tc:SAML:2.0:status:Success", null + ); + String issuer = messageContext.getUriInfo().getAbsolutePath().toString(); + Response response = + SAML2PResponseComponentBuilder.createSAMLResponse(requestID, issuer, status); + + // Create an AuthenticationAssertion + SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler(); + callbackHandler.setIssuer(issuer); + String user = messageContext.getSecurityContext().getUserPrincipal().getName(); + callbackHandler.setSubjectName(user); + + // Subject Confirmation Data + SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean(); + subjectConfirmationData.setAddress(messageContext.getHttpServletRequest().getRemoteAddr()); + subjectConfirmationData.setInResponseTo(requestID); + subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5)); + subjectConfirmationData.setRecipient(racs); + callbackHandler.setSubjectConfirmationData(subjectConfirmationData); + + // Audience Restriction + ConditionsBean conditions = new ConditionsBean(); + conditions.setTokenPeriodMinutes(5); + + AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean(); + audienceRestriction.setAudienceURIs(Collections.singletonList(requestIssuer)); + conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction)); + callbackHandler.setConditions(conditions); + + SAMLCallback samlCallback = new SAMLCallback(); + SAMLUtil.doSAMLCallback(callbackHandler, samlCallback); + SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback); + + Crypto issuerCrypto = CryptoFactory.getInstance("stsKeystoreB.properties"); + assertion.signAssertion("realmb", "realmb", issuerCrypto, false); + + response.getAssertions().add(assertion.getSaml2()); + + Element policyElement = OpenSAMLUtil.toDom(response, doc); + doc.appendChild(policyElement); + + return policyElement; + } + + private String encodeResponse(Element response) throws IOException { + String responseMessage = DOM2Writer.nodeToString(response); + System.out.println("RESP: " + responseMessage); + + DeflateEncoderDecoder encoder = new DeflateEncoderDecoder(); + byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes("UTF-8")); + + return Base64Utility.encode(deflatedBytes); + } + +} + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/log4j.properties b/systests/federation/samlIdpWebapp/src/main/resources/log4j.properties new file mode 100644 index 0000000..5fc56e3 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/resources/log4j.properties @@ -0,0 +1,17 @@ +# Set root category priority to INFO and its only appender to CONSOLE. +log4j.rootCategory=FATAL, CONSOLE +#log4j.rootCategory=DEBUG, CONSOLE + +# CONSOLE is set to be a ConsoleAppender using a PatternLayout. +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Threshold=DEBUG +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n + +# LOGFILE is set to be a File appender using a PatternLayout. +log4j.appender.LOGFILE=org.apache.log4j.FileAppender +log4j.appender.LOGFILE.File=target/wss4j.log +log4j.appender.LOGFILE.Append=false +log4j.appender.LOGFILE.Threshold=DEBUG +log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout +log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/resources/logging.properties ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/logging.properties b/systests/federation/samlIdpWebapp/src/main/resources/logging.properties new file mode 100644 index 0000000..c185d61 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/resources/logging.properties @@ -0,0 +1,52 @@ +############################################################ +# Default Logging Configuration File +# +# You can use a different file by specifying a filename +# with the java.util.logging.config.file system property. +# For example java -Djava.util.logging.config.file=myfile +############################################################ + +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +# Note that these classes must be on the system classpath. +# By default we only configure a ConsoleHandler, which will only +# show messages at the WARNING and above levels. +handlers= java.util.logging.ConsoleHandler +#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler + +# Default global logging level. +# This specifies which kinds of events are logged across +# all loggers. For any given facility this global level +# can be overridden by a facility specific level +# Note that the ConsoleHandler also has a separate level +# setting to limit messages printed to the console. +.level= INFO + +############################################################ +# Handler specific properties. +# Describes specific configuration info for Handlers. +############################################################ + +# default file output is in user's home directory. +java.util.logging.FileHandler.pattern = %h/java%u.log +java.util.logging.FileHandler.limit = 50000 +java.util.logging.FileHandler.count = 1 +java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter + +# Limit the message that are printed on the console to WARNING and above. +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + + +############################################################ +# Facility specific properties. +# Provides extra control for each logger. +############################################################ + +# For example, set the com.xyz.foo logger to only log SEVERE +# messages: +#com.xyz.foo.level = SEVERE http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/resources/stsKeystoreB.properties ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/stsKeystoreB.properties b/systests/federation/samlIdpWebapp/src/main/resources/stsKeystoreB.properties new file mode 100644 index 0000000..16f4a3c --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/resources/stsKeystoreB.properties @@ -0,0 +1,6 @@ +org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin +org.apache.ws.security.crypto.merlin.keystore.type=jks +org.apache.ws.security.crypto.merlin.keystore.password=storepass +org.apache.ws.security.crypto.merlin.keystore.alias=realmb +org.apache.ws.security.crypto.merlin.keystore.file=stsrealm_b.jks + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/resources/stsrealm_b.jks ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/stsrealm_b.jks b/systests/federation/samlIdpWebapp/src/main/resources/stsrealm_b.jks new file mode 100644 index 0000000..02075e6 Binary files /dev/null and b/systests/federation/samlIdpWebapp/src/main/resources/stsrealm_b.jks differ http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/resources/ststrust.jks ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/resources/ststrust.jks b/systests/federation/samlIdpWebapp/src/main/resources/ststrust.jks new file mode 100644 index 0000000..911945c Binary files /dev/null and b/systests/federation/samlIdpWebapp/src/main/resources/ststrust.jks differ http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/applicationContext.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/applicationContext.xml b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/applicationContext.xml new file mode 100644 index 0000000..eea155c --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/applicationContext.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/cxf-service.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/cxf-service.xml b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/cxf-service.xml new file mode 100644 index 0000000..4e44578 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/cxf-service.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/web.xml b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..11a1587 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,29 @@ + + + SAML SSO Mock IdP + SAML SSO Mock IdP + + + contextConfigLocation + WEB-INF/applicationContext.xml + + + + org.springframework.web.context.ContextLoaderListener + + + + SAMLSSOServlet + org.apache.cxf.transport.servlet.CXFServlet + 1 + + + + SAMLSSOServlet + /* + + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/webapp/index.html ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/webapp/index.html b/systests/federation/samlIdpWebapp/src/main/webapp/index.html new file mode 100644 index 0000000..a9dbdaa --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/webapp/index.html @@ -0,0 +1,25 @@ + + +WS Federation Tomcat Examples + + + +

+

Hello World

+

+ http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlIdpWebapp/src/main/webapp/secure/test.html ---------------------------------------------------------------------- diff --git a/systests/federation/samlIdpWebapp/src/main/webapp/secure/test.html b/systests/federation/samlIdpWebapp/src/main/webapp/secure/test.html new file mode 100644 index 0000000..9cf7366 --- /dev/null +++ b/systests/federation/samlIdpWebapp/src/main/webapp/secure/test.html @@ -0,0 +1,25 @@ + + +WS Federation Tomcat Examples + + + +

+

Secure Test

+

+ http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlsso/pom.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/pom.xml b/systests/federation/samlsso/pom.xml index 3fd7390..2458033 100644 --- a/systests/federation/samlsso/pom.xml +++ b/systests/federation/samlsso/pom.xml @@ -127,6 +127,7 @@ idp.https.port + idp.samlsso.https.port rp.https.port @@ -162,6 +163,14 @@ target/tomcat/idp/webapps/fediz-idp-sts + org.apache.cxf.fediz.systests.federation + fediz-systests-federation-samlIdpWebapp + ${project.version} + war + true + target/tomcat/idpsamlsso/webapps/idpsaml + + org.apache.cxf.fediz.systests fediz-systests-simpleWebapp ${project.version} @@ -235,6 +244,7 @@ true ${idp.https.port} + ${idp.samlsso.https.port} ${rp.https.port} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java b/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java index 17913e1..dd879c6 100644 --- a/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java +++ b/systests/federation/samlsso/src/test/java/org/apache/cxf/fediz/integrationtests/SAMLSSOTest.java @@ -21,24 +21,38 @@ package org.apache.cxf.fediz.integrationtests; import java.io.File; +import java.io.IOException; + +import com.gargoylesoftware.htmlunit.CookieManager; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; import org.apache.catalina.Context; import org.apache.catalina.LifecycleState; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.cxf.fediz.tomcat.FederationAuthenticator; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; /** + * This is a test for federation in the IdP. The RP application is configured to use a home realm of "realm b". The + * client gets redirected to the IdP for "realm a", which in turn redirects to the IdP for "realm b", which is a + * SAML SSO IdP. The IdP for "realm a" will convert the signin request to a SAML SSO sign in request. The IdP for + * realm b authenticates the user, who is then redirected back to the IdP for "realm a" to get a SAML token from + * the STS + then back to the application. */ public class SAMLSSOTest { static String idpHttpsPort; + static String idpSamlSSOHttpsPort; static String rpHttpsPort; private static Tomcat idpServer; + private static Tomcat idpSamlSSOServer; private static Tomcat rpServer; @BeforeClass @@ -54,10 +68,13 @@ public class SAMLSSOTest { idpHttpsPort = System.getProperty("idp.https.port"); Assert.assertNotNull("Property 'idp.https.port' null", idpHttpsPort); + idpSamlSSOHttpsPort = System.getProperty("idp.samlsso.https.port"); + Assert.assertNotNull("Property 'idp.samlsso.https.port' null", idpSamlSSOHttpsPort); rpHttpsPort = System.getProperty("rp.https.port"); Assert.assertNotNull("Property 'rp.https.port' null", rpHttpsPort); initIdp(); + initSamlSSOIdp(); initRp(); } @@ -97,6 +114,41 @@ public class SAMLSSOTest { } } + private static void initSamlSSOIdp() { + try { + idpSamlSSOServer = new Tomcat(); + idpSamlSSOServer.setPort(0); + String currentDir = new File(".").getCanonicalPath(); + idpSamlSSOServer.setBaseDir(currentDir + File.separator + "target"); + + idpSamlSSOServer.getHost().setAppBase("tomcat/idpsamlsso/webapps"); + idpSamlSSOServer.getHost().setAutoDeploy(true); + idpSamlSSOServer.getHost().setDeployOnStartup(true); + + Connector httpsConnector = new Connector(); + httpsConnector.setPort(Integer.parseInt(idpSamlSSOHttpsPort)); + httpsConnector.setSecure(true); + httpsConnector.setScheme("https"); + //httpsConnector.setAttribute("keyAlias", keyAlias); + httpsConnector.setAttribute("keystorePass", "tompass"); + httpsConnector.setAttribute("keystoreFile", "test-classes/server.jks"); + httpsConnector.setAttribute("truststorePass", "tompass"); + httpsConnector.setAttribute("truststoreFile", "test-classes/server.jks"); + httpsConnector.setAttribute("clientAuth", "want"); + // httpsConnector.setAttribute("clientAuth", "false"); + httpsConnector.setAttribute("sslProtocol", "TLS"); + httpsConnector.setAttribute("SSLEnabled", true); + + idpSamlSSOServer.getService().addConnector(httpsConnector); + + idpSamlSSOServer.addWebapp("/idp", "idpsaml"); + + idpSamlSSOServer.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + private static void initRp() { try { rpServer = new Tomcat(); @@ -181,15 +233,19 @@ public class SAMLSSOTest { @org.junit.Ignore public void testSAMLSSO() throws Exception { String url = "https://localhost:" + getRpHttpsPort() + "/fedizhelloworld/secure/fedservlet"; - // String user = "alice"; - // String password = "ecila"; - System.out.println("URL: " + url); + // Thread.sleep(60 * 2 * 1000); - Thread.sleep(60 * 2 * 1000); /* + * TODO Logging in fine to the SAML SSO IdP, but when redirected the context is missing or something + * Maybe a bug with htmlunit + * + String user = "ALICE"; // realm b credentials + String password = "ECILA"; final String bodyTextContent = - HTTPTestUtils.login(url, user, password, getIdpHttpsPort()); + login(url, user, password, idpSamlSSOHttpsPort, idpHttpsPort); + + System.out.println("BODY: " + bodyTextContent); Assert.assertTrue("Principal not " + user, bodyTextContent.contains("userPrincipal=" + user)); @@ -212,4 +268,27 @@ public class SAMLSSOTest { */ } + protected static String login(String url, String user, String password, + String idpPort, String rpIdpPort) throws IOException { + // + // Access the RP + get redirected to the IdP for "realm a". Then get redirected to the IdP for + // "realm b". + // + final WebClient webClient = new WebClient(); + CookieManager cookieManager = new CookieManager(); + webClient.setCookieManager(cookieManager); + webClient.getOptions().setUseInsecureSSL(true); + webClient.getCredentialsProvider().setCredentials( + new AuthScope("localhost", Integer.parseInt(idpPort)), + new UsernamePasswordCredentials(user, password)); + + //webClient.getOptions().setJavaScriptEnabled(false); + final HtmlPage idpPage = webClient.getPage(url); + //webClient.getOptions().setJavaScriptEnabled(true); + // Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); + + return idpPage.getBody().getTextContent(); + + } + } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/samlsso/src/test/resources/entities-realma.xml ---------------------------------------------------------------------- diff --git a/systests/federation/samlsso/src/test/resources/entities-realma.xml b/systests/federation/samlsso/src/test/resources/entities-realma.xml index 2f66591..8f8f319 100644 --- a/systests/federation/samlsso/src/test/resources/entities-realma.xml +++ b/systests/federation/samlsso/src/test/resources/entities-realma.xml @@ -81,7 +81,7 @@ class="org.apache.cxf.fediz.service.idp.service.jpa.TrustedIdpEntity"> - + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/1858cb9e/systests/federation/wsfed/src/test/java/org/apache/cxf/fediz/integrationtests/WSFedTest.java ---------------------------------------------------------------------- diff --git a/systests/federation/wsfed/src/test/java/org/apache/cxf/fediz/integrationtests/WSFedTest.java b/systests/federation/wsfed/src/test/java/org/apache/cxf/fediz/integrationtests/WSFedTest.java index 0e076b4..a4f62d3 100644 --- a/systests/federation/wsfed/src/test/java/org/apache/cxf/fediz/integrationtests/WSFedTest.java +++ b/systests/federation/wsfed/src/test/java/org/apache/cxf/fediz/integrationtests/WSFedTest.java @@ -270,7 +270,7 @@ public class WSFedTest { bodyTextContent.contains(claim + "=alice@realma.org")); } - public static String login(String url, String user, String password, + private static String login(String url, String user, String password, String idpPort, String rpIdpPort) throws IOException { // // Access the RP + get redirected to the IdP for "realm a". Then get redirected to the IdP for