[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/8be5d4d3
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/8be5d4d3
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/8be5d4d3

Branch: refs/heads/SYNCOPE-808
Commit: 8be5d4d347990681cb89c881f51208e7251b9d77
Parents: b292d3c
Author: Francesco Chicchiriccò <ilgro...@apache.org>
Authored: Wed Apr 12 17:03:46 2017 +0200
Committer: Francesco Chicchiriccò <ilgro...@apache.org>
Committed: Wed Apr 12 17:03:46 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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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 7c8417e..95094e9 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.encodeBase64String(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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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/8be5d4d3/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 35eacaf..07b4f44 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.List;
 import java.util.Map;
 import org.apache.commons.codec.binary.Base64;
 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);
 
@@ -121,13 +126,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;
     }
@@ -136,12 +149,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/8be5d4d3/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 baa3882..11e83cf 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,14 +19,19 @@
 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.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 org.apache.commons.codec.binary.Base64;
 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.decodeBase64(decodedResponse);
-        tokenStream = !postBinding && useDeflateEncoding
+        byte[] deflatedToken = Base64.decodeBase64(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.encodeBase64String(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.encodeBase64String(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 {
             }
         }
     }
-
 }

Reply via email to