[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/d9079e13 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/d9079e13 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/d9079e13 Branch: refs/heads/2_0_X Commit: d9079e138b5ab5ba25b7ccae9d4023f8efaa32ba Parents: df0626e Author: Francesco Chicchiriccò <[email protected]> Authored: Mon Mar 20 08:39:32 2017 +0100 Committer: Francesco Chicchiriccò <[email protected]> Committed: Fri Mar 31 15:25:11 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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/core/logic/pom.xml ---------------------------------------------------------------------- diff --git a/core/logic/pom.xml b/core/logic/pom.xml index 7603b58..4762a71 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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/core/provisioning-java/pom.xml ---------------------------------------------------------------------- diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml index beb1a7b..7e4db39 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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/ext/pom.xml ---------------------------------------------------------------------- diff --git a/ext/pom.xml b/ext/pom.xml index b10ffa6..c1b837e 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/d9079e13/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..a2a0306 --- /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.0.3-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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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/d9079e13/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..4963817 --- /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.0.3-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/d9079e13/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); + } + +}
