syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [03/15] syncope git commit: [SYNCOPE-1041] SAML 2.0 SP extension: core components
Date Fri, 31 Mar 2017 13:43:40 GMT
[SYNCOPE-1041] SAML 2.0 SP extension: core components


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/ff26658e
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/ff26658e
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/ff26658e

Branch: refs/heads/master
Commit: ff26658e78ccde5ace071fc5450a21ddee0e5b6a
Parents: f0ad5d5
Author: Francesco Chicchiriccò <ilgrosso@apache.org>
Authored: Mon Mar 20 08:39:32 2017 +0100
Committer: Francesco Chicchiriccò <ilgrosso@apache.org>
Committed: Fri Mar 31 15:37:26 2017 +0200

----------------------------------------------------------------------
 .../panels/AccessTokenDirectoryPanel.java       |  16 -
 .../panels/AccessTokenDirectoryPanel.properties |   1 -
 .../AccessTokenDirectoryPanel_it.properties     |   1 -
 .../AccessTokenDirectoryPanel_pt_BR.properties  |   1 -
 .../AccessTokenDirectoryPanel_ru.properties     |   1 -
 .../syncope/common/lib/SyncopeConstants.java    |   2 -
 core/logic/pom.xml                              |   5 -
 .../syncope/core/logic/AccessTokenLogic.java    |  81 +--
 .../entity/ExternalResourceValidator.java       |  11 +-
 .../api/data/AccessTokenDataBinder.java         |   8 +-
 core/provisioning-java/pom.xml                  |   5 +
 .../java/data/AccessTokenDataBinderImpl.java    | 113 ++-
 .../provisioning/java/utils/MappingUtils.java   |  34 +-
 .../src/test/resources/provisioningTest.xml     |  19 +
 .../cxf/service/AccessTokenServiceImpl.java     |   2 +-
 .../security/JWTAuthenticationProvider.java     |   8 -
 .../security/SyncopeAuthenticationDetails.java  |   8 -
 ext/pom.xml                                     |   1 +
 ext/saml2sp/agent/pom.xml                       | 126 ++++
 .../ext/saml2lsp/agent/AssertionConsumer.java   |  73 ++
 .../syncope/ext/saml2lsp/agent/Constants.java   |  42 ++
 .../syncope/ext/saml2lsp/agent/Login.java       |  63 ++
 .../syncope/ext/saml2lsp/agent/Logout.java      | 106 +++
 .../syncope/ext/saml2lsp/agent/Metadata.java    |  56 ++
 .../ext/saml2lsp/agent/SAML2PostBinding.java    |  51 ++
 .../ext/saml2lsp/agent/SAML2SPAgentSetup.java   |  92 +++
 .../META-INF/resources/saml2sp/loginError.jsp   |  35 +
 .../META-INF/resources/saml2sp/loginSuccess.jsp |  48 ++
 .../META-INF/resources/saml2sp/logoutError.jsp  |  35 +
 .../resources/saml2sp/logoutSuccess.jsp         |  27 +
 .../main/resources/META-INF/web-fragment.xml    |  26 +
 .../src/main/resources/saml2sp-agent.properties |  26 +
 ext/saml2sp/common-lib/pom.xml                  |  56 ++
 .../syncope/common/lib/to/SAML2IdPTO.java       | 122 ++++
 .../common/lib/to/SAML2LoginResponseTO.java     | 120 ++++
 .../syncope/common/lib/to/SAML2RequestTO.java   |  61 ++
 .../common/lib/types/SAML2SPEntitlement.java    |  58 ++
 ext/saml2sp/logic/pom.xml                       |  77 +++
 .../syncope/core/logic/SAML2IdPLogic.java       | 226 ++++++
 .../apache/syncope/core/logic/SAML2SPLogic.java | 689 +++++++++++++++++++
 .../syncope/core/logic/init/SAML2SPLoader.java  | 140 ++++
 .../syncope/core/logic/saml2/SAML2IdPCache.java |  90 +++
 .../logic/saml2/SAML2IdPCallbackHandler.java    |  45 ++
 .../core/logic/saml2/SAML2IdPEntity.java        | 155 +++++
 .../core/logic/saml2/SAML2ReaderWriter.java     | 145 ++++
 .../syncope/core/logic/saml2/SAML2Signer.java   | 104 +++
 .../src/main/resources/saml2sp-logic.properties |  23 +
 ext/saml2sp/persistence-api/pom.xml             |  56 ++
 .../core/persistence/api/dao/SAML2IdPDAO.java   |  36 +
 .../api/entity/SAML2EntityFactory.java          |  25 +
 .../core/persistence/api/entity/SAML2IdP.java   |  46 ++
 ext/saml2sp/persistence-jpa/pom.xml             | 134 ++++
 .../persistence/jpa/dao/JPASAML2IdPDAO.java     |  77 +++
 .../jpa/entity/JPASAML2EntityFactory.java       |  43 ++
 .../persistence/jpa/entity/JPASAML2IdP.java     | 125 ++++
 .../jpa/validation/entity/SAML2IdPCheck.java    |  41 ++
 .../validation/entity/SAML2IdPValidator.java    |  89 +++
 ext/saml2sp/pom.xml                             |  52 ++
 ext/saml2sp/provisioning-api/pom.xml            |  67 ++
 .../api/data/SAML2IdPDataBinder.java            |  32 +
 ext/saml2sp/provisioning-java/pom.xml           |  61 ++
 .../java/data/SAML2IdPDataBinderImpl.java       | 221 ++++++
 ext/saml2sp/rest-api/pom.xml                    |  77 +++
 .../rest/api/service/SAML2IdPService.java       |  90 +++
 .../common/rest/api/service/SAML2SPService.java |  93 +++
 ext/saml2sp/rest-cxf/pom.xml                    |  72 ++
 .../rest/cxf/service/SAML2IdPServiceImpl.java   |  67 ++
 .../rest/cxf/service/SAML2SPServiceImpl.java    |  82 +++
 fit/console-reference/pom.xml                   |   6 +
 .../src/main/resources/saml2sp-agent.properties |  26 +
 .../src/test/resources/rebel.xml                |   2 +
 fit/core-reference/pom.xml                      |  21 +
 .../main/resources/all/saml2sp-logic.properties |  23 +
 .../src/main/resources/log4j2.xml               |  16 +
 fit/core-reference/src/test/resources/keystore  | Bin 0 -> 4329 bytes
 fit/core-reference/src/test/resources/rebel.xml |  17 +
 pom.xml                                         |  70 ++
 standalone/pom.xml                              |   2 +
 78 files changed, 4857 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/client/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
