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 d96d08f SYNCOPE-1580: Manage WA Configuration Properties/Schema (#207)
d96d08f is described below
commit d96d08f128c98fb4b6ccc4aec6c23e4cf83fbcc3
Author: Misagh Moayyed <[email protected]>
AuthorDate: Mon Jul 27 16:55:09 2020 +0400
SYNCOPE-1580: Manage WA Configuration Properties/Schema (#207)
---
.../syncope/common/lib/types/AMEntitlement.java | 12 +
.../rest/api/service/wa/WAConfigService.java | 101 ++++++++
.../apache/syncope/core/logic/WAConfigLogic.java | 161 +++++++++++++
.../rest/cxf/service/wa/WAConfigServiceImpl.java | 74 ++++++
.../core/persistence/api/dao/auth/WAConfigDAO.java | 40 ++++
.../persistence/api/entity/auth/WAConfigEntry.java | 30 +++
.../persistence/jpa/dao/auth/JPAWAConfigDAO.java | 74 ++++++
.../persistence/jpa/entity/JPAEntityFactory.java | 4 +
.../jpa/entity/auth/JPAWAConfigEntry.java | 54 +++++
.../core/persistence/jpa/inner/WAConfigTest.java | 100 ++++++++
.../provisioning/api/data/WAConfigDataBinder.java | 31 +++
.../java/data/WAConfigDataBinderImpl.java | 61 +++++
.../org/apache/syncope/fit/AbstractITCase.java | 4 +
.../apache/syncope/fit/core/WAConfigITCase.java | 74 ++++++
.../bootstrap/SyncopeWAPropertySourceLocator.java | 261 ++++++++++++++-------
.../wa/starter/SyncopeCoreTestingServer.java | 50 ++--
16 files changed, 1022 insertions(+), 109 deletions(-)
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 d32468b..2be85c9 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
@@ -118,6 +118,18 @@ public final class AMEntitlement {
public static final String U2F_UPDATE_DEVICE = "U2F_UPDATE_DEVICE";
+ public static final String WA_CONFIG_LIST = "WA_CONFIG_LIST";
+
+ public static final String WA_CONFIG_UPDATE = "WA_CONFIG_UPDATE";
+
+ public static final String WA_CONFIG_DELETE = "WA_CONFIG_DELETE";
+
+ public static final String WA_CONFIG_READ = "WA_CONFIG_READ";
+
+ public static final String WA_CONFIG_CREATE = "WA_CONFIG_CREATE";
+
+ public static final String WA_CONFIG_PUSH = "WA_CONFIG_PUSH";
+
private static final Set<String> VALUES;
static {
diff --git
a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAConfigService.java
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAConfigService.java
new file mode 100644
index 0000000..82fa78c
--- /dev/null
+++
b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAConfigService.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wa;
+
+import io.swagger.v3.oas.annotations.headers.Header;
+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.Attr;
+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.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.util.List;
+
+/**
+ * REST operations for WA Configuration.
+ */
+@Tag(name = "WA")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer")})
+@Path("wa/config")
+public interface WAConfigService extends JAXRSService {
+
+ @GET
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ List<Attr> list();
+
+ @GET
+ @Path("{key}")
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ Attr read(@NotNull @PathParam("key") String key);
+
+ @ApiResponses({
+ @ApiResponse(responseCode = "201",
+ description = "WAConfigTO 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 = "Config already existing")})
+ @POST
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ Response create(@NotNull Attr configTO);
+
+ @PUT
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ void update(Attr configTO);
+
+ @DELETE
+ @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML})
+ @Path("{key}")
+ void delete(@NotNull @PathParam("key") String key);
+
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was
successful"))
+ @POST
+ @Path("push")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML,
MediaType.APPLICATION_XML })
+ void pushToWA();
+}
diff --git
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/WAConfigLogic.java
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/WAConfigLogic.java
new file mode 100644
index 0000000..f3158de
--- /dev/null
+++
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/WAConfigLogic.java
@@ -0,0 +1,161 @@
+/*
+ * 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.commons.lang3.StringUtils;
+import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
+import org.apache.syncope.common.keymaster.client.api.KeymasterException;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+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.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
+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 javax.annotation.Resource;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.core.HttpHeaders;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Component
+public class WAConfigLogic extends AbstractTransactionalLogic<EntityTO> {
+ @Autowired
+ private ServiceOps serviceOps;
+
+ @Autowired
+ private WAConfigDataBinder binder;
+
+ @Autowired
+ private WAConfigDAO configDAO;
+
+ @Resource(name = "anonymousUser")
+ private String anonymousUser;
+
+ @Resource(name = "anonymousKey")
+ private String anonymousKey;
+
+ @Override
+ protected EntityTO resolveReference(final Method method, final Object...
args)
+ throws UnresolvedReferenceException {
+ String key = null;
+ if (ArrayUtils.isNotEmpty(args)) {
+ for (int i = 0; key == null && i < args.length; i++) {
+ if (args[i] instanceof String) {
+ key = (String) args[i];
+ } else if (args[i] instanceof Attr) {
+ key = ((Attr) args[i]).getSchema();
+ }
+ }
+ }
+
+ if (key != null) {
+ try {
+ Attr attr = binder.getAttr(configDAO.find(key));
+ return new EntityTO() {
+ private static final long serialVersionUID =
-2683326649597260323L;
+ @Override
+ public String getKey() {
+ return attr.getSchema();
+ }
+
+ @Override
+ public void setKey(final String key) {
+ }
+ };
+ } catch (final Throwable e) {
+ LOG.debug("Unresolved reference", e);
+ throw new UnresolvedReferenceException(e);
+ }
+ }
+
+ throw new UnresolvedReferenceException();
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_LIST + "') or
hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public List<Attr> list() {
+ return
configDAO.findAll().stream().map(binder::getAttr).collect(Collectors.toList());
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_UPDATE + "')")
+ public void update(final Attr configTO) {
+ WAConfigEntry entry = configDAO.find(configTO.getSchema());
+ if (entry == null) {
+ throw new NotFoundException("Configuration entry " +
configTO.getSchema() + " not found");
+ }
+ binder.update(entry, configTO);
+ configDAO.save(entry);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_DELETE + "')")
+ public void delete(final String key) {
+ configDAO.delete(key);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_READ + "') or
hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public Attr read(final String key) {
+ WAConfigEntry entry = configDAO.find(key);
+ if (entry == null) {
+ throw new NotFoundException("Configuration entry " + key + " not
found");
+ }
+ return binder.getAttr(entry);
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_CREATE + "')")
+ public Attr create(final Attr configTO) {
+ return binder.getAttr(configDAO.save(binder.create(configTO)));
+ }
+
+ @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_PUSH + "')")
+ public void pushToWA() {
+ try {
+ NetworkService wa = serviceOps.get(NetworkService.Type.WA);
+ HttpClient.newBuilder().build().send(
+ HttpRequest.newBuilder(URI.create(
+ StringUtils.appendIfMissing(wa.getAddress(), "/") +
"actuator/refresh")).
+ header(HttpHeaders.AUTHORIZATION,
+
DefaultBasicAuthSupplier.getBasicAuthHeader(anonymousUser, anonymousKey)).
+ POST(HttpRequest.BodyPublishers.noBody()).build(),
+ HttpResponse.BodyHandlers.discarding());
+ } catch (KeymasterException e) {
+ throw new NotFoundException("Could not find any WA instance", e);
+ } catch (IOException | InterruptedException e) {
+ throw new InternalServerErrorException("Errors while communicating
with WA instance", e);
+ }
+ }
+}
diff --git
a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceImpl.java
b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceImpl.java
new file mode 100644
index 0000000..d8ad501
--- /dev/null
+++
b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceImpl.java
@@ -0,0 +1,74 @@
+/*
+ * 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.wa;
+
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
+import org.apache.syncope.core.logic.WAConfigLogic;
+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;
+import java.util.List;
+
+@Service
+public class WAConfigServiceImpl extends AbstractServiceImpl implements
WAConfigService {
+ @Autowired
+ private WAConfigLogic logic;
+
+ @Override
+ public List<Attr> list() {
+ return logic.list();
+ }
+
+ @Override
+ public Attr read(final String key) {
+ return logic.read(key);
+ }
+
+ @Override
+ public Response create(final Attr configTO) {
+ final Attr config = logic.create(configTO);
+ URI location =
uriInfo.getAbsolutePathBuilder().path(config.getSchema()).build();
+ return Response.created(location).
+ header(RESTHeaders.RESOURCE_KEY, config.getSchema()).
+ entity(config).
+ build();
+ }
+
+ @Override
+ public void update(final Attr configTO) {
+ logic.update(configTO);
+ }
+
+ @Override
+ public void delete(final String key) {
+ logic.delete(key);
+ }
+
+ @Override
+ public void pushToWA() {
+ logic.pushToWA();
+ }
+}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java
new file mode 100644
index 0000000..7fff8a6
--- /dev/null
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java
@@ -0,0 +1,40 @@
+/*
+ * 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.WAConfigEntry;
+
+import java.util.List;
+
+public interface WAConfigDAO extends DAO<WAConfigEntry> {
+
+ WAConfigEntry find(String key);
+
+ List<WAConfigEntry> findAll();
+
+ WAConfigEntry save(WAConfigEntry configEntry);
+
+ void delete(String key);
+
+ void delete(WAConfigEntry configEntry);
+
+ void deleteAll();
+
+}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
new file mode 100644
index 0000000..f4cc6cf
--- /dev/null
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
@@ -0,0 +1,30 @@
+/*
+ * 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.ProvidedKeyEntity;
+
+import java.util.List;
+
+public interface WAConfigEntry extends ProvidedKeyEntity {
+
+ List<String> getValues();
+
+ void setValues(List<String> value);
+}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
new file mode 100644
index 0000000..af82fbd
--- /dev/null
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
@@ -0,0 +1,74 @@
+/*
+ * 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.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPAWAConfigEntry;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+
+import java.util.List;
+
+@Repository
+public class JPAWAConfigDAO extends AbstractDAO<WAConfigEntry> implements
WAConfigDAO {
+ @Transactional(readOnly = true)
+ @Override
+ public WAConfigEntry find(final String key) {
+ return entityManager().find(JPAWAConfigEntry.class, key);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<WAConfigEntry> findAll() {
+ TypedQuery<WAConfigEntry> query = entityManager().createQuery(
+ "SELECT e FROM " + JPAWAConfigEntry.class.getSimpleName() + " e",
WAConfigEntry.class);
+ return query.getResultList();
+ }
+
+ @Override
+ public WAConfigEntry save(final WAConfigEntry configEntry) {
+ return entityManager().merge(configEntry);
+ }
+
+ @Override
+ public void delete(final String key) {
+ WAConfigEntry entry = find(key);
+ if (entry == null) {
+ return;
+ }
+ delete(entry);
+ }
+
+ @Override
+ public void delete(final WAConfigEntry configEntry) {
+ entityManager().remove(configEntry);
+ }
+
+ @Override
+ public void deleteAll() {
+ Query delete = entityManager().createQuery("DELETE FROM " +
JPAWAConfigEntry.class.getSimpleName());
+ delete.executeUpdate();
+ }
+}
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 5ec9130..333ba50 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
@@ -65,6 +65,7 @@ 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.auth.WAConfigEntry;
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;
@@ -121,6 +122,7 @@ 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.auth.JPAWAConfigEntry;
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;
@@ -345,6 +347,8 @@ public class JPAEntityFactory implements EntityFactory {
result = (E) new JPAAuthProfile();
} else if (reference.equals(OIDCJWKS.class)) {
result = (E) new JPAOIDCJWKS();
+ } else if (reference.equals(WAConfigEntry.class)) {
+ result = (E) new JPAWAConfigEntry();
} 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/JPAWAConfigEntry.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAWAConfigEntry.java
new file mode 100644
index 0000000..bd75c1c
--- /dev/null
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAWAConfigEntry.java
@@ -0,0 +1,54 @@
+/*
+ * 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.WAConfigEntry;
+import
org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.Table;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = JPAWAConfigEntry.TABLE)
+public class JPAWAConfigEntry extends AbstractProvidedKeyEntity implements
WAConfigEntry {
+ public static final String TABLE = "ConfigEntry";
+
+ private static final long serialVersionUID = 6422422526695279794L;
+
+ @ElementCollection
+ @CollectionTable(name = TABLE + "Values", joinColumns = @JoinColumn(name =
"id"))
+ @Column(nullable = false)
+ private List<String> values = new ArrayList<>();
+
+ @Override
+ public List<String> getValues() {
+ return values;
+ }
+
+ public void setValues(final List<String> values) {
+ this.values = values;
+ }
+}
diff --git
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
new file mode 100644
index 0000000..eab1ae6
--- /dev/null
+++
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@Transactional("Master")
+public class WAConfigTest extends AbstractTest {
+
+ @Autowired
+ private WAConfigDAO configDAO;
+
+ @BeforeEach
+ public void beforeEach() {
+ configDAO.deleteAll();
+ }
+
+ @Test
+ public void saveCommaSeparatedValueStrings() {
+ create("system.example.key[0]", Arrays.asList("value1", "value2",
"value3"));
+ assertFalse(configDAO.findAll().isEmpty());
+ }
+
+ @Test
+ public void saveNumbers() {
+ create("system.example.key[0]", List.of("1984"));
+ assertFalse(configDAO.findAll().isEmpty());
+ }
+
+ @Test
+ public void saveCollection() {
+ WAConfigEntry entry = create("system.example.key[0]", new
ArrayList<>(Arrays.asList("1", "2")));
+ assertNotNull(entry.getValues());
+ assertFalse(configDAO.findAll().isEmpty());
+ }
+
+ @Test
+ public void saveMap() {
+ HashMap<String, Double> map = new HashMap<>();
+ create("system.example.key[0].key1", List.of("value1"));
+ assertFalse(configDAO.findAll().isEmpty());
+ }
+
+ @Test
+ public void update() {
+ WAConfigEntry entry = create("system.syncope.key[0]",
Arrays.asList("1", "2", "3", "4"));
+ assertNotNull(entry);
+ entry.setValues(List.of("v1"));
+
+ entry = configDAO.save(entry);
+ assertNotNull(entry);
+ assertNotNull(entry.getKey());
+ WAConfigEntry found = configDAO.find(entry.getKey());
+ assertNotNull(found);
+ assertEquals(List.of("v1"), found.getValues());
+ }
+
+ private WAConfigEntry create(final String name, final List<String> value) {
+ WAConfigEntry entry = entityFactory.newEntity(WAConfigEntry.class);
+ entry.setKey(name);
+ entry.setValues(value);
+ configDAO.save(entry);
+ assertNotNull(entry);
+ assertNotNull(entry.getKey());
+ assertNotNull(configDAO.find(entry.getKey()));
+ return entry;
+ }
+
+}
diff --git
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
new file mode 100644
index 0000000..0faa17a
--- /dev/null
+++
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
@@ -0,0 +1,31 @@
+/*
+ * 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.Attr;
+import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+
+public interface WAConfigDataBinder {
+ Attr getAttr(WAConfigEntry waConfigEntry);
+
+ WAConfigEntry create(Attr config);
+
+ WAConfigEntry update(WAConfigEntry entry, Attr config);
+}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
new file mode 100644
index 0000000..b67db28
--- /dev/null
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Attr;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class WAConfigDataBinderImpl implements WAConfigDataBinder {
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Override
+ public Attr getAttr(final WAConfigEntry waConfigEntry) {
+ return new
Attr.Builder(waConfigEntry.getKey()).values(waConfigEntry.getValues()).build();
+ }
+
+ @Override
+ public WAConfigEntry create(final Attr configTO) {
+ return update(entityFactory.newEntity(WAConfigEntry.class), configTO);
+ }
+
+ @Override
+ public WAConfigEntry update(final WAConfigEntry entry, final Attr
configTO) {
+ return getConfigEntry(entry, configTO);
+ }
+
+ private WAConfigEntry getConfigEntry(
+ final WAConfigEntry configEntry,
+ final Attr config) {
+
+ WAConfigEntry result = configEntry;
+ if (result == null) {
+ result = entityFactory.newEntity(WAConfigEntry.class);
+ }
+ result.setValues(config.getValues());
+ result.setKey(config.getSchema());
+ return result;
+ }
+}
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 6c607c0..a4f69c4 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
@@ -146,6 +146,7 @@ import
org.apache.syncope.common.rest.api.service.SAML2SPMetadataService;
import org.apache.syncope.common.rest.api.service.SRARouteService;
import org.apache.syncope.common.rest.api.service.UserWorkflowTaskService;
import org.apache.syncope.common.rest.api.service.wa.U2FRegistrationService;
+import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
import org.apache.syncope.fit.core.CoreITContext;
import org.apache.syncope.fit.core.UserITCase;
import org.junit.jupiter.api.BeforeAll;
@@ -340,6 +341,8 @@ public abstract class AbstractITCase {
protected static U2FRegistrationService u2FRegistrationService;
+ protected static WAConfigService waConfigService;
+
@BeforeAll
public static void securitySetup() {
try (InputStream propStream =
AbstractITCase.class.getResourceAsStream("/security.properties")) {
@@ -419,6 +422,7 @@ public abstract class AbstractITCase {
authProfileService = adminClient.getService(AuthProfileService.class);
oidcJWKSService = adminClient.getService(OIDCJWKSService.class);
u2FRegistrationService =
adminClient.getService(U2FRegistrationService.class);
+ waConfigService = adminClient.getService(WAConfigService.class);
}
@Autowired
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAConfigITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAConfigITCase.java
new file mode 100644
index 0000000..0fc7b68
--- /dev/null
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAConfigITCase.java
@@ -0,0 +1,74 @@
+/*
+ * 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.Attr;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+
+import javax.ws.rs.core.Response;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class WAConfigITCase extends AbstractITCase {
+ private static Attr runTest(final List<String> initialValue, final
List<String> updatedValue) {
+ Attr configTO = new Attr.Builder(UUID.randomUUID().toString())
+ .values(initialValue)
+ .build();
+ Response response = waConfigService.create(configTO);
+ String key = response.getHeaderString(RESTHeaders.RESOURCE_KEY);
+ assertNotNull(key);
+
+ assertFalse(waConfigService.list().isEmpty());
+
+ configTO = waConfigService.read(key);
+ assertNotNull(configTO);
+
+ configTO = new Attr.Builder(configTO.getSchema())
+ .values(updatedValue)
+ .build();
+ waConfigService.update(configTO);
+
+ Attr updatedTO = waConfigService.read(key);
+ updatedTO.getValues().stream().allMatch(((Collection)
updatedValue)::contains);
+ return updatedTO;
+ }
+
+ private static <T extends Serializable> void deleteEntry(final Attr
configTO) {
+ waConfigService.delete(configTO.getSchema());
+ assertThrows(SyncopeClientException.class, () ->
waConfigService.read(configTO.getSchema()));
+ }
+
+ @Test
+ public void verify() {
+ deleteEntry(runTest(List.of("v1", "v2"), List.of("newValue")));
+ deleteEntry(runTest(List.of("12345"), List.of("98765")));
+ deleteEntry(runTest(List.of("123.45"), List.of("987.65")));
+ deleteEntry(runTest(List.of("1", "2", "3"), List.of("4", "5", "6")));
+ }
+}
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 a005c5a..566082c 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
@@ -18,26 +18,27 @@
*/
package org.apache.syncope.wa.bootstrap;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.CasCoreConfigurationUtils;
+import
org.apereo.cas.configuration.model.core.authentication.AuthenticationProperties;
import
org.apereo.cas.configuration.model.support.generic.AcceptAuthenticationProperties;
import
org.apereo.cas.configuration.model.support.jaas.JaasAuthenticationProperties;
+import
org.apereo.cas.configuration.model.support.jdbc.JdbcAuthenticationProperties;
import
org.apereo.cas.configuration.model.support.jdbc.authn.QueryJdbcAuthenticationProperties;
import
org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
+import
org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProperties;
import
org.apereo.cas.configuration.model.support.mfa.gauth.GoogleAuthenticatorMultifactorProperties;
import
org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorProperties;
+import
org.apereo.cas.configuration.model.support.pac4j.Pac4jDelegatedAuthenticationProperties;
import
org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties;
import
org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties;
import
org.apereo.cas.configuration.model.support.pac4j.saml.Pac4jSamlClientProperties;
import org.apereo.cas.configuration.model.support.radius.RadiusProperties;
import
org.apereo.cas.configuration.model.support.syncope.SyncopeAuthenticationProperties;
import org.apereo.cas.util.model.TriStateBoolean;
+
+import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.auth.AuthModuleConf;
@@ -52,6 +53,7 @@ import
org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
import org.apache.syncope.common.rest.api.service.AuthModuleService;
+import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
@@ -60,6 +62,11 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
@Order
public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
@@ -71,41 +78,51 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
this.waRestClient = waRestClient;
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final SyncopeAuthModuleConf conf,
- final String address) {
+ private static Map<String, Object> mapAuthModule(
+
+ final String authModule,
+ final SyncopeAuthModuleConf conf,
+ final String address) {
SyncopeAuthenticationProperties syncopeProps = new
SyncopeAuthenticationProperties();
syncopeProps.setName(authModule);
syncopeProps.setDomain(conf.getDomain());
syncopeProps.setUrl(StringUtils.substringBefore(address, "/rest"));
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
casProperties.getAuthn().setSyncope(syncopeProps);
- return "cas.authn.syncope.";
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
+
filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getSyncope)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final StaticAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final StaticAuthModuleConf conf) {
AcceptAuthenticationProperties staticProps = new
AcceptAuthenticationProperties();
staticProps.setName(authModule);
String users = conf.getUsers().entrySet().stream().
- map(entry -> entry.getKey() + "::" + entry.getValue()).
- collect(Collectors.joining(","));
+ map(entry -> entry.getKey() + "::" + entry.getValue()).
+ collect(Collectors.joining(","));
staticProps.setUsers(users);
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().setAccept(staticProps);
- return "cas.authn.accept.";
+
filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getAccept)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final LDAPAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final LDAPAuthModuleConf conf) {
LdapAuthenticationProperties ldapProps = new
LdapAuthenticationProperties();
ldapProps.setName(authModule);
@@ -117,14 +134,19 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
ldapProps.setSubtreeSearch(conf.isSubtreeSearch());
ldapProps.setPrincipalAttributeList(conf.getPrincipalAttributeList());
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getLdap().add(ldapProps);
- return "cas.authn.ldap.";
+
filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getLdap)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final GoogleMfaAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final GoogleMfaAuthModuleConf conf) {
GoogleAuthenticatorMultifactorProperties props = new
GoogleAuthenticatorMultifactorProperties();
props.setName(authModule);
@@ -134,14 +156,24 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.setTimeStepSize(conf.getTimeStepSize());
props.setWindowSize(conf.getWindowSize());
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getMfa().setGauth(props);
- return "cas.authn.mfa.gauth.";
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getMfa))).
+
addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(MultifactorAuthenticationProperties.class,
+ MultifactorAuthenticationProperties::getGauth)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final U2FAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final U2FAuthModuleConf conf) {
U2FMultifactorProperties props = new U2FMultifactorProperties();
props.setName(authModule);
@@ -150,14 +182,25 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.setExpireRegistrations(conf.getExpireRegistrations());
props.setExpireRegistrationsTimeUnit(TimeUnit.valueOf(conf.getExpireRegistrationsTimeUnit()));
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getMfa().setU2f(props);
- return "cas.authn.mfa.u2f.";
+
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getMfa))).
+
addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(MultifactorAuthenticationProperties.class,
+ MultifactorAuthenticationProperties::getU2f)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final JaasAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final JaasAuthModuleConf conf) {
JaasAuthenticationProperties props = new
JaasAuthenticationProperties();
props.setName(authModule);
@@ -167,14 +210,20 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.setLoginConfigType(conf.getLoginConfigurationFile());
props.setRealm(conf.getRealm());
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getJaas().add(props);
- return "cas.authn.jaas.";
+
+
filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getJaas)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final JDBCAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final JDBCAuthModuleConf conf) {
QueryJdbcAuthenticationProperties props = new
QueryJdbcAuthenticationProperties();
props.setName(authModule);
@@ -189,14 +238,25 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.setUser(conf.getUser());
props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getJdbc().getQuery().add(props);
- return "cas.authn.jdbc.query.";
+
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getJdbc))).
+
addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(JdbcAuthenticationProperties.class,
+ JdbcAuthenticationProperties::getQuery)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final OIDCAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final OIDCAuthModuleConf conf) {
Pac4jGenericOidcClientProperties props = new
Pac4jGenericOidcClientProperties();
props.setId(conf.getId());
@@ -213,14 +273,25 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
Pac4jOidcClientProperties client = new Pac4jOidcClientProperties();
client.setGeneric(props);
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getPac4j().getOidc().add(client);
- return "cas.authn.pac4j.oidc.";
+
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getPac4j))).
+
addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(Pac4jDelegatedAuthenticationProperties.class,
+ Pac4jDelegatedAuthenticationProperties::getOidc)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final RadiusAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final RadiusAuthModuleConf conf) {
RadiusProperties props = new RadiusProperties();
props.setName(authModule);
@@ -241,14 +312,20 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.getServer().setProtocol(conf.getProtocol());
props.getServer().setRetries(conf.getRetries());
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().setRadius(props);
- return "cas.authn.radius.";
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getRadius)));
+ return filterCasProperties(casProperties, filterProvider);
}
- private static String mapAuthModule(
- final CasConfigurationProperties casProperties,
- final String authModule,
- final SAML2IdPAuthModuleConf conf) {
+ private static Map<String, Object> mapAuthModule(
+ final String authModule,
+ final SAML2IdPAuthModuleConf conf) {
Pac4jSamlClientProperties props = new Pac4jSamlClientProperties();
props.setClientName(authModule);
@@ -272,11 +349,36 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
props.setSignatureReferenceDigestMethods(conf.getSignatureReferenceDigestMethods());
props.setPrincipalAttributeId(conf.getUserIdAttribute());
props.setNameIdPolicyAllowCreate(StringUtils.isBlank(conf.getNameIdPolicyAllowCreate())
- ? TriStateBoolean.UNDEFINED
- :
TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
+ ? TriStateBoolean.UNDEFINED
+ :
TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
+ CasConfigurationProperties casProperties = new
CasConfigurationProperties();
+ SimpleFilterProvider filterProvider = getParentCasFilterProvider();
casProperties.getAuthn().getPac4j().getSaml().add(props);
- return "cas.authn.pac4j.saml.";
+
+ filterProvider.
+ addFilter(AuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(AuthenticationProperties.class,
+ AuthenticationProperties::getPac4j))).
+
addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
+ SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(Pac4jDelegatedAuthenticationProperties.class,
+ Pac4jDelegatedAuthenticationProperties::getSaml)));
+ return filterCasProperties(casProperties, filterProvider);
+ }
+
+ private static SimpleFilterProvider getParentCasFilterProvider() {
+ return new SimpleFilterProvider().
+ setFailOnUnknownId(false).
+ addFilter(CasConfigurationProperties.class.getSimpleName(),
SimpleBeanPropertyFilter.filterOutAllExcept(
+
CasCoreConfigurationUtils.getPropertyName(CasConfigurationProperties.class,
+ CasConfigurationProperties::getAuthn)));
+ }
+
+ private static Map<String, Object> filterCasProperties(final
CasConfigurationProperties casProperties,
+ final
SimpleFilterProvider filters) {
+ return CasCoreConfigurationUtils.asMap(casProperties.withHolder(),
filters);
}
@Override
@@ -288,48 +390,39 @@ public class SyncopeWAPropertySourceLocator implements
PropertySourceLocator {
}
LOG.info("Bootstrapping WA configuration");
-
- CasConfigurationProperties casProperties = new
CasConfigurationProperties();
- List<String> filters = new ArrayList<>();
+ Map<String, Object> properties = new TreeMap<>();
syncopeClient.getService(AuthModuleService.class).list().forEach(authModuleTO
-> {
AuthModuleConf authConf = authModuleTO.getConf();
LOG.debug("Mapping auth module {} ", authModuleTO.getKey());
if (authConf instanceof LDAPAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (LDAPAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(LDAPAuthModuleConf) authConf));
} else if (authConf instanceof StaticAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (StaticAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(StaticAuthModuleConf) authConf));
} else if (authConf instanceof SyncopeAuthModuleConf) {
- filters.add(mapAuthModule(
- casProperties,
- authModuleTO.getKey(),
- (SyncopeAuthModuleConf) authConf,
- waRestClient.getSyncopeClient().getAddress()));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
+ (SyncopeAuthModuleConf) authConf,
syncopeClient.getAddress()));
} else if (authConf instanceof GoogleMfaAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (GoogleMfaAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(GoogleMfaAuthModuleConf) authConf));
} else if (authConf instanceof JaasAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (JaasAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(JaasAuthModuleConf) authConf));
} else if (authConf instanceof JDBCAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (JDBCAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(JDBCAuthModuleConf) authConf));
} else if (authConf instanceof OIDCAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (OIDCAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(OIDCAuthModuleConf) authConf));
} else if (authConf instanceof RadiusAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (RadiusAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(RadiusAuthModuleConf) authConf));
} else if (authConf instanceof SAML2IdPAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (SAML2IdPAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(SAML2IdPAuthModuleConf) authConf));
} else if (authConf instanceof U2FAuthModuleConf) {
- filters.add(mapAuthModule(casProperties,
authModuleTO.getKey(), (U2FAuthModuleConf) authConf));
+ properties.putAll(mapAuthModule(authModuleTO.getKey(),
(U2FAuthModuleConf) authConf));
}
});
- Map<String, Object> properties =
CasCoreConfigurationUtils.asMap(casProperties.withHolder()).
- entrySet().stream().
- filter(entry -> filters.stream().filter(Objects::nonNull).
- anyMatch(prefix -> entry.getKey().startsWith(prefix))).
- collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue));
+ syncopeClient.getService(WAConfigService.class).list()
+ .forEach(configTO -> properties.put(configTO.getSchema(),
configTO.getValues()));
LOG.debug("Collected WA properties: {}", properties);
-
return new MapPropertySource(getClass().getName(), properties);
}
}
diff --git
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
index 05af05d..bcb7adc 100644
---
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
+++
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
@@ -19,17 +19,6 @@
package org.apache.syncope.wa.starter;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.NotFoundException;
-import javax.ws.rs.core.Response;
-
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.syncope.common.keymaster.client.api.ServiceOps;
@@ -45,13 +34,24 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
@Component
public class SyncopeCoreTestingServer implements
ApplicationListener<ContextRefreshedEvent> {
- private static final String ADDRESS = "http://localhost:9080/syncope/rest";
-
public static final List<WAClientApp> APPS = new ArrayList<>();
+ private static final String ADDRESS = "http://localhost:9080/syncope/rest";
+
@Autowired
private ServiceOps serviceOps;
@@ -64,11 +64,11 @@ public class SyncopeCoreTestingServer implements
ApplicationListener<ContextRefr
sf.setAddress(ADDRESS);
sf.setResourceClasses(WAClientAppService.class,
GoogleMfaAuthTokenService.class);
sf.setResourceProvider(
- WAClientAppService.class,
- new SingletonResourceProvider(new
StubWAClientAppService(), true));
+ WAClientAppService.class,
+ new SingletonResourceProvider(new
StubWAClientAppService(), true));
sf.setResourceProvider(
- GoogleMfaAuthTokenService.class,
- new SingletonResourceProvider(new
StubGoogleMfaAuthTokenService(), true));
+ GoogleMfaAuthTokenService.class,
+ new SingletonResourceProvider(new
StubGoogleMfaAuthTokenService(), true));
sf.setProviders(List.of(new JacksonJsonProvider()));
sf.create();
@@ -125,16 +125,16 @@ public class SyncopeCoreTestingServer implements
ApplicationListener<ContextRefr
@Override
public GoogleMfaAuthToken findTokenFor(@NotNull final String owner,
@NotNull final Integer token) {
return tokens.stream()
- .filter(to -> to.getToken().equals(token) &&
to.getOwner().equalsIgnoreCase(owner))
- .findFirst().get();
+ .filter(to -> to.getToken().equals(token) &&
to.getOwner().equalsIgnoreCase(owner))
+ .findFirst().get();
}
@Override
public PagedResult<GoogleMfaAuthToken> findTokensFor(@NotNull final
String user) {
PagedResult<GoogleMfaAuthToken> result = new PagedResult<>();
result.getResult().addAll(tokens.stream().
- filter(to -> to.getOwner().equalsIgnoreCase(user)).
- collect(Collectors.toList()));
+ filter(to -> to.getOwner().equalsIgnoreCase(user)).
+ collect(Collectors.toList()));
result.setSize(result.getResult().size());
result.setTotalCount(result.getSize());
return result;
@@ -143,8 +143,8 @@ public class SyncopeCoreTestingServer implements
ApplicationListener<ContextRefr
@Override
public GoogleMfaAuthToken findTokenFor(@NotNull final String key) {
return tokens.stream()
- .filter(to -> to.getKey().equalsIgnoreCase(key))
- .findFirst().get();
+ .filter(to -> to.getKey().equalsIgnoreCase(key))
+ .findFirst().get();
}
@Override
@@ -166,13 +166,13 @@ public class SyncopeCoreTestingServer implements
ApplicationListener<ContextRefr
@Override
public WAClientApp read(final Long clientAppId, final ClientAppType
type) {
return APPS.stream().filter(app -> Objects.equals(clientAppId,
app.getClientAppTO().getClientAppId())).
- findFirst().orElseThrow(() -> new
NotFoundException("ClientApp with clientId " + clientAppId));
+ findFirst().orElseThrow(() -> new NotFoundException("ClientApp
with clientId " + clientAppId));
}
@Override
public WAClientApp read(final String name, final ClientAppType type) {
return APPS.stream().filter(app -> Objects.equals(name,
app.getClientAppTO().getName())).
- findFirst().orElseThrow(() -> new
NotFoundException("ClientApp with name " + name));
+ findFirst().orElseThrow(() -> new NotFoundException("ClientApp
with name " + name));
}
}
}