You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by mm...@apache.org on 2020/05/12 09:31:55 UTC
[syncope] branch master updated: SYNCOPE-1558: restful SAML SP
metadata/keystore for WA authN delegation (#181)
This is an automated email from the ASF dual-hosted git repository.
mmoayyed pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 272fcb7 SYNCOPE-1558: restful SAML SP metadata/keystore for WA authN delegation (#181)
272fcb7 is described below
commit 272fcb759421bec3ea467b9fad92ea9df69cbccc
Author: Misagh Moayyed <mm...@gmail.com>
AuthorDate: Tue May 12 14:01:46 2020 +0430
SYNCOPE-1558: restful SAML SP metadata/keystore for WA authN delegation (#181)
---
.../common/lib/auth/SAML2IdPAuthModuleConf.java | 25 ----
.../syncope/common/lib/to/SAML2SPKeystoreTO.java | 125 ++++++++++++++++
.../syncope/common/lib/to/SAML2SPMetadataTO.java | 126 +++++++++++++++++
.../syncope/common/lib/types/AMEntitlement.java | 12 ++
.../api/service/SAML2SPKeystoreConfService.java | 64 +++++++++
.../api/service/SAML2SPMetadataConfService.java | 64 +++++++++
.../service/{ => wa}/SAML2IdPMetadataService.java | 7 +-
.../SAML2SPKeystoreService.java} | 58 ++++----
.../SAML2SPMetadataService.java} | 58 ++++----
.../syncope/core/logic/SAML2SPKeystoreLogic.java | 113 +++++++++++++++
.../syncope/core/logic/SAML2SPMetadataLogic.java | 113 +++++++++++++++
.../service/SAML2SPKeystoreConfServiceImpl.java | 37 +++++
.../service/SAML2SPMetadataConfServiceImpl.java | 37 +++++
.../{ => wa}/SAML2IdPMetadataServiceImpl.java | 7 +-
.../SAML2SPKeystoreServiceImpl.java} | 35 ++---
.../SAML2SPMetadataServiceImpl.java} | 35 ++---
.../api/dao/auth/SAML2SPKeystoreDAO.java | 32 +++++
.../api/dao/auth/SAML2SPMetadataDAO.java | 32 +++++
.../api/entity/auth/SAML2SPKeystore.java | 32 +++++
.../api/entity/auth/SAML2SPMetadata.java | 32 +++++
core/persistence-jpa-json/pom.xml | 52 ++++---
.../src/test/resources/domains/MasterContent.xml | 5 +-
core/persistence-jpa/pom.xml | 10 ++
.../jpa/dao/auth/JPASAML2SPKeystoreDAO.java | 62 ++++++++
.../jpa/dao/auth/JPASAML2SPMetadataDAO.java | 62 ++++++++
.../persistence/jpa/entity/JPAEntityFactory.java | 8 ++
.../jpa/entity/auth/JPASAML2SPKeystore.java | 64 +++++++++
.../jpa/entity/auth/JPASAML2SPMetadata.java | 64 +++++++++
.../core/persistence/jpa/inner/AuthModuleTest.java | 2 -
.../persistence/jpa/inner/SAML2SPKeystoreTest.java | 157 +++++++++++++++++++++
.../persistence/jpa/inner/SAML2SPMetadataTest.java | 83 +++++++++++
.../src/test/resources/domains/MasterContent.xml | 5 +-
.../src/test/resources/idp-metadata.xml | 144 +++++++++++++++++++
.../src/test/resources/sp-metadata.xml | 43 ++++++
.../api/data/SAML2SPKeystoreBinder.java | 32 +++++
.../api/data/SAML2SPMetadataBinder.java | 32 +++++
.../java/data/SAML2SPKeystoreBinderImpl.java | 71 ++++++++++
.../java/data/SAML2SPMetadataBinderImpl.java | 72 ++++++++++
fit/core-reference/pom.xml | 25 ++++
.../org/apache/syncope/fit/AbstractITCase.java | 48 ++++++-
.../apache/syncope/fit/core/AuthModuleITCase.java | 2 -
.../syncope/fit/core/SAML2IdPMetadataITCase.java | 8 +-
.../syncope/fit/core/SAML2SPKeystoreITCase.java | 104 ++++++++++++++
.../syncope/fit/core/SAML2SPMetadataITCase.java | 104 ++++++++++++++
.../org/apache/syncope/fit/core/SearchITCase.java | 1 -
.../src/main/resources/idp-metadata.xml | 144 +++++++++++++++++++
pom.xml | 13 +-
.../bootstrap/SyncopeWAPropertySourceLocator.java | 12 +-
wa/starter/pom.xml | 8 ++
.../syncope/wa/starter/SyncopeWAApplication.java | 20 +--
.../syncope/wa/starter/SyncopeWAConfiguration.java | 61 ++++----
.../wa/starter/SyncopeWARefreshContextJob.java | 13 +-
.../pac4j/saml/SyncopeWASAML2ClientCustomizer.java | 50 +++++++
.../SyncopeWASAML2ClientKeystoreGenerator.java | 109 ++++++++++++++
.../SyncopeWASAML2ClientMetadataGenerator.java | 77 ++++++++++
.../pac4j/saml/SyncopeWASAML2MetadataResolver.java | 63 +++++++++
.../metadata/RestfulSamlIdPMetadataGenerator.java | 2 +-
.../metadata/RestfulSamlIdPMetadataLocator.java | 4 +-
wa/starter/src/main/resources/wa.properties | 2 +-
.../pac4j/saml/BaseSyncopeWASAML2ClientTest.java | 117 +++++++++++++++
.../saml/SyncopeWASAML2ClientCustomizerTest.java | 80 +++++++++++
.../SyncopeWASAML2ClientKeystoreGeneratorTest.java | 93 ++++++++++++
.../SyncopeWASAML2ClientMetadataGeneratorTest.java | 90 ++++++++++++
.../saml/SyncopeWASAML2MetadataResolverTest.java | 67 +++++++++
wa/starter/src/test/resources/idp-metadata.xml | 144 +++++++++++++++++++
wa/starter/src/test/resources/sp-metadata.xml | 43 ++++++
66 files changed, 3305 insertions(+), 211 deletions(-)
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
index 03b6aed..32079b4 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
@@ -55,10 +55,6 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
*/
private String privateKeyPassword;
- /**
- * Location of the keystore to use and generate the SP keystore.
- */
- private String keystorePath;
/**
* The metadata location of the identity provider that is to handle authentications.
@@ -93,11 +89,6 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
private String serviceProviderEntityId;
/**
- * Location of the SP metadata to use and generate.
- */
- private String serviceProviderMetadataPath;
-
- /**
* Whether authentication requests should be tagged as forced auth.
*/
private boolean forceAuth;
@@ -232,14 +223,6 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
this.privateKeyPassword = privateKeyPassword;
}
- public String getKeystorePath() {
- return keystorePath;
- }
-
- public void setKeystorePath(final String keystorePath) {
- this.keystorePath = keystorePath;
- }
-
public String getIdentityProviderMetadataPath() {
return identityProviderMetadataPath;
}
@@ -272,14 +255,6 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
this.serviceProviderEntityId = serviceProviderEntityId;
}
- public String getServiceProviderMetadataPath() {
- return serviceProviderMetadataPath;
- }
-
- public void setServiceProviderMetadataPath(final String serviceProviderMetadataPath) {
- this.serviceProviderMetadataPath = serviceProviderMetadataPath;
- }
-
public boolean isForceAuth() {
return forceAuth;
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPKeystoreTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPKeystoreTO.java
new file mode 100644
index 0000000..c3cd496
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPKeystoreTO.java
@@ -0,0 +1,125 @@
+/*
+ * 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 org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "saml2spMetadataKeystore")
+@XmlType
+public class SAML2SPKeystoreTO extends BaseBean implements EntityTO {
+
+ private static final long serialVersionUID = 3211073386484148953L;
+
+ private String key;
+
+ private String keystore;
+
+ private String owner;
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @PathParam("key")
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public String getKeystore() {
+ return keystore;
+ }
+
+ public void setKeystore(final String keystore) {
+ this.keystore = keystore;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(final String owner) {
+ this.owner = owner;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().
+ append(key).
+ append(keystore).
+ append(owner).
+ build();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SAML2SPKeystoreTO other = (SAML2SPKeystoreTO) obj;
+ return new EqualsBuilder().
+ append(key, other.key).
+ append(keystore, other.keystore).
+ append(owner, other.owner).
+ build();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .appendSuper(super.toString())
+ .append("key", key)
+ .append("owner", owner)
+ .toString();
+ }
+
+ public static class Builder {
+
+ private final SAML2SPKeystoreTO instance = new SAML2SPKeystoreTO();
+
+ public Builder keystore(final String keystore) {
+ instance.setKeystore(keystore);
+ return this;
+ }
+
+ public Builder owner(final String owner) {
+ instance.setOwner(owner);
+ return this;
+ }
+
+ public SAML2SPKeystoreTO build() {
+ return instance;
+ }
+ }
+
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPMetadataTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPMetadataTO.java
new file mode 100644
index 0000000..104b90f
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/SAML2SPMetadataTO.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "saml2spMetadata")
+@XmlType
+public class SAML2SPMetadataTO extends BaseBean implements EntityTO {
+
+ private static final long serialVersionUID = 6215073386484048953L;
+
+ private String key;
+
+ private String metadata;
+
+ private String owner;
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @PathParam("key")
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public String getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(final String metadata) {
+ this.metadata = metadata;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(final String owner) {
+ this.owner = owner;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().
+ append(key).
+ append(metadata).
+ append(owner).
+ build();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SAML2SPMetadataTO other = (SAML2SPMetadataTO) obj;
+ return new EqualsBuilder().
+ append(key, other.key).
+ append(metadata, other.metadata).
+ append(owner, other.owner).
+ build();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .appendSuper(super.toString())
+ .append("key", key)
+ .append("owner", owner)
+ .toString();
+ }
+
+
+ public static class Builder {
+
+ private final SAML2SPMetadataTO instance = new SAML2SPMetadataTO();
+
+ public Builder metadata(final String metadata) {
+ instance.setMetadata(metadata);
+ return this;
+ }
+
+ public Builder owner(final String owner) {
+ instance.setOwner(owner);
+ return this;
+ }
+
+ public SAML2SPMetadataTO build() {
+ return instance;
+ }
+ }
+
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index eefe798..6f28b1f 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -60,6 +60,18 @@ public final class AMEntitlement {
public static final String SAML2_IDP_METADATA_READ = "SAML2_IDP_METADATA_READ";
+ public static final String SAML2_SP_METADATA_CREATE = "SAML2_SP_METADATA_CREATE";
+
+ public static final String SAML2_SP_METADATA_UPDATE = "SAML2_SP_METADATA_UPDATE";
+
+ public static final String SAML2_SP_METADATA_READ = "SAML2_SP_METADATA_READ";
+
+ public static final String SAML2_SP_KEYSTORE_CREATE = "SAML2_SP_KEYSTORE_CREATE";
+
+ public static final String SAML2_SP_KEYSTORE_UPDATE = "SAML2_SP_KEYSTORE_UPDATE";
+
+ public static final String SAML2_SP_KEYSTORE_READ = "SAML2_SP_KEYSTORE_READ";
+
private static final Set<String> VALUES;
static {
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreConfService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreConfService.java
new file mode 100644
index 0000000..ffc169e
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreConfService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rest.api.service;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * REST operations for SAML 2.0 SP Keystore.
+ */
+@Tag(name = "SAML 2.0 SP Keystore")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
+@Path("saml2sp/conf/keystore")
+public interface SAML2SPKeystoreConfService extends JAXRSService {
+
+ /**
+ * Updates SAML 2.0 SP keystore matching the given key.
+ *
+ * @param keystoreTO SAML2SPKeystoreTO to replace existing SAML 2.0 SP metadata
+ */
+ @Parameter(name = "key", description = "SAML2SPKeystoreTO's key", in = ParameterIn.PATH, schema =
+ @Schema(type = "string"))
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was successful"))
+ @PUT
+ @Path("{key}")
+ @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ void update(@NotNull SAML2SPKeystoreTO keystoreTO);
+
+}
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataConfService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataConfService.java
new file mode 100644
index 0000000..227a0d0
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataConfService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rest.api.service;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * REST operations for SAML 2.0 SP metadata.
+ */
+@Tag(name = "SAML 2.0 SP Metadata")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer")})
+@Path("saml2sp/conf/metadata")
+public interface SAML2SPMetadataConfService extends JAXRSService {
+
+ /**
+ * Updates SAML 2.0 SP metadata matching the given key.
+ *
+ * @param metadataTO SAML2SPMetadata to replace existing SAML 2.0 SP metadata
+ */
+ @Parameter(name = "key", description = "SAML2SPMetadata's key", in = ParameterIn.PATH, schema =
+ @Schema(type = "string"))
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was successful"))
+ @PUT
+ @Path("{key}")
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML})
+ void update(@NotNull SAML2SPMetadataTO metadataTO);
+
+}
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2IdPMetadataService.java
similarity index 94%
copy from common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
copy to common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2IdPMetadataService.java
index 7791372..00201b7 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2IdPMetadataService.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.rest.api.service;
+package org.apache.syncope.common.rest.api.service.wa;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -39,6 +39,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
/**
* REST operations for SAML 2.0 IdP metadata.
@@ -47,7 +48,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
@SecurityRequirements({
@SecurityRequirement(name = "BasicAuthentication"),
@SecurityRequirement(name = "Bearer") })
-@Path("saml2idp/metadata")
+@Path("wa/saml2idp/metadata")
public interface SAML2IdPMetadataService extends JAXRSService {
/**
@@ -59,7 +60,7 @@ public interface SAML2IdPMetadataService extends JAXRSService {
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- SAML2IdPMetadataTO get(@QueryParam("appliesTo") @DefaultValue("Syncope") String appliesTo);
+ SAML2IdPMetadataTO getByOwner(@QueryParam("appliesTo") @DefaultValue("Syncope") String appliesTo);
/**
* Returns the SAML 2.0 IdP metadata matching the given key.
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPKeystoreService.java
similarity index 58%
copy from common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
copy to common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPKeystoreService.java
index 7791372..549af13 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPKeystoreService.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.rest.api.service;
+package org.apache.syncope.common.rest.api.service.wa;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -25,9 +25,12 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -37,61 +40,58 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
-import org.apache.syncope.common.rest.api.RESTHeaders;
/**
- * REST operations for SAML 2.0 IdP metadata.
+ * REST operations for SAML 2.0 IdP keystore.
*/
-@Tag(name = "SAML 2.0 IdP Metadata")
+@Tag(name = "SAML 2.0 SP Metadata Keystore")
@SecurityRequirements({
@SecurityRequirement(name = "BasicAuthentication"),
@SecurityRequirement(name = "Bearer") })
-@Path("saml2idp/metadata")
-public interface SAML2IdPMetadataService extends JAXRSService {
+@Path("wa/saml2sp/keystore")
+public interface SAML2SPKeystoreService extends JAXRSService {
/**
- * Returns a document outlining keys and metadata of Syncope as SAML 2.0 IdP.
+ * Returns a document outlining keystore for Syncope as SAML 2.0 SP.
*
- * @param appliesTo indicates the SAML 2.0 IdP metadata document owner and applicability, where a value of 'Syncope'
- * indicates the Syncope server as the global owner of the metadata and keys.
- * @return SAML 2.0 IdP metadata
+ * @param name indicates the SAML 2.0 SP keystore document owner.
+ * @return SAML 2.0 SP keystore
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- SAML2IdPMetadataTO get(@QueryParam("appliesTo") @DefaultValue("Syncope") String appliesTo);
+ SAML2SPKeystoreTO getByOwner(@QueryParam("name") String name);
/**
- * Returns the SAML 2.0 IdP metadata matching the given key.
+ * Returns the SAML 2.0 SP keystore matching the given key.
*
- * @param key key of requested SAML 2.0 IdP metadata
- * @return SAML 2.0 IdP metadata with matching id
+ * @param key key of requested SAML 2.0 SP keystore
+ * @return SAML 2.0 SP keystore with matching id
*/
@GET
@Path("{key}")
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- SAML2IdPMetadataTO read(@NotNull @PathParam("key") String key);
+ SAML2SPKeystoreTO read(@NotNull @PathParam("key") String key);
/**
- * Store the metadata and keys to finalize the metadata generation process.
+ * Store the keystore to finalize the keystore generation process.
*
- * @param saml2IdPMetadataTO SAML2IdPMetadata to be created
- * @return Response object featuring Location header of created SAML 2.0 IdP metadata
+ * @param keystoreTO SAML2SPMetadataKeystoreTO to be created
+ * @return Response object featuring Location header of created SAML 2.0 SP keystore
*/
@ApiResponses({
@ApiResponse(responseCode = "201",
- description = "SAML2IdPMetadata successfully created", headers = {
- @Header(name = RESTHeaders.RESOURCE_KEY, schema =
- @Schema(type = "string"),
- description = "UUID generated for the entity created"),
- @Header(name = HttpHeaders.LOCATION, schema =
- @Schema(type = "string"),
- description = "URL of the entity created") }),
+ description = "SAML2SPKeystoreTO successfully created", headers = {
+ @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+ @Schema(type = "string"),
+ description = "UUID generated for the entity created"),
+ @Header(name = HttpHeaders.LOCATION, schema =
+ @Schema(type = "string"),
+ description = "URL of the entity created") }),
@ApiResponse(responseCode = "409",
- description = "Metadata already existing") })
+ description = "Metadata already existing") })
@POST
@Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- Response set(@NotNull SAML2IdPMetadataTO saml2IdPMetadataTO);
+ Response set(@NotNull SAML2SPKeystoreTO keystoreTO);
}
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPMetadataService.java
similarity index 58%
rename from common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
rename to common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPMetadataService.java
index 7791372..24bb918 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/SAML2SPMetadataService.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.rest.api.service;
+package org.apache.syncope.common.rest.api.service.wa;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -25,9 +25,12 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -37,61 +40,58 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
-import org.apache.syncope.common.rest.api.RESTHeaders;
/**
- * REST operations for SAML 2.0 IdP metadata.
+ * REST operations for SAML 2.0 SP metadata.
*/
-@Tag(name = "SAML 2.0 IdP Metadata")
+@Tag(name = "SAML 2.0 SP Metadata")
@SecurityRequirements({
@SecurityRequirement(name = "BasicAuthentication"),
@SecurityRequirement(name = "Bearer") })
-@Path("saml2idp/metadata")
-public interface SAML2IdPMetadataService extends JAXRSService {
+@Path("wa/saml2sp/metadata")
+public interface SAML2SPMetadataService extends JAXRSService {
/**
- * Returns a document outlining keys and metadata of Syncope as SAML 2.0 IdP.
+ * Returns a document outlining metadata for Syncope as SAML 2.0 SP.
*
- * @param appliesTo indicates the SAML 2.0 IdP metadata document owner and applicability, where a value of 'Syncope'
- * indicates the Syncope server as the global owner of the metadata and keys.
- * @return SAML 2.0 IdP metadata
+ * @param name indicates the SAML 2.0 SP metadata document owner.
+ * @return SAML 2.0 SP metadata
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- SAML2IdPMetadataTO get(@QueryParam("appliesTo") @DefaultValue("Syncope") String appliesTo);
+ SAML2SPMetadataTO getByOwner(@QueryParam("name") String name);
/**
- * Returns the SAML 2.0 IdP metadata matching the given key.
+ * Returns the SAML 2.0 SP metadata matching the given key.
*
- * @param key key of requested SAML 2.0 IdP metadata
- * @return SAML 2.0 IdP metadata with matching id
+ * @param key key of requested SAML 2.0 SP metadata
+ * @return SAML 2.0 SP metadata with matching id
*/
@GET
@Path("{key}")
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- SAML2IdPMetadataTO read(@NotNull @PathParam("key") String key);
+ SAML2SPMetadataTO read(@NotNull @PathParam("key") String key);
/**
- * Store the metadata and keys to finalize the metadata generation process.
+ * Store the metadata to finalize the metadata generation process.
*
- * @param saml2IdPMetadataTO SAML2IdPMetadata to be created
- * @return Response object featuring Location header of created SAML 2.0 IdP metadata
+ * @param metadataTO SAML2SPMetadataTO to be created
+ * @return Response object featuring Location header of created SAML 2.0 SP metadata
*/
@ApiResponses({
@ApiResponse(responseCode = "201",
- description = "SAML2IdPMetadata successfully created", headers = {
- @Header(name = RESTHeaders.RESOURCE_KEY, schema =
- @Schema(type = "string"),
- description = "UUID generated for the entity created"),
- @Header(name = HttpHeaders.LOCATION, schema =
- @Schema(type = "string"),
- description = "URL of the entity created") }),
+ description = "SAML2SPMetadata successfully created", headers = {
+ @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+ @Schema(type = "string"),
+ description = "UUID generated for the entity created"),
+ @Header(name = HttpHeaders.LOCATION, schema =
+ @Schema(type = "string"),
+ description = "URL of the entity created") }),
@ApiResponse(responseCode = "409",
- description = "Metadata already existing") })
+ description = "Metadata already existing") })
@POST
@Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- Response set(@NotNull SAML2IdPMetadataTO saml2IdPMetadataTO);
+ Response set(@NotNull SAML2SPMetadataTO metadataTO);
}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPKeystoreLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPKeystoreLogic.java
new file mode 100644
index 0000000..103850c
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPKeystoreLogic.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPKeystoreDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.provisioning.api.data.SAML2SPKeystoreBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.reflect.Method;
+
+@Component
+public class SAML2SPKeystoreLogic extends AbstractTransactionalLogic<SAML2SPKeystoreTO> {
+
+ @Autowired
+ private SAML2SPKeystoreBinder binder;
+
+ @Autowired
+ private SAML2SPKeystoreDAO saml2SPKeystoreDAO;
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_KEYSTORE_READ + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public SAML2SPKeystoreTO read(final String key) {
+ final SAML2SPKeystore keystore = saml2SPKeystoreDAO.find(key);
+ if (keystore == null) {
+ throw new NotFoundException(key + " not found");
+ }
+ return binder.getSAML2SPKeystoreTO(keystore);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_KEYSTORE_READ + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public SAML2SPKeystoreTO get(final String name) {
+ final SAML2SPKeystore keystore = saml2SPKeystoreDAO.findByOwner(name);
+ if (keystore == null) {
+ throw new NotFoundException("SAML2 SP keystore owned by " + name + " not found");
+ }
+
+ return binder.getSAML2SPKeystoreTO(keystore);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_KEYSTORE_CREATE + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ public SAML2SPKeystoreTO set(final SAML2SPKeystoreTO keystoreTO) {
+ final SAML2SPKeystore keystore = saml2SPKeystoreDAO.findByOwner(keystoreTO.getOwner());
+ if (keystore == null) {
+ return binder.getSAML2SPKeystoreTO(saml2SPKeystoreDAO.save(binder.create(keystoreTO)));
+ }
+ throw SyncopeClientException.build(ClientExceptionType.EntityExists);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_KEYSTORE_UPDATE + "')")
+ public SAML2SPKeystoreTO update(final SAML2SPKeystoreTO keystoreTO) {
+ final SAML2SPKeystore keystore = saml2SPKeystoreDAO.find(keystoreTO.getKey());
+ if (keystore == null) {
+ throw new NotFoundException(keystoreTO.getKey() + " not found");
+ }
+ return binder.getSAML2SPKeystoreTO(saml2SPKeystoreDAO.save(binder.update(keystore, keystoreTO)));
+ }
+
+ @Override
+ protected SAML2SPKeystoreTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+ String name = null;
+ if (ArrayUtils.isNotEmpty(args)) {
+ for (int i = 0; name == null && i < args.length; i++) {
+ if (args[i] instanceof String) {
+ name = (String) args[i];
+ } else if (args[i] instanceof SAML2SPKeystoreTO) {
+ name = ((SAML2SPKeystoreTO) args[i]).getKey();
+ }
+ }
+ }
+
+ if (name != null) {
+ try {
+ return binder.getSAML2SPKeystoreTO(saml2SPKeystoreDAO.findByOwner(name));
+ } catch (final Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+ throw new UnresolvedReferenceException();
+ }
+}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPMetadataLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPMetadataLogic.java
new file mode 100644
index 0000000..09182bc
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPMetadataLogic.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPMetadataDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+import org.apache.syncope.core.provisioning.api.data.SAML2SPMetadataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.reflect.Method;
+
+@Component
+public class SAML2SPMetadataLogic extends AbstractTransactionalLogic<SAML2SPMetadataTO> {
+
+ @Autowired
+ private SAML2SPMetadataBinder binder;
+
+ @Autowired
+ private SAML2SPMetadataDAO saml2SPMetadataDAO;
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_METADATA_READ + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public SAML2SPMetadataTO read(final String key) {
+ final SAML2SPMetadata metadata = saml2SPMetadataDAO.find(key);
+ if (metadata == null) {
+ throw new NotFoundException(key + " not found");
+ }
+ return binder.getSAML2SPMetadataTO(metadata);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_METADATA_READ + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public SAML2SPMetadataTO get(final String name) {
+ final SAML2SPMetadata metadata = saml2SPMetadataDAO.findByOwner(name);
+ if (metadata == null) {
+ throw new NotFoundException("SAML2 SP Metadata owned by " + name + " not found");
+ }
+
+ return binder.getSAML2SPMetadataTO(metadata);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_METADATA_CREATE + "') "
+ + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ public SAML2SPMetadataTO set(final SAML2SPMetadataTO metadataTO) {
+ SAML2SPMetadata metadata = saml2SPMetadataDAO.findByOwner(metadataTO.getOwner());
+ if (metadata == null) {
+ return binder.getSAML2SPMetadataTO(saml2SPMetadataDAO.save(binder.create(metadataTO)));
+ }
+ throw SyncopeClientException.build(ClientExceptionType.EntityExists);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SP_METADATA_UPDATE + "')")
+ public SAML2SPMetadataTO update(final SAML2SPMetadataTO metadataTO) {
+ final SAML2SPMetadata metadata = saml2SPMetadataDAO.find(metadataTO.getKey());
+ if (metadata == null) {
+ throw new NotFoundException(metadataTO.getKey() + " not found");
+ }
+ return binder.getSAML2SPMetadataTO(saml2SPMetadataDAO.save(binder.update(metadata, metadataTO)));
+ }
+
+ @Override
+ protected SAML2SPMetadataTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+ String name = null;
+ if (ArrayUtils.isNotEmpty(args)) {
+ for (int i = 0; name == null && i < args.length; i++) {
+ if (args[i] instanceof String) {
+ name = (String) args[i];
+ } else if (args[i] instanceof SAML2SPMetadataTO) {
+ name = ((SAML2SPMetadataTO) args[i]).getKey();
+ }
+ }
+ }
+
+ if (name != null) {
+ try {
+ return binder.getSAML2SPMetadataTO(saml2SPMetadataDAO.findByOwner(name));
+ } catch (final Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+ throw new UnresolvedReferenceException();
+ }
+}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPKeystoreConfServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPKeystoreConfServiceImpl.java
new file mode 100644
index 0000000..43aaedf
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPKeystoreConfServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.rest.api.service.SAML2SPKeystoreConfService;
+import org.apache.syncope.core.logic.SAML2SPKeystoreLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SAML2SPKeystoreConfServiceImpl extends AbstractServiceImpl implements SAML2SPKeystoreConfService {
+
+ @Autowired
+ private SAML2SPKeystoreLogic logic;
+
+ @Override
+ public void update(final SAML2SPKeystoreTO metadataTO) {
+ logic.update(metadataTO);
+ }
+}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPMetadataConfServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPMetadataConfServiceImpl.java
new file mode 100644
index 0000000..13f9239
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2SPMetadataConfServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.SAML2SPMetadataConfService;
+import org.apache.syncope.core.logic.SAML2SPMetadataLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SAML2SPMetadataConfServiceImpl extends AbstractServiceImpl implements SAML2SPMetadataConfService {
+
+ @Autowired
+ private SAML2SPMetadataLogic logic;
+
+ @Override
+ public void update(final SAML2SPMetadataTO metadataTO) {
+ logic.update(metadataTO);
+ }
+}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2IdPMetadataServiceImpl.java
similarity index 87%
copy from core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
copy to core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2IdPMetadataServiceImpl.java
index b1eb6a3..6bc3dc2 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2IdPMetadataServiceImpl.java
@@ -16,14 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.rest.cxf.service;
+package org.apache.syncope.core.rest.cxf.service.wa;
import java.net.URI;
import javax.ws.rs.core.Response;
import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2IdPMetadataService;
import org.apache.syncope.core.logic.SAML2IdPMetadataLogic;
+import org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -34,7 +35,7 @@ public class SAML2IdPMetadataServiceImpl extends AbstractServiceImpl implements
private SAML2IdPMetadataLogic logic;
@Override
- public SAML2IdPMetadataTO get(final String appliesTo) {
+ public SAML2IdPMetadataTO getByOwner(final String appliesTo) {
return logic.get(appliesTo);
}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPKeystoreServiceImpl.java
similarity index 56%
copy from core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
copy to core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPKeystoreServiceImpl.java
index b1eb6a3..852a701 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPKeystoreServiceImpl.java
@@ -16,39 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.rest.cxf.service;
+package org.apache.syncope.core.rest.cxf.service.wa;
-import java.net.URI;
-import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
-import org.apache.syncope.core.logic.SAML2IdPMetadataLogic;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPKeystoreService;
+import org.apache.syncope.core.logic.SAML2SPKeystoreLogic;
+import org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import javax.ws.rs.core.Response;
+
+import java.net.URI;
+
@Service
-public class SAML2IdPMetadataServiceImpl extends AbstractServiceImpl implements SAML2IdPMetadataService {
+public class SAML2SPKeystoreServiceImpl extends AbstractServiceImpl implements SAML2SPKeystoreService {
@Autowired
- private SAML2IdPMetadataLogic logic;
+ private SAML2SPKeystoreLogic logic;
@Override
- public SAML2IdPMetadataTO get(final String appliesTo) {
- return logic.get(appliesTo);
+ public SAML2SPKeystoreTO getByOwner(final String name) {
+ return logic.get(name);
}
@Override
- public SAML2IdPMetadataTO read(final String key) {
+ public SAML2SPKeystoreTO read(final String key) {
return logic.read(key);
}
@Override
- public Response set(final SAML2IdPMetadataTO saml2IdPMetadataTO) {
- SAML2IdPMetadataTO saml2IdPMetadata = logic.set(saml2IdPMetadataTO);
- URI location = uriInfo.getAbsolutePathBuilder().path(saml2IdPMetadata.getKey()).build();
+ public Response set(final SAML2SPKeystoreTO keystoreTO) {
+ final SAML2SPKeystoreTO keystore = logic.set(keystoreTO);
+ URI location = uriInfo.getAbsolutePathBuilder().path(keystore.getKey()).build();
return Response.created(location).
- header(RESTHeaders.RESOURCE_KEY, saml2IdPMetadata.getKey()).
- build();
+ header(RESTHeaders.RESOURCE_KEY, keystore.getKey()).
+ build();
}
}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPMetadataServiceImpl.java
similarity index 56%
rename from core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
rename to core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPMetadataServiceImpl.java
index b1eb6a3..3a8bf55 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPMetadataServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/SAML2SPMetadataServiceImpl.java
@@ -16,39 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.rest.cxf.service;
+package org.apache.syncope.core.rest.cxf.service.wa;
-import java.net.URI;
-import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
-import org.apache.syncope.core.logic.SAML2IdPMetadataLogic;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.core.logic.SAML2SPMetadataLogic;
+import org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import javax.ws.rs.core.Response;
+
+import java.net.URI;
+
@Service
-public class SAML2IdPMetadataServiceImpl extends AbstractServiceImpl implements SAML2IdPMetadataService {
+public class SAML2SPMetadataServiceImpl extends AbstractServiceImpl implements SAML2SPMetadataService {
@Autowired
- private SAML2IdPMetadataLogic logic;
+ private SAML2SPMetadataLogic logic;
@Override
- public SAML2IdPMetadataTO get(final String appliesTo) {
- return logic.get(appliesTo);
+ public SAML2SPMetadataTO getByOwner(final String name) {
+ return logic.get(name);
}
@Override
- public SAML2IdPMetadataTO read(final String key) {
+ public SAML2SPMetadataTO read(final String key) {
return logic.read(key);
}
@Override
- public Response set(final SAML2IdPMetadataTO saml2IdPMetadataTO) {
- SAML2IdPMetadataTO saml2IdPMetadata = logic.set(saml2IdPMetadataTO);
- URI location = uriInfo.getAbsolutePathBuilder().path(saml2IdPMetadata.getKey()).build();
+ public Response set(final SAML2SPMetadataTO metadataTO) {
+ SAML2SPMetadataTO spMetadata = logic.set(metadataTO);
+ URI location = uriInfo.getAbsolutePathBuilder().path(spMetadata.getKey()).build();
return Response.created(location).
- header(RESTHeaders.RESOURCE_KEY, saml2IdPMetadata.getKey()).
- build();
+ header(RESTHeaders.RESOURCE_KEY, spMetadata.getKey()).
+ build();
}
}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPKeystoreDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPKeystoreDAO.java
new file mode 100644
index 0000000..5352a28
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPKeystoreDAO.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.DAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+
+public interface SAML2SPKeystoreDAO extends DAO<SAML2SPKeystore> {
+
+ SAML2SPKeystore find(String key);
+
+ SAML2SPKeystore findByOwner(String name);
+
+ SAML2SPKeystore save(SAML2SPKeystore keystore);
+
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPMetadataDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPMetadataDAO.java
new file mode 100644
index 0000000..ef1894f
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPMetadataDAO.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.DAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+
+public interface SAML2SPMetadataDAO extends DAO<SAML2SPMetadata> {
+
+ SAML2SPMetadata find(String key);
+
+ SAML2SPMetadata findByOwner(String name);
+
+ SAML2SPMetadata save(SAML2SPMetadata metadata);
+
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPKeystore.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPKeystore.java
new file mode 100644
index 0000000..83fe696
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPKeystore.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+
+public interface SAML2SPKeystore extends Entity {
+
+ String getKeystore();
+
+ void setKeystore(String keystore);
+
+ String getOwner();
+
+ void setOwner(String owner);
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPMetadata.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPMetadata.java
new file mode 100644
index 0000000..78b4c10
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPMetadata.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+
+public interface SAML2SPMetadata extends Entity {
+
+ String getMetadata();
+
+ void setMetadata(String metadata);
+
+ String getOwner();
+
+ void setOwner(String owner);
+}
diff --git a/core/persistence-jpa-json/pom.xml b/core/persistence-jpa-json/pom.xml
index da89ec5..83db64d 100644
--- a/core/persistence-jpa-json/pom.xml
+++ b/core/persistence-jpa-json/pom.xml
@@ -45,6 +45,16 @@ under the License.
</dependency>
<!-- TEST -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@@ -125,6 +135,26 @@ under the License.
<filtering>true</filtering>
</resource>
</resources>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ <excludes>
+ <exclude>domains/MasterContent.xml</exclude>
+ </excludes>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/../persistence-jpa/src/test/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>*-metadata.xml</include>
+ </includes>
+ </testResource>
+ </testResources>
</build>
<profiles>
@@ -286,21 +316,10 @@ under the License.
<testResources>
<testResource>
- <directory>src/test/resources</directory>
- <filtering>true</filtering>
- </testResource>
- <testResource>
<directory>src/test/resources/pgjsonb</directory>
<filtering>true</filtering>
</testResource>
<testResource>
- <directory>src/main/resources</directory>
- <filtering>true</filtering>
- <excludes>
- <exclude>domains/MasterContent.xml</exclude>
- </excludes>
- </testResource>
- <testResource>
<directory>src/main/resources/pgjsonb</directory>
<filtering>true</filtering>
<excludes>
@@ -413,21 +432,10 @@ under the License.
<testResources>
<testResource>
- <directory>src/test/resources</directory>
- <filtering>true</filtering>
- </testResource>
- <testResource>
<directory>src/test/resources/myjson</directory>
<filtering>true</filtering>
</testResource>
<testResource>
- <directory>src/main/resources</directory>
- <filtering>true</filtering>
- <excludes>
- <exclude>domains/MasterContent.xml</exclude>
- </excludes>
- </testResource>
- <testResource>
<directory>src/main/resources/myjson</directory>
<filtering>true</filtering>
<excludes>
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index 1683d0b..3394661 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -68,9 +68,10 @@ under the License.
<AuthModule id="4c3ed4e6-7008-11ea-bc55-0242ac130003" name="DefaultGoogleMfaAuthModule"
description="Google Mfa auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","name":"MyGoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest"}'/>
<AuthModule id="4c3ed8f6-7008-11ea-bc55-0242ac130003" name="DefaultOIDCAuthModule"
- description="OIDC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"www.testurl.com"}'/>
+ description="OIDC auth module"
+ jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"https://accounts.google.com/.well-known/openid-configuration", "id":"client-id", "secret": "client-secret" }'/>
<AuthModule id="4c3ed9d2-7008-11ea-bc55-0242ac130003" name="DefaultSAML2IdPAuthModule"
- description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "providerName":"testProviderName","serviceProviderMetadataPath":"file:/etc/metadata"}'/>
+ description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "keystorePassword":"p@$$word","privateKeyPassword":"p@$$word","identityProviderMetadataPath":"classpath:/idp-metadata.xml", "serviceProviderEntityId":"syncope:apache:org"}'/>
<AuthModule id="4c3edbbc-7008-11ea-bc55-0242ac130003" name="DefaultJaasAuthModule"
description="Jaas auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","name":"MyJaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value"}'/>
<AuthModule id="4c3edc98-7008-11ea-bc55-0242ac130003" name="DefaultStaticAuthModule"
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index 6f5114e..0d6b810 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -128,6 +128,16 @@ under the License.
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPKeystoreDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPKeystoreDAO.java
new file mode 100644
index 0000000..c3b9761
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPKeystoreDAO.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPKeystoreDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPKeystore;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+@Repository
+public class JPASAML2SPKeystoreDAO extends AbstractDAO<SAML2SPKeystore> implements SAML2SPKeystoreDAO {
+
+ @Transactional(readOnly = true)
+ @Override
+ public SAML2SPKeystore find(final String key) {
+ return entityManager().find(JPASAML2SPKeystore.class, key);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public SAML2SPKeystore findByOwner(final String owner) {
+ TypedQuery<SAML2SPKeystore> query = entityManager().createQuery(
+ "SELECT e FROM " + JPASAML2SPKeystore.class.getSimpleName() + " e WHERE e.owner=:owner",
+ SAML2SPKeystore.class);
+ query.setParameter("owner", owner);
+
+ SAML2SPKeystore result = null;
+ try {
+ result = query.getSingleResult();
+ } catch (final NoResultException e) {
+ LOG.debug("No SAML2 SP Keystore found with appliesTo = {}", owner);
+ }
+ return result;
+ }
+
+ @Override
+ public SAML2SPKeystore save(final SAML2SPKeystore saml2IdPMetadata) {
+ return entityManager().merge(saml2IdPMetadata);
+ }
+
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPMetadataDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPMetadataDAO.java
new file mode 100644
index 0000000..21ee9bf
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPMetadataDAO.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPMetadataDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPMetadata;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+@Repository
+public class JPASAML2SPMetadataDAO extends AbstractDAO<SAML2SPMetadata> implements SAML2SPMetadataDAO {
+
+ @Transactional(readOnly = true)
+ @Override
+ public SAML2SPMetadata find(final String key) {
+ return entityManager().find(JPASAML2SPMetadata.class, key);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public SAML2SPMetadata findByOwner(final String owner) {
+ TypedQuery<SAML2SPMetadata> query = entityManager().createQuery(
+ "SELECT e FROM " + JPASAML2SPMetadata.class.getSimpleName() + " e WHERE e.owner=:owner",
+ SAML2SPMetadata.class);
+ query.setParameter("owner", owner);
+
+ SAML2SPMetadata result = null;
+ try {
+ result = query.getSingleResult();
+ } catch (final NoResultException e) {
+ LOG.debug("No SAML2 SP Metadata found with appliesTo = {}", owner);
+ }
+ return result;
+ }
+
+ @Override
+ public SAML2SPMetadata save(final SAML2SPMetadata saml2IdPMetadata) {
+ return entityManager().merge(saml2IdPMetadata);
+ }
+
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 28e6e66..7eafee6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -61,6 +61,8 @@ import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
import org.apache.syncope.core.persistence.api.entity.auth.OIDCRP;
import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPMetadata;
import org.apache.syncope.core.persistence.api.entity.auth.SAML2SP;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrUniqueValue;
import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
@@ -112,6 +114,8 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship
import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCRP;
import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SP;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPKeystore;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPMetadata;
import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttr;
import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrUniqueValue;
import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrValue;
@@ -325,6 +329,10 @@ public class JPAEntityFactory implements EntityFactory {
result = (E) new JPASAML2SP();
} else if (reference.equals(SAML2IdPMetadata.class)) {
result = (E) new JPASAML2IdPMetadata();
+ } else if (reference.equals(SAML2SPMetadata.class)) {
+ result = (E) new JPASAML2SPMetadata();
+ } else if (reference.equals(SAML2SPKeystore.class)) {
+ result = (E) new JPASAML2SPKeystore();
} else {
throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPKeystore.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPKeystore.java
new file mode 100644
index 0000000..b7ef84c
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPKeystore.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = JPASAML2SPKeystore.TABLE)
+public class JPASAML2SPKeystore extends AbstractGeneratedKeyEntity implements SAML2SPKeystore {
+
+ public static final String TABLE = "SAML2SPKeystore";
+
+ private static final long serialVersionUID = 22342617217394093L;
+
+ @Column(unique = true)
+ private String owner;
+
+ @Lob
+ @Column
+ private String keystore;
+
+ @Override
+ public String getKeystore() {
+ return keystore;
+ }
+
+ @Override
+ public void setKeystore(final String keystore) {
+ this.keystore = keystore;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(final String name) {
+ this.owner = name;
+ }
+
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPMetadata.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPMetadata.java
new file mode 100644
index 0000000..e43e5ac
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPMetadata.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = JPASAML2SPMetadata.TABLE)
+public class JPASAML2SPMetadata extends AbstractGeneratedKeyEntity implements SAML2SPMetadata {
+
+ public static final String TABLE = "SAML2SPMetadata";
+
+ private static final long serialVersionUID = 12342617217394093L;
+
+ @Column(unique = true)
+ private String owner;
+
+ @Lob
+ @Column
+ private String metadata;
+
+ @Override
+ public String getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public void setMetadata(final String metadata) {
+ this.metadata = metadata;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(final String name) {
+ this.owner = name;
+ }
+
+}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
index 8bd2c13..730a67e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
@@ -267,8 +267,6 @@ public class AuthModuleTest extends AbstractTest {
SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf();
conf.setServiceProviderEntityId("testEntityId");
conf.setProviderName("testProviderName");
- conf.setServiceProviderMetadataPath("file:/etc/metadata");
-
saveAuthModule("SAML2IdPAuthModuleTest", conf);
}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPKeystoreTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPKeystoreTest.java
new file mode 100644
index 0000000..9dfbcd4
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPKeystoreTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPKeystoreDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificate;
+import org.bouncycastle.asn1.x509.Time;
+import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+import java.util.Date;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@Transactional("Master")
+public class SAML2SPKeystoreTest extends AbstractTest {
+
+ @Autowired
+ private SAML2SPKeystoreDAO saml2SPKeystoreDAO;
+
+ private static Certificate createSelfSignedCert(final KeyPair keyPair) throws Exception {
+ final X500Name dn = new X500Name("cn=Unknown");
+ final V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
+
+ certGen.setSerialNumber(new ASN1Integer(BigInteger.valueOf(1)));
+ certGen.setIssuer(dn);
+ certGen.setSubject(dn);
+ certGen.setStartDate(new Time(new Date(System.currentTimeMillis() - 1000L)));
+
+ final Date expiration = new Date(System.currentTimeMillis() + 100000);
+ certGen.setEndDate(new Time(expiration));
+
+ final AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE);
+ certGen.setSignature(sigAlgID);
+ certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+
+ final Signature sig = Signature.getInstance("SHA1WithRSA");
+ sig.initSign(keyPair.getPrivate());
+ sig.update(certGen.generateTBSCertificate().getEncoded(ASN1Encoding.DER));
+
+ final TBSCertificate tbsCert = certGen.generateTBSCertificate();
+ final ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCert);
+ v.add(sigAlgID);
+ v.add(new DERBitString(sig.sign()));
+
+ final Certificate cert = CertificateFactory.getInstance("X.509")
+ .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
+ cert.verify(keyPair.getPublic());
+ return cert;
+ }
+
+ @Test
+ public void find() throws Exception {
+ create("Syncope");
+ SAML2SPKeystore saml2SPKeystore = saml2SPKeystoreDAO.findByOwner("Syncope");
+ assertNotNull(saml2SPKeystore);
+
+ saml2SPKeystore = saml2SPKeystoreDAO.findByOwner(UUID.randomUUID().toString());
+ assertNull(saml2SPKeystore);
+ }
+
+ @Test
+ public void save() throws Exception {
+ final SAML2SPKeystore keystore = create("SyncopeCreate");
+ final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ char[] pwdArray = "password".toCharArray();
+ ks.load(new ByteArrayInputStream(Base64.getDecoder().decode(keystore.getKeystore())), pwdArray);
+ assertTrue(ks.size() > 0);
+ }
+
+ @Test
+ public void update() throws Exception {
+ SAML2SPKeystore saml2SPKeystore = create("SyncopeUpdate");
+ assertNotNull(saml2SPKeystore);
+ saml2SPKeystore.setOwner("OtherSyncope");
+
+ saml2SPKeystore = saml2SPKeystoreDAO.save(saml2SPKeystore);
+ assertNotNull(saml2SPKeystore);
+ assertNotNull(saml2SPKeystore.getKey());
+ SAML2SPKeystore found = saml2SPKeystoreDAO.findByOwner(saml2SPKeystore.getOwner());
+ assertNotNull(found);
+ assertEquals("OtherSyncope", found.getOwner());
+ }
+
+ private SAML2SPKeystore create(final String owner) throws Exception {
+ final SAML2SPKeystore saml2SPKeystore = entityFactory.newEntity(SAML2SPKeystore.class);
+ saml2SPKeystore.setOwner(owner);
+
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ char[] pwdArray = "password".toCharArray();
+ ks.load(null, pwdArray);
+
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(4096);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ Certificate certificate = createSelfSignedCert(keyPair);
+ ks.setKeyEntry("main", keyPair.getPrivate(), "password".toCharArray(), new Certificate[]{certificate});
+
+ try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
+ ks.store(fos, pwdArray);
+ fos.flush();
+ String keystore = Base64.getEncoder().encodeToString(fos.toByteArray());
+ saml2SPKeystore.setKeystore(keystore);
+ }
+ saml2SPKeystoreDAO.save(saml2SPKeystore);
+ assertNotNull(saml2SPKeystore);
+ assertNotNull(saml2SPKeystore.getKey());
+ assertNotNull(saml2SPKeystoreDAO.findByOwner(saml2SPKeystore.getOwner()));
+ return saml2SPKeystore;
+ }
+}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPMetadataTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPMetadataTest.java
new file mode 100644
index 0000000..f6cbeae
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPMetadataTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPMetadataDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.apache.tika.io.IOUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@Transactional("Master")
+public class SAML2SPMetadataTest extends AbstractTest {
+
+ @Autowired
+ private SAML2SPMetadataDAO saml2SPMetadataDAO;
+
+ @Test
+ public void find() throws Exception {
+ create("Syncope");
+ SAML2SPMetadata saml2SPMetadata = saml2SPMetadataDAO.findByOwner("Syncope");
+ assertNotNull(saml2SPMetadata);
+
+ saml2SPMetadata = saml2SPMetadataDAO.findByOwner(UUID.randomUUID().toString());
+ assertNull(saml2SPMetadata);
+ }
+
+ @Test
+ public void save() throws Exception {
+ create("SyncopeCreate");
+ }
+
+ @Test
+ public void update() throws Exception {
+ SAML2SPMetadata saml2SPMetadata = create("SyncopeUpdate");
+ assertNotNull(saml2SPMetadata);
+ saml2SPMetadata.setOwner("OtherSyncope");
+
+ saml2SPMetadata = saml2SPMetadataDAO.save(saml2SPMetadata);
+ assertNotNull(saml2SPMetadata);
+ assertNotNull(saml2SPMetadata.getKey());
+ SAML2SPMetadata found = saml2SPMetadataDAO.findByOwner(saml2SPMetadata.getOwner());
+ assertNotNull(found);
+ assertEquals("OtherSyncope", found.getOwner());
+ }
+
+ private SAML2SPMetadata create(final String owner) throws Exception {
+ SAML2SPMetadata saml2SPMetadata = entityFactory.newEntity(SAML2SPMetadata.class);
+ saml2SPMetadata.setOwner(owner);
+ String metadata = IOUtils.toString(new ClassPathResource("sp-metadata.xml").getInputStream());
+ saml2SPMetadata.setMetadata(metadata);
+ saml2SPMetadataDAO.save(saml2SPMetadata);
+ assertNotNull(saml2SPMetadata);
+ assertNotNull(saml2SPMetadata.getKey());
+ assertNotNull(saml2SPMetadataDAO.findByOwner(saml2SPMetadata.getOwner()));
+ return saml2SPMetadata;
+ }
+
+}
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 8a5780c..7e1c33b 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -68,9 +68,10 @@ under the License.
<AuthModule id="4c3ed4e6-7008-11ea-bc55-0242ac130003" name="DefaultGoogleMfaAuthModule"
description="Google Mfa auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","name":"MyGoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest"}'/>
<AuthModule id="4c3ed8f6-7008-11ea-bc55-0242ac130003" name="DefaultOIDCAuthModule"
- description="OIDC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"www.testurl.com", "id":"client-id", "secret": "client-secret" }'/>
+ description="OIDC auth module"
+ jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"https://accounts.google.com/.well-known/openid-configuration", "id":"client-id", "secret": "client-secret" }'/>
<AuthModule id="4c3ed9d2-7008-11ea-bc55-0242ac130003" name="DefaultSAML2IdPAuthModule"
- description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "keystorePassword":"p@$$word","privateKeyPassword":"p@$$word","keystorePath":"file:/etc/metadata/keystore.jks","identityProviderMetadataPath":"file:/etc/metadata/idp.xml", "serviceProviderMetadataPath":"file:/etc/metadata/sp.xml", "serviceProviderEntityId":"syncope:apache:org"}'/>
+ description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "keystorePassword":"p@$$word","privateKeyPassword":"p@$$word","identityProviderMetadataPath":"classpath:/idp-metadata.xml", "serviceProviderEntityId":"syncope:apache:org"}'/>
<AuthModule id="4c3edbbc-7008-11ea-bc55-0242ac130003" name="DefaultJaasAuthModule"
description="Jaas auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","name":"MyJaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value", "loginConfigType": "JavaLoginConfig", "loginConfigurationFile": "file:/etc/jaas/login.conf"}'/>
<AuthModule id="4c3edc98-7008-11ea-bc55-0242ac130003" name="DefaultStaticAuthModule"
diff --git a/core/persistence-jpa/src/test/resources/idp-metadata.xml b/core/persistence-jpa/src/test/resources/idp-metadata.xml
new file mode 100644
index 0000000..2e0162b
--- /dev/null
+++ b/core/persistence-jpa/src/test/resources/idp-metadata.xml
@@ -0,0 +1,144 @@
+<?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.
+ -->
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" entityID="https://syncope.apache.org/idp">
+ <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+ BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+ NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+ R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+ BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+ I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+ EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+ 9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+ HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+ L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+ sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+ K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
+ +Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+ 9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+ i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+ zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <KeyDescriptor use="encryption">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUKymtgciRE6pWwDfrsI58qL9pQMgwDQYJKoZIhvcNAQEL
+ BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+ NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKrT805sny3vlaYjTn
+ +m6m3VbUoStvnacgwHH+orNFhHoV1HP2ndoH5BsEDB9tQYuyRbGUm/nYVOSHayzB
+ G3bzMGdU7woO6rsCqpHuxUyEvojd/y/N/r9jMzeBOCu0KDBTrn3BJhnGSwSTfhOS
+ 3r20JFmDuTkHmabRs7ro0BvDaQ29jh38ro1iwB4E/4mqb1zYP13NI3ooErN/o6pl
+ XKpnFY37bDDOyOuocjN9tfPNIANNFKah0HjWOP0Nso0D1g6jHOSzmOw/Yxg61vBk
+ qOD4aKhLYPAxsXRl80nDrwTnm3/9xLQj9D3uLAtDLnn9pSqn3jCLxsxsHfKL/zkB
+ IKEBAgMBAAGjYzBhMB0GA1UdDgQWBBSrPjAgCJIHYmsofDcDIPzEhnYxmTBABgNV
+ HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+ L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAI8MlofbE0tbq8ez2d0Lq
+ Syhp4Q/shMEwjqcDarOwR+ACB9McOannUpAG7TCDp8Ch5E/V1B0Uo/5DF2tAzB1y
+ 7sgAmy2mY9/mFhMYpOqTCagufwewaMkn9n7ETzC/6vQEjYrjiNyNR0F3UQQz2bhe
+ ROM3YuKctuOnMthc+ZE7vn+AXCGumRHBhyCaYdzfeUh7id+yrd9B51+o3iF4eu6w
+ zJi5z7FMCS6I4PSc/uWYDw1ahzoPONjazWSEWGUibZaJYM3pJHkuwqyWKOFGVknH
+ J1Qv4WCfSPb6eva94TZX0lkLM01C7NZObnfxY3fvJGcyFl8wlRTUYvuqM8md5CEp
+ LA==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+
+ <!--
+ <ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
+ Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/ArtifactResolution" index="1"/>
+ -->
+
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SLO"/>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SLO" />
+
+ <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST-SimpleSign/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/ECP"/>
+ </IDPSSODescriptor>
+
+ <!--
+ <AttributeAuthorityDescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
++Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/AttributeQuery"/>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/AttributeQuery"/>
+ </AttributeAuthorityDescriptor>
+ -->
+
+ <!--
+ <Organization>
+ <OrganizationName xml:lang="en">Institution Name</OrganizationName>
+ <OrganizationDisplayName xml:lang="en">Institution DisplayName</OrganizationDisplayName>
+ <OrganizationURL xml:lang="en">URL</OrganizationURL>
+ </Organization>
+ <ContactPerson contactType="administrative">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="technical">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="support">
+ <GivenName>IT Services Support</GivenName>
+ <EmailAddress>support@example.org</EmailAddress>
+ </ContactPerson>
+ -->
+</EntityDescriptor>
diff --git a/core/persistence-jpa/src/test/resources/sp-metadata.xml b/core/persistence-jpa/src/test/resources/sp-metadata.xml
new file mode 100644
index 0000000..351d556
--- /dev/null
+++ b/core/persistence-jpa/src/test/resources/sp-metadata.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="_lipmbmwyb3ujt0osvfect39lzglxnsdi7bxhvf5" entityID="urn:mace:saml:pac4j.org" validUntil="2038-08-10T19:43:10.846Z">
+ <md:Extensions xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport">
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha224"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
+ </md:Extensions>
+ <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">
+ <md:Extensions xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init">
+ <init:RequestInitiator Binding="urn:oasis:names:tc:SAML:profiles:SSO:request-init" Location="https://mmoayyed.unicon.net:8443/cas/login?client_name=SAML2Client"/>
+ </md:Extensions>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mmoayyed.unicon.net:8443/cas/login?client_name=SAML2Client" index="0"/>
+ </md:SPSSODescriptor>
+</md:EntityDescriptor>
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPKeystoreBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPKeystoreBinder.java
new file mode 100644
index 0000000..e0cc306
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPKeystoreBinder.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+
+public interface SAML2SPKeystoreBinder {
+
+ SAML2SPKeystore create(SAML2SPKeystoreTO keystoreTO);
+
+ SAML2SPKeystore update(SAML2SPKeystore keystore, SAML2SPKeystoreTO keystoreTO);
+
+ SAML2SPKeystoreTO getSAML2SPKeystoreTO(SAML2SPKeystore keystore);
+
+}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPMetadataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPMetadataBinder.java
new file mode 100644
index 0000000..c49d64f
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPMetadataBinder.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+
+public interface SAML2SPMetadataBinder {
+
+ SAML2SPMetadata create(SAML2SPMetadataTO metadataTO);
+
+ SAML2SPMetadata update(SAML2SPMetadata metadata, SAML2SPMetadataTO metadataTO);
+
+ SAML2SPMetadataTO getSAML2SPMetadataTO(SAML2SPMetadata metadata);
+
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPKeystoreBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPKeystoreBinderImpl.java
new file mode 100644
index 0000000..02fd805
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPKeystoreBinderImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPKeystore;
+import org.apache.syncope.core.provisioning.api.data.SAML2SPKeystoreBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SAML2SPKeystoreBinderImpl implements SAML2SPKeystoreBinder {
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ private SAML2SPKeystore getSAML2SPKeystore(
+ final SAML2SPKeystore keystore,
+ final SAML2SPKeystoreTO keystoreTO) {
+
+ SAML2SPKeystore result = keystore;
+ if (result == null) {
+ result = entityFactory.newEntity(SAML2SPKeystore.class);
+ }
+ result.setKeystore(keystoreTO.getKeystore());
+ result.setOwner(keystoreTO.getOwner());
+
+ return result;
+ }
+
+ @Override
+ public SAML2SPKeystore create(final SAML2SPKeystoreTO keystoreTO) {
+ return update(entityFactory.newEntity(SAML2SPKeystore.class), keystoreTO);
+ }
+
+ @Override
+ public SAML2SPKeystore update(
+ final SAML2SPKeystore keystore,
+ final SAML2SPKeystoreTO keystoreTO) {
+
+ return getSAML2SPKeystore(keystore, keystoreTO);
+ }
+
+ @Override
+ public SAML2SPKeystoreTO getSAML2SPKeystoreTO(final SAML2SPKeystore keystore) {
+ SAML2SPKeystoreTO keystoreTO = new SAML2SPKeystoreTO();
+
+ keystoreTO.setKey(keystore.getKey());
+ keystoreTO.setKeystore(keystore.getKeystore());
+ keystoreTO.setOwner(keystore.getOwner());
+
+ return keystoreTO;
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPMetadataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPMetadataBinderImpl.java
new file mode 100644
index 0000000..3e4e62b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPMetadataBinderImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPMetadata;
+import org.apache.syncope.core.provisioning.api.data.SAML2SPMetadataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SAML2SPMetadataBinderImpl implements SAML2SPMetadataBinder {
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ private SAML2SPMetadata getSAML2SPMetadata(
+ final SAML2SPMetadata metadata,
+ final SAML2SPMetadataTO metadataTO) {
+
+ SAML2SPMetadata result = metadata;
+ if (result == null) {
+ result = entityFactory.newEntity(SAML2SPMetadata.class);
+ }
+
+ result.setMetadata(metadataTO.getMetadata());
+ result.setOwner(metadataTO.getOwner());
+
+ return result;
+ }
+
+ @Override
+ public SAML2SPMetadata create(final SAML2SPMetadataTO metadataTO) {
+ return update(entityFactory.newEntity(SAML2SPMetadata.class), metadataTO);
+ }
+
+ @Override
+ public SAML2SPMetadata update(
+ final SAML2SPMetadata metadata,
+ final SAML2SPMetadataTO metadataTO) {
+
+ return getSAML2SPMetadata(metadata, metadataTO);
+ }
+
+ @Override
+ public SAML2SPMetadataTO getSAML2SPMetadataTO(final SAML2SPMetadata saml2IdPMetadata) {
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO();
+
+ metadataTO.setKey(saml2IdPMetadata.getKey());
+ metadataTO.setMetadata(saml2IdPMetadata.getMetadata());
+ metadataTO.setOwner(saml2IdPMetadata.getOwner());
+
+ return metadataTO;
+ }
+}
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index f6c9016..8a3a33c 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -222,6 +222,11 @@ under the License.
<artifactId>bcpkix-jdk15on</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -788,6 +793,16 @@ under the License.
<version>${jdbc.postgresql.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -1060,6 +1075,16 @@ under the License.
<version>${jdbc.mysql.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 91c7dcf..01936ca 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -63,11 +63,12 @@ import org.apache.syncope.common.lib.policy.PolicyTO;
import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
import org.apache.syncope.common.lib.to.SchemaTO;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.policy.AccessPolicyTO;
-import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf;
import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
@@ -84,13 +85,11 @@ import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.to.AuthModuleTO;
import org.apache.syncope.common.lib.policy.AuthPolicyTO;
import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
-import org.apache.syncope.common.lib.to.ImplementationTO;
import org.apache.syncope.common.lib.to.client.ClientAppTO;
import org.apache.syncope.common.lib.to.client.OIDCRPTO;
import org.apache.syncope.common.lib.to.client.SAML2SPTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientAppType;
-import org.apache.syncope.common.lib.types.ImplementationEngine;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.lib.types.PolicyType;
@@ -111,6 +110,10 @@ import org.apache.syncope.common.rest.api.service.ConnectorService;
import org.apache.syncope.common.rest.api.service.DynRealmService;
import org.apache.syncope.common.rest.api.service.LoggerService;
import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.apache.syncope.common.rest.api.service.SAML2SPKeystoreConfService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPKeystoreService;
+import org.apache.syncope.common.rest.api.service.SAML2SPMetadataConfService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
import org.apache.syncope.common.rest.api.service.SAML2SPService;
import org.apache.syncope.common.rest.api.service.PolicyService;
import org.apache.syncope.common.rest.api.service.ReportService;
@@ -138,9 +141,8 @@ import org.apache.syncope.common.rest.api.service.UserRequestService;
import org.apache.syncope.common.rest.api.service.BpmnProcessService;
import org.apache.syncope.common.rest.api.service.GatewayRouteService;
import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataConfService;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2IdPMetadataService;
import org.apache.syncope.common.rest.api.service.UserWorkflowTaskService;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.fit.core.CoreITContext;
import org.apache.syncope.fit.core.UserITCase;
import org.identityconnectors.common.security.Encryptor;
@@ -292,6 +294,14 @@ public abstract class AbstractITCase {
protected static AuthModuleService authModuleService;
+ protected static SAML2SPMetadataService saml2SPMetadataService;
+
+ protected static SAML2SPMetadataConfService saml2SPMetadataConfService;
+
+ protected static SAML2SPKeystoreService saml2SPKeystoreService;
+
+ protected static SAML2SPKeystoreConfService saml2SPKeystoreConfService;
+
protected static SAML2IdPMetadataService saml2IdPMetadataService;
protected static SAML2IdPMetadataConfService saml2IdPMetadataConfService;
@@ -382,15 +392,19 @@ public abstract class AbstractITCase {
remediationService = adminClient.getService(RemediationService.class);
gatewayRouteService = adminClient.getService(GatewayRouteService.class);
camelRouteService = adminClient.getService(CamelRouteService.class);
- saml2SpService = adminClient.getService(org.apache.syncope.common.rest.api.service.SAML2SPService.class);
+ saml2SpService = adminClient.getService(SAML2SPService.class);
saml2IdPService = adminClient.getService(SAML2IdPService.class);
oidcClientService = adminClient.getService(OIDCClientService.class);
oidcProviderService = adminClient.getService(OIDCProviderService.class);
scimConfService = adminClient.getService(SCIMConfService.class);
clientAppService = adminClient.getService(ClientAppService.class);
authModuleService = adminClient.getService(AuthModuleService.class);
+ saml2SPMetadataService = adminClient.getService(SAML2SPMetadataService.class);
+ saml2SPMetadataConfService = adminClient.getService(SAML2SPMetadataConfService.class);
saml2IdPMetadataService = adminClient.getService(SAML2IdPMetadataService.class);
saml2IdPMetadataConfService = adminClient.getService(SAML2IdPMetadataConfService.class);
+ saml2SPKeystoreService = adminClient.getService(SAML2SPKeystoreService.class);
+ saml2SPKeystoreConfService = adminClient.getService(SAML2SPKeystoreConfService.class);
}
@Autowired
@@ -617,6 +631,28 @@ public abstract class AbstractITCase {
return getObject(response.getLocation(), SAML2IdPMetadataService.class, saml2IdPMetadata.getClass());
}
+ protected SAML2SPMetadataTO createSAML2SPMetadata(final SAML2SPMetadataTO metadata) {
+ Response response = saml2SPMetadataService.set(metadata);
+ if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+ Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+ if (ex != null) {
+ throw (RuntimeException) ex;
+ }
+ }
+ return getObject(response.getLocation(), SAML2SPMetadataService.class, metadata.getClass());
+ }
+
+ protected SAML2SPKeystoreTO createSAML2SPKeystore(final SAML2SPKeystoreTO keystoreTO) {
+ Response response = saml2SPKeystoreService.set(keystoreTO);
+ if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+ Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+ if (ex != null) {
+ throw (RuntimeException) ex;
+ }
+ }
+ return getObject(response.getLocation(), SAML2SPKeystoreService.class, keystoreTO.getClass());
+ }
+
protected ResourceTO createResource(final ResourceTO resourceTO) {
Response response = resourceService.create(resourceTO);
if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
index ab51c1f..ba5fc5a 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
@@ -126,8 +126,6 @@ public class AuthModuleITCase extends AbstractITCase {
SAML2IdPAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderEntityId("testEntityId");
SAML2IdPAuthModuleConf.class.cast(conf).setProviderName("testProviderName");
- SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderMetadataPath(
- "file:/etc/metadata");
break;
case SYNCOPE:
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2IdPMetadataITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2IdPMetadataITCase.java
index 58facb8..2971cb7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2IdPMetadataITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2IdPMetadataITCase.java
@@ -63,7 +63,7 @@ public class SAML2IdPMetadataITCase extends AbstractITCase {
public void read() {
SAML2IdPMetadataTO saml2IdPMetadataTO = null;
try {
- saml2IdPMetadataTO = saml2IdPMetadataService.get(APPLIES_TO);
+ saml2IdPMetadataTO = saml2IdPMetadataService.getByOwner(APPLIES_TO);
} catch (SyncopeClientException e) {
saml2IdPMetadataTO = createSAML2IdPMetadata();
}
@@ -76,7 +76,7 @@ public class SAML2IdPMetadataITCase extends AbstractITCase {
@Test
public void create() {
try {
- saml2IdPMetadataService.get(APPLIES_TO);
+ saml2IdPMetadataService.getByOwner(APPLIES_TO);
} catch (SyncopeClientException e) {
createSAML2IdPMetadata();
}
@@ -96,7 +96,7 @@ public class SAML2IdPMetadataITCase extends AbstractITCase {
public void update() {
SAML2IdPMetadataTO saml2IdPMetadataTO = null;
try {
- saml2IdPMetadataTO = saml2IdPMetadataService.get(APPLIES_TO);
+ saml2IdPMetadataTO = saml2IdPMetadataService.getByOwner(APPLIES_TO);
} catch (NotFoundException e) {
saml2IdPMetadataTO = createSAML2IdPMetadata();
}
@@ -104,7 +104,7 @@ public class SAML2IdPMetadataITCase extends AbstractITCase {
assertNotNull(saml2IdPMetadataTO);
saml2IdPMetadataTO.setEncryptionKey("newKey");
saml2IdPMetadataConfService.update(saml2IdPMetadataTO);
- saml2IdPMetadataTO = saml2IdPMetadataService.get(saml2IdPMetadataTO.getAppliesTo());
+ saml2IdPMetadataTO = saml2IdPMetadataService.getByOwner(saml2IdPMetadataTO.getAppliesTo());
assertNotNull(saml2IdPMetadataTO);
assertEquals("newKey", saml2IdPMetadataTO.getEncryptionKey());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPKeystoreITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPKeystoreITCase.java
new file mode 100644
index 0000000..cc7e40c
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPKeystoreITCase.java
@@ -0,0 +1,104 @@
+/*
+ * 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.fit.core;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.util.StringUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SAML2SPKeystoreITCase extends AbstractITCase {
+
+ private static final String OWNER = "Syncope";
+
+ private static void testIsValid(final SAML2SPKeystoreTO keystoreTO) {
+ assertFalse(StringUtils.isBlank(keystoreTO.getOwner()));
+ assertFalse(StringUtils.isBlank(keystoreTO.getKeystore()));
+ }
+
+ private SAML2SPKeystoreTO createSAML2SPKeystore() {
+ SAML2SPKeystoreTO result = createSAML2SPKeystore(new SAML2SPKeystoreTO.Builder().
+ owner(OWNER).
+ keystore("testkyStore").
+ build());
+ assertNotNull(result);
+ testIsValid(result);
+ return result;
+ }
+
+ @Test
+ public void read() {
+ SAML2SPKeystoreTO keystoreTO;
+ try {
+ keystoreTO = saml2SPKeystoreService.getByOwner(OWNER);
+ } catch (SyncopeClientException e) {
+ keystoreTO = createSAML2SPKeystore();
+ }
+ assertNotNull(keystoreTO);
+ assertEquals(OWNER, keystoreTO.getOwner());
+ testIsValid(keystoreTO);
+ }
+
+ @Test
+ public void create() {
+ try {
+ saml2SPMetadataService.getByOwner(OWNER);
+ } catch (SyncopeClientException e) {
+ createSAML2SPKeystore();
+ }
+
+ try {
+ createSAML2SPKeystore(new SAML2SPKeystoreTO.Builder().
+ owner(OWNER).
+ keystore("testMetadata").
+ build());
+ fail("This should not happen");
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.EntityExists, e.getType());
+ }
+ }
+
+ @Test
+ public void update() {
+ SAML2SPKeystoreTO keystoreTO;
+ try {
+ keystoreTO = saml2SPKeystoreService.getByOwner(OWNER);
+ } catch (NotFoundException e) {
+ keystoreTO = createSAML2SPKeystore();
+ }
+ assertNotNull(keystoreTO);
+ keystoreTO.setKeystore("new-keystore");
+ keystoreTO.setOwner("Syncope4");
+
+ saml2SPKeystoreConfService.update(keystoreTO);
+ keystoreTO = saml2SPKeystoreService.read(keystoreTO.getKey());
+ assertNotNull(keystoreTO);
+ assertEquals("new-keystore", keystoreTO.getKeystore());
+ assertEquals("Syncope4", keystoreTO.getOwner());
+ }
+
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPMetadataITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPMetadataITCase.java
new file mode 100644
index 0000000..3fe0f0d
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2SPMetadataITCase.java
@@ -0,0 +1,104 @@
+/*
+ * 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.fit.core;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.util.StringUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SAML2SPMetadataITCase extends AbstractITCase {
+
+ private static final String OWNER = "Syncope";
+
+ private static void testIsValid(final SAML2SPMetadataTO metadataTO) {
+ assertFalse(StringUtils.isBlank(metadataTO.getOwner()));
+ assertFalse(StringUtils.isBlank(metadataTO.getMetadata()));
+ }
+
+ private SAML2SPMetadataTO createSAML2SPMetadata() {
+ SAML2SPMetadataTO result = createSAML2SPMetadata(new SAML2SPMetadataTO.Builder().
+ owner(OWNER).
+ metadata("testMetadata").
+ build());
+ assertNotNull(result);
+ testIsValid(result);
+ return result;
+ }
+
+ @Test
+ public void read() {
+ SAML2SPMetadataTO metadataTO;
+ try {
+ metadataTO = saml2SPMetadataService.getByOwner(OWNER);
+ } catch (SyncopeClientException e) {
+ metadataTO = createSAML2SPMetadata();
+ }
+ assertNotNull(metadataTO);
+ assertEquals(OWNER, metadataTO.getOwner());
+ testIsValid(metadataTO);
+ }
+
+ @Test
+ public void create() {
+ try {
+ saml2SPMetadataService.getByOwner(OWNER);
+ } catch (SyncopeClientException e) {
+ createSAML2SPMetadata();
+ }
+
+ try {
+ createSAML2SPMetadata(new SAML2SPMetadataTO.Builder().
+ owner(OWNER).
+ metadata("testMetadata").
+ build());
+ fail("This should not happen");
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.EntityExists, e.getType());
+ }
+ }
+
+ @Test
+ public void update() {
+ SAML2SPMetadataTO metadataTO;
+ try {
+ metadataTO = saml2SPMetadataService.getByOwner(OWNER);
+ } catch (NotFoundException e) {
+ metadataTO = createSAML2SPMetadata();
+ }
+ assertNotNull(metadataTO);
+ metadataTO.setMetadata("new-metadata");
+ metadataTO.setOwner("Syncope4");
+
+ saml2SPMetadataConfService.update(metadataTO);
+ metadataTO = saml2SPMetadataService.read(metadataTO.getKey());
+ assertNotNull(metadataTO);
+ assertEquals("new-metadata", metadataTO.getMetadata());
+ assertEquals("Syncope4", metadataTO.getOwner());
+ }
+
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index 336b74f..0e5e122 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -44,7 +44,6 @@ import org.apache.syncope.common.lib.request.MembershipUR;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.request.AttrPatch;
-import org.apache.syncope.common.lib.search.UserPartialCondition;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.AnyTypeTO;
import org.apache.syncope.common.lib.to.PagedResult;
diff --git a/fit/wa-reference/src/main/resources/idp-metadata.xml b/fit/wa-reference/src/main/resources/idp-metadata.xml
new file mode 100644
index 0000000..2e0162b
--- /dev/null
+++ b/fit/wa-reference/src/main/resources/idp-metadata.xml
@@ -0,0 +1,144 @@
+<?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.
+ -->
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" entityID="https://syncope.apache.org/idp">
+ <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+ BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+ NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+ R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+ BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+ I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+ EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+ 9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+ HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+ L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+ sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+ K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
+ +Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+ 9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+ i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+ zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <KeyDescriptor use="encryption">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUKymtgciRE6pWwDfrsI58qL9pQMgwDQYJKoZIhvcNAQEL
+ BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+ NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKrT805sny3vlaYjTn
+ +m6m3VbUoStvnacgwHH+orNFhHoV1HP2ndoH5BsEDB9tQYuyRbGUm/nYVOSHayzB
+ G3bzMGdU7woO6rsCqpHuxUyEvojd/y/N/r9jMzeBOCu0KDBTrn3BJhnGSwSTfhOS
+ 3r20JFmDuTkHmabRs7ro0BvDaQ29jh38ro1iwB4E/4mqb1zYP13NI3ooErN/o6pl
+ XKpnFY37bDDOyOuocjN9tfPNIANNFKah0HjWOP0Nso0D1g6jHOSzmOw/Yxg61vBk
+ qOD4aKhLYPAxsXRl80nDrwTnm3/9xLQj9D3uLAtDLnn9pSqn3jCLxsxsHfKL/zkB
+ IKEBAgMBAAGjYzBhMB0GA1UdDgQWBBSrPjAgCJIHYmsofDcDIPzEhnYxmTBABgNV
+ HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+ L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAI8MlofbE0tbq8ez2d0Lq
+ Syhp4Q/shMEwjqcDarOwR+ACB9McOannUpAG7TCDp8Ch5E/V1B0Uo/5DF2tAzB1y
+ 7sgAmy2mY9/mFhMYpOqTCagufwewaMkn9n7ETzC/6vQEjYrjiNyNR0F3UQQz2bhe
+ ROM3YuKctuOnMthc+ZE7vn+AXCGumRHBhyCaYdzfeUh7id+yrd9B51+o3iF4eu6w
+ zJi5z7FMCS6I4PSc/uWYDw1ahzoPONjazWSEWGUibZaJYM3pJHkuwqyWKOFGVknH
+ J1Qv4WCfSPb6eva94TZX0lkLM01C7NZObnfxY3fvJGcyFl8wlRTUYvuqM8md5CEp
+ LA==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+
+ <!--
+ <ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
+ Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/ArtifactResolution" index="1"/>
+ -->
+
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SLO"/>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SLO" />
+
+ <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST-SimpleSign/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/ECP"/>
+ </IDPSSODescriptor>
+
+ <!--
+ <AttributeAuthorityDescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
++Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/AttributeQuery"/>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/AttributeQuery"/>
+ </AttributeAuthorityDescriptor>
+ -->
+
+ <!--
+ <Organization>
+ <OrganizationName xml:lang="en">Institution Name</OrganizationName>
+ <OrganizationDisplayName xml:lang="en">Institution DisplayName</OrganizationDisplayName>
+ <OrganizationURL xml:lang="en">URL</OrganizationURL>
+ </Organization>
+ <ContactPerson contactType="administrative">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="technical">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="support">
+ <GivenName>IT Services Support</GivenName>
+ <EmailAddress>support@example.org</EmailAddress>
+ </ContactPerson>
+ -->
+</EntityDescriptor>
diff --git a/pom.xml b/pom.xml
index 623d9f4..f1d5941 100644
--- a/pom.xml
+++ b/pom.xml
@@ -405,6 +405,7 @@ under the License.
<connid.servicenow.version>1.0.0</connid.servicenow.version>
<cxf.version>3.3.6</cxf.version>
+ <bouncycastle.version>1.65</bouncycastle.version>
<jackson.version>2.11.0</jackson.version>
@@ -1625,6 +1626,11 @@ under the License.
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
+ <artifactId>cas-server-support-pac4j-core-clients</artifactId>
+ <version>${cas.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-consent-webflow</artifactId>
<version>${cas.version}</version>
</dependency>
@@ -1984,7 +1990,12 @@ under the License.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
- <version>1.65</version>
+ <version>${bouncycastle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <version>${bouncycastle.version}</version>
</dependency>
<!-- TEST -->
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
index f4190b0..a93aa9d 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
@@ -80,6 +80,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
syncopeProps.setUrl(conf.getUrl());
casProperties.getAuthn().setSyncope(syncopeProps);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapStaticAuthModuleConf(
@@ -96,6 +97,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
staticProps.setUsers(users);
casProperties.getAuthn().setAccept(staticProps);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapLdapAuthModuleConf(
@@ -115,6 +117,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
ldapProps.setPrincipalAttributeList(conf.getPrincipalAttributeList());
casProperties.getAuthn().getLdap().add(ldapProps);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapGoogleMfaAuthModuleConf(
@@ -132,6 +135,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.setWindowSize(conf.getWindowSize());
casProperties.getAuthn().getMfa().setGauth(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapU2fAuthModuleConf(
@@ -148,6 +152,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.setExpireRegistrationsTimeUnit(TimeUnit.valueOf(conf.getExpireRegistrationsTimeUnit()));
casProperties.getAuthn().getMfa().setU2f(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapJaasAuthModuleConf(
@@ -165,6 +170,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.setRealm(conf.getRealm());
casProperties.getAuthn().getJaas().add(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapJdbcAuthModuleConf(
@@ -187,6 +193,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
casProperties.getAuthn().getJdbc().getQuery().add(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapOidcAuthModuleConf(
@@ -211,6 +218,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
client.setGeneric(props);
casProperties.getAuthn().getPac4j().getOidc().add(client);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapRadiusAuthModuleConf(
@@ -239,6 +247,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.getServer().setRetries(conf.getRetries());
casProperties.getAuthn().setRadius(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
private static void mapSaml2IdPAuthModuleConf(
@@ -258,13 +267,11 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
props.setIdentityProviderMetadataPath(conf.getIdentityProviderMetadataPath());
props.setKeystoreAlias(conf.getKeystoreAlias());
props.setKeystorePassword(conf.getKeystorePassword());
- props.setKeystorePath(conf.getKeystorePath());
props.setMaximumAuthenticationLifetime(conf.getMaximumAuthenticationLifetime());
props.setNameIdPolicyFormat(conf.getNameIdPolicyFormat());
props.setPrivateKeyPassword(conf.getPrivateKeyPassword());
props.setProviderName(conf.getProviderName());
props.setServiceProviderEntityId(conf.getServiceProviderEntityId());
- props.setServiceProviderMetadataPath(conf.getServiceProviderMetadataPath());
props.setSignatureAlgorithms(conf.getSignatureAlgorithms());
props.setSignatureCanonicalizationAlgorithm(conf.getSignatureCanonicalizationAlgorithm());
props.setSignatureReferenceDigestMethods(conf.getSignatureReferenceDigestMethods());
@@ -274,6 +281,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
: TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
casProperties.getAuthn().getPac4j().getSaml().add(props);
+ LOG.debug("Added {} to WA configuration", conf.getName());
}
@Override
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index 180672f..18d92b4 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -192,6 +192,10 @@ under the License.
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
+ <artifactId>cas-server-support-pac4j-core-clients</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-consent-webflow</artifactId>
</dependency>
<dependency>
@@ -240,6 +244,10 @@ under the License.
<dependency>
<groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
index 058274a..e82294c 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
@@ -18,11 +18,9 @@
*/
package org.apache.syncope.wa.starter;
-import org.apache.commons.lang.StringUtils;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.CasConfigurationPropertiesValidator;
-import org.apereo.cas.util.AsciiArtUtils;
-import org.apereo.cas.util.DateTimeUtils;
+
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
@@ -57,6 +55,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
+
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
@@ -96,17 +95,8 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
new SpringApplicationBuilder(SyncopeWAApplication.class).run(args);
}
- private static void advertiseReady(final ApplicationReadyEvent event) {
- AsciiArtUtils.printAsciiArtReady(LOG, StringUtils.EMPTY);
- LOG.info("Ready to process requests @ [{}]", DateTimeUtils.zonedDateTimeOf(event.getTimestamp()));
- }
-
private static void validateConfiguration(final ApplicationReadyEvent event) {
- if (!Boolean.getBoolean("SKIP_CONFIG_VALIDATION")) {
- CasConfigurationPropertiesValidator validator =
- new CasConfigurationPropertiesValidator(event.getApplicationContext());
- validator.validate();
- }
+ new CasConfigurationPropertiesValidator(event.getApplicationContext()).validate();
}
/**
@@ -118,7 +108,6 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
public void handleApplicationReadyEvent(final ApplicationReadyEvent event) {
validateConfiguration(event);
scheduleJobToRefreshContext();
- advertiseReady(event);
}
private void scheduleJobToRefreshContext() {
@@ -131,9 +120,8 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
JobDetail job = JobBuilder.newJob(SyncopeWARefreshContextJob.class).
withIdentity(jobKey).
build();
-
+ LOG.info("Scheduled job to refresh application context @ [{}]", date);
scheduler.getScheduler().scheduleJob(job, trigger);
-
} catch (final SchedulerException e) {
throw new RuntimeException("Could not schedule refresh job", e);
}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
index 62e2d7b..2a85826 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
@@ -18,9 +18,17 @@
*/
package org.apache.syncope.wa.starter;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
+import org.apereo.cas.configuration.CasConfigurationProperties;
+import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
+import org.apereo.cas.services.ServiceRegistryListener;
+import org.apereo.cas.support.pac4j.authentication.DelegatedClientFactoryCustomizer;
+import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator;
+import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGeneratorConfigurationContext;
+import org.apereo.cas.support.saml.idp.metadata.locator.SamlIdPMetadataLocator;
+import org.apereo.cas.support.saml.idp.metadata.writer.SamlIdPCertificateAndKeyWriter;
+import org.apereo.cas.util.crypto.CipherExecutor;
+
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
@@ -34,17 +42,10 @@ import org.apache.syncope.wa.starter.mapping.AuthMapper;
import org.apache.syncope.wa.starter.mapping.ClientAppMapFor;
import org.apache.syncope.wa.starter.mapping.ClientAppMapper;
import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
+import org.apache.syncope.wa.starter.pac4j.saml.SyncopeWASAML2ClientCustomizer;
import org.apache.syncope.wa.starter.saml.idp.metadata.RestfulSamlIdPMetadataGenerator;
import org.apache.syncope.wa.starter.saml.idp.metadata.RestfulSamlIdPMetadataLocator;
-import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
-import org.apereo.cas.configuration.CasConfigurationProperties;
-import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
-import org.apereo.cas.services.ServiceRegistryListener;
-import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator;
-import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGeneratorConfigurationContext;
-import org.apereo.cas.support.saml.idp.metadata.locator.SamlIdPMetadataLocator;
-import org.apereo.cas.support.saml.idp.metadata.writer.SamlIdPCertificateAndKeyWriter;
-import org.apereo.cas.util.crypto.CipherExecutor;
+import org.pac4j.core.client.Client;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -55,6 +56,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
@Configuration
public class SyncopeWAConfiguration {
@@ -114,19 +119,19 @@ public class SyncopeWAConfiguration {
});
return new RegisteredServiceMapper(
- authPolicyConfMappers,
- accessPolicyConfMappers,
- attrReleasePolicyConfMappers,
- clientAppTOMappers);
+ authPolicyConfMappers,
+ accessPolicyConfMappers,
+ attrReleasePolicyConfMappers,
+ clientAppTOMappers);
}
@Autowired
@Bean
public ServiceRegistryExecutionPlanConfigurer syncopeServiceRegistryConfigurer(
- final WARestClient restClient, final RegisteredServiceMapper registeredServiceMapper) {
+ final WARestClient restClient, final RegisteredServiceMapper registeredServiceMapper) {
SyncopeServiceRegistry registry = new SyncopeServiceRegistry(
- restClient, registeredServiceMapper, applicationContext, serviceRegistryListeners);
+ restClient, registeredServiceMapper, applicationContext, serviceRegistryListeners);
return plan -> plan.registerServiceRegistry(registry);
}
@@ -134,13 +139,13 @@ public class SyncopeWAConfiguration {
@Bean
public SamlIdPMetadataGenerator samlIdPMetadataGenerator(final WARestClient restClient) {
SamlIdPMetadataGeneratorConfigurationContext context =
- SamlIdPMetadataGeneratorConfigurationContext.builder().
- samlIdPMetadataLocator(samlIdPMetadataLocator(restClient)).
- samlIdPCertificateAndKeyWriter(samlSelfSignedCertificateWriter.getObject()).
- resourceLoader(resourceLoader).
- casProperties(casProperties).
- metadataCipherExecutor(CipherExecutor.noOpOfStringToString()).
- build();
+ SamlIdPMetadataGeneratorConfigurationContext.builder().
+ samlIdPMetadataLocator(samlIdPMetadataLocator(restClient)).
+ samlIdPCertificateAndKeyWriter(samlSelfSignedCertificateWriter.getObject()).
+ resourceLoader(resourceLoader).
+ casProperties(casProperties).
+ metadataCipherExecutor(CipherExecutor.noOpOfStringToString()).
+ build();
return new RestfulSamlIdPMetadataGenerator(context, restClient);
}
@@ -156,6 +161,12 @@ public class SyncopeWAConfiguration {
return plan -> plan.registerAuditTrailManager(new SyncopeWAAuditTrailManager(restClient));
}
+ @Autowired
+ @Bean
+ public DelegatedClientFactoryCustomizer<Client> delegatedClientCustomizer(final WARestClient restClient) {
+ return new SyncopeWASAML2ClientCustomizer(restClient);
+ }
+
@Bean
public KeymasterStart keymasterStart() {
return new KeymasterStart(NetworkService.Type.WA);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWARefreshContextJob.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWARefreshContextJob.java
index 9d5a1cc..8f4edf7 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWARefreshContextJob.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWARefreshContextJob.java
@@ -19,8 +19,11 @@
package org.apache.syncope.wa.starter;
import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apache.commons.lang.StringUtils;
import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator;
import org.apereo.cas.support.saml.services.idp.metadata.SamlIdPMetadataDocument;
+import org.apereo.cas.util.AsciiArtUtils;
+
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
@@ -46,19 +49,27 @@ public class SyncopeWARefreshContextJob implements Job {
@Override
public void execute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
- LOG.debug("Refreshing WA application context");
+ LOG.debug("Attempting to refresh WA application context");
if (!WARestClient.isReady()) {
LOG.debug("Syncope client is not yet ready");
throw new RuntimeException("Syncope core is not yet ready to access requests");
}
contextRefresher.refresh();
+ LOG.info("Refreshed application context to bootstrap property sources, etc...");
LOG.info("Generating SAML2 IdP metadata metadata");
SamlIdPMetadataDocument document = metadataGenerator.generate(Optional.empty());
LOG.info("Generated SAML2 IdP metadata for {}", document.getAppliesTo());
+ advertiseReady();
+
} catch (RuntimeException e) {
throw new JobExecutionException("While generating SAML2 IdP metadata", e);
}
}
+
+ private static void advertiseReady() {
+ AsciiArtUtils.printAsciiArtReady(LOG, StringUtils.EMPTY);
+ LOG.info("Ready to process requests");
+ }
}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
new file mode 100644
index 0000000..0dfc403
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
@@ -0,0 +1,50 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apereo.cas.support.pac4j.authentication.DelegatedClientFactoryCustomizer;
+
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.pac4j.core.client.Client;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.config.SAML2Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SyncopeWASAML2ClientCustomizer implements DelegatedClientFactoryCustomizer<Client> {
+ private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientCustomizer.class);
+
+ private final WARestClient restClient;
+
+ public SyncopeWASAML2ClientCustomizer(final WARestClient restClient) {
+ this.restClient = restClient;
+ }
+
+ @Override
+ public void customize(final Client client) {
+ if (client instanceof SAML2Client) {
+ LOG.debug("Customizing SAML2 client {}", client.getName());
+ final SAML2Client saml2Client = (SAML2Client) client;
+ SAML2Configuration configuration = saml2Client.getConfiguration();
+ configuration.setKeystoreGenerator(new SyncopeWASAML2ClientKeystoreGenerator(restClient, saml2Client));
+ configuration.setMetadataGenerator(new SyncopeWASAML2ClientMetadataGenerator(restClient, saml2Client));
+ }
+ }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java
new file mode 100644
index 0000000..77f84f6
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java
@@ -0,0 +1,109 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPKeystoreService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.metadata.keystore.BaseSAML2KeystoreGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Response;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+
+public class SyncopeWASAML2ClientKeystoreGenerator extends BaseSAML2KeystoreGenerator {
+ private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientKeystoreGenerator.class);
+
+ private final WARestClient restClient;
+
+ private final SAML2Client saml2Client;
+
+ SyncopeWASAML2ClientKeystoreGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
+ super(saml2Client.getConfiguration());
+ this.restClient = restClient;
+ this.saml2Client = saml2Client;
+ }
+
+ @Override
+ public boolean shouldGenerate() {
+ return true;
+ }
+
+ @Override
+ protected void store(final KeyStore ks, final X509Certificate certificate,
+ final PrivateKey privateKey) throws Exception {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ char[] password = saml2Configuration.getKeystorePassword().toCharArray();
+ ks.store(out, password);
+ out.flush();
+ String encodedKeystore = Base64.getEncoder().encodeToString(out.toByteArray());
+ LOG.debug("Encoded keystore {}", encodedKeystore);
+
+ SAML2SPKeystoreService keystoreService = restClient.getSyncopeClient().
+ getService(SAML2SPKeystoreService.class);
+
+ SAML2SPKeystoreTO keystoreTO = new SAML2SPKeystoreTO.Builder().
+ keystore(encodedKeystore).
+ owner(saml2Client.getName()).
+ build();
+
+ LOG.debug("Storing keystore {}", keystoreTO);
+ Response response = keystoreService.set(keystoreTO);
+ if (response.getStatus() == Response.Status.CREATED.getStatusCode()) {
+ LOG.info("Stored keystore for SAML2 SP {}", saml2Client.getName());
+ } else if (response.getStatus() == Response.Status.CONFLICT.getStatusCode()) {
+ LOG.info("Stored keystore for SAML2 SP {} already exists", saml2Client.getName());
+ } else {
+ LOG.error("Unexpected response when storing SAML2 SP keystore: {}\n{}",
+ response.getStatus(), response.getHeaders());
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+ sce.getElements().add("Unexpected response when storing SAML2 SP keystore");
+ throw sce;
+ }
+ }
+ }
+
+ @Override
+ public InputStream retrieve() throws Exception {
+ try {
+ SAML2SPKeystoreService keystoreService = restClient.getSyncopeClient().
+ getService(SAML2SPKeystoreService.class);
+ SAML2SPKeystoreTO keystoreTO = keystoreService.getByOwner(saml2Client.getName());
+ LOG.debug("Retrieved keystore {}", keystoreTO);
+ byte[] decode = Base64.getDecoder().decode(keystoreTO.getKeystore());
+ return new ByteArrayInputStream(decode);
+ } catch (final Exception e) {
+ final String message = "Unable to fetch SAML2 SP keystore for " + saml2Client.getName();
+ LOG.error(message, e);
+ throw new Exception(message);
+ }
+ }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java
new file mode 100644
index 0000000..9ba283c
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java
@@ -0,0 +1,77 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.opensaml.saml.metadata.resolver.impl.AbstractBatchMetadataResolver;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.metadata.BaseSAML2MetadataGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+
+import javax.ws.rs.core.Response;
+
+public class SyncopeWASAML2ClientMetadataGenerator extends BaseSAML2MetadataGenerator {
+ private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientMetadataGenerator.class);
+
+ private final WARestClient restClient;
+
+ private final SAML2Client saml2Client;
+
+ SyncopeWASAML2ClientMetadataGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
+ this.restClient = restClient;
+ this.saml2Client = saml2Client;
+ }
+
+ @Override
+ protected AbstractBatchMetadataResolver createMetadataResolver(final Resource metadataResource) {
+ return new SyncopeWASAML2MetadataResolver(restClient, saml2Client);
+ }
+
+ @Override
+ public boolean storeMetadata(final String metadata, final Resource resource, final boolean force) throws Exception {
+ SAML2SPMetadataService metadataService = restClient.getSyncopeClient().
+ getService(SAML2SPMetadataService.class);
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO.Builder().
+ metadata(metadata).
+ owner(saml2Client.getName()).
+ build();
+ LOG.debug("Storing metadata {}", metadataTO);
+ Response response = metadataService.set(metadataTO);
+ if (response.getStatus() == Response.Status.CREATED.getStatusCode()) {
+ LOG.info("Stored metadata for SAML2 SP {}", saml2Client.getName());
+ return true;
+ }
+ if (response.getStatus() == Response.Status.CONFLICT.getStatusCode()) {
+ LOG.info("Stored metadata for SAML2 SP {} already exists", saml2Client.getName());
+ return true;
+ }
+ LOG.error("Unexpected response when storing SAML2 SP metadata: {}\n{}",
+ response.getStatus(), response.getHeaders());
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+ sce.getElements().add("Unexpected response when storing SAML2 SP metadata");
+ throw sce;
+ }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.java
new file mode 100644
index 0000000..da71d2a
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.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.wa.starter.pac4j.saml;
+
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.opensaml.saml.metadata.resolver.impl.AbstractReloadingMetadataResolver;
+import org.pac4j.saml.client.SAML2Client;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+
+public class SyncopeWASAML2MetadataResolver extends AbstractReloadingMetadataResolver {
+ private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2MetadataResolver.class);
+
+ private final WARestClient restClient;
+
+ private final SAML2Client saml2Client;
+
+ SyncopeWASAML2MetadataResolver(final WARestClient restClient, final SAML2Client saml2Client) {
+ this.restClient = restClient;
+ this.saml2Client = saml2Client;
+ }
+
+ @Override
+ protected String getMetadataIdentifier() {
+ return saml2Client.getName();
+ }
+
+ @Override
+ protected byte[] fetchMetadata() throws ResolverException {
+ try {
+ SAML2SPMetadataService metadataService = restClient.getSyncopeClient().
+ getService(SAML2SPMetadataService.class);
+ SAML2SPMetadataTO metadataTO = metadataService.getByOwner(saml2Client.getName());
+ return metadataTO.getMetadata().getBytes(StandardCharsets.UTF_8);
+ } catch (final Exception e) {
+ final String message = "Unable to fetch SP metadata for " + saml2Client.getName();
+ LOG.error(message, e);
+ throw new ResolverException(message);
+ }
+ }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataGenerator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataGenerator.java
index ceeff87..dd33e68 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataGenerator.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataGenerator.java
@@ -21,7 +21,7 @@ package org.apache.syncope.wa.starter.saml.idp.metadata;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2IdPMetadataService;
import org.apache.syncope.wa.bootstrap.WARestClient;
import org.apereo.cas.support.saml.idp.metadata.generator.BaseSamlIdPMetadataGenerator;
import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGeneratorConfigurationContext;
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
index d2a1634..eb2c220 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
@@ -22,7 +22,7 @@ import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.common.rest.api.service.SAML2IdPMetadataService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2IdPMetadataService;
import org.apache.syncope.wa.bootstrap.WARestClient;
import org.apereo.cas.support.saml.idp.metadata.locator.AbstractSamlIdPMetadataLocator;
import org.apereo.cas.support.saml.services.SamlRegisteredService;
@@ -59,7 +59,7 @@ public class RestfulSamlIdPMetadataLocator extends AbstractSamlIdPMetadataLocato
try {
LOG.info("Locating SAML2 IdP metadata document");
SAML2IdPMetadataTO saml2IdPMetadataTO = getSyncopeClient().getService(SAML2IdPMetadataService.class).
- get(getAppliesToFor(registeredService));
+ getByOwner(getAppliesToFor(registeredService));
if (saml2IdPMetadataTO == null) {
LOG.warn("No SAML2 IdP metadata document obtained from core");
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index c98a6e7..8a344e6 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -21,7 +21,7 @@ useGZIPCompression=true
# Conf directories
conf.directory=${conf.directory}
-cas.standalone.configurationDirectory=${conf.directory}
+cas.standalone.configuration-directory=${conf.directory}
cas.authn.oidc.jwks.jwksFile=file:${conf.directory}/oidc.keystore
cas.server.name=http://localhost:8080
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
new file mode 100644
index 0000000..253b6f8
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.TBSCertificate;
+import org.bouncycastle.asn1.x509.Time;
+import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.config.SAML2Configuration;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+import java.util.Date;
+
+public abstract class BaseSyncopeWASAML2ClientTest {
+
+ protected static Certificate createSelfSignedCert(final KeyPair keyPair) throws Exception {
+ final X500Name dn = new X500Name("cn=Unknown");
+ final V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
+
+ certGen.setSerialNumber(new ASN1Integer(BigInteger.valueOf(1)));
+ certGen.setIssuer(dn);
+ certGen.setSubject(dn);
+ certGen.setStartDate(new Time(new Date(System.currentTimeMillis() - 1000L)));
+
+ final Date expiration = new Date(System.currentTimeMillis() + 100000);
+ certGen.setEndDate(new Time(expiration));
+
+ final AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE);
+ certGen.setSignature(sigAlgID);
+ certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+
+ final Signature sig = Signature.getInstance("SHA1WithRSA");
+ sig.initSign(keyPair.getPrivate());
+ sig.update(certGen.generateTBSCertificate().getEncoded(ASN1Encoding.DER));
+
+ final TBSCertificate tbsCert = certGen.generateTBSCertificate();
+ final ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(tbsCert);
+ v.add(sigAlgID);
+ v.add(new DERBitString(sig.sign()));
+
+ final Certificate cert = CertificateFactory.getInstance("X.509")
+ .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
+ cert.verify(keyPair.getPublic());
+ return cert;
+ }
+
+ protected static SAML2Client getSAML2Client() {
+ SAML2Configuration saml2Configuration = new SAML2Configuration();
+ saml2Configuration.setKeystorePassword("password");
+ saml2Configuration.setPrivateKeyPassword("password");
+ saml2Configuration.setKeystoreAlias("Syncope");
+ saml2Configuration.setIdentityProviderMetadataResource(new ClassPathResource("idp-metadata.xml"));
+ SAML2Client client = new SAML2Client(saml2Configuration);
+ client.setCallbackUrl("https://syncope.apache.org");
+ return client;
+ }
+
+ protected static KeyStore getKeystore() throws Exception {
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ char[] pwdArray = "password".toCharArray();
+ ks.load(null, pwdArray);
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(4096);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ Certificate certificate = createSelfSignedCert(keyPair);
+ ks.setKeyEntry("Syncope", keyPair.getPrivate(), "password".toCharArray(), new Certificate[]{certificate});
+ return ks;
+ }
+
+ protected static String getKeystoreAsString() throws Exception {
+ char[] pwdArray = "password".toCharArray();
+ try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
+ getKeystore().store(fos, pwdArray);
+ fos.flush();
+ return Base64.getEncoder().encodeToString(fos.toByteArray());
+ }
+ }
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java
new file mode 100644
index 0000000..f752fdd
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPKeystoreService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.junit.jupiter.api.Test;
+import org.pac4j.saml.client.SAML2Client;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.ws.rs.core.Response;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SyncopeWASAML2ClientCustomizerTest extends BaseSyncopeWASAML2ClientTest {
+
+ @Test
+ public void customize() throws Exception {
+
+ SAML2SPKeystoreTO keystoreTO = new SAML2SPKeystoreTO.Builder()
+ .keystore(getKeystoreAsString())
+ .owner("CAS")
+ .build();
+ SAML2SPKeystoreService saml2SPKeystoreService = mock(SAML2SPKeystoreService.class);
+ when(saml2SPKeystoreService.getByOwner(anyString())).thenReturn(keystoreTO);
+ when(saml2SPKeystoreService.set(any())).thenReturn(Response.created(new URI("http://localhost:9080/syncop-wa")).build());
+
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO.Builder()
+ .owner("Syncope")
+ .metadata(IOUtils.toString(new ClassPathResource("sp-metadata.xml").getInputStream(), StandardCharsets.UTF_8))
+ .build();
+
+ SAML2SPMetadataService saml2SPMetadataService = mock(SAML2SPMetadataService.class);
+ when(saml2SPMetadataService.getByOwner(anyString())).thenReturn(metadataTO);
+ when(saml2SPMetadataService.set(any())).thenReturn(Response.created(new URI("http://localhost:9080/syncop-wa")).build());
+
+ WARestClient restClient = mock(WARestClient.class);
+
+ SyncopeClient syncopeClient = mock(SyncopeClient.class);
+ when(syncopeClient.getService(SAML2SPKeystoreService.class)).thenReturn(saml2SPKeystoreService);
+ when(syncopeClient.getService(SAML2SPMetadataService.class)).thenReturn(saml2SPMetadataService);
+ when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+
+ SyncopeWASAML2ClientCustomizer customizer = new SyncopeWASAML2ClientCustomizer(restClient);
+ SAML2Client client = getSAML2Client();
+ customizer.customize(client);
+ client.init();
+ assertTrue(client.getConfiguration().getKeystoreGenerator() instanceof SyncopeWASAML2ClientKeystoreGenerator);
+ assertTrue(client.getConfiguration().toMetadataGenerator() instanceof SyncopeWASAML2ClientMetadataGenerator);
+ }
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java
new file mode 100644
index 0000000..8c50d22
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+
+import org.apache.syncope.common.lib.to.SAML2SPKeystoreTO;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPKeystoreService;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.pac4j.saml.exceptions.SAMLException;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.metadata.keystore.SAML2KeystoreGenerator;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.ws.rs.core.Response;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SyncopeWASAML2ClientKeystoreGeneratorTest extends BaseSyncopeWASAML2ClientTest {
+ private static WARestClient getWaRestClient(final Response response) throws Exception {
+ WARestClient restClient = mock(WARestClient.class);
+
+ SAML2SPKeystoreTO keystoreTO = new SAML2SPKeystoreTO.Builder()
+ .keystore(getKeystoreAsString())
+ .owner("CAS")
+ .build();
+ SAML2SPKeystoreService saml2SPKeystoreService = mock(SAML2SPKeystoreService.class);
+ when(saml2SPKeystoreService.getByOwner(anyString())).thenReturn(keystoreTO);
+ when(saml2SPKeystoreService.set(any())).thenReturn(response);
+
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO.Builder()
+ .owner("Syncope")
+ .metadata(IOUtils.toString(new ClassPathResource("sp-metadata.xml").getInputStream(), StandardCharsets.UTF_8))
+ .build();
+
+ SAML2SPMetadataService saml2SPMetadataService = mock(SAML2SPMetadataService.class);
+ when(saml2SPMetadataService.getByOwner(anyString())).thenReturn(metadataTO);
+ when(saml2SPMetadataService.set(any())).thenReturn(response);
+
+ SyncopeClient syncopeClient = mock(SyncopeClient.class);
+
+ when(syncopeClient.getService(SAML2SPMetadataService.class)).thenReturn(saml2SPMetadataService);
+ when(syncopeClient.getService(SAML2SPKeystoreService.class)).thenReturn(saml2SPKeystoreService);
+
+ when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+ return restClient;
+ }
+
+ @Test
+ public void generate() throws Exception {
+ SAML2Client client = getSAML2Client();
+ SAML2KeystoreGenerator generator1 = new SyncopeWASAML2ClientKeystoreGenerator(
+ getWaRestClient(Response.created(new URI("http://localhost:9080/syncop-wa")).build()), client);
+ assertDoesNotThrow(new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ generator1.generate();
+ }
+ });
+ SAML2KeystoreGenerator generator2 = new SyncopeWASAML2ClientKeystoreGenerator(getWaRestClient(Response.serverError().build()), client);
+ assertThrows(SAMLException.class, () -> generator2.generate());
+ }
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java
new file mode 100644
index 0000000..11d01ae
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.junit.jupiter.api.Test;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.pac4j.saml.client.SAML2Client;
+import org.pac4j.saml.metadata.SAML2MetadataGenerator;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.ws.rs.core.Response;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SyncopeWASAML2ClientMetadataGeneratorTest extends BaseSyncopeWASAML2ClientTest {
+ private static WARestClient getWaRestClient(final Response response) throws IOException {
+ WARestClient restClient = mock(WARestClient.class);
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO.Builder()
+ .owner("Syncope")
+ .metadata(IOUtils.toString(new ClassPathResource("sp-metadata.xml").getInputStream(), StandardCharsets.UTF_8))
+ .build();
+
+ SAML2SPMetadataService saml2SPMetadataService = mock(SAML2SPMetadataService.class);
+ when(saml2SPMetadataService.getByOwner(anyString())).thenReturn(metadataTO);
+ when(saml2SPMetadataService.set(any())).thenReturn(response);
+
+ SyncopeClient syncopeClient = mock(SyncopeClient.class);
+ when(syncopeClient.getService(SAML2SPMetadataService.class)).thenReturn(saml2SPMetadataService);
+ when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+ return restClient;
+ }
+
+ @Test
+ public void storeMetadata() throws Exception {
+ SAML2Client client = getSAML2Client();
+ String keystoreFile = File.createTempFile("keystore", "jks").getCanonicalPath();
+ client.getConfiguration().setKeystoreResourceFilepath(keystoreFile);
+
+ SAML2MetadataGenerator generator = new SyncopeWASAML2ClientMetadataGenerator(
+ getWaRestClient(Response.created(new URI("http://localhost:9080/syncop-wa")).build()), client);
+ EntityDescriptor entityDescriptor = generator.buildEntityDescriptor();
+ String metadata = generator.getMetadata(entityDescriptor);
+ assertNotNull(generator.storeMetadata(metadata, null, false));
+ }
+
+ @Test
+ public void storeMetadataFails() throws Exception {
+ SAML2Client client = getSAML2Client();
+ String keystoreFile = File.createTempFile("keystore", "jks").getCanonicalPath();
+ client.getConfiguration().setKeystoreResourceFilepath(keystoreFile);
+ WARestClient restClient = getWaRestClient(Response.serverError().build());
+ SAML2MetadataGenerator generator = new SyncopeWASAML2ClientMetadataGenerator(restClient, client);
+ EntityDescriptor entityDescriptor = generator.buildEntityDescriptor();
+ String metadata = generator.getMetadata(entityDescriptor);
+ assertThrows(SyncopeClientException.class, () -> generator.storeMetadata(metadata, null, false));
+ }
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java
new file mode 100644
index 0000000..b7a630f
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.wa.starter.pac4j.saml;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.to.SAML2SPMetadataTO;
+import org.apache.syncope.common.rest.api.service.wa.SAML2SPMetadataService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.junit.jupiter.api.Test;
+import org.pac4j.saml.client.SAML2Client;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.ws.rs.core.Response;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SyncopeWASAML2MetadataResolverTest extends BaseSyncopeWASAML2ClientTest {
+ @Test
+ public void fetchMetadata() throws Exception {
+ SAML2Client client = getSAML2Client();
+ String keystoreFile = File.createTempFile("keystore", "jks").getCanonicalPath();
+ client.getConfiguration().setKeystoreResourceFilepath(keystoreFile);
+ WARestClient restClient = mock(WARestClient.class);
+
+ SAML2SPMetadataTO metadataTO = new SAML2SPMetadataTO.Builder()
+ .owner("Syncope")
+ .metadata(IOUtils.toString(new ClassPathResource("sp-metadata.xml").getInputStream(), StandardCharsets.UTF_8))
+ .build();
+
+ SAML2SPMetadataService saml2SPMetadataService = mock(SAML2SPMetadataService.class);
+ when(saml2SPMetadataService.getByOwner(anyString())).thenReturn(metadataTO);
+ when(saml2SPMetadataService.set(any())).thenReturn(Response.created(new URI("http://localhost:9080/syncop-wa")).build());
+
+ SyncopeClient syncopeClient = mock(SyncopeClient.class);
+ when(syncopeClient.getService(SAML2SPMetadataService.class)).thenReturn(saml2SPMetadataService);
+ when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+
+ SyncopeWASAML2MetadataResolver resolver = new SyncopeWASAML2MetadataResolver(restClient, client);
+ assertNotNull(resolver.fetchMetadata());
+ }
+}
diff --git a/wa/starter/src/test/resources/idp-metadata.xml b/wa/starter/src/test/resources/idp-metadata.xml
new file mode 100644
index 0000000..6e8f2f9
--- /dev/null
+++ b/wa/starter/src/test/resources/idp-metadata.xml
@@ -0,0 +1,144 @@
+<?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.
+-->
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" entityID="https://syncope.apache.org/idp">
+ <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
++Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <KeyDescriptor use="encryption">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUKymtgciRE6pWwDfrsI58qL9pQMgwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKrT805sny3vlaYjTn
++m6m3VbUoStvnacgwHH+orNFhHoV1HP2ndoH5BsEDB9tQYuyRbGUm/nYVOSHayzB
+G3bzMGdU7woO6rsCqpHuxUyEvojd/y/N/r9jMzeBOCu0KDBTrn3BJhnGSwSTfhOS
+3r20JFmDuTkHmabRs7ro0BvDaQ29jh38ro1iwB4E/4mqb1zYP13NI3ooErN/o6pl
+XKpnFY37bDDOyOuocjN9tfPNIANNFKah0HjWOP0Nso0D1g6jHOSzmOw/Yxg61vBk
+qOD4aKhLYPAxsXRl80nDrwTnm3/9xLQj9D3uLAtDLnn9pSqn3jCLxsxsHfKL/zkB
+IKEBAgMBAAGjYzBhMB0GA1UdDgQWBBSrPjAgCJIHYmsofDcDIPzEhnYxmTBABgNV
+HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAI8MlofbE0tbq8ez2d0Lq
+Syhp4Q/shMEwjqcDarOwR+ACB9McOannUpAG7TCDp8Ch5E/V1B0Uo/5DF2tAzB1y
+7sgAmy2mY9/mFhMYpOqTCagufwewaMkn9n7ETzC/6vQEjYrjiNyNR0F3UQQz2bhe
+ROM3YuKctuOnMthc+ZE7vn+AXCGumRHBhyCaYdzfeUh7id+yrd9B51+o3iF4eu6w
+zJi5z7FMCS6I4PSc/uWYDw1ahzoPONjazWSEWGUibZaJYM3pJHkuwqyWKOFGVknH
+J1Qv4WCfSPb6eva94TZX0lkLM01C7NZObnfxY3fvJGcyFl8wlRTUYvuqM8md5CEp
+LA==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+
+ <!--
+ <ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
+ Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/ArtifactResolution" index="1"/>
+ -->
+
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SLO"/>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SLO" />
+
+ <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="https://syncope.apache.org/cas/idp/profile/SAML2/POST-SimpleSign/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://syncope.apache.org/cas/idp/profile/SAML2/Redirect/SSO"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/ECP"/>
+ </IDPSSODescriptor>
+
+ <!--
+ <AttributeAuthorityDescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+ <Extensions>
+ <shibmd:Scope regexp="false">example.net</shibmd:Scope>
+ </Extensions>
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo>
+ <ds:X509Data>
+ <ds:X509Certificate>MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3
+NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u
+ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg
+R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p
+BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2
+I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs
+EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n
+ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S
+9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV
+HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0
+L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh
+sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n
+K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL
++Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde
+9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e
+i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF
+zg==</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://syncope.apache.org/cas/idp/profile/SAML1/SOAP/AttributeQuery"/>
+ <AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://syncope.apache.org/cas/idp/profile/SAML2/SOAP/AttributeQuery"/>
+ </AttributeAuthorityDescriptor>
+ -->
+
+ <!--
+ <Organization>
+ <OrganizationName xml:lang="en">Institution Name</OrganizationName>
+ <OrganizationDisplayName xml:lang="en">Institution DisplayName</OrganizationDisplayName>
+ <OrganizationURL xml:lang="en">URL</OrganizationURL>
+ </Organization>
+ <ContactPerson contactType="administrative">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="technical">
+ <GivenName>John Smith</GivenName>
+ <EmailAddress>jsmith@example.org</EmailAddress>
+ </ContactPerson>
+ <ContactPerson contactType="support">
+ <GivenName>IT Services Support</GivenName>
+ <EmailAddress>support@example.org</EmailAddress>
+ </ContactPerson>
+ -->
+</EntityDescriptor>
diff --git a/wa/starter/src/test/resources/sp-metadata.xml b/wa/starter/src/test/resources/sp-metadata.xml
new file mode 100644
index 0000000..351d556
--- /dev/null
+++ b/wa/starter/src/test/resources/sp-metadata.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="_lipmbmwyb3ujt0osvfect39lzglxnsdi7bxhvf5" entityID="urn:mace:saml:pac4j.org" validUntil="2038-08-10T19:43:10.846Z">
+ <md:Extensions xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport">
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha224"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+ <alg:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
+ </md:Extensions>
+ <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.0:protocol urn:oasis:names:tc:SAML:1.1:protocol">
+ <md:Extensions xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init">
+ <init:RequestInitiator Binding="urn:oasis:names:tc:SAML:profiles:SSO:request-init" Location="https://mmoayyed.unicon.net:8443/cas/login?client_name=SAML2Client"/>
+ </md:Extensions>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mmoayyed.unicon.net:8443/cas/login?client_name=SAML2Client" index="0"/>
+ </md:SPSSODescriptor>
+</md:EntityDescriptor>