index 5b08c29..d5489ac 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
@@ -41,7 +41,6 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.PageReference;
@@ -108,21 +107,6 @@ public class AccessTokenDirectoryPanel
 
         columns.add(new DatePropertyColumn<AccessTokenTO>(new ResourceModel("expiryTime"), "expiryTime", "expiryTime"));
 
-        columns.add(new AbstractColumn<AccessTokenTO, String>(new ResourceModel("remoteHost", "")) {
-
-            private static final long serialVersionUID = -1822504503325964706L;
-
-            @Override
-            public void populateItem(
-                    final Item<ICellPopulator<AccessTokenTO>> cellItem,
-                    final String componentId,
-                    final IModel<AccessTokenTO> model) {
-
-                JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(model.getObject().getBody());
-                cellItem.add(new Label(componentId,
-                        consumer.getJwtClaims().getClaim(SyncopeConstants.JWT_CLAIM_REMOTE_HOST).toString()));
-            }
-        });
 
         columns.add(new ActionColumn<AccessTokenTO, String>(new ResourceModel("actions", "")) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
index 89b23f2..b55a13f 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
@@ -19,4 +19,3 @@ any.new=New access token
 owner=Owner
 expiryTime=Expiry
 issuedAt=Start
-remoteHost=Remote Host

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
index cfda8cc..c5b59ae 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
@@ -19,4 +19,3 @@ any.new=Nuovo token di accesso
 owner=Proprietario
 expiryTime=Termine
 issuedAt=Inizio
-remoteHost=Host remoto

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
index bd27aea..125ba73 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
@@ -19,4 +19,3 @@ any.new=Novo token de acesso
 owner=Propriet\u00e1rio
 expiryTime=Termo
 issuedAt=Iniciar
-remoteHost=Host remoto

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
index e5bbbd0..33830e1 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
@@ -20,4 +20,3 @@ any.new=\u041d\u043e\u0432\u044b\u0439 \u043c\u0430\u0440\u043a\u0435\u0440 \u04
 owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
 expiryTime=\u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u0441\u0440\u043e\u043a\u0430
 issuedAt=\u041d\u0430\u0447\u0430\u043b\u043e
-remoteHost=\u0423\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0443\u0437\u0435\u043b

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
index 3c4e39b..9b804bc 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
@@ -28,8 +28,6 @@ public final class SyncopeConstants {
 
     public static final String NS = "http://syncope.apache.org/2.0";
 
-    public static final String JWT_CLAIM_REMOTE_HOST = "remoteHost";
-
     public static final String MASTER_DOMAIN = "Master";
 
     public static final String ROOT_REALM = "/";

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/logic/pom.xml
----------------------------------------------------------------------
diff --git a/core/logic/pom.xml b/core/logic/pom.xml
index 8442b9c..35518bd 100644
--- a/core/logic/pom.xml
+++ b/core/logic/pom.xml
@@ -44,11 +44,6 @@ under the License.
     </dependency>
 
     <dependency>
-      <groupId>com.fasterxml.uuid</groupId>
-      <artifactId>java-uuid-generator</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-dbcp2</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
index 6ca1b87..ece23d0 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
@@ -18,29 +18,16 @@
  */
 package org.apache.syncope.core.logic;
 
-import com.fasterxml.uuid.Generators;
-import com.fasterxml.uuid.impl.RandomBasedGenerator;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
+import java.util.Collections;
 import java.util.List;
 import javax.annotation.Resource;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
-import org.apache.cxf.rs.security.jose.common.JoseType;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
-import org.apache.cxf.rs.security.jose.jwt.JwtToken;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
-import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
@@ -53,66 +40,22 @@ import org.springframework.stereotype.Component;
 @Component
 public class AccessTokenLogic extends AbstractTransactionalLogic<AccessTokenTO> {
 
-    private static final RandomBasedGenerator UUID_GENERATOR = Generators.randomBasedGenerator();
-
-    private static final JwsHeaders JWS_HEADERS = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
-
-    @Resource(name = "jwtIssuer")
-    private String jwtIssuer;
-
     @Resource(name = "anonymousUser")
     private String anonymousUser;
 
     @Autowired
-    private JwsSignatureProvider jwsSignatureProvider;
-
-    @Autowired
     private AccessTokenDataBinder binder;
 
     @Autowired
     private AccessTokenDAO accessTokenDAO;
 
-    @Autowired
-    private ConfDAO confDAO;
-
     @PreAuthorize("isAuthenticated()")
-    public String login(final String remoteHost) {
+    public String login() {
         if (anonymousUser.equals(AuthContextUtils.getUsername())) {
             throw new IllegalArgumentException(anonymousUser + " cannot be granted for an access token");
         }
 
-        String body = null;
-
-        AccessToken accessToken = accessTokenDAO.findByOwner(AuthContextUtils.getUsername());
-        if (accessToken != null) {
-            body = accessToken.getBody();
-        }
-
-        if (body == null) {
-            Date now = new Date();
-            Calendar expiry = Calendar.getInstance();
-            expiry.setTime(now);
-            expiry.add(Calendar.MINUTE,
-                    confDAO.find("jwt.lifetime.minutes", "120").getValues().get(0).getLongValue().intValue());
-
-            JwtClaims claims = new JwtClaims();
-            claims.setTokenId(UUID_GENERATOR.generate().toString());
-            claims.setSubject(AuthContextUtils.getUsername());
-            claims.setIssuedAt(now.getTime());
-            claims.setIssuer(jwtIssuer);
-            claims.setExpiryTime(expiry.getTime().getTime());
-            claims.setNotBefore(now.getTime());
-            claims.setClaim(SyncopeConstants.JWT_CLAIM_REMOTE_HOST, remoteHost);
-
-            JwtToken token = new JwtToken(JWS_HEADERS, claims);
-            JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
-
-            body = producer.signWith(jwsSignatureProvider);
-
-            binder.create(claims.getTokenId(), body, expiry.getTime());
-        }
-
-        return body;
+        return binder.create(AuthContextUtils.getUsername(), Collections.<String, Object>emptyMap(), false);
     }
 
     @PreAuthorize("isAuthenticated()")
@@ -122,23 +65,7 @@ public class AccessTokenLogic extends AbstractTransactionalLogic<AccessTokenTO>
             throw new NotFoundException("AccessToken for " + AuthContextUtils.getUsername());
         }
 
-        JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(accessToken.getBody());
-
-        Date now = new Date();
-        Calendar expiry = Calendar.getInstance();
-        expiry.setTime(now);
-        expiry.add(Calendar.MINUTE,
-                confDAO.find("jwt.lifetime.minutes", "120").getValues().get(0).getLongValue().intValue());
-        consumer.getJwtClaims().setExpiryTime(expiry.getTime().getTime());
-
-        JwtToken token = new JwtToken(JWS_HEADERS, consumer.getJwtClaims());
-        JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
-
-        String body = producer.signWith(jwsSignatureProvider);
-
-        binder.update(accessToken, body, expiry.getTime());
-
-        return body;
+        return binder.update(accessToken);
     }
 
     @PreAuthorize("isAuthenticated()")

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
index b8f2cab..80f4d6e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java
@@ -56,12 +56,13 @@ public class ExternalResourceValidator extends AbstractValidator<ExternalResourc
 
         boolean isValid = true;
 
-        int passwords = 0;
-        for (MappingItem item : mapping.getItems()) {
-            if (item.isPassword()) {
-                passwords++;
+        long passwords = IterableUtils.countMatches(mapping.getItems(), new Predicate<MappingItem>() {
+
+            @Override
+            public boolean evaluate(final MappingItem item) {
+                return item.isPassword();
             }
-        }
+        });
         if (passwords > 1) {
             context.buildConstraintViolationWithTemplate(
                     getTemplate(EntityViolationType.InvalidMapping, "One password mapping is allowed at most")).

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AccessTokenDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AccessTokenDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AccessTokenDataBinder.java
index b06b4c2..b24137f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AccessTokenDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AccessTokenDataBinder.java
@@ -19,14 +19,18 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import java.util.Date;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
 
 public interface AccessTokenDataBinder {
 
-    void create(String key, String body, Date expiryTime);
+    Triple<String, String, Date> generateJWT(String subject, int duration, Map<String, Object> claims);
 
-    void update(AccessToken accessToken, String body, Date expiryTime);
+    String create(String subject, Map<String, Object> claims, boolean replaceExisting);
+
+    String update(AccessToken accessToken);
 
     AccessTokenTO getAccessTokenTO(AccessToken accessToken);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 852cd06..53cd3c5 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -50,6 +50,11 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>com.fasterxml.uuid</groupId>
+      <artifactId>java-uuid-generator</artifactId>
+    </dependency>
+    
+    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context-support</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
index d15664c..b278e30 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
@@ -18,14 +18,28 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
+import com.fasterxml.uuid.Generators;
+import com.fasterxml.uuid.impl.RandomBasedGenerator;
+import java.util.Calendar;
 import java.util.Date;
+import java.util.Map;
+import javax.annotation.Resource;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.cxf.rs.security.jose.common.JoseType;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
+import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.entity.AccessToken;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
 import org.apache.syncope.core.spring.BeanUtils;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -34,29 +48,108 @@ public class AccessTokenDataBinderImpl implements AccessTokenDataBinder {
 
     private static final String[] IGNORE_PROPERTIES = { "owner" };
 
+    private static final RandomBasedGenerator UUID_GENERATOR = Generators.randomBasedGenerator();
+
+    private static final JwsHeaders JWS_HEADERS = new JwsHeaders(JoseType.JWT, SignatureAlgorithm.HS512);
+
+    @Resource(name = "jwtIssuer")
+    private String jwtIssuer;
+
+    @Autowired
+    private JwsSignatureProvider jwsSignatureProvider;
+
     @Autowired
     private AccessTokenDAO accessTokenDAO;
 
     @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Override
-    public void create(final String key, final String body, final Date expiryTime) {
-        AccessToken accessToken = entityFactory.newEntity(AccessToken.class);
-        accessToken.setKey(key);
-        accessToken.setBody(body);
-        accessToken.setExpiryTime(expiryTime);
-        accessToken.setOwner(AuthContextUtils.getUsername());
+    public Triple<String, String, Date> generateJWT(
+            final String subject, final int duration, final Map<String, Object> claims) {
 
-        accessTokenDAO.save(accessToken);
+        Date now = new Date();
+
+        Calendar expiry = Calendar.getInstance();
+        expiry.setTime(now);
+        expiry.add(Calendar.MINUTE, duration);
+
+        JwtClaims jwtClaims = new JwtClaims();
+        jwtClaims.setTokenId(UUID_GENERATOR.generate().toString());
+        jwtClaims.setSubject(subject);
+        jwtClaims.setIssuedAt(now.getTime());
+        jwtClaims.setIssuer(jwtIssuer);
+        jwtClaims.setExpiryTime(expiry.getTime().getTime());
+        jwtClaims.setNotBefore(now.getTime());
+        for (Map.Entry<String, Object> entry : claims.entrySet()) {
+            jwtClaims.setClaim(entry.getKey(), entry.getValue());
+        }
+
+        JwtToken token = new JwtToken(JWS_HEADERS, jwtClaims);
+        JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
+
+        String signed = producer.signWith(jwsSignatureProvider);
+
+        return Triple.of(jwtClaims.getTokenId(), signed, expiry.getTime());
     }
 
     @Override
-    public void update(final AccessToken accessToken, final String body, final Date expiryTime) {
+    public String create(final String subject, final Map<String, Object> claims, final boolean replaceExisting) {
+        String body = null;
+
+        AccessToken existing = accessTokenDAO.findByOwner(subject);
+        if (existing != null) {
+            body = existing.getBody();
+        }
+
+        if (replaceExisting || body == null) {
+            Triple<String, String, Date> created = generateJWT(
+                    subject,
+                    confDAO.find("jwt.lifetime.minutes", "120").getValues().get(0).getLongValue().intValue(),
+                    claims);
+
+            body = created.getMiddle();
+
+            AccessToken accessToken = entityFactory.newEntity(AccessToken.class);
+            accessToken.setKey(created.getLeft());
+            accessToken.setBody(body);
+            accessToken.setExpiryTime(created.getRight());
+            accessToken.setOwner(subject);
+            accessTokenDAO.save(accessToken);
+        }
+
+        if (replaceExisting && existing != null) {
+            accessTokenDAO.delete(existing);
+        }
+
+        return body;
+    }
+
+    @Override
+    public String update(final AccessToken accessToken) {
+        JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(accessToken.getBody());
+
+        Date now = new Date();
+        Calendar expiry = Calendar.getInstance();
+        expiry.setTime(now);
+        expiry.add(Calendar.MINUTE,
+                confDAO.find("jwt.lifetime.minutes", "120").getValues().get(0).getLongValue().intValue());
+        consumer.getJwtClaims().setExpiryTime(expiry.getTime().getTime());
+
+        JwtToken token = new JwtToken(JWS_HEADERS, consumer.getJwtClaims());
+        JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
+
+        String body = producer.signWith(jwsSignatureProvider);
+
         accessToken.setBody(body);
-        accessToken.setExpiryTime(expiryTime);
+        accessToken.setExpiryTime(expiry.getTime());
 
         accessTokenDAO.save(accessToken);
+
+        return body;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
index f8e93b4..e719a38 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
@@ -28,6 +28,7 @@ import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.MappingItemTO;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
@@ -168,24 +169,27 @@ public final class MappingUtils {
         return name;
     }
 
-    public static List<MappingItemTransformer> getMappingItemTransformers(final MappingItem mappingItem) {
+    private static List<MappingItemTransformer> getMappingItemTransformers(
+            final String propagationJEXLTransformer,
+            final String pullJEXLTransformer,
+            final List<String> mappingItemTransformerClassNames) {
+
         List<MappingItemTransformer> result = new ArrayList<>();
 
         // First consider the JEXL transformation expressions
-        if (StringUtils.isNotBlank(mappingItem.getPropagationJEXLTransformer())
-                || StringUtils.isNotBlank(mappingItem.getPullJEXLTransformer())) {
-
+        if (StringUtils.isNotBlank(propagationJEXLTransformer) || StringUtils.isNotBlank(pullJEXLTransformer)) {
             JEXLMappingItemTransformer jexlTransformer =
                     (JEXLMappingItemTransformer) ApplicationContextProvider.getBeanFactory().
-                    createBean(JEXLMappingItemTransformerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+                            createBean(JEXLMappingItemTransformerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME,
+                                    false);
 
-            jexlTransformer.setPropagationJEXL(mappingItem.getPropagationJEXLTransformer());
-            jexlTransformer.setPullJEXL(mappingItem.getPullJEXLTransformer());
+            jexlTransformer.setPropagationJEXL(propagationJEXLTransformer);
+            jexlTransformer.setPullJEXL(pullJEXLTransformer);
             result.add(jexlTransformer);
         }
 
         // Then other custom tranaformers
-        for (String className : mappingItem.getMappingItemTransformerClassNames()) {
+        for (String className : mappingItemTransformerClassNames) {
             try {
                 Class<?> transformerClass = ClassUtils.getClass(className);
 
@@ -199,6 +203,20 @@ public final class MappingUtils {
         return result;
     }
 
+    public static List<MappingItemTransformer> getMappingItemTransformers(final MappingItemTO mappingItem) {
+        return getMappingItemTransformers(
+                mappingItem.getPropagationJEXLTransformer(),
+                mappingItem.getPullJEXLTransformer(),
+                mappingItem.getMappingItemTransformerClassNames());
+    }
+
+    public static List<MappingItemTransformer> getMappingItemTransformers(final MappingItem mappingItem) {
+        return getMappingItemTransformers(
+                mappingItem.getPropagationJEXLTransformer(),
+                mappingItem.getPullJEXLTransformer(),
+                mappingItem.getMappingItemTransformerClassNames());
+    }
+
     /**
      * Build options for requesting all mapped connector attributes.
      *

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/provisioning-java/src/test/resources/provisioningTest.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/resources/provisioningTest.xml b/core/provisioning-java/src/test/resources/provisioningTest.xml
index 88b61b4..b16780f 100644
--- a/core/provisioning-java/src/test/resources/provisioningTest.xml
+++ b/core/provisioning-java/src/test/resources/provisioningTest.xml
@@ -37,5 +37,24 @@ under the License.
     <property name="ignoreResourceNotFound" value="true"/>
     <property name="ignoreUnresolvablePlaceholders" value="true"/>
   </bean>
+
+  <bean id="jwtIssuer" class="java.lang.String">
+    <constructor-arg value="${jwtIssuer}"/>
+  </bean>
+  <bean id="jwsKey" class="java.lang.String">
+    <constructor-arg value="${jwsKey}"/>
+  </bean>
+  <bean id="jwsSignatureVerifier" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier">
+    <constructor-arg value="${jwsKey}.bytes" index="0"/>
+    <constructor-arg index="1">
+      <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">HS512</value>
+    </constructor-arg>
+  </bean>
+  <bean id="jwsSignatureProvider" class="org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider">
+    <constructor-arg value="${jwsKey}.bytes" index="0"/>
+    <constructor-arg index="1">
+      <value type="org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm">HS512</value>
+    </constructor-arg>
+  </bean>
   
 </beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceImpl.java
index 2b19add..f5859db 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceImpl.java
@@ -37,7 +37,7 @@ public class AccessTokenServiceImpl extends AbstractServiceImpl implements Acces
     @Override
     public Response login() {
         return Response.noContent().
-                header(RESTHeaders.TOKEN, logic.login(messageContext.getHttpServletRequest().getRemoteHost())).
+                header(RESTHeaders.TOKEN, logic.login()).
                 build();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
index 30e2be7..1ff6f0a 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.spring.security;
 import java.util.Date;
 import javax.annotation.Resource;
 import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.BadCredentialsException;
@@ -74,13 +73,6 @@ public class JWTAuthenticationProvider implements AuthenticationProvider {
             throw new BadCredentialsException("Invalid JWT issuer");
         }
 
-        if (!claims.containsProperty(SyncopeConstants.JWT_CLAIM_REMOTE_HOST)
-                || !claims.getClaim(SyncopeConstants.JWT_CLAIM_REMOTE_HOST).
-                        equals(jwtAuthentication.getDetails().getRemoteHost())) {
-
-            throw new BadCredentialsException("Unexpected property found in JWT");
-        }
-
         jwtAuthentication.setAuthenticated(true);
         return jwtAuthentication;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetails.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetails.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetails.java
index d2080aa..e5a6726 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetails.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetails.java
@@ -32,18 +32,14 @@ public class SyncopeAuthenticationDetails implements Serializable {
 
     private static final long serialVersionUID = -5899959397393502897L;
 
-    private final String remoteHost;
-
     private final String domain;
 
     public SyncopeAuthenticationDetails(final HttpServletRequest request) {
         this.domain = request.getHeader(RESTHeaders.DOMAIN);
-        this.remoteHost = request.getRemoteHost();
     }
 
     public SyncopeAuthenticationDetails(final String domain) {
         this.domain = domain;
-        this.remoteHost = null;
     }
 
     public String getDomain() {
@@ -52,10 +48,6 @@ public class SyncopeAuthenticationDetails implements Serializable {
                 : domain;
     }
 
-    public String getRemoteHost() {
-        return remoteHost;
-    }
-
     @Override
     public boolean equals(final Object obj) {
         return EqualsBuilder.reflectionEquals(this, obj);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/pom.xml
----------------------------------------------------------------------
diff --git a/ext/pom.xml b/ext/pom.xml
index b2c276a..156fc1c 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -79,6 +79,7 @@ under the License.
   <modules>
     <module>camel</module>
     <module>swagger-ui</module>
+    <module>saml2sp</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/pom.xml
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/pom.xml b/ext/saml2sp/agent/pom.xml
new file mode 100644
index 0000000..532f760
--- /dev/null
+++ b/ext/saml2sp/agent/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-saml2sp</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Extensions: SAML 2.0 SP Agent</name>
+  <description>Apache Syncope Extensions: SAML 2.0 SP Agent</description>
+  <groupId>org.apache.syncope.ext.saml2sp</groupId>
+  <artifactId>syncope-ext-saml2sp-agent</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency> 
+      <groupId>javax.servlet</groupId> 
+      <artifactId>javax.servlet-api</artifactId> 
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet.jsp</groupId>
+      <artifactId>javax.servlet.jsp-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>jstl</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.saml2sp</groupId>
+      <artifactId>syncope-ext-saml2sp-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+      
+      <resource>
+        <directory>${basedir}</directory>
+        <targetPath>META-INF</targetPath>
+        <includes>
+          <include>LICENSE</include>
+          <include>NOTICE</include>
+        </includes>
+      </resource>
+    </resources>
+  </build>
+  
+  <profiles>
+    <profile>
+      <id>apache-release</id>
+
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <inherited>false</inherited>
+            <executions>
+              <execution>
+                <id>attach-sources</id>
+                <goals>
+                  <goal>jar-no-fork</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>${basedir}/LICENSE</include>
+                    <include>${basedir}/NOTICE</include>
+                  </includes>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..6461a47
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/AssertionConsumer.java
@@ -0,0 +1,73 @@
+/*
+ * 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.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 {
+
+    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 {
+
+        SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+        try {
+            SAML2LoginResponseTO responseTO = anonymous.getService(SAML2SPService.class).
+                    validateLoginResponse(request.getInputStream());
+
+            request.getSession(true).setAttribute(Constants.SAML2SPJWT, responseTO.getAccessToken());
+
+            String successURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_SUCCESS_URL);
+            if (successURL == null) {
+                request.setAttribute("responseTO", responseTO);
+                request.getRequestDispatcher("loginSuccess.jsp").forward(request, response);
+            } else {
+                response.sendRedirect(successURL);
+            }
+        } catch (Exception e) {
+            LOG.error("While processing authentication response from IdP", e);
+
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?message=" + e.getMessage());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Constants.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Constants.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Constants.java
new file mode 100644
index 0000000..619e4b8
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Constants.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+public final class Constants {
+
+    public static final String SYNCOPE_CLIENT_FACTORY = "SyncopeClientFactory";
+
+    public static final String SYNCOPE_ANONYMOUS_CLIENT = "SyncopeAnonymousClient";
+
+    public static final String PARAM_IDP = "idp";
+
+    public static final String CONTEXT_PARAM_LOGIN_SUCCESS_URL = "saml2sp.login.success.url";
+
+    public static final String CONTEXT_PARAM_LOGIN_ERROR_URL = "saml2sp.login.error.url";
+
+    public static final String CONTEXT_PARAM_LOGOUT_SUCCESS_URL = "saml2sp.logout.success.url";
+
+    public static final String CONTEXT_PARAM_LOGOUT_ERROR_URL = "saml2sp.logout.error.url";
+
+    public static final String SAML2SPJWT = "saml2sp.jwt";
+
+    private Constants() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..198f6b3
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
@@ -0,0 +1,63 @@
+/*
+ * 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.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+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 {
+
+    private static final long serialVersionUID = 968480296813639041L;
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        String idp = request.getParameter(Constants.PARAM_IDP);
+
+        SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+        try {
+            SAML2RequestTO requestTO = anonymous.getService(SAML2SPService.class).createLoginRequest(
+                    StringUtils.substringBefore(request.getRequestURL().toString(), "/saml2sp"), idp);
+
+            prepare(response, requestTO);
+        } catch (Exception e) {
+            LOG.error("While preparing authentication request to IdP", e);
+
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?message=" + e.getMessage());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..b41d004
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Logout.java
@@ -0,0 +1,106 @@
+/*
+ * 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.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServletRequest;
+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.to.SAML2RequestTO;
+import org.apache.syncope.common.rest.api.service.SAML2SPService;
+
+@WebServlet(name = "logout", urlPatterns = { "/saml2sp/logout" })
+public class Logout extends SAML2PostBinding {
+
+    private static final long serialVersionUID = 3010286040376932117L;
+
+    @Override
+    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 ");
+            }
+
+            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 + "?message=" + e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    protected void doPost(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 ");
+            }
+
+            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);
+
+            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 + "?message=" + e.getMessage());
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..aaa3df6
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Metadata.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.ext.saml2lsp.agent;
+
+import java.io.IOException;
+import java.io.InputStream;
+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 javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.rest.api.service.SAML2SPService;
+
+@WebServlet(name = "metadata", urlPatterns = { "/saml2sp/metadata" })
+public class Metadata extends HttpServlet {
+
+    private static final long serialVersionUID = 694030186105137875L;
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+        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"));
+
+        response.setContentType(metadataResponse.getMediaType().toString());
+        IOUtils.copy((InputStream) metadataResponse.getEntity(), response.getOutputStream());
+        ((InputStream) metadataResponse.getEntity()).close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..0868841
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2PostBinding.java
@@ -0,0 +1,51 @@
+/*
+ * 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/ff26658e/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java
new file mode 100644
index 0000000..e42d438
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java
@@ -0,0 +1,92 @@
+/*
+ * 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.File;
+import java.io.InputStream;
+import java.util.Properties;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+
+@WebListener
+public class SAML2SPAgentSetup implements ServletContextListener {
+
+    private static final String SAML2SP_AGENT_PROPERTIES = "saml2sp-agent.properties";
+
+    private static <T> T assertNotNull(final T argument, final String name) {
+        if (argument == null) {
+            throw new IllegalArgumentException("Argument '" + name + "' may not be null.");
+        }
+        return argument;
+    }
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce) {
+        // read saml2spagent.properties
+        Properties props = new Properties();
+        try (InputStream is = getClass().getResourceAsStream("/" + SAML2SP_AGENT_PROPERTIES)) {
+            props.load(is);
+            File confDir = new File(props.getProperty("conf.directory"));
+            if (confDir.exists() && confDir.canRead() && confDir.isDirectory()) {
+                File consoleDirProps = FileUtils.getFile(confDir, SAML2SP_AGENT_PROPERTIES);
+                if (consoleDirProps.exists() && consoleDirProps.canRead() && consoleDirProps.isFile()) {
+                    props.clear();
+                    props.load(FileUtils.openInputStream(consoleDirProps));
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Could not read " + SAML2SP_AGENT_PROPERTIES, e);
+        }
+
+        String anonymousUser = props.getProperty("anonymousUser");
+        assertNotNull(anonymousUser, "<anonymousUser>");
+        String anonymousKey = props.getProperty("anonymousKey");
+        assertNotNull(anonymousKey, "<anonymousKey>");
+
+        String scheme = props.getProperty("scheme");
+        assertNotNull(scheme, "<scheme>");
+        String host = props.getProperty("host");
+        assertNotNull(host, "<host>");
+        String port = props.getProperty("port");
+        assertNotNull(port, "<port>");
+        String rootPath = props.getProperty("rootPath");
+        assertNotNull(rootPath, "<rootPath>");
+        String useGZIPCompression = props.getProperty("useGZIPCompression");
+        assertNotNull(useGZIPCompression, "<useGZIPCompression>");
+
+        SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().
+                setAddress(scheme + "://" + host + ":" + port + "/" + rootPath).
+                setUseCompression(BooleanUtils.toBoolean(useGZIPCompression));
+
+        sce.getServletContext().setAttribute(Constants.SYNCOPE_CLIENT_FACTORY, clientFactory);
+        sce.getServletContext().setAttribute(
+                Constants.SYNCOPE_ANONYMOUS_CLIENT,
+                clientFactory.create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey)));
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginError.jsp
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginError.jsp b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginError.jsp
new file mode 100644
index 0000000..a997479
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginError.jsp
@@ -0,0 +1,35 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%
+    Exception exception = (Exception) request.getAttribute("exception");
+%>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - SAML 2.0 SP - Login Error</title>
+  </head>
+  <body>
+    <h1>An error was found</h1>
+
+    <h2><%=exception.getMessage()%></h2>
+    <pre>
+      <%exception.printStackTrace(new java.io.PrintWriter(out));%>
+    </pre>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginSuccess.jsp
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginSuccess.jsp b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginSuccess.jsp
new file mode 100644
index 0000000..7f70349
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/loginSuccess.jsp
@@ -0,0 +1,48 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@page import="org.apache.syncope.common.lib.to.SAML2LoginResponseTO"%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set var="responseTO" value="${requestScope['responseTO']}"/>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - SAML 2.0 SP - Successful Login</title>
+  </head>
+  <body>
+    <h1>Welcome ${responseTO.username}</h1>
+
+    <p>You have been successfully authenticated by the requested SAML 2.0 IdP with
+      <tt>NameID ${responseTO.nameID}</tt> and <tt>SessionIndex ${responseTO.sessionIndex}</tt>.</p>
+    
+    <p>Your current session is valid:</p>
+    <ul>
+      <li>not before ${responseTO.authInstant}</li>
+      <li>not on or after ${responseTO.notOnOrAfter}</li>
+    </ul>
+
+    <p>The following attributes are available:</p>
+    <ul>
+      <c:forEach items="${responseTO.attrs}" var="attr">
+        <li>
+          <b>${attr.schema}</b><br/>${attr.values}
+        </li>
+      </c:forEach>
+    </ul>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutError.jsp
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutError.jsp b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutError.jsp
new file mode 100644
index 0000000..d40e5d4
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutError.jsp
@@ -0,0 +1,35 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%
+    Exception exception = (Exception) request.getAttribute("exception");
+%>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - SAML 2.0 SP - Logout Error</title>
+  </head>
+  <body>
+    <h1>An error was found</h1>
+
+    <h2><%=exception.getMessage()%></h2>
+    <pre>
+      <%exception.printStackTrace(new java.io.PrintWriter(out));%>
+    </pre>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutSuccess.jsp
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutSuccess.jsp b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutSuccess.jsp
new file mode 100644
index 0000000..2e81d48
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/META-INF/resources/saml2sp/logoutSuccess.jsp
@@ -0,0 +1,27 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - SAML 2.0 SP - Successful Logout</title>
+  </head>
+  <body>
+    <h1>You have been successfully logged out.</h1>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/META-INF/web-fragment.xml
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/META-INF/web-fragment.xml b/ext/saml2sp/agent/src/main/resources/META-INF/web-fragment.xml
new file mode 100644
index 0000000..fab858a
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/META-INF/web-fragment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                                  http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
+              id="${pom.artifactId}" version="3.1">
+
+</web-fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/agent/src/main/resources/saml2sp-agent.properties
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/resources/saml2sp-agent.properties b/ext/saml2sp/agent/src/main/resources/saml2sp-agent.properties
new file mode 100644
index 0000000..d3fee30
--- /dev/null
+++ b/ext/saml2sp/agent/src/main/resources/saml2sp-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=8080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/ext/saml2sp/common-lib/pom.xml
----------------------------------------------------------------------
diff --git a/ext/saml2sp/common-lib/pom.xml b/ext/saml2sp/common-lib/pom.xml
new file mode 100644
index 0000000..80c7157
--- /dev/null
+++ b/ext/saml2sp/common-lib/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-saml2sp</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Extensions: SAML 2.0 SP Common Lib</name>
+  <description>Apache Syncope Extensions: SAML 2.0 SP Common Lib</description>
+  <groupId>org.apache.syncope.ext.saml2sp</groupId>
+  <artifactId>syncope-ext-saml2sp-common-lib</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ff26658e/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
new file mode 100644
index 0000000..9b5eb20
--- /dev/null
+++ b/ext/saml2sp/common-lib/src/main/java/org/apache/syncope/common/lib/to/SAML2IdPTO.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+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;
+
+@XmlRootElement(name = "saml2idp")
+@XmlType
+public class SAML2IdPTO extends AbstractBaseBean implements EntityTO {
+
+    private static final long serialVersionUID = 4426527052873779881L;
+
+    private String key;
+
+    private String entityID;
+
+    private String metadata;
+
+    private boolean useDeflateEncoding;
+
+    private final List<MappingItemTO> mappingItems = new ArrayList<>();
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getEntityID() {
+        return entityID;
+    }
+
+    public void setEntityID(final String entityID) {
+        this.entityID = entityID;
+    }
+
+    public String getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(final String metadata) {
+        this.metadata = metadata;
+    }
+
+    public boolean isUseDeflateEncoding() {
+        return useDeflateEncoding;
+    }
+
+    public void setUseDeflateEncoding(final boolean useDeflateEncoding) {
+        this.useDeflateEncoding = useDeflateEncoding;
+    }
+
+    public MappingItemTO getConnObjectKeyItem() {
+        return IterableUtils.find(getMappingItems(), new Predicate<MappingItemTO>() {
+
+            @Override
+            public boolean evaluate(final MappingItemTO item) {
+                return item.isConnObjectKey();
+            }
+        });
+    }
+
+    protected boolean addConnObjectKeyItem(final MappingItemTO connObjectItem) {
+        connObjectItem.setMandatoryCondition("true");
+        connObjectItem.setConnObjectKey(true);
+
+        return this.add(connObjectItem);
+    }
+
+    public boolean setConnObjectKeyItem(final MappingItemTO connObjectKeyItem) {
+        return connObjectKeyItem == null
+                ? remove(getConnObjectKeyItem())
+                : addConnObjectKeyItem(connObjectKeyItem);
+    }
+
+    @XmlElementWrapper(name = "mappingItems")
+    @XmlElement(name = "mappingItem")
+    @JsonProperty("mappingItems")
+    public List<MappingItemTO> getMappingItems() {
+        return mappingItems;
+    }
+
+    public boolean add(final MappingItemTO item) {
+        return item == null ? false : this.mappingItems.contains(item) || this.mappingItems.add(item);
+    }
+
+    public boolean remove(final MappingItemTO item) {
+        return this.mappingItems.remove(item);
+    }
+
+}


Mime
View raw message