You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2017/04/12 15:10:52 UTC
[4/4] syncope git commit: [SYNCOPE-1061] Both POST and Redirect
binding profiles are now supported;
if both are available for a given IdP, the one to be used is configurable,
with POST predefined
[SYNCOPE-1061] Both POST and Redirect binding profiles are now supported; if both are available for a given IdP, the one to be used is configurable, with POST predefined
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/847e0c9b
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/847e0c9b
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/847e0c9b
Branch: refs/heads/master
Commit: 847e0c9ba9e2a353386f18baf5f95043422be338
Parents: f1329b7
Author: Francesco Chicchiricc� <il...@apache.org>
Authored: Wed Apr 12 17:03:46 2017 +0200
Committer: Francesco Chicchiricc� <il...@apache.org>
Committed: Wed Apr 12 17:10:33 2017 +0200
----------------------------------------------------------------------
.../saml2lsp/agent/AbstractSAML2SPServlet.java | 94 ++++++++++
.../ext/saml2lsp/agent/AssertionConsumer.java | 9 +-
.../syncope/ext/saml2lsp/agent/Login.java | 2 +-
.../syncope/ext/saml2lsp/agent/Logout.java | 99 +++++++----
.../syncope/ext/saml2lsp/agent/Metadata.java | 2 +-
.../ext/saml2lsp/agent/SAML2PostBinding.java | 51 ------
.../console/panels/SAML2IdPsDirectoryPanel.java | 2 +
.../console/wizards/SAML2IdPWizardBuilder.java | 9 +
.../panels/SAML2IdPsDirectoryPanel.properties | 1 +
.../SAML2IdPsDirectoryPanel_it.properties | 1 +
.../SAML2IdPsDirectoryPanel_pt_BR.properties | 1 +
.../SAML2IdPsDirectoryPanel_ru.properties | 1 +
.../apache/syncope/common/lib/SSOConstants.java | 36 ++++
.../syncope/common/lib/to/SAML2IdPTO.java | 11 ++
.../common/lib/to/SAML2ReceivedResponseTO.java | 62 +++++++
.../syncope/common/lib/to/SAML2RequestTO.java | 31 ++++
.../common/lib/types/SAML2BindingType.java | 56 ++++++
.../syncope/core/logic/SAML2IdPLogic.java | 49 ++++--
.../apache/syncope/core/logic/SAML2SPLogic.java | 171 +++++++++----------
.../syncope/core/logic/init/SAML2SPLoader.java | 5 -
.../syncope/core/logic/saml2/SAML2IdPCache.java | 16 +-
.../logic/saml2/SAML2IdPCallbackHandler.java | 45 -----
.../core/logic/saml2/SAML2IdPEntity.java | 23 ++-
.../core/logic/saml2/SAML2ReaderWriter.java | 106 ++++++++++--
.../syncope/core/logic/saml2/SAML2Signer.java | 98 -----------
.../core/logic/saml2/SAMLSPCallbackHandler.java | 45 +++++
ext/saml2sp/persistence-api/pom.xml | 5 +
.../core/persistence/api/entity/SAML2IdP.java | 5 +
.../persistence/jpa/entity/JPASAML2IdP.java | 14 ++
.../java/data/SAML2IdPDataBinderImpl.java | 2 +
.../common/rest/api/service/SAML2SPService.java | 16 +-
.../rest/cxf/service/SAML2SPServiceImpl.java | 16 +-
.../org/apache/syncope/fit/SAML2SPDetector.java | 2 +-
.../apache/syncope/fit/core/SAML2ITCase.java | 2 +-
34 files changed, 701 insertions(+), 387 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AbstractSAML2SPServlet.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AbstractSAML2SPServlet.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AbstractSAML2SPServlet.java
new file mode 100644
index 0000000..bd295e3
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AbstractSAML2SPServlet.java
@@ -0,0 +1,94 @@
+/*
+ * 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.syncope.ext.saml2lsp.agent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.syncope.common.lib.SSOConstants;
+import org.apache.syncope.common.lib.to.SAML2ReceivedResponseTO;
+import org.apache.syncope.common.lib.to.SAML2RequestTO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractSAML2SPServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 7969539245875799817L;
+
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractSAML2SPServlet.class);
+
+ protected void prepare(final HttpServletResponse response, final SAML2RequestTO requestTO) throws IOException {
+ response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
+ response.setHeader("Pragma", "no-cache");
+ switch (requestTO.getBindingType()) {
+ case REDIRECT:
+ UriBuilder ub = UriBuilder.fromUri(requestTO.getIdpServiceAddress());
+ ub.queryParam(SSOConstants.SAML_REQUEST, requestTO.getContent());
+ ub.queryParam(SSOConstants.RELAY_STATE, requestTO.getRelayState());
+ ub.queryParam(SSOConstants.SIG_ALG, requestTO.getSignAlg());
+ ub.queryParam(SSOConstants.SIGNATURE, requestTO.getSignature());
+
+ response.setStatus(HttpServletResponse.SC_SEE_OTHER);
+ response.setHeader("Location", ub.build().toASCIIString());
+ break;
+
+ case POST:
+ default:
+ response.setContentType(MediaType.TEXT_HTML);
+ response.getWriter().write(""
+ + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ + " <body onLoad=\"document.forms[0].submit();\">"
+ + " <form action=\"" + requestTO.getIdpServiceAddress() + "\" method=\"POST\">"
+ + " <input type=\"hidden\" name=\"" + SSOConstants.SAML_REQUEST + "\""
+ + " value=\"" + requestTO.getContent() + "\"/>"
+ + " <input type=\"hidden\" name=\"" + SSOConstants.RELAY_STATE + "\""
+ + " value=\"" + requestTO.getRelayState() + "\"/>"
+ + " <input type=\"submit\" style=\"visibility: hidden;\"/>"
+ + " </form>"
+ + " </body>"
+ + "</html>");
+ }
+ }
+
+ protected SAML2ReceivedResponseTO extract(final InputStream response) throws IOException {
+ String strForm = IOUtils.toString(response);
+ MultivaluedMap<String, String> params = JAXRSUtils.getStructuredParams(strForm, "&", false, false);
+
+ String samlResponse = URLDecoder.decode(
+ params.getFirst(SSOConstants.SAML_RESPONSE), StandardCharsets.UTF_8.name());
+ LOG.debug("Received SAML Response: {}", samlResponse);
+
+ String relayState = params.getFirst(SSOConstants.RELAY_STATE);
+ LOG.debug("Received Relay State: {}", relayState);
+
+ SAML2ReceivedResponseTO receivedResponseTO = new SAML2ReceivedResponseTO();
+ receivedResponseTO.setSamlResponse(samlResponse);
+ receivedResponseTO.setRelayState(relayState);
+ return receivedResponseTO;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
index 00a853e..6407b4d 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
@@ -23,22 +23,17 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
-import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.to.SAML2LoginResponseTO;
import org.apache.syncope.common.rest.api.service.SAML2SPService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
@WebServlet(name = "assertionConsumer", urlPatterns = { "/saml2sp/assertion-consumer" })
-public class AssertionConsumer extends HttpServlet {
+public class AssertionConsumer extends AbstractSAML2SPServlet {
private static final long serialVersionUID = 968480296813639041L;
- private static final Logger LOG = LoggerFactory.getLogger(AssertionConsumer.class);
-
@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
@@ -47,7 +42,7 @@ public class AssertionConsumer extends HttpServlet {
getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
try {
SAML2LoginResponseTO responseTO = anonymous.getService(SAML2SPService.class).
- validateLoginResponse(request.getInputStream());
+ validateLoginResponse(extract(request.getInputStream()));
request.getSession(true).setAttribute(Constants.SAML2SPJWT, responseTO.getAccessToken());
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
index a675ca7..e206ff7 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
@@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.to.SAML2RequestTO;
import org.apache.syncope.common.rest.api.service.SAML2SPService;
@WebServlet(name = "login", urlPatterns = { "/saml2sp/login" })
-public class Login extends SAML2PostBinding {
+public class Login extends AbstractSAML2SPServlet {
private static final long serialVersionUID = 968480296813639041L;
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
index df4c5e5..3ad191c 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
@@ -28,17 +28,21 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.SSOConstants;
+import org.apache.syncope.common.lib.to.SAML2ReceivedResponseTO;
import org.apache.syncope.common.lib.to.SAML2RequestTO;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.common.rest.api.service.SAML2SPService;
@WebServlet(name = "logout", urlPatterns = { "/saml2sp/logout" })
-public class Logout extends SAML2PostBinding {
+public class Logout extends AbstractSAML2SPServlet {
private static final long serialVersionUID = 3010286040376932117L;
- @Override
- protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
- throws ServletException, IOException {
+ private void doLogout(
+ final SAML2ReceivedResponseTO receivedResponse,
+ final HttpServletRequest request,
+ final HttpServletResponse response) throws ServletException, IOException {
SyncopeClientFactoryBean clientFactory = (SyncopeClientFactoryBean) request.getServletContext().
getAttribute(Constants.SYNCOPE_CLIENT_FACTORY);
@@ -49,12 +53,17 @@ public class Logout extends SAML2PostBinding {
}
SyncopeClient client = clientFactory.create(accessToken);
- SAML2RequestTO requestTO = client.getService(SAML2SPService.class).createLogoutRequest(
- StringUtils.substringBefore(request.getRequestURL().toString(), "/saml2sp"));
+ client.getService(SAML2SPService.class).validateLogoutResponse(receivedResponse);
- prepare(response, requestTO);
+ String successURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGOUT_SUCCESS_URL);
+ if (successURL == null) {
+ request.getRequestDispatcher("logoutSuccess.jsp").forward(request, response);
+ } else {
+ response.sendRedirect(successURL);
+ }
+ request.getSession().removeAttribute(Constants.SAML2SPJWT);
} catch (Exception e) {
- LOG.error("While preparing logout request to IdP", e);
+ LOG.error("While processing authentication response from IdP", e);
String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGOUT_ERROR_URL);
if (errorURL == null) {
@@ -70,41 +79,57 @@ public class Logout extends SAML2PostBinding {
}
@Override
- protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
+ protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
- SyncopeClientFactoryBean clientFactory = (SyncopeClientFactoryBean) request.getServletContext().
- getAttribute(Constants.SYNCOPE_CLIENT_FACTORY);
- try {
- String accessToken = (String) request.getSession().getAttribute(Constants.SAML2SPJWT);
- if (StringUtils.isBlank(accessToken)) {
- throw new IllegalArgumentException("No access token found ");
+ String samlResponse = request.getParameter(SSOConstants.SAML_RESPONSE);
+ String relayState = request.getParameter(SSOConstants.RELAY_STATE);
+ if (samlResponse == null) { // prepare logout response
+ SyncopeClientFactoryBean clientFactory = (SyncopeClientFactoryBean) request.getServletContext().
+ getAttribute(Constants.SYNCOPE_CLIENT_FACTORY);
+ try {
+ String accessToken = (String) request.getSession().getAttribute(Constants.SAML2SPJWT);
+ if (StringUtils.isBlank(accessToken)) {
+ throw new IllegalArgumentException("No access token found ");
+ }
+
+ SyncopeClient client = clientFactory.create(accessToken);
+ SAML2RequestTO requestTO = client.getService(SAML2SPService.class).createLogoutRequest(
+ StringUtils.substringBefore(request.getRequestURL().toString(), "/saml2sp"));
+
+ prepare(response, requestTO);
+ } catch (Exception e) {
+ LOG.error("While preparing logout request to IdP", e);
+
+ String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGOUT_ERROR_URL);
+ if (errorURL == null) {
+ request.setAttribute("exception", e);
+ request.getRequestDispatcher("logoutError.jsp").forward(request, response);
+
+ e.printStackTrace(response.getWriter());
+ } else {
+ response.sendRedirect(errorURL + "?errorMessage="
+ + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
+ }
}
+ } else { // process REDIRECT binding logout response
+ SAML2ReceivedResponseTO receivedResponse = new SAML2ReceivedResponseTO();
+ receivedResponse.setSamlResponse(samlResponse);
+ receivedResponse.setRelayState(relayState);
+ receivedResponse.setBindingType(SAML2BindingType.REDIRECT);
- SyncopeClient client = clientFactory.create(accessToken);
- client.getService(SAML2SPService.class).validateLogoutResponse(request.getInputStream());
-
- String successURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGOUT_SUCCESS_URL);
- if (successURL == null) {
- request.getRequestDispatcher("logoutSuccess.jsp").forward(request, response);
- } else {
- response.sendRedirect(successURL);
- }
- request.getSession().removeAttribute(Constants.SAML2SPJWT);
- } catch (Exception e) {
- LOG.error("While processing authentication response from IdP", e);
+ doLogout(receivedResponse, request, response);
+ }
+ }
- String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGOUT_ERROR_URL);
- if (errorURL == null) {
- request.setAttribute("exception", e);
- request.getRequestDispatcher("logoutError.jsp").forward(request, response);
+ @Override
+ protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
+ throws ServletException, IOException {
- e.printStackTrace(response.getWriter());
- } else {
- response.sendRedirect(errorURL + "?errorMessage="
- + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
- }
- }
+ // process POST binding logout response
+ SAML2ReceivedResponseTO receivedResponse = extract(request.getInputStream());
+ receivedResponse.setBindingType(SAML2BindingType.POST);
+ doLogout(receivedResponse, request, response);
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.java
index aaa3df6..73229f7 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.java
@@ -47,7 +47,7 @@ public class Metadata extends HttpServlet {
SAML2SPService service = anonymous.getService(SAML2SPService.class);
WebClient.client(service).accept(MediaType.APPLICATION_XML_TYPE).type(MediaType.APPLICATION_XML_TYPE);
Response metadataResponse = service.getMetadata(
- StringUtils.substringBefore(request.getRequestURL().toString(), "/saml2sp"));
+ StringUtils.substringBefore(request.getRequestURL().toString(), "/saml2sp"), "saml2sp");
response.setContentType(metadataResponse.getMediaType().toString());
IOUtils.copy((InputStream) metadataResponse.getEntity(), response.getOutputStream());
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2PostBinding.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2PostBinding.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2PostBinding.java
deleted file mode 100644
index 0868841..0000000
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2PostBinding.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.syncope.ext.saml2lsp.agent;
-
-import java.io.IOException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import org.apache.syncope.common.lib.to.SAML2RequestTO;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class SAML2PostBinding extends HttpServlet {
-
- private static final long serialVersionUID = 7969539245875799817L;
-
- protected static final Logger LOG = LoggerFactory.getLogger(SAML2PostBinding.class);
-
- protected void prepare(final HttpServletResponse response, final SAML2RequestTO requestTO) throws IOException {
- response.setContentType(MediaType.TEXT_HTML);
- response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
- response.setHeader("Pragma", "no-cache");
- response.getWriter().write(""
- + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
- + " <body onLoad=\"document.forms[0].submit();\">"
- + " <form action=\"" + requestTO.getIdpServiceAddress() + "\" method=\"POST\">"
- + " <input type=\"hidden\" name=\"SAMLRequest\" value=\"" + requestTO.getContent() + "\"/>"
- + " <input type=\"hidden\" name=\"RelayState\" value=\"" + requestTO.getRelayState() + "\"/>"
- + " <input type=\"submit\" style=\"visibility: hidden;\"/>"
- + " </form>"
- + " </body>"
- + "</html>");
- }
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
index 2b3dd70..be93d28 100644
--- a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
+++ b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
@@ -140,6 +140,8 @@ public class SAML2IdPsDirectoryPanel extends DirectoryPanel<
columns.add(new PropertyColumn<SAML2IdPTO, String>(new ResourceModel("entityID"), "entityID", "entityID"));
columns.add(new BooleanPropertyColumn<SAML2IdPTO>(
new ResourceModel("useDeflateEncoding"), "useDeflateEncoding", "useDeflateEncoding"));
+ columns.add(new PropertyColumn<SAML2IdPTO, String>(
+ new ResourceModel("bindingType"), "bindingType", "bindingType"));
columns.add(new BooleanPropertyColumn<SAML2IdPTO>(
new ResourceModel("logoutSupported"), "logoutSupported", "logoutSupported"));
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPWizardBuilder.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPWizardBuilder.java b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPWizardBuilder.java
index 333c00e..d824807 100644
--- a/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPWizardBuilder.java
+++ b/ext/saml2sp/client-console/src/main/java/org/apache/syncope/client/console/wizards/SAML2IdPWizardBuilder.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.console.wizards;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
@@ -27,12 +28,14 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.panels.SAML2IdPsDirectoryPanel;
import org.apache.syncope.client.console.rest.SAML2IdPsRestClient;
import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
import org.apache.syncope.client.console.wizards.resources.MappingItemTransformersTogglePanel;
import org.apache.syncope.common.lib.to.MappingItemTO;
import org.apache.syncope.common.lib.to.SAML2IdPTO;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.extensions.wizard.WizardModel;
@@ -95,6 +98,12 @@ public class SAML2IdPWizardBuilder extends AjaxWizardBuilder<SAML2IdPTO> {
"field", "useDeflateEncoding", new PropertyModel<Boolean>(idpTO, "useDeflateEncoding"), false);
fields.add(useDeflateEncoding);
+ AjaxDropDownChoicePanel<SAML2BindingType> bindingType =
+ new AjaxDropDownChoicePanel<>("field", "bindingType",
+ new PropertyModel<SAML2BindingType>(idpTO, "bindingType"), false);
+ bindingType.setChoices(Arrays.asList(SAML2BindingType.values()));
+ fields.add(bindingType);
+
add(new ListView<Component>("fields", fields) {
private static final long serialVersionUID = -9180479401817023838L;
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.properties b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.properties
index e1ecb17..8c5125d 100644
--- a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.properties
+++ b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.properties
@@ -19,3 +19,4 @@ useDeflateEncoding=Deflate Encoding
logoutSupported=Logout supported
any.edit=Edit ${entityID}
connObjectKeyValidation=There must be exactly one Remote Key
+bindingType=Binding
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_it.properties b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_it.properties
index 3fa90a9..d0ee370 100644
--- a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_it.properties
+++ b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_it.properties
@@ -19,3 +19,4 @@ useDeflateEncoding=Deflate Encoding
logoutSupported=Logout supportato
any.edit=Modifica ${entityID}
connObjectKeyValidation=Deve essere definito esattamente una Chiave remota
+bindingType=Binding
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_pt_BR.properties b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_pt_BR.properties
index 9bb0b82..87a0c40 100644
--- a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_pt_BR.properties
+++ b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_pt_BR.properties
@@ -19,3 +19,4 @@ useDeflateEncoding=Deflate Encoding
logoutSupported=Logout supported
any.edit=Alterar ${entityID}
connObjectKeyValidation=Precisa ser exatamente um Remote Key
+bindingType=Binding
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_ru.properties b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_ru.properties
index ba85289..479faea 100644
--- a/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_ru.properties
+++ b/ext/saml2sp/client-console/src/main/resources/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel_ru.properties
@@ -19,3 +19,4 @@ useDeflateEncoding=Deflate Encoding
logoutSupported=Logout supported
any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c ${entityID}
connObjectKeyValidation=\u0422\u0430\u043c \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430
+bindingType=Binding
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/SSOConstants.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/SSOConstants.java b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/SSOConstants.java
new file mode 100644
index 0000000..2390dde
--- /dev/null
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/SSOConstants.java
@@ -0,0 +1,36 @@
+/*
+ * 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.syncope.common.lib;
+
+public final class SSOConstants {
+
+ public static final String SAML_REQUEST = "SAMLRequest";
+
+ public static final String SAML_RESPONSE = "SAMLResponse";
+
+ public static final String RELAY_STATE = "RelayState";
+
+ public static final String SIG_ALG = "SigAlg";
+
+ public static final String SIGNATURE = "Signature";
+
+ private SSOConstants() {
+ // private constructor for static utility class
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java
index d22dfcd..109af15 100644
--- a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java
@@ -29,6 +29,7 @@ import javax.xml.bind.annotation.XmlType;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
@XmlRootElement(name = "saml2idp")
@XmlType
@@ -46,6 +47,8 @@ public class SAML2IdPTO extends AbstractBaseBean implements EntityTO {
private boolean useDeflateEncoding;
+ private SAML2BindingType bindingType;
+
private boolean logoutSupported;
private final List<MappingItemTO> mappingItems = new ArrayList<>();
@@ -93,6 +96,14 @@ public class SAML2IdPTO extends AbstractBaseBean implements EntityTO {
this.useDeflateEncoding = useDeflateEncoding;
}
+ public SAML2BindingType getBindingType() {
+ return bindingType;
+ }
+
+ public void setBindingType(final SAML2BindingType bindingType) {
+ this.bindingType = bindingType;
+ }
+
public boolean isLogoutSupported() {
return logoutSupported;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2ReceivedResponseTO.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2ReceivedResponseTO.java b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2ReceivedResponseTO.java
new file mode 100644
index 0000000..b8d82ae
--- /dev/null
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2ReceivedResponseTO.java
@@ -0,0 +1,62 @@
+/*
+ * 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.syncope.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
+
+@XmlRootElement(name = "saml2ReceivedResponse")
+@XmlType
+public class SAML2ReceivedResponseTO extends AbstractBaseBean {
+
+ private static final long serialVersionUID = 6102419133516694822L;
+
+ private String samlResponse;
+
+ private String relayState;
+
+ private SAML2BindingType bindingType;
+
+ public String getSamlResponse() {
+ return samlResponse;
+ }
+
+ public void setSamlResponse(final String samlResponse) {
+ this.samlResponse = samlResponse;
+ }
+
+ public String getRelayState() {
+ return relayState;
+ }
+
+ public void setRelayState(final String relayState) {
+ this.relayState = relayState;
+ }
+
+ public SAML2BindingType getBindingType() {
+ return bindingType;
+ }
+
+ public void setBindingType(final SAML2BindingType bindingType) {
+ this.bindingType = bindingType;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2RequestTO.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2RequestTO.java b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2RequestTO.java
index 136b58e..143a1b3 100644
--- a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2RequestTO.java
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2RequestTO.java
@@ -21,6 +21,7 @@ package org.apache.syncope.common.lib.to;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
@XmlRootElement(name = "saml2request")
@XmlType
@@ -30,10 +31,16 @@ public class SAML2RequestTO extends AbstractBaseBean {
private String idpServiceAddress;
+ private SAML2BindingType bindingType;
+
private String content;
private String relayState;
+ private String signAlg;
+
+ private String signature;
+
public String getIdpServiceAddress() {
return idpServiceAddress;
}
@@ -42,6 +49,14 @@ public class SAML2RequestTO extends AbstractBaseBean {
this.idpServiceAddress = idpServiceAddress;
}
+ public SAML2BindingType getBindingType() {
+ return bindingType;
+ }
+
+ public void setBindingType(final SAML2BindingType bindingType) {
+ this.bindingType = bindingType;
+ }
+
public String getContent() {
return content;
}
@@ -58,4 +73,20 @@ public class SAML2RequestTO extends AbstractBaseBean {
this.relayState = relayState;
}
+ public String getSignAlg() {
+ return signAlg;
+ }
+
+ public void setSignAlg(final String signAlg) {
+ this.signAlg = signAlg;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public void setSignature(final String signature) {
+ this.signature = signature;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/types/SAML2BindingType.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/types/SAML2BindingType.java b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/types/SAML2BindingType.java
new file mode 100644
index 0000000..04c0704
--- /dev/null
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/types/SAML2BindingType.java
@@ -0,0 +1,56 @@
+/*
+ * 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.syncope.common.lib.types;
+
+import javax.xml.bind.annotation.XmlEnum;
+
+@XmlEnum
+public enum SAML2BindingType {
+ POST("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", 0),
+ REDIRECT("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", 1);
+
+ private final String uri;
+
+ private final int index;
+
+ SAML2BindingType(final String uri, final int index) {
+ this.uri = uri;
+ this.index = index;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public static SAML2BindingType fromUri(final String uri) {
+ SAML2BindingType bindingType = null;
+
+ for (SAML2BindingType value : values()) {
+ if (value.getUri().equals(uri)) {
+ bindingType = value;
+ }
+ }
+
+ return bindingType;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPLogic.java
index 678db98..bbd170d 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPLogic.java
@@ -33,6 +33,7 @@ import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.MappingItemTO;
import org.apache.syncope.common.lib.to.SAML2IdPTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.common.lib.types.SAML2SPEntitlement;
import org.apache.syncope.core.logic.saml2.SAML2ReaderWriter;
import org.apache.syncope.core.logic.saml2.SAML2IdPCache;
@@ -81,7 +82,8 @@ public class SAML2IdPLogic extends AbstractSAML2Logic<SAML2IdPTO> {
idpTO.setLogoutSupported(idpEntity == null
? false
- : idpEntity.getSLOLocation(SAMLConstants.SAML2_POST_BINDING_URI) != null);
+ : idpEntity.getSLOLocation(SAML2BindingType.POST) != null
+ || idpEntity.getSLOLocation(SAML2BindingType.REDIRECT) != null);
return idpTO;
}
@@ -146,17 +148,27 @@ public class SAML2IdPLogic extends AbstractSAML2Logic<SAML2IdPTO> {
idpTO.setEntityID(idpEntityDescriptor.getEntityID());
idpTO.setName(idpEntityDescriptor.getEntityID());
idpTO.setUseDeflateEncoding(false);
+
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
saml2rw.write(new OutputStreamWriter(baos), idpEntityDescriptor, false);
idpTO.setMetadata(Base64.getEncoder().encodeToString(baos.toByteArray()));
}
+
MappingItemTO connObjectKeyItem = new MappingItemTO();
connObjectKeyItem.setIntAttrName("username");
connObjectKeyItem.setExtAttrName("NameID");
idpTO.setConnObjectKeyItem(connObjectKeyItem);
- result.add(idpTO);
- cache.put(idpEntityDescriptor, connObjectKeyItem, false);
+ SAML2IdPEntity idp = cache.put(idpEntityDescriptor, connObjectKeyItem, false, SAML2BindingType.POST);
+ if (idp.getSSOLocation(SAML2BindingType.POST) != null) {
+ idpTO.setBindingType(SAML2BindingType.POST);
+ } else if (idp.getSSOLocation(SAML2BindingType.REDIRECT) != null) {
+ idpTO.setBindingType(SAML2BindingType.REDIRECT);
+ } else {
+ throw new IllegalArgumentException("Not POST nor REDIRECT artifacts supported by " + idp.getId());
+ }
+
+ result.add(idpTO);
}
return result;
@@ -177,9 +189,9 @@ public class SAML2IdPLogic extends AbstractSAML2Logic<SAML2IdPTO> {
throw e;
} catch (Exception e) {
LOG.error("Unexpected error while importing IdP metadata", e);
- SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
- ex.getElements().add(e.getMessage());
- throw ex;
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
+ sce.getElements().add(e.getMessage());
+ throw sce;
}
return imported;
@@ -194,13 +206,28 @@ public class SAML2IdPLogic extends AbstractSAML2Logic<SAML2IdPTO> {
throw new NotFoundException("SAML 2.0 IdP '" + saml2IdpTO.getKey() + "'");
}
- saml2Idp = idpDAO.save(binder.update(saml2Idp, saml2IdpTO));
-
SAML2IdPEntity idpEntity = cache.get(saml2Idp.getEntityID());
- if (idpEntity != null) {
- idpEntity.setUseDeflateEncoding(saml2Idp.isUseDeflateEncoding());
- idpEntity.setConnObjectKeyItem(binder.getIdPTO(saml2Idp).getConnObjectKeyItem());
+ if (idpEntity == null) {
+ try {
+ idpEntity = cache.put(saml2Idp);
+ } catch (Exception e) {
+ LOG.error("Unexpected error while updating {}", saml2Idp.getEntityID(), e);
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
}
+ if (idpEntity.getSSOLocation(saml2IdpTO.getBindingType()) == null) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
+ sce.getElements().add(saml2IdpTO.getBindingType() + " not supported by " + saml2Idp.getEntityID());
+ throw sce;
+ }
+
+ saml2Idp = idpDAO.save(binder.update(saml2Idp, saml2IdpTO));
+
+ idpEntity.setUseDeflateEncoding(saml2Idp.isUseDeflateEncoding());
+ idpEntity.setBindingType(saml2Idp.getBindingType());
+ idpEntity.setConnObjectKeyItem(binder.getIdPTO(saml2Idp).getConnObjectKeyItem());
}
@PreAuthorize("hasRole('" + SAML2SPEntitlement.IDP_DELETE + "')")
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 527d58c..61d272a 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -20,12 +20,10 @@ package org.apache.syncope.core.logic;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.RandomBasedGenerator;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
-import java.net.URLDecoder;
+import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
@@ -33,29 +31,25 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
-import org.apache.cxf.helpers.IOUtils;
-import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.saml.sso.SSOConstants;
import org.apache.syncope.common.lib.AbstractBaseBean;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AttrTO;
import org.apache.syncope.common.lib.to.SAML2RequestTO;
import org.apache.syncope.common.lib.to.SAML2LoginResponseTO;
import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.SAML2ReceivedResponseTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.core.logic.saml2.SAML2ReaderWriter;
import org.apache.syncope.core.logic.saml2.SAML2IdPCache;
import org.apache.syncope.core.logic.saml2.SAML2IdPEntity;
-import org.apache.syncope.core.logic.saml2.SAML2Signer;
import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -171,11 +165,8 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
@Autowired
private SAML2ReaderWriter saml2rw;
- @Autowired
- private SAML2Signer saml2Signer;
-
@PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
- public void getMetadata(final String spEntityID, final OutputStream os) {
+ public void getMetadata(final String spEntityID, final String urlContext, final OutputStream os) {
check();
try {
@@ -185,6 +176,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
SPSSODescriptor spSSODescriptor = new SPSSODescriptorBuilder().buildObject();
spSSODescriptor.setWantAssertionsSigned(true);
spSSODescriptor.setAuthnRequestsSigned(true);
+ spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
@@ -195,12 +187,6 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
keyDescriptor.setKeyInfo(keyInfoGenerator.generate(loader.getCredential()));
spSSODescriptor.getKeyDescriptors().add(keyDescriptor);
- SingleLogoutService singleLogoutService = new SingleLogoutServiceBuilder().buildObject();
- singleLogoutService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
- singleLogoutService.setLocation(spEntityID + "saml2sp/logout");
- singleLogoutService.setResponseLocation(spEntityID + "saml2sp/logout");
- spSSODescriptor.getSingleLogoutServices().add(singleLogoutService);
-
NameIDFormat nameIDFormat = new NameIDFormatBuilder().buildObject();
nameIDFormat.setFormat(NameIDType.PERSISTENT);
spSSODescriptor.getNameIDFormats().add(nameIDFormat);
@@ -208,13 +194,20 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
nameIDFormat.setFormat(NameIDType.TRANSIENT);
spSSODescriptor.getNameIDFormats().add(nameIDFormat);
- AssertionConsumerService assertionConsumerService = new AssertionConsumerServiceBuilder().buildObject();
- assertionConsumerService.setIndex(0);
- assertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
- assertionConsumerService.setLocation(spEntityID + "saml2sp/assertion-consumer");
-
- spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);
- spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
+ for (SAML2BindingType bindingType : SAML2BindingType.values()) {
+ AssertionConsumerService assertionConsumerService = new AssertionConsumerServiceBuilder().buildObject();
+ assertionConsumerService.setIndex(bindingType.getIndex());
+ assertionConsumerService.setBinding(bindingType.getUri());
+ assertionConsumerService.setLocation(spEntityID + urlContext + "/assertion-consumer");
+ spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);
+ spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);
+
+ SingleLogoutService singleLogoutService = new SingleLogoutServiceBuilder().buildObject();
+ singleLogoutService.setBinding(bindingType.getUri());
+ singleLogoutService.setLocation(spEntityID + urlContext + "/logout");
+ singleLogoutService.setResponseLocation(spEntityID + urlContext + "/logout");
+ spSSODescriptor.getSingleLogoutServices().add(singleLogoutService);
+ }
spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);
@@ -270,7 +263,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
: "SAML 2.0 IdP '" + idpEntityID + "'");
}
- if (idp.getSSOLocation(SAMLConstants.SAML2_POST_BINDING_URI) == null) {
+ if (idp.getSSOLocation(idp.getBindingType()) == null) {
throw new IllegalArgumentException("No SingleSignOnService available for " + idp.getId());
}
@@ -297,29 +290,44 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
AuthnRequest authnRequest = new AuthnRequestBuilder().buildObject();
authnRequest.setID("_" + UUID_GENERATOR.generate().toString());
- authnRequest.setAssertionConsumerServiceURL(spEntityID + "saml2sp/assertion-consumer");
authnRequest.setForceAuthn(false);
authnRequest.setIsPassive(false);
authnRequest.setVersion(SAMLVersion.VERSION_20);
- authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+ authnRequest.setProtocolBinding(idp.getBindingType().getUri());
authnRequest.setIssueInstant(new DateTime());
authnRequest.setIssuer(issuer);
authnRequest.setNameIDPolicy(nameIDPolicy);
authnRequest.setRequestedAuthnContext(requestedAuthnContext);
- authnRequest.setDestination(idp.getSSOLocation(SAMLConstants.SAML2_POST_BINDING_URI).getLocation());
+ authnRequest.setDestination(idp.getSSOLocation(idp.getBindingType()).getLocation());
SAML2RequestTO requestTO = new SAML2RequestTO();
requestTO.setIdpServiceAddress(authnRequest.getDestination());
+ requestTO.setBindingType(idp.getBindingType());
try {
- // 3. sign and encode AuthnRequest
- requestTO.setContent(saml2Signer.signAndEncode(authnRequest, idp.isUseDeflateEncoding()));
-
- // 4. generate relay state as JWT
+ // 3. generate relay state as JWT
Map<String, Object> claims = new HashMap<>();
claims.put(JWT_CLAIM_IDP_DEFLATE, idp.isUseDeflateEncoding());
Triple<String, String, Date> relayState =
accessTokenDataBinder.generateJWT(authnRequest.getID(), JWT_RELAY_STATE_DURATION, claims);
- requestTO.setRelayState(relayState.getMiddle());
+
+ // 4. sign and encode AuthnRequest
+ switch (idp.getBindingType()) {
+ case REDIRECT:
+ requestTO.setRelayState(URLEncoder.encode(relayState.getMiddle(), StandardCharsets.UTF_8.name()));
+ requestTO.setContent(URLEncoder.encode(
+ saml2rw.encode(authnRequest, true), StandardCharsets.UTF_8.name()));
+ requestTO.setSignAlg(URLEncoder.encode(saml2rw.getSigAlgo(), StandardCharsets.UTF_8.name()));
+ requestTO.setSignature(URLEncoder.encode(
+ saml2rw.sign(requestTO.getContent(), requestTO.getRelayState()),
+ StandardCharsets.UTF_8.name()));
+ break;
+
+ case POST:
+ default:
+ requestTO.setRelayState(relayState.getMiddle());
+ saml2rw.sign(authnRequest);
+ requestTO.setContent(saml2rw.encode(authnRequest, idp.isUseDeflateEncoding()));
+ }
} catch (Exception e) {
LOG.error("While generating AuthnRequest", e);
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
@@ -397,47 +405,23 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
return result;
}
- private Pair<String, String> extract(final InputStream response) throws IOException {
- String strForm = IOUtils.toString(response);
- MultivaluedMap<String, String> params = JAXRSUtils.getStructuredParams(strForm, "&", false, false);
-
- String samlResponse = URLDecoder.decode(
- params.getFirst(SSOConstants.SAML_RESPONSE), StandardCharsets.UTF_8.name());
- LOG.debug("Received SAML Response: {}", samlResponse);
-
- String relayState = params.getFirst(SSOConstants.RELAY_STATE);
- LOG.debug("Received Relay State: {}", relayState);
-
- return Pair.of(samlResponse, relayState);
- }
-
@PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
- public SAML2LoginResponseTO validateLoginResponse(final InputStream response) {
+ public SAML2LoginResponseTO validateLoginResponse(final SAML2ReceivedResponseTO response) {
check();
- // 1. extract raw SAML response and relay state
- Pair<String, String> extracted;
- try {
- extracted = extract(response);
- } catch (Exception e) {
- LOG.error("While reading AuthnResponse", e);
- SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
- sce.getElements().add(e.getMessage());
- throw sce;
- }
-
- // 2. first checks for the provided relay state
- JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(extracted.getRight());
+ // 1. first checks for the provided relay state
+ JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
if (!relayState.verifySignatureWith(jwsSignatureCerifier)) {
throw new IllegalArgumentException("Invalid signature found in Relay State");
}
Boolean useDeflateEncoding = Boolean.valueOf(
relayState.getJwtClaims().getClaim(JWT_CLAIM_IDP_DEFLATE).toString());
- // 3. parse the provided SAML response
+ // 2. parse the provided SAML response
Response samlResponse;
try {
- XMLObject responseObject = saml2rw.read(true, useDeflateEncoding, extracted.getLeft());
+ XMLObject responseObject = saml2rw.read(
+ SAML2BindingType.POST, useDeflateEncoding, response.getSamlResponse());
if (!(responseObject instanceof Response)) {
throw new IllegalArgumentException("Expected " + Response.class.getName()
+ ", got " + responseObject.getClass().getName());
@@ -450,18 +434,18 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
throw sce;
}
- // 4. further checks:
- // 4a. the SAML Reponse's InResponseTo
+ // 3. further checks:
+ // 3a. the SAML Reponse's InResponseTo
if (!relayState.getJwtClaims().getSubject().equals(samlResponse.getInResponseTo())) {
throw new IllegalArgumentException("Unmatching request ID: " + samlResponse.getInResponseTo());
}
- // 4b. the SAML Response status
+ // 3b. the SAML Response status
if (!StatusCode.SUCCESS.equals(samlResponse.getStatus().getStatusCode().getValue())) {
throw new BadCredentialsException("The SAML IdP replied with "
+ samlResponse.getStatus().getStatusCode().getValue());
}
- // 5. validate the SAML response and, if needed, decrypt the provided assertion(s)
+ // 4. validate the SAML response and, if needed, decrypt the provided assertion(s)
SAML2IdPEntity idp = getIdP(samlResponse.getIssuer().getValue());
if (idp.getConnObjectKeyItem() == null) {
throw new IllegalArgumentException("No mapping provided for SAML 2.0 IdP '" + idp.getId() + "'");
@@ -475,10 +459,10 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
throw sce;
}
- // 6. prepare the result: find matching user (if any) and return the received attributes
+ // 5. prepare the result: find matching user (if any) and return the received attributes
SAML2LoginResponseTO responseTO = new SAML2LoginResponseTO();
responseTO.setIdp(idp.getId());
- responseTO.setSloSupported(idp.getSLOLocation(SAMLConstants.SAML2_POST_BINDING_URI) != null);
+ responseTO.setSloSupported(idp.getSLOLocation(idp.getBindingType()) != null);
NameID nameID = null;
String keyValue = null;
@@ -541,7 +525,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
responseTO.setUsername(userDAO.find(matchingUsers.get(0)).getUsername());
responseTO.setNameID(nameID.getValue());
- // 7. generate JWT for further access
+ // 6. generate JWT for further access
Map<String, Object> claims = new HashMap<>();
claims.put(JWT_CLAIM_IDP_ENTITYID, idp.getId());
claims.put(JWT_CLAIM_NAMEID_FORMAT, nameID.getFormat());
@@ -571,14 +555,14 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
if (idp == null) {
throw new NotFoundException("SAML 2.0 IdP '" + idpEntityID + "'");
}
- if (idp.getSLOLocation(SAMLConstants.SAML2_POST_BINDING_URI) == null) {
+ if (idp.getSLOLocation(idp.getBindingType()) == null) {
throw new IllegalArgumentException("No SingleLogoutService available for " + idp.getId());
}
// 3. create LogoutRequest
LogoutRequest logoutRequest = new LogoutRequestBuilder().buildObject();
logoutRequest.setID("_" + UUID_GENERATOR.generate().toString());
- logoutRequest.setDestination(idp.getSLOLocation(SAMLConstants.SAML2_POST_BINDING_URI).getLocation());
+ logoutRequest.setDestination(idp.getSLOLocation(idp.getBindingType()).getLocation());
DateTime now = new DateTime();
logoutRequest.setIssueInstant(now);
@@ -599,16 +583,28 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
SAML2RequestTO requestTO = new SAML2RequestTO();
requestTO.setIdpServiceAddress(logoutRequest.getDestination());
+ requestTO.setBindingType(idp.getBindingType());
try {
- // 3. sign and encode LogoutRequest
- requestTO.setContent(saml2Signer.signAndEncode(logoutRequest, idp.isUseDeflateEncoding()));
-
- // 4. generate relay state as JWT
+ // 3. generate relay state as JWT
Map<String, Object> claims = new HashMap<>();
claims.put(JWT_CLAIM_IDP_DEFLATE, idp.isUseDeflateEncoding());
Triple<String, String, Date> relayState =
accessTokenDataBinder.generateJWT(logoutRequest.getID(), JWT_RELAY_STATE_DURATION, claims);
requestTO.setRelayState(relayState.getMiddle());
+
+ // 4. sign and encode AuthnRequest
+ switch (idp.getBindingType()) {
+ case REDIRECT:
+ requestTO.setContent(saml2rw.encode(logoutRequest, true));
+ requestTO.setSignAlg(saml2rw.getSigAlgo());
+ requestTO.setSignature(saml2rw.sign(requestTO.getContent(), requestTO.getRelayState()));
+ break;
+
+ case POST:
+ default:
+ saml2rw.sign(logoutRequest);
+ requestTO.setContent(saml2rw.encode(logoutRequest, idp.isUseDeflateEncoding()));
+ }
} catch (Exception e) {
LOG.error("While generating LogoutRequest", e);
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
@@ -620,7 +616,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
}
@PreAuthorize("isAuthenticated() and not(hasRole('" + StandardEntitlement.ANONYMOUS + "'))")
- public void validateLogoutResponse(final String accessToken, final InputStream response) {
+ public void validateLogoutResponse(final String accessToken, final SAML2ReceivedResponseTO response) {
check();
// 1. fetch the current JWT used for Syncope authentication
@@ -630,21 +626,11 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
}
// 2. extract raw SAML response and relay state
- Pair<String, String> extracted;
- try {
- extracted = extract(response);
- } catch (Exception e) {
- LOG.error("While reading LogoutResponse", e);
- SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
- sce.getElements().add(e.getMessage());
- throw sce;
- }
-
JwsJwtCompactConsumer relayState = null;
Boolean useDeflateEncoding = false;
- if (StringUtils.isNotBlank(extracted.getRight())) {
+ if (StringUtils.isNotBlank(response.getRelayState())) {
// first checks for the provided relay state, if available
- relayState = new JwsJwtCompactConsumer(extracted.getRight());
+ relayState = new JwsJwtCompactConsumer(response.getRelayState());
if (!relayState.verifySignatureWith(jwsSignatureCerifier)) {
throw new IllegalArgumentException("Invalid signature found in Relay State");
}
@@ -655,7 +641,8 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
// 3. parse the provided SAML response
LogoutResponse logoutResponse;
try {
- XMLObject responseObject = saml2rw.read(true, useDeflateEncoding, extracted.getLeft());
+ XMLObject responseObject = saml2rw.read(
+ response.getBindingType(), useDeflateEncoding, response.getSamlResponse());
if (!(responseObject instanceof LogoutResponse)) {
throw new IllegalArgumentException("Expected " + LogoutResponse.class.getName()
+ ", got " + responseObject.getClass().getName());
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java
index c4f4507..f9a5eec 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java
@@ -31,7 +31,6 @@ import org.apache.syncope.core.persistence.api.SyncopeLoader;
import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
import org.apache.syncope.common.lib.types.SAML2SPEntitlement;
import org.apache.syncope.core.logic.saml2.SAML2ReaderWriter;
-import org.apache.syncope.core.logic.saml2.SAML2Signer;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
import org.apache.wss4j.common.saml.OpenSAMLUtil;
@@ -64,9 +63,6 @@ public class SAML2SPLoader implements SyncopeLoader {
@Autowired
private SAML2ReaderWriter saml2rw;
- @Autowired
- private SAML2Signer signer;
-
private boolean inited;
private KeyStore keystore;
@@ -138,7 +134,6 @@ public class SAML2SPLoader implements SyncopeLoader {
LOG.debug("SAML 2.0 Service Provider certificate loaded");
saml2rw.init();
- signer.init();
inited = true;
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCache.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCache.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCache.java
index 21e185d..a5ab6c3 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCache.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCache.java
@@ -29,6 +29,7 @@ import java.util.HashMap;
import java.util.Map;
import net.shibboleth.utilities.java.support.xml.XMLParserException;
import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.core.logic.init.SAML2SPLoader;
import org.apache.syncope.core.persistence.api.entity.SAML2IdP;
import org.apache.syncope.core.provisioning.api.data.SAML2IdPDataBinder;
@@ -66,11 +67,14 @@ public class SAML2IdPCache {
public SAML2IdPEntity put(
final EntityDescriptor entityDescriptor,
final MappingItemTO connObjectKeyItem,
- final boolean useDeflateEncoding)
+ final boolean useDeflateEncoding,
+ final SAML2BindingType bindingType)
throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException {
- return cache.put(entityDescriptor.getEntityID(),
- new SAML2IdPEntity(entityDescriptor, connObjectKeyItem, useDeflateEncoding, loader.getKeyPass()));
+ SAML2IdPEntity idp = new SAML2IdPEntity(
+ entityDescriptor, connObjectKeyItem, useDeflateEncoding, bindingType, loader.getKeyPass());
+ cache.put(entityDescriptor.getEntityID(), idp);
+ return idp;
}
@Transactional(readOnly = true)
@@ -81,7 +85,11 @@ public class SAML2IdPCache {
Element element = OpenSAMLUtil.getParserPool().parse(
new InputStreamReader(new ByteArrayInputStream(idp.getMetadata()))).getDocumentElement();
EntityDescriptor entityDescriptor = (EntityDescriptor) OpenSAMLUtil.fromDom(element);
- return put(entityDescriptor, binder.getIdPTO(idp).getConnObjectKeyItem(), idp.isUseDeflateEncoding());
+ return put(
+ entityDescriptor,
+ binder.getIdPTO(idp).getConnObjectKeyItem(),
+ idp.isUseDeflateEncoding(),
+ idp.getBindingType());
}
public SAML2IdPEntity remove(final String entityID) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCallbackHandler.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCallbackHandler.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCallbackHandler.java
deleted file mode 100644
index 17cf6f0..0000000
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPCallbackHandler.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.syncope.core.logic.saml2;
-
-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 SAML2IdPCallbackHandler implements CallbackHandler {
-
- private final String keyPass;
-
- public SAML2IdPCallbackHandler(final String keyPass) {
- this.keyPass = keyPass;
- }
-
- @Override
- public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
- for (Callback callback : callbacks) {
- if (callback instanceof WSPasswordCallback) {
- WSPasswordCallback wspc = (WSPasswordCallback) callback;
- wspc.setPassword(keyPass);
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPEntity.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPEntity.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPEntity.java
index a9b1b0d..bd861d1 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPEntity.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2IdPEntity.java
@@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.metadata.Endpoint;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
@@ -52,6 +53,8 @@ public class SAML2IdPEntity {
private boolean useDeflateEncoding;
+ private SAML2BindingType bindingType;
+
private MappingItemTO connObjectKeyItem;
private final Map<String, Endpoint> ssoBindings = new HashMap<>();
@@ -66,12 +69,14 @@ public class SAML2IdPEntity {
final EntityDescriptor entityDescriptor,
final MappingItemTO connObjectKeyItem,
final boolean useDeflateEncoding,
+ final SAML2BindingType bindingType,
final String keyPass)
throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException {
this.id = entityDescriptor.getEntityID();
this.connObjectKeyItem = connObjectKeyItem;
this.useDeflateEncoding = useDeflateEncoding;
+ this.bindingType = bindingType;
IDPSSODescriptor idpdescriptor = entityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
@@ -122,13 +127,21 @@ public class SAML2IdPEntity {
}
public boolean isUseDeflateEncoding() {
- return useDeflateEncoding;
+ return bindingType == SAML2BindingType.REDIRECT ? true : useDeflateEncoding;
}
public void setUseDeflateEncoding(final boolean useDeflateEncoding) {
this.useDeflateEncoding = useDeflateEncoding;
}
+ public SAML2BindingType getBindingType() {
+ return bindingType;
+ }
+
+ public void setBindingType(final SAML2BindingType bindingType) {
+ this.bindingType = bindingType;
+ }
+
public MappingItemTO getConnObjectKeyItem() {
return connObjectKeyItem;
}
@@ -137,12 +150,12 @@ public class SAML2IdPEntity {
this.connObjectKeyItem = connObjectKeyItem;
}
- public Endpoint getSSOLocation(final String binding) {
- return ssoBindings.get(binding);
+ public Endpoint getSSOLocation(final SAML2BindingType bindingType) {
+ return ssoBindings.get(bindingType.getUri());
}
- public Endpoint getSLOLocation(final String binding) {
- return sloBindings.get(binding);
+ public Endpoint getSLOLocation(final SAML2BindingType bindingType) {
+ return sloBindings.get(bindingType.getUri());
}
public boolean supportsNameIDFormat(final String nameIDFormat) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/847e0c9b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
index 2f0e23b..4c1878f 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
@@ -19,15 +19,20 @@
package org.apache.syncope.core.logic.saml2;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
-import java.net.URLDecoder;
+import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.util.Base64;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.SignatureException;
import java.util.zip.DataFormatException;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLStreamException;
@@ -40,12 +45,20 @@ import javax.xml.transform.stream.StreamResult;
import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.syncope.common.lib.SSOConstants;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.core.logic.init.SAML2SPLoader;
import org.apache.wss4j.common.crypto.Merlin;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.OpenSAMLUtil;
import org.opensaml.core.xml.XMLObject;
+import org.opensaml.saml.common.SignableSAMLObject;
+import org.opensaml.saml.saml2.core.RequestAbstractType;
import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.security.SecurityException;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
+import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -71,15 +84,37 @@ public class SAML2ReaderWriter {
@Autowired
private SAML2SPLoader loader;
+ private KeyInfoGenerator keyInfoGenerator;
+
+ private String sigAlgo;
+
+ private String jceSigAlgo;
+
private SAMLProtocolResponseValidator protocolValidator;
- private SAML2IdPCallbackHandler callbackHandler;
+ private SAMLSPCallbackHandler callbackHandler;
public void init() {
+ X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
+ keyInfoGeneratorFactory.setEmitEntityCertificate(true);
+ keyInfoGenerator = keyInfoGeneratorFactory.newInstance();
+
+ sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+ jceSigAlgo = "SHA1withRSA";
+ String pubKeyAlgo = loader.getCredential().getPublicKey().getAlgorithm();
+ if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+ sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA_SHA1;
+ jceSigAlgo = "SHA1withDSA";
+ }
+
protocolValidator = new SAMLProtocolResponseValidator();
protocolValidator.setKeyInfoMustBeAvailable(true);
- callbackHandler = new SAML2IdPCallbackHandler(loader.getKeyPass());
+ callbackHandler = new SAMLSPCallbackHandler(loader.getKeyPass());
+ }
+
+ public String getSigAlgo() {
+ return sigAlgo;
}
public void write(final Writer writer, final XMLObject object, final boolean signObject)
@@ -91,18 +126,12 @@ public class SAML2ReaderWriter {
transformer.transform(source, streamResult);
}
- public XMLObject read(final boolean postBinding, final boolean useDeflateEncoding, final String response)
+ public XMLObject read(final SAML2BindingType bindingType, final boolean useDeflateEncoding, final String response)
throws DataFormatException, UnsupportedEncodingException, XMLStreamException, WSSecurityException {
- String decodedResponse = response;
- // URL Decoding only applies for the redirect binding
- if (!postBinding) {
- decodedResponse = URLDecoder.decode(response, StandardCharsets.UTF_8.name());
- }
-
InputStream tokenStream;
- byte[] deflatedToken = Base64.getDecoder().decode(decodedResponse);
- tokenStream = !postBinding && useDeflateEncoding
+ byte[] deflatedToken = Base64.getDecoder().decode(response);
+ tokenStream = bindingType != SAML2BindingType.POST && useDeflateEncoding
? new DeflateEncoderDecoder().inflateToken(deflatedToken)
: new ByteArrayInputStream(deflatedToken);
@@ -125,6 +154,58 @@ public class SAML2ReaderWriter {
return responseObject;
}
+ public void sign(final RequestAbstractType request) throws SecurityException {
+ org.opensaml.xmlsec.signature.Signature signature = OpenSAMLUtil.buildSignature();
+ signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+ signature.setSignatureAlgorithm(sigAlgo);
+ signature.setSigningCredential(loader.getCredential());
+ signature.setKeyInfo(keyInfoGenerator.generate(loader.getCredential()));
+
+ SignableSAMLObject signableObject = (SignableSAMLObject) request;
+ signableObject.setSignature(signature);
+ signableObject.releaseDOM();
+ signableObject.releaseChildrenDOM(true);
+ }
+
+ public String sign(final String request, final String relayState)
+ throws NoSuchAlgorithmException, WSSecurityException, InvalidKeyException, UnsupportedEncodingException,
+ SignatureException {
+
+ Merlin crypto = new Merlin();
+ crypto.setKeyStore(loader.getKeyStore());
+ PrivateKey privateKey = crypto.getPrivateKey(loader.getCredential().getPublicKey(), callbackHandler);
+
+ java.security.Signature signature = java.security.Signature.getInstance(jceSigAlgo);
+ signature.initSign(privateKey);
+
+ String requestToSign =
+ SSOConstants.SAML_REQUEST + "=" + request + "&"
+ + SSOConstants.RELAY_STATE + "=" + relayState + "&"
+ + SSOConstants.SIG_ALG + "=" + URLEncoder.encode(sigAlgo, StandardCharsets.UTF_8.name());
+ signature.update(requestToSign.getBytes(StandardCharsets.UTF_8));
+ return Base64.getEncoder().encodeToString(signature.sign());
+ }
+
+ public String encode(final RequestAbstractType request, final boolean useDeflateEncoding)
+ throws WSSecurityException, TransformerException, IOException {
+
+ StringWriter writer = new StringWriter();
+ write(writer, request, true);
+ writer.close();
+
+ String requestMessage = writer.toString();
+ byte[] deflatedBytes;
+ // not correct according to the spec but required by some IdPs.
+ if (useDeflateEncoding) {
+ deflatedBytes = new DeflateEncoderDecoder().
+ deflateToken(requestMessage.getBytes(StandardCharsets.UTF_8));
+ } else {
+ deflatedBytes = requestMessage.getBytes(StandardCharsets.UTF_8);
+ }
+
+ return Base64.getEncoder().encodeToString(deflatedBytes);
+ }
+
public void validate(final Response samlResponse, final KeyStore idpTrustStore) throws WSSecurityException {
// validate the SAML response and, if needed, decrypt the provided assertion(s)
Merlin crypto = new Merlin();
@@ -145,5 +226,4 @@ public class SAML2ReaderWriter {
}
}
}
-
}