This is an automated email from the ASF dual-hosted git repository.
ilgrosso 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 d17d4ff7f5 [SYNCOPE-1968] Map each SRA and WA instance to a single
Domain (#1517)
d17d4ff7f5 is described below
commit d17d4ff7f584939d06341859aa6ac37a3dc2338c
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Tue Oct 20 05:30:13 2026 +0200
[SYNCOPE-1968] Map each SRA and WA instance to a single Domain (#1517)
---
.github/workflows/fit_WA_Multitenancy.yml | 45 -------
.../authprofiles/AuthProfileWizardBuilder.java | 3 +-
.../apache/syncope/client/console/pages/SRA.java | 3 +-
.../apache/syncope/client/console/pages/WA.java | 3 +-
.../syncope/client/enduser/pages/AuthProfile.java | 3 +-
.../client/console/SyncopeConsoleApplication.java | 4 +-
.../apache/syncope/client/console/pages/Logs.java | 7 +-
.../panels/NetworkServiceDirectoryPanel.java | 19 +--
.../panels/NetworkServiceDirectoryPanel.properties | 1 +
.../NetworkServiceDirectoryPanel_it.properties | 1 +
.../NetworkServiceDirectoryPanel_ja.properties | 1 +
.../NetworkServiceDirectoryPanel_pt_BR.properties | 1 +
.../NetworkServiceDirectoryPanel_ru.properties | 1 +
.../client/enduser/SyncopeEnduserApplication.java | 4 +-
.../common/keymaster/client/api/ServiceOps.java | 11 ++
.../keymaster/client/api/model/NetworkService.java | 13 ++
.../client/api/startstop/KeymasterStart.java | 14 +-
.../client/api/startstop/KeymasterStartStop.java | 32 ++++-
.../client/api/startstop/KeymasterStop.java | 14 +-
.../zookeeper/ZookeeperKeymasterClientContext.java | 8 +-
.../zookeeper/ZookeeperServiceDiscoveryOps.java | 78 ++++++++---
.../client/zookeeper/ZookeeperServiceOps.java | 37 +++++-
.../client/self/SelfKeymasterServiceOps.java | 26 +++-
.../rest/api/service/NetworkServiceService.java | 10 +-
.../apache/syncope/core/logic/SRARouteLogic.java | 30 +++--
.../syncope/core/logic/wa/WAConfigLogic.java | 15 ++-
.../apache/syncope/core/logic/DummyServiceOps.java | 10 ++
.../apache/syncope/core/logic/DummyServiceOps.java | 10 ++
.../api/dao/keymaster/NetworkServiceDAO.java | 2 +
.../api/entity/keymaster/NetworkServiceEntity.java | 4 +
.../common/validation/NetworkServiceCheck.java} | 25 ++--
.../validation/NetworkServiceValidator.java} | 24 +++-
.../jpa/dao/repo/NetworkServiceRepoExt.java | 2 +
.../jpa/dao/repo/NetworkServiceRepoExtImpl.java | 11 ++
.../jpa/entity/keymaster/JPANetworkService.java | 14 ++
.../spring/DomainRoutingEntityManagerFactory.java | 16 ++-
.../neo4j/dao/repo/NetworkServiceRepoExt.java | 2 +
.../neo4j/dao/repo/NetworkServiceRepoExtImpl.java | 11 ++
.../entity/keymaster/Neo4jNetworkService.java | 14 ++
.../provisioning/java/job/DefaultJobManager.java | 13 +-
.../internal/SelfKeymasterInternalServiceOps.java | 22 +++
.../cxf/service/NetworkServiceServiceImpl.java | 13 +-
.../syncope/core/logic/NetworkServiceLogic.java | 39 ++++--
.../jws/MSEntraJWSVerifierCacheLoader.java | 5 +-
docker/wa/src/main/resources/wa-docker.properties | 2 +
.../apache/syncope/fit/core/KeymasterITCase.java | 2 +
fit/wa-reference/pom.xml | 8 ++
.../src/main/resources/wa-embedded.properties | 2 +-
.../src/main/resources/wa-multitenancy.properties | 17 ---
.../org/apache/syncope/fit/AbstractITCase.java | 5 +-
.../org/apache/syncope/fit/MultitenancyITCase.java | 148 ---------------------
.../java/org/apache/syncope/sra/RouteProvider.java | 5 +
.../java/org/apache/syncope/sra/SRAProperties.java | 11 ++
.../apache/syncope/sra/SyncopeSRAApplication.java | 10 +-
sra/src/main/resources/sra.properties | 1 +
.../asciidoc/reference-guide/concepts/domains.adoc | 5 +-
.../wa/bootstrap/WABootstrapConfiguration.java | 6 +-
.../apache/syncope/wa/bootstrap/WAProperties.java | 11 ++
.../apache/syncope/wa/bootstrap/WARestClient.java | 54 ++++----
.../syncope/wa/starter/config/WAContext.java | 30 +----
.../wa/starter/multitenancy/WATenantsManager.java | 143 --------------------
wa/starter/src/main/resources/wa.properties | 3 +-
.../src/test/resources/debug/wa-debug.properties | 2 +-
63 files changed, 560 insertions(+), 526 deletions(-)
diff --git a/.github/workflows/fit_WA_Multitenancy.yml
b/.github/workflows/fit_WA_Multitenancy.yml
deleted file mode 100644
index b05a1776cc..0000000000
--- a/.github/workflows/fit_WA_Multitenancy.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License
-name: "FIT WA Multitenancy"
-
-on:
- push:
- branches: [master]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [master]
-
-jobs:
- fit_WA_Multitenancy:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/[email protected]
- - name: Setup Java JDK
- uses: actions/setup-java@v6
- with:
- distribution: 'temurin'
- java-version: 25
- - name: Setup Maven
- uses: stCarolas/[email protected]
- with:
- maven-version: 3.9.6
- - name: Build
- run: mvn -U -T 1C -P 'skipTests,all'
- - name: 'WA / Multitenancy'
- run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true
-Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true
-Djacoco.skip=true -Dspring.profiles.active=embedded,https,all,multitenancy
-Dit.test=MultitenancyITCase
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
index 44c53dc93e..10440b912d 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.console.authprofiles;
import java.util.List;
import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.console.panels.BeanPanel;
import org.apache.syncope.client.console.rest.AuthProfileRestClient;
import
org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel;
@@ -104,7 +105,7 @@ public abstract class AuthProfileWizardBuilder<T extends
BaseBean> extends BaseA
String attributes = "{}";
try {
attributes = authProfileRestClient.readConsentAttributes(
- serviceOps.get(NetworkService.Type.WA),
+ serviceOps.get(NetworkService.Type.WA,
SyncopeConsoleSession.get().getDomain()),
consentDecision.getPrincipal(),
consentDecision.getId());
} catch (Exception e) {
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java
index 3904aff8f7..86b66beedc 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/SRA.java
@@ -60,7 +60,8 @@ public class SRA extends BasePage {
body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr",
Dashboard.class));
body.setOutputMarkupId(true);
- List<NetworkService> instances =
serviceOps.list(NetworkService.Type.SRA);
+ List<NetworkService> instances = serviceOps.list(
+ NetworkService.Type.SRA,
SyncopeConsoleSession.get().getDomain());
AjaxLink<?> push = new AjaxLink<>("push") {
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
index b16398bc17..d2bf20b89e 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
@@ -110,7 +110,8 @@ public class WA extends BasePage {
body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr",
Dashboard.class));
body.setOutputMarkupId(true);
- List<NetworkService> instances =
serviceOps.list(NetworkService.Type.WA);
+ List<NetworkService> instances = serviceOps.list(
+ NetworkService.Type.WA,
SyncopeConsoleSession.get().getDomain());
modal = new BaseModal<>("push-modal");
modal.setWindowClosedCallback(target -> modal.show(false));
diff --git
a/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java
b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java
index 8f752e7fa3..191126e799 100644
---
a/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java
+++
b/client/am/enduser/src/main/java/org/apache/syncope/client/enduser/pages/AuthProfile.java
@@ -20,6 +20,7 @@ package org.apache.syncope.client.enduser.pages;
import java.util.List;
import java.util.stream.Collectors;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.enduser.rest.AuthProfileRestClient;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.annotations.AMPage;
@@ -237,7 +238,7 @@ public class AuthProfile extends BaseReauthPage {
String attributes = "{}";
try {
attributes = restClient.readConsentAttributes(
- serviceOps.get(NetworkService.Type.WA),
+ serviceOps.get(NetworkService.Type.WA,
SyncopeEnduserSession.get().getDomain()),
item.getModelObject().getPrincipal(),
item.getModelObject().getId());
} catch (Exception e) {
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
index d58f35f5e8..da0bad2f3a 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
@@ -125,13 +125,13 @@ public class SyncopeConsoleApplication extends
SpringBootServletInitializer {
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
public KeymasterStart keymasterStart() {
- return new KeymasterStart(NetworkService.Type.CONSOLE);
+ return new KeymasterStart.Builder(NetworkService.Type.CONSOLE).build();
}
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
public KeymasterStop keymasterStop() {
- return new KeymasterStop(NetworkService.Type.CONSOLE);
+ return new KeymasterStop.Builder(NetworkService.Type.CONSOLE).build();
}
}
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Logs.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Logs.java
index d9d96d21ac..edf0399367 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Logs.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Logs.java
@@ -25,6 +25,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.console.panels.LogsPanel;
import org.apache.syncope.client.console.rest.LoggerConf;
import org.apache.syncope.client.console.rest.LoggerConfOp;
@@ -123,7 +124,8 @@ public class Logs extends BasePage {
});
}
- List<NetworkService> waInstances =
serviceOps.list(NetworkService.Type.WA);
+ List<NetworkService> waInstances = serviceOps.list(
+ NetworkService.Type.WA,
SyncopeConsoleSession.get().getDomain());
if (!waInstances.isEmpty()) {
tabs.add(new AbstractTab(Model.of(NetworkService.Type.WA.name())) {
@@ -136,7 +138,8 @@ public class Logs extends BasePage {
});
}
- List<NetworkService> sraInstances =
serviceOps.list(NetworkService.Type.SRA);
+ List<NetworkService> sraInstances = serviceOps.list(
+ NetworkService.Type.SRA,
SyncopeConsoleSession.get().getDomain());
if (!sraInstances.isEmpty()) {
tabs.add(new AbstractTab(Model.of(NetworkService.Type.SRA.name()))
{
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
index 2e2051bb89..5b29dd7c89 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
@@ -40,6 +40,7 @@ import
org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import
org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
@@ -58,12 +59,10 @@ public class NetworkServiceDirectoryPanel extends
DirectoryPanel<
final NetworkService.Type type,
final SyncopeRestClient syncopeRestClient,
final PageReference pageRef) {
+
super(id, syncopeRestClient, pageRef, true);
this.type = type;
- NetworkService service = new NetworkService();
- service.setType(type);
-
disableCheckBoxes();
modal.size(Modal.Size.Large);
@@ -80,6 +79,10 @@ public class NetworkServiceDirectoryPanel extends
DirectoryPanel<
columns.add(new PropertyColumn<>(new StringResourceModel("address",
this), "address", "address"));
+ if (type == NetworkService.Type.SRA || type == NetworkService.Type.WA)
{
+ columns.add(new PropertyColumn<>(new StringResourceModel("domain",
this), "domain", "domain"));
+ }
+
return columns;
}
@@ -145,15 +148,7 @@ public class NetworkServiceDirectoryPanel extends
DirectoryPanel<
@Override
public IModel<NetworkService> model(final NetworkService service) {
- return new IModel<>() {
-
- private static final long serialVersionUID =
999513782683391483L;
-
- @Override
- public NetworkService getObject() {
- return service;
- }
- };
+ return Model.of(service);
}
}
}
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.properties
index e0ee5575e3..28fb44a689 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.properties
@@ -17,3 +17,4 @@
any.edit=Edit implementation ${key}
any.new=New ${type} implementation
address=Address
+domain=Domain
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_it.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_it.properties
index 4ef085aba8..c17bb07cf3 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_it.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_it.properties
@@ -17,3 +17,4 @@
any.edit=Modifica implementazione ${key}
any.new=Nuova implementazione di ${type}
address=Indirizzo
+domain=Dominio
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ja.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ja.properties
index 292bdb74dd..3df7b49a8c 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ja.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ja.properties
@@ -17,3 +17,4 @@
any.edit=\u5c0e\u5165 ${key} \u3092\u7de8\u96c6
any.new=\u65b0\u3057\u3044 ${type} \u5c0e\u5165
address=\u30a2\u30c9\u30ec\u30b9
+domain=Domain
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_pt_BR.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_pt_BR.properties
index e0ee5575e3..28fb44a689 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_pt_BR.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_pt_BR.properties
@@ -17,3 +17,4 @@
any.edit=Edit implementation ${key}
any.new=New ${type} implementation
address=Address
+domain=Domain
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ru.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ru.properties
index ad0f9f648f..d86b76433b 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ru.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel_ru.properties
@@ -18,3 +18,4 @@
any.edit=Edit implementation ${key}
any.new=New ${type} implementation
address=Address
+domain=Domain
diff --git
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
index 21ef0d75c1..998e343a50 100644
---
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
+++
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
@@ -86,13 +86,13 @@ public class SyncopeEnduserApplication extends
SpringBootServletInitializer {
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
public KeymasterStart keymasterStart() {
- return new KeymasterStart(NetworkService.Type.ENDUSER);
+ return new KeymasterStart.Builder(NetworkService.Type.ENDUSER).build();
}
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
public KeymasterStop keymasterStop() {
- return new KeymasterStop(NetworkService.Type.ENDUSER);
+ return new KeymasterStop.Builder(NetworkService.Type.ENDUSER).build();
}
}
diff --git
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/ServiceOps.java
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/ServiceOps.java
index 1499bf341a..739c02609e 100644
---
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/ServiceOps.java
+++
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/ServiceOps.java
@@ -32,6 +32,8 @@ public interface ServiceOps {
List<NetworkService> list(NetworkService.Type serviceType);
+ List<NetworkService> list(NetworkService.Type serviceType, String domain);
+
/**
* Returns the service instance to invoke, for the given type.
*
@@ -39,4 +41,13 @@ public interface ServiceOps {
* @return service instance to invoke, for the given type
*/
NetworkService get(NetworkService.Type serviceType);
+
+ /**
+ * Returns the service instance to invoke, for the given type and domain.
+ *
+ * @param serviceType service type
+ * @param domain domain
+ * @return service instance to invoke, for the given type and domain
+ */
+ NetworkService get(NetworkService.Type serviceType, String domain);
}
diff --git
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/NetworkService.java
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/NetworkService.java
index 42579d9d0f..207d3343b2 100644
---
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/NetworkService.java
+++
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/NetworkService.java
@@ -39,6 +39,8 @@ public class NetworkService implements Serializable {
private String address;
+ private String domain;
+
public Type getType() {
return type;
}
@@ -55,11 +57,20 @@ public class NetworkService implements Serializable {
this.address = address;
}
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(final String domain) {
+ this.domain = domain;
+ }
+
@Override
public int hashCode() {
return new HashCodeBuilder().
append(type).
append(address).
+ append(domain).
build();
}
@@ -78,6 +89,7 @@ public class NetworkService implements Serializable {
return new EqualsBuilder().
append(type, other.type).
append(address, other.address).
+ append(domain, other.domain).
build();
}
@@ -86,6 +98,7 @@ public class NetworkService implements Serializable {
return "NetworkService{"
+ "type=" + type
+ ", address=" + address
+ + ", domain=" + domain
+ '}';
}
}
diff --git
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStart.java
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStart.java
index abbf86d4b9..91e4683499 100644
---
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStart.java
+++
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStart.java
@@ -24,7 +24,19 @@ import
org.springframework.context.event.ContextRefreshedEvent;
public class KeymasterStart extends KeymasterStartStop implements
ApplicationListener<ContextRefreshedEvent> {
- public KeymasterStart(final NetworkService.Type networkServiceType) {
+ public static class Builder extends
KeymasterStartStop.Builder<KeymasterStart, Builder> {
+
+ public Builder(final NetworkService.Type networkServiceType) {
+ super(networkServiceType);
+ }
+
+ @Override
+ protected KeymasterStart newInstance(final NetworkService.Type
networkServiceType) {
+ return new KeymasterStart(networkServiceType);
+ }
+ }
+
+ protected KeymasterStart(final NetworkService.Type networkServiceType) {
super(networkServiceType);
}
diff --git
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStartStop.java
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStartStop.java
index 29a161b795..c3a5dc3814 100644
---
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStartStop.java
+++
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStartStop.java
@@ -25,22 +25,50 @@ import org.springframework.beans.factory.annotation.Value;
abstract class KeymasterStartStop {
+ protected abstract static class Builder<S extends KeymasterStartStop, B
extends Builder<S, B>> {
+
+ protected final S instance;
+
+ Builder(final NetworkService.Type networkServiceType) {
+ instance = newInstance(networkServiceType);
+ }
+
+ protected abstract S newInstance(NetworkService.Type
networkServiceType);
+
+ @SuppressWarnings("unchecked")
+ public B domain(final String domain) {
+ instance.setDomain(domain);
+ return (B) this;
+ }
+
+ public S build() {
+ return instance;
+ }
+ }
+
@Autowired
protected ServiceOps serviceOps;
protected final NetworkService.Type networkServiceType;
+ private String domain;
+
+ @Value("${service.discovery.address}")
+ private String address;
+
protected KeymasterStartStop(final NetworkService.Type networkServiceType)
{
this.networkServiceType = networkServiceType;
}
- @Value("${service.discovery.address}")
- private String address;
+ protected void setDomain(final String domain) {
+ this.domain = domain;
+ }
protected NetworkService getNetworkService() {
NetworkService ns = new NetworkService();
ns.setType(networkServiceType);
ns.setAddress(address);
+ ns.setDomain(domain);
return ns;
}
}
diff --git
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStop.java
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStop.java
index 81270569a7..f7632f5d4e 100644
---
a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStop.java
+++
b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/startstop/KeymasterStop.java
@@ -24,7 +24,19 @@ import org.springframework.context.event.ContextClosedEvent;
public class KeymasterStop extends KeymasterStartStop implements
ApplicationListener<ContextClosedEvent> {
- public KeymasterStop(final NetworkService.Type networkServiceType) {
+ public static class Builder extends
KeymasterStartStop.Builder<KeymasterStop, Builder> {
+
+ public Builder(final NetworkService.Type networkServiceType) {
+ super(networkServiceType);
+ }
+
+ @Override
+ protected KeymasterStop newInstance(final NetworkService.Type
networkServiceType) {
+ return new KeymasterStop(networkServiceType);
+ }
+ }
+
+ protected KeymasterStop(final NetworkService.Type networkServiceType) {
super(networkServiceType);
}
diff --git
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
index 1ad112f7e1..a7bc420319 100644
---
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
+++
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
@@ -50,13 +50,19 @@ public class ZookeeperKeymasterClientContext {
private static final Pattern IPV4 = Pattern.compile(
"^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$");
+ private static final Pattern HOSTNAME = Pattern.compile(
+
"^(?=.{1,253}:)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*:[0-9]+$",
+ Pattern.CASE_INSENSITIVE);
+
static class ZookeeperCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(final ConditionContext
context, final AnnotatedTypeMetadata metadata) {
String keymasterAddress =
context.getEnvironment().getProperty("keymaster.address");
return new ConditionOutcome(
- keymasterAddress != null &&
IPV4.matcher(keymasterAddress).matches(),
+ keymasterAddress != null
+ && (IPV4.matcher(keymasterAddress).matches()
+ || HOSTNAME.matcher(keymasterAddress).matches()),
"Keymaster address not set for Zookeeper: " +
keymasterAddress);
}
}
diff --git
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceDiscoveryOps.java
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceDiscoveryOps.java
index 2618398710..2cf6ecd0d5 100644
---
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceDiscoveryOps.java
+++
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceDiscoveryOps.java
@@ -21,7 +21,6 @@ package org.apache.syncope.common.keymaster.client.zookeeper;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
@@ -44,26 +43,37 @@ public class ZookeeperServiceDiscoveryOps implements
ServiceOps, InitializingBea
private static final String SERVICE_PATH = "/services";
- private final Map<NetworkService.Type, ServiceProvider<Void>> providers =
new ConcurrentHashMap<>();
+ protected static NetworkService toNetworkService(
+ final NetworkService.Type serviceType,
+ final ServiceInstance<String> serviceInstance) {
+
+ NetworkService ns = new NetworkService();
+ ns.setType(serviceType);
+ ns.setAddress(serviceInstance.getAddress());
+ ns.setDomain(serviceInstance.getPayload());
+ return ns;
+ }
+
+ private final Map<NetworkService.Type, ServiceProvider<String>> providers
= new ConcurrentHashMap<>();
@Autowired
private CuratorFramework client;
- private ServiceDiscovery<Void> discovery;
+ private ServiceDiscovery<String> discovery;
@Override
public void afterPropertiesSet() throws Exception {
- discovery = ServiceDiscoveryBuilder.builder(Void.class).
+ discovery = ServiceDiscoveryBuilder.builder(String.class).
client(client).
basePath(SERVICE_PATH).
build();
discovery.start();
}
- private ServiceProvider<Void> getProvider(final NetworkService.Type type) {
+ private ServiceProvider<String> getProvider(final NetworkService.Type
type) {
return providers.computeIfAbsent(type, t -> {
try {
- ServiceProvider<Void> provider = discovery.
+ ServiceProvider<String> provider = discovery.
serviceProviderBuilder().
serviceName(t.name()).build();
provider.start();
@@ -81,9 +91,10 @@ public class ZookeeperServiceDiscoveryOps implements
ServiceOps, InitializingBea
try {
unregister(service);
- ServiceInstance<Void> instance = ServiceInstance.<Void>builder().
+ ServiceInstance<String> instance =
ServiceInstance.<String>builder().
name(service.getType().name()).
address(service.getAddress()).
+ payload(service.getDomain()).
build();
discovery.registerService(instance);
} catch (KeymasterException e) {
@@ -99,7 +110,7 @@ public class ZookeeperServiceDiscoveryOps implements
ServiceOps, InitializingBea
try {
discovery.queryForInstances(service.getType().name()).stream().
filter(instance ->
instance.getName().equals(service.getType().name())
- &&
instance.getAddress().equals(service.getAddress())).findFirst().
+ &&
instance.getAddress().equals(service.getAddress())).findFirst().
ifPresent(instance -> {
try {
discovery.unregisterService(instance);
@@ -116,22 +127,26 @@ public class ZookeeperServiceDiscoveryOps implements
ServiceOps, InitializingBea
}
}
- private static NetworkService toNetworkService(
- final NetworkService.Type serviceType,
- final ServiceInstance<Void> serviceInstance) {
-
- NetworkService ns = new NetworkService();
- ns.setType(serviceType);
- ns.setAddress(serviceInstance.getAddress());
- return ns;
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType) {
+ try {
+ return discovery.queryForInstances(serviceType.name()).stream().
+ map(serviceInstance -> toNetworkService(serviceType,
serviceInstance)).
+ toList();
+ } catch (KeymasterException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
}
@Override
- public List<NetworkService> list(final NetworkService.Type serviceType) {
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
try {
return discovery.queryForInstances(serviceType.name()).stream().
+ filter(serviceInstance ->
domain.equals(serviceInstance.getPayload())).
map(serviceInstance -> toNetworkService(serviceType,
serviceInstance)).
- collect(Collectors.toList());
+ toList();
} catch (KeymasterException e) {
throw e;
} catch (Exception e) {
@@ -141,11 +156,30 @@ public class ZookeeperServiceDiscoveryOps implements
ServiceOps, InitializingBea
@Override
public NetworkService get(final NetworkService.Type serviceType) {
- ServiceInstance<Void> serviceInstance = null;
+ ServiceInstance<String> serviceInstance = null;
try {
- if (!discovery.queryForInstances(serviceType.name()).isEmpty()) {
- serviceInstance = getProvider(serviceType).getInstance();
- }
+ serviceInstance =
discovery.queryForInstances(serviceType.name()).stream().
+ findFirst().
+ orElse(null);
+ } catch (KeymasterException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
+
+ if (serviceInstance == null) {
+ throw new KeymasterException("No services found for " +
serviceType);
+ }
+ return toNetworkService(serviceType, serviceInstance);
+ }
+
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ ServiceInstance<String> serviceInstance = null;
+ try {
+ serviceInstance =
discovery.queryForInstances(serviceType.name()).stream().
+ filter(s -> domain.equals(s.getPayload())).findFirst().
+ orElse(null);
} catch (KeymasterException e) {
throw e;
} catch (Exception e) {
diff --git
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java
index 4057e8c667..1769062358 100644
---
a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java
+++
b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOps.java
@@ -115,7 +115,8 @@ public class ZookeeperServiceOps implements ServiceOps {
List<NetworkService> list = new ArrayList<>();
for (String child :
client.getChildren().forPath(buildServicePath(serviceType))) {
-
list.add(MAPPER.readValue(client.getData().forPath(buildServicePath(serviceType,
child)),
+ list.add(MAPPER.readValue(
+ client.getData().forPath(buildServicePath(serviceType,
child)),
NetworkService.class));
}
@@ -125,6 +126,29 @@ public class ZookeeperServiceOps implements ServiceOps {
}
}
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ try {
+ if (client.checkExists().forPath(buildServicePath(serviceType)) ==
null) {
+
client.create().creatingParentContainersIfNeeded().forPath(buildServicePath(serviceType));
+ }
+
+ List<NetworkService> list = new ArrayList<>();
+ for (String child :
client.getChildren().forPath(buildServicePath(serviceType))) {
+ NetworkService service = MAPPER.readValue(
+ client.getData().forPath(buildServicePath(serviceType,
child)),
+ NetworkService.class);
+ if (domain.equals(service.getDomain())) {
+ list.add(service);
+ }
+ }
+
+ return list;
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
+ }
+
@Override
public NetworkService get(final NetworkService.Type serviceType) {
List<NetworkService> list = list(serviceType);
@@ -135,4 +159,15 @@ public class ZookeeperServiceOps implements ServiceOps {
// always returns first instance, can be improved
return list.getFirst();
}
+
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ List<NetworkService> list = list(serviceType, domain);
+ if (list.isEmpty()) {
+ throw new KeymasterException("No registered services for type " +
serviceType + " and domain " + domain);
+ }
+
+ // always returns first instance, can be improved
+ return list.getFirst();
+ }
}
diff --git
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
index 394bfccb8b..e3e023680d 100644
---
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
+++
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
@@ -53,7 +53,18 @@ public class SelfKeymasterServiceOps extends
SelfKeymasterOps implements Service
@Override
public List<NetworkService> list(final NetworkService.Type serviceType) {
try {
- return client(NetworkServiceService.class,
Map.of()).list(serviceType);
+ return client(NetworkServiceService.class,
Map.of()).list(serviceType, null);
+ } catch (KeymasterException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
+ }
+
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ try {
+ return client(NetworkServiceService.class,
Map.of()).list(serviceType, domain);
} catch (KeymasterException e) {
throw e;
} catch (Exception e) {
@@ -64,7 +75,18 @@ public class SelfKeymasterServiceOps extends
SelfKeymasterOps implements Service
@Override
public NetworkService get(final NetworkService.Type serviceType) {
try {
- return client(NetworkServiceService.class,
Map.of()).get(serviceType);
+ return client(NetworkServiceService.class,
Map.of()).get(serviceType, null);
+ } catch (KeymasterException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
+ }
+
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ try {
+ return client(NetworkServiceService.class,
Map.of()).get(serviceType, domain);
} catch (KeymasterException e) {
throw e;
} catch (Exception e) {
diff --git
a/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
index 2a52f8a55e..1abb9efedb 100644
---
a/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
+++
b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
@@ -55,23 +55,29 @@ public interface NetworkServiceService extends Serializable
{
* Returns the list of registered services.
*
* @param serviceType service type
+ * @param domain optional domain
* @return list of registered services
*/
@GET
@Path("{serviceType}")
@Produces({ MediaType.APPLICATION_JSON })
- List<NetworkService> list(@NotNull @PathParam("serviceType")
NetworkService.Type serviceType);
+ List<NetworkService> list(
+ @NotNull @PathParam("serviceType") NetworkService.Type serviceType,
+ @QueryParam("domain") String domain);
/**
* Returns the service instance to invoke, for the given type.
*
* @param serviceType service type
+ * @param domain optional domain
* @return service instance to invoke, for the given type
*/
@GET
@Path("{serviceType}/get")
@Produces({ MediaType.APPLICATION_JSON })
- NetworkService get(@NotNull @PathParam("serviceType") NetworkService.Type
serviceType);
+ NetworkService get(
+ @NotNull @PathParam("serviceType") NetworkService.Type serviceType,
+ @QueryParam("domain") String domain);
/**
* (Un)registers the given service.
diff --git
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
index 99dbdd118d..a2e61d7b46 100644
---
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
+++
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
@@ -38,6 +38,7 @@ import
org.apache.syncope.core.persistence.api.dao.SRARouteDAO;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.SRARoute;
import org.apache.syncope.core.provisioning.api.data.SRARouteDataBinder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.security.SecurityProperties;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -110,18 +111,23 @@ public class SRARouteLogic extends
AbstractTransactionalLogic<SRARouteTO> {
@PreAuthorize("hasRole('" + AMEntitlement.SRA_ROUTE_PUSH + "')")
public void pushToSRA() {
- HttpClient client = HttpClient.newHttpClient();
- try {
- serviceOps.list(NetworkService.Type.SRA).forEach(sra ->
client.sendAsync(
- HttpRequest.newBuilder(URI.create(
- Strings.CS.appendIfMissing(sra.getAddress(), "/")
+ "actuator/gateway/refresh")).
- header(HttpHeaders.AUTHORIZATION,
DefaultBasicAuthSupplier.getBasicAuthHeader(
- securityProperties.getAnonymousUser(),
securityProperties.getAnonymousKey())).
- POST(HttpRequest.BodyPublishers.noBody()).build(),
- HttpResponse.BodyHandlers.discarding()).
- thenAcceptAsync(response -> LOG.info(
- "Pushed to SRA instance {} with HTTP status: {}",
- sra.getAddress(), response.statusCode())));
+ try (HttpClient client = HttpClient.newHttpClient()) {
+ serviceOps.list(NetworkService.Type.SRA,
AuthContextUtils.getDomain()).forEach(sra -> {
+ String target = Strings.CS.appendIfMissing(sra.getAddress(),
"/") + "actuator/gateway/refresh";
+ client.sendAsync(
+ HttpRequest.newBuilder(URI.create(target)).
+ header(HttpHeaders.AUTHORIZATION,
DefaultBasicAuthSupplier.getBasicAuthHeader(
+ securityProperties.getAnonymousUser(),
securityProperties.getAnonymousKey())).
+
POST(HttpRequest.BodyPublishers.noBody()).build(),
+ HttpResponse.BodyHandlers.discarding()).
+ thenAcceptAsync(response -> LOG.info(
+ "Pushed to SRA instance {} with HTTP status:
{}",
+ sra.getAddress(), response.statusCode())).
+ exceptionally(error -> {
+ LOG.error("Failed to push to {}", target, error);
+ return null;
+ });
+ });
} catch (KeymasterException e) {
throw new NotFoundException("Could not find any SRA instance", e);
}
diff --git
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
index 9426dc1ef7..baafdcf2e8 100644
---
a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
+++
b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
@@ -40,6 +40,7 @@ import
org.apache.syncope.core.logic.UnresolvedReferenceException;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.security.SecurityProperties;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
@@ -100,7 +101,11 @@ public class WAConfigLogic extends
AbstractTransactionalLogic<EntityTO> {
GET().build(),
HttpResponse.BodyHandlers.discarding()).
thenAcceptAsync(response -> LOG.info(
- "Pushed to {} with HTTP status: {}", target,
response.statusCode()));
+ "Pushed to {} with HTTP status: {}", target,
response.statusCode())).
+ exceptionally(error -> {
+ LOG.error("Failed to push to {}", target, error);
+ return null;
+ });
}
protected void refresh(final HttpClient client, final String
serviceAddress) {
@@ -112,13 +117,17 @@ public class WAConfigLogic extends
AbstractTransactionalLogic<EntityTO> {
POST(HttpRequest.BodyPublishers.noBody()).build(),
HttpResponse.BodyHandlers.discarding()).
thenAcceptAsync(response -> LOG.info(
- "Pushed to {} with HTTP status: {}", target,
response.statusCode()));
+ "Pushed to {} with HTTP status: {}", target,
response.statusCode())).
+ exceptionally(error -> {
+ LOG.error("Failed to push to {}", target, error);
+ return null;
+ });
}
@PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_PUSH + "')")
public void pushToWA(final WAConfigService.PushSubject subject, final
List<String> services) {
try (HttpClient client = HttpClient.newHttpClient()) {
- serviceOps.list(NetworkService.Type.WA).stream().
+ serviceOps.list(NetworkService.Type.WA,
AuthContextUtils.getDomain()).stream().
filter(wa -> CollectionUtils.isEmpty(services) ||
services.contains(wa.getAddress())).
forEach(wa -> {
switch (subject) {
diff --git
a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
index efdedf03b9..5a9661fb46 100644
---
a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
+++
b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
@@ -39,8 +39,18 @@ public class DummyServiceOps implements ServiceOps {
return List.of();
}
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ return List.of();
+ }
+
@Override
public NetworkService get(final NetworkService.Type serviceType) {
return null;
}
+
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ return null;
+ }
}
diff --git
a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
index efdedf03b9..5a9661fb46 100644
---
a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
+++
b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
@@ -39,8 +39,18 @@ public class DummyServiceOps implements ServiceOps {
return List.of();
}
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ return List.of();
+ }
+
@Override
public NetworkService get(final NetworkService.Type serviceType) {
return null;
}
+
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ return null;
+ }
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java
index 0334abfa14..7a4b3b0122 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java
@@ -27,5 +27,7 @@ public interface NetworkServiceDAO extends
DAO<NetworkServiceEntity> {
List<NetworkServiceEntity> findAll(NetworkService.Type serviceType);
+ List<NetworkServiceEntity> findAll(NetworkService.Type serviceType, String
domain);
+
void deleteAll(NetworkService service);
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
index ff9959697b..4669668202 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
@@ -30,4 +30,8 @@ public interface NetworkServiceEntity extends Entity {
String getAddress();
void setAddress(String address);
+
+ String getDomain();
+
+ void setDomain(String domain);
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceCheck.java
similarity index 54%
copy from
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
copy to
core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceCheck.java
index ff9959697b..7a0ea76ff5 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java
+++
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceCheck.java
@@ -16,18 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.persistence.api.entity.keymaster;
+package org.apache.syncope.core.persistence.common.validation;
-import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
-import org.apache.syncope.core.persistence.api.entity.Entity;
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
-public interface NetworkServiceEntity extends Entity {
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = NetworkServiceValidator.class)
+@Documented
+public @interface NetworkServiceCheck {
- NetworkService.Type getType();
+ String message() default
"{org.apache.syncope.core.persistence.validation.service}";
- void setType(NetworkService.Type type);
+ Class<?>[] groups() default {};
- String getAddress();
-
- void setAddress(String address);
+ Class<? extends Payload>[] payload() default {};
}
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceValidator.java
similarity index 51%
copy from
core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
copy to
core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceValidator.java
index 331a258062..f616786f4d 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
+++
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/NetworkServiceValidator.java
@@ -16,18 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.persistence.neo4j.dao.repo;
+package org.apache.syncope.core.persistence.common.validation;
-import java.util.List;
+import jakarta.validation.ConstraintValidatorContext;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.lib.types.EntityViolationType;
import
org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity;
-import
org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jNetworkService;
-public interface NetworkServiceRepoExt {
+public class NetworkServiceValidator extends
AbstractValidator<NetworkServiceCheck, NetworkServiceEntity> {
- List<NetworkServiceEntity> findAll(NetworkService.Type serviceType);
+ @Override
+ public boolean isValid(final NetworkServiceEntity service, final
ConstraintValidatorContext context) {
+ context.disableDefaultConstraintViolation();
- <S extends Neo4jNetworkService> S save(S service);
+ if ((service.getType() == NetworkService.Type.SRA || service.getType()
== NetworkService.Type.WA)
+ && service.getDomain() == null) {
- void deleteAll(NetworkService service);
+ context.buildConstraintViolationWithTemplate(
+ getTemplate(EntityViolationType.Standard, "Domain must be
set for SRA and WA services")).
+ addPropertyNode("domain").addConstraintViolation();
+ return false;
+ }
+
+ return true;
+ }
}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java
index 25bcf4ebf7..264c29788f 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java
@@ -26,5 +26,7 @@ public interface NetworkServiceRepoExt {
List<NetworkServiceEntity> findAll(NetworkService.Type serviceType);
+ List<NetworkServiceEntity> findAll(NetworkService.Type serviceType, String
domain);
+
void deleteAll(NetworkService service);
}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java
index 8f4b280022..0bc6d4a13d 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java
@@ -45,6 +45,17 @@ public class NetworkServiceRepoExtImpl implements
NetworkServiceRepoExt {
return query.getResultList();
}
+ @Transactional(readOnly = true)
+ @Override
+ public List<NetworkServiceEntity> findAll(final NetworkService.Type
serviceType, final String domain) {
+ TypedQuery<NetworkServiceEntity> query = entityManager.createQuery(
+ "SELECT e FROM " + JPANetworkService.class.getSimpleName()
+ + " e WHERE e.type=:serviceType AND e.domain=:domain",
NetworkServiceEntity.class);
+ query.setParameter("serviceType", serviceType);
+ query.setParameter("domain", domain);
+ return query.getResultList();
+ }
+
@Override
public void deleteAll(final NetworkService service) {
Query query = entityManager.createQuery(
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java
index a1ddf5ae03..cef7c083fa 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java
@@ -26,11 +26,13 @@ import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import
org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity;
+import
org.apache.syncope.core.persistence.common.validation.NetworkServiceCheck;
import
org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
@Entity
@Table(name = JPANetworkService.TABLE, uniqueConstraints =
@UniqueConstraint(columnNames = { "type", "address" }))
+@NetworkServiceCheck
public class JPANetworkService extends AbstractGeneratedKeyEntity implements
NetworkServiceEntity {
private static final long serialVersionUID = 8742750097008236475L;
@@ -44,6 +46,8 @@ public class JPANetworkService extends
AbstractGeneratedKeyEntity implements Net
@NotNull
private String address;
+ private String domain;
+
@Override
public NetworkService.Type getType() {
return type;
@@ -63,4 +67,14 @@ public class JPANetworkService extends
AbstractGeneratedKeyEntity implements Net
public void setAddress(final String address) {
this.address = address;
}
+
+ @Override
+ public String getDomain() {
+ return domain;
+ }
+
+ @Override
+ public void setDomain(final String domain) {
+ this.domain = domain;
+ }
}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
index f9c83a2a6c..10f155eaad 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
@@ -31,6 +31,10 @@ import jakarta.persistence.TypedQueryReference;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.metamodel.Metamodel;
import java.io.Closeable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -53,7 +57,9 @@ import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
-public class DomainRoutingEntityManagerFactory implements
EntityManagerFactory, Closeable {
+public class DomainRoutingEntityManagerFactory implements
EntityManagerFactory, Closeable, Serializable {
+
+ private static final long serialVersionUID = 9013192242319940958L;
protected static final Logger LOG =
LoggerFactory.getLogger(DomainRoutingEntityManagerFactory.class);
@@ -280,4 +286,12 @@ public class DomainRoutingEntityManagerFactory implements
EntityManagerFactory,
public <R> R callInTransaction(final Function<EntityManager, R> fnctn) {
return delegate().callInTransaction(fnctn);
}
+
+ private void writeObject(final ObjectOutputStream oos) throws IOException {
+ // prevents unwanted serialization by JCache provider
+ }
+
+ private void readObject(final ObjectInputStream ois) throws IOException,
ClassNotFoundException {
+ // prevents unwanted deserialization by JCache provider
+ }
}
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
index 331a258062..5bba82679d 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java
@@ -27,6 +27,8 @@ public interface NetworkServiceRepoExt {
List<NetworkServiceEntity> findAll(NetworkService.Type serviceType);
+ List<NetworkServiceEntity> findAll(NetworkService.Type serviceType, String
domain);
+
<S extends Neo4jNetworkService> S save(S service);
void deleteAll(NetworkService service);
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java
index d519e107d9..936b12eaa3 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java
@@ -48,6 +48,17 @@ public class NetworkServiceRepoExtImpl implements
NetworkServiceRepoExt {
Neo4jNetworkService.class).stream().map(NetworkServiceEntity.class::cast).toList();
}
+ @Transactional(readOnly = true)
+ @Override
+ public List<NetworkServiceEntity> findAll(final NetworkService.Type
serviceType, final String domain) {
+ return neo4jTemplate.findAll(
+ "MATCH (n:" + Neo4jNetworkService.NODE + ") "
+ + "WHERE n.type = $serviceType AND n.domain = $ domain "
+ + "RETURN n",
+ Map.of("serviceType", serviceType.name(), "domain", domain),
+
Neo4jNetworkService.class).stream().map(NetworkServiceEntity.class::cast).toList();
+ }
+
@Override
public <S extends Neo4jNetworkService> S save(final S service) {
if (findAll(service.getType()).stream().anyMatch(s ->
s.getAddress().equals(service.getAddress()))) {
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java
index ec1e24e6ab..48219f384a 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java
@@ -21,10 +21,12 @@ package
org.apache.syncope.core.persistence.neo4j.entity.keymaster;
import jakarta.validation.constraints.NotNull;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import
org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity;
+import
org.apache.syncope.core.persistence.common.validation.NetworkServiceCheck;
import
org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode;
import org.springframework.data.neo4j.core.schema.Node;
@Node(Neo4jNetworkService.NODE)
+@NetworkServiceCheck
public class Neo4jNetworkService extends AbstractGeneratedKeyNode implements
NetworkServiceEntity {
private static final long serialVersionUID = 8742750097008236475L;
@@ -37,6 +39,8 @@ public class Neo4jNetworkService extends
AbstractGeneratedKeyNode implements Net
@NotNull
private String address;
+ private String domain;
+
@Override
public NetworkService.Type getType() {
return type;
@@ -56,4 +60,14 @@ public class Neo4jNetworkService extends
AbstractGeneratedKeyNode implements Net
public void setAddress(final String address) {
this.address = address;
}
+
+ @Override
+ public String getDomain() {
+ return domain;
+ }
+
+ @Override
+ public void setDomain(final String domain) {
+ this.domain = domain;
+ }
}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
index 10ab55c671..65b714ea8f 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java
@@ -18,6 +18,8 @@
*/
package org.apache.syncope.core.provisioning.java.job;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.time.OffsetDateTime;
import java.util.HashSet;
import java.util.Iterator;
@@ -61,6 +63,15 @@ public class DefaultJobManager implements JobManager,
SyncopeCoreLoader {
protected static final Logger LOG =
LoggerFactory.getLogger(JobManager.class);
+ protected static String getHostname() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ LOG.debug("While attempting to get hostname", e);
+ return "unknown-host";
+ }
+ }
+
protected final DomainHolder<?> domainHolder;
protected final SyncopeTaskScheduler scheduler;
@@ -394,7 +405,7 @@ public class DefaultJobManager implements JobManager,
SyncopeCoreLoader {
JobExecutionContext context = new JobExecutionContext(
domain,
-
StringUtils.uncapitalize(SystemLoadReporterJob.class.getSimpleName()),
+
StringUtils.uncapitalize(SystemLoadReporterJob.class.getSimpleName()) + "_on_"
+ getHostname(),
securityProperties.getAdminUser(),
false);
try {
diff --git
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
index a6b76a865d..03a0ee4ed0 100644
---
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
+++
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
@@ -47,6 +47,15 @@ public class SelfKeymasterInternalServiceOps implements
ServiceOps {
() -> logic.list(serviceType));
}
+ @Override
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ return AuthContextUtils.callAs(
+ SyncopeConstants.MASTER_DOMAIN,
+ props.getUsername(),
+ List.of(),
+ () -> logic.list(serviceType, domain));
+ }
+
@Override
public NetworkService get(final NetworkService.Type serviceType) {
try {
@@ -60,6 +69,19 @@ public class SelfKeymasterInternalServiceOps implements
ServiceOps {
}
}
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ try {
+ return AuthContextUtils.callAs(
+ SyncopeConstants.MASTER_DOMAIN,
+ props.getUsername(),
+ List.of(),
+ () -> logic.get(serviceType, domain));
+ } catch (Exception e) {
+ throw new KeymasterException(e);
+ }
+ }
+
@Override
public void register(final NetworkService service) {
AuthContextUtils.callAs(
diff --git
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
index c5cd51b1de..9bfcaa5cb0 100644
---
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
+++
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
@@ -19,6 +19,7 @@
package org.apache.syncope.core.keymaster.rest.cxf.service;
import java.util.List;
+import java.util.Optional;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import
org.apache.syncope.common.keymaster.rest.api.service.NetworkServiceService;
import org.apache.syncope.core.logic.NetworkServiceLogic;
@@ -34,13 +35,17 @@ public class NetworkServiceServiceImpl implements
NetworkServiceService {
}
@Override
- public List<NetworkService> list(final NetworkService.Type serviceType) {
- return logic.list(serviceType);
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ return Optional.ofNullable(domain).
+ map(d -> logic.list(serviceType, d)).
+ orElseGet(() -> logic.list(serviceType));
}
@Override
- public NetworkService get(final NetworkService.Type serviceType) {
- return logic.get(serviceType);
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ return Optional.ofNullable(domain).
+ map(d -> logic.get(serviceType, d)).
+ orElseGet(() -> logic.get(serviceType));
}
@Override
diff --git
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
index 094f1cde38..cfa4ac8c7e 100644
---
a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
+++
b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
@@ -32,6 +32,14 @@ import
org.springframework.transaction.annotation.Transactional;
public class NetworkServiceLogic extends AbstractTransactionalLogic<EntityTO> {
+ protected static NetworkService toNetworkService(final
NetworkServiceEntity service) {
+ NetworkService ns = new NetworkService();
+ ns.setType(service.getType());
+ ns.setAddress(service.getAddress());
+ ns.setDomain(service.getDomain());
+ return ns;
+ }
+
protected final NetworkServiceDAO serviceDAO;
protected final EntityFactory entityFactory;
@@ -41,21 +49,16 @@ public class NetworkServiceLogic extends
AbstractTransactionalLogic<EntityTO> {
this.entityFactory = entityFactory;
}
- protected NetworkService toNetworkService(
- final NetworkService.Type serviceType,
- final NetworkServiceEntity service) {
-
- NetworkService ns = new NetworkService();
- ns.setType(serviceType);
- ns.setAddress(service.getAddress());
- return ns;
+ @PreAuthorize("@environment.getProperty('keymaster.username') ==
authentication.name")
+ @Transactional(readOnly = true)
+ public List<NetworkService> list(final NetworkService.Type serviceType) {
+ return
serviceDAO.findAll(serviceType).stream().map(NetworkServiceLogic::toNetworkService).toList();
}
@PreAuthorize("@environment.getProperty('keymaster.username') ==
authentication.name")
@Transactional(readOnly = true)
- public List<NetworkService> list(final NetworkService.Type serviceType) {
- return serviceDAO.findAll(serviceType).stream().
- map(service -> toNetworkService(serviceType,
service)).toList();
+ public List<NetworkService> list(final NetworkService.Type serviceType,
final String domain) {
+ return serviceDAO.findAll(serviceType,
domain).stream().map(NetworkServiceLogic::toNetworkService).toList();
}
@PreAuthorize("@environment.getProperty('keymaster.username') ==
authentication.name")
@@ -71,6 +74,19 @@ public class NetworkServiceLogic extends
AbstractTransactionalLogic<EntityTO> {
: list.get(SecureRandomUtils.generateRandomInt(0,
list.size()));
}
+ @PreAuthorize("@environment.getProperty('keymaster.username') ==
authentication.name")
+ @Transactional(readOnly = true)
+ public NetworkService get(final NetworkService.Type serviceType, final
String domain) {
+ List<NetworkService> list = list(serviceType, domain);
+ if (list.isEmpty()) {
+ throw new NotFoundException("No registered services for type " +
serviceType + " and domain " + domain);
+ }
+
+ return list.size() == 1
+ ? list.getFirst()
+ : list.get(SecureRandomUtils.generateRandomInt(0,
list.size()));
+ }
+
@PreAuthorize("@environment.getProperty('keymaster.username') ==
authentication.name")
public void register(final NetworkService networkService) {
if (serviceDAO.findAll(networkService.getType()).stream().
@@ -79,6 +95,7 @@ public class NetworkServiceLogic extends
AbstractTransactionalLogic<EntityTO> {
NetworkServiceEntity service =
entityFactory.newEntity(NetworkServiceEntity.class);
service.setType(networkService.getType());
service.setAddress(networkService.getAddress());
+ service.setDomain(networkService.getDomain());
serviceDAO.save(service);
}
}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java
index c0a3951d0c..5e6bbb670b 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoader.java
@@ -77,9 +77,8 @@ public class MSEntraJWSVerifierCacheLoader implements
CacheLoader<String, JWSVer
}
protected String fetchDocument(final String url) {
- HttpResponse<String> response;
- try {
- response = HttpClient.newHttpClient().send(
+ try (HttpClient client = HttpClient.newHttpClient()) {
+ HttpResponse<String> response = client.send(
HttpRequest.newBuilder().uri(URI.create(url)).build(),
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
diff --git a/docker/wa/src/main/resources/wa-docker.properties
b/docker/wa/src/main/resources/wa-docker.properties
index 4c91c4f52e..a56a6092ff 100644
--- a/docker/wa/src/main/resources/wa-docker.properties
+++ b/docker/wa/src/main/resources/wa-docker.properties
@@ -25,6 +25,8 @@ service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
wa.anonymousUser=${ANONYMOUS_USER:anonymous}
wa.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+wa.domain=${DOMAIN:Master}
+
spring.security.user.name=${ANONYMOUS_USER:anonymous}
spring.security.user.password=${ANONYMOUS_KEY:anonymousKey}
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
index 34e76ff91e..14b2c7982f 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
@@ -175,6 +175,7 @@ public class KeymasterITCase extends AbstractITCase {
NetworkService sra1 = new NetworkService();
sra1.setType(NetworkService.Type.SRA);
sra1.setAddress("http://localhost:9080/syncope-sra");
+ sra1.setDomain(SyncopeConstants.MASTER_DOMAIN);
serviceOps.register(sra1);
list = findNetworkServices(NetworkService.Type.SRA, List::isEmpty, 30);
@@ -187,6 +188,7 @@ public class KeymasterITCase extends AbstractITCase {
NetworkService sra2 = new NetworkService();
sra2.setType(NetworkService.Type.SRA);
sra2.setAddress("http://localhost:9080/syncope-sra");
+ sra2.setDomain(SyncopeConstants.MASTER_DOMAIN);
assertEquals(sra1, sra2);
serviceOps.register(sra2);
diff --git a/fit/wa-reference/pom.xml b/fit/wa-reference/pom.xml
index b2721d3883..2a0e78053a 100644
--- a/fit/wa-reference/pom.xml
+++ b/fit/wa-reference/pom.xml
@@ -365,6 +365,14 @@ under the License.
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
+ <resource>
+ <directory>${basedir}/../../wa/starter/src/main/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>cas-theme-default.properties</include>
+ <include>wa.properties</include>
+ </includes>
+ </resource>
</resources>
<testResources>
<testResource>
diff --git a/fit/wa-reference/src/main/resources/wa-embedded.properties
b/fit/wa-reference/src/main/resources/wa-embedded.properties
index c243c0ca03..40d6658489 100644
--- a/fit/wa-reference/src/main/resources/wa-embedded.properties
+++ b/fit/wa-reference/src/main/resources/wa-embedded.properties
@@ -44,4 +44,4 @@
cas.tgc.crypto.encryption.key=mW6lMvsSo48eZ1Ntt74a-O9jjQQQ_OLUE24RVN2_A_sPX43mpB
cas.webflow.crypto.signing.key=Md6kkPlXx5L18TD0mFELpQXWnDbMffj-uPutPckMnAPPuJQEbfcLLYBnOynYIEDgnEpd7sxUwGYd8_sVYFMcjw
cas.webflow.crypto.encryption.key=FhLgLpaPL8GVNuqqo7gtiw
-management.endpoints.web.exposure.include=info,health,env,beans,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent,multitenancy
+management.endpoints.web.exposure.include=info,health,env,beans,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent
diff --git a/fit/wa-reference/src/main/resources/wa-multitenancy.properties
b/fit/wa-reference/src/main/resources/wa-multitenancy.properties
deleted file mode 100644
index a95a51ea7a..0000000000
--- a/fit/wa-reference/src/main/resources/wa-multitenancy.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-cas.multitenancy.core.enabled=true
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index d49586bf83..994a5e2d3c 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -248,8 +248,7 @@ public abstract class AbstractITCase {
final String password,
final String body,
final CloseableHttpClient httpclient,
- final HttpClientContext context,
- final String... tenant)
+ final HttpClientContext context)
throws IOException {
List<NameValuePair> form = new ArrayList<>();
@@ -259,7 +258,7 @@ public abstract class AbstractITCase {
form.add(new BasicNameValuePair("password", password));
form.add(new BasicNameValuePair("geolocation", ""));
- HttpPost post = new HttpPost(WA_ADDRESS + (tenant.length == 0 ? "" :
"/tenants/" + tenant[0]) + "/login");
+ HttpPost post = new HttpPost(WA_ADDRESS + "/login");
post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8));
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/MultitenancyITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/MultitenancyITCase.java
deleted file mode 100644
index 28bb560b18..0000000000
---
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/MultitenancyITCase.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit;
-
-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;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
-
-import jakarta.ws.rs.core.GenericType;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.Response;
-import java.io.IOException;
-import java.util.Optional;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.impl.client.BasicCookieStore;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
-import org.apache.syncope.common.lib.request.UserCR;
-import org.apache.syncope.common.lib.to.AuthModuleTO;
-import org.apache.syncope.common.lib.to.ProvisioningResult;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.rest.api.beans.RealmQuery;
-import org.apache.syncope.common.rest.api.service.AuthModuleService;
-import org.apache.syncope.common.rest.api.service.RealmService;
-import org.apache.syncope.common.rest.api.service.UserService;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-public class MultitenancyITCase extends AbstractITCase {
-
- protected static final String MT_USERNAME = "multitenancy";
-
- protected static final String MT_PASSWORD = "password";
-
- @BeforeAll
- public static void multitenancySetup() {
-
assumeTrue(Optional.ofNullable(System.getProperties().getProperty("spring.profiles.active")).
- map(profiles ->
profiles.contains("multitenancy")).orElse(false));
-
- CLIENT_FACTORY = new
SyncopeClientFactoryBean().setAddress(CORE_ADDRESS).setDomain("Two");
-
- ADMIN_CLIENT = CLIENT_FACTORY.create(ADMIN_UNAME, "password2");
-
- // 1. create Syncope auth module
- AuthModuleService authModuleService =
ADMIN_CLIENT.getService(AuthModuleService.class);
- AuthModuleTO syncopeAuthModule = null;
- try {
- syncopeAuthModule = authModuleService.read("syncopeTwo");
- } catch (SyncopeClientException e) {
- if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
- SyncopeAuthModuleConf conf = new SyncopeAuthModuleConf();
- conf.setDomain("Two");
-
- syncopeAuthModule = new AuthModuleTO();
- syncopeAuthModule.setKey("syncopeTwo");
- syncopeAuthModule.setConf(conf);
-
- Response response =
authModuleService.create(syncopeAuthModule);
- assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatusInfo().getStatusCode());
- }
- }
- assertNotNull(syncopeAuthModule);
-
- // 2. create user
- assertNull(ADMIN_CLIENT.getService(RealmService.class).
- search(new
RealmQuery.Builder().build()).getResult().getFirst().getPasswordPolicy());
-
- UserService userService = ADMIN_CLIENT.getService(UserService.class);
- UserTO user = null;
- try {
- user = userService.read(MT_USERNAME);
- } catch (SyncopeClientException e) {
- if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
- UserCR userCR = new UserCR();
- userCR.setRealm(SyncopeConstants.ROOT_REALM);
- userCR.setUsername(MT_USERNAME);
- userCR.setPassword(MT_PASSWORD);
-
- Response response =
ADMIN_CLIENT.getService(UserService.class).create(userCR);
- assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatus());
-
- ProvisioningResult<UserTO> result = response.readEntity(new
GenericType<>() {
- });
- user = result.getEntity();
- }
- }
- assertNotNull(user);
- }
-
- @Test
- public void login() throws IOException {
- try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
- HttpClientContext context = HttpClientContext.create();
- context.setCookieStore(new BasicCookieStore());
-
- String loginPageBody;
- try (CloseableHttpResponse response =
- httpclient.execute(new HttpGet(WA_ADDRESS +
"/tenants/Two/login"), context)) {
-
- assertEquals(HttpStatus.SC_OK,
response.getStatusLine().getStatusCode());
- loginPageBody = EntityUtils.toString(response.getEntity());
- }
- assertNotNull(loginPageBody);
-
- String location;
- try (CloseableHttpResponse response =
- authenticateToWA(MT_USERNAME, MT_PASSWORD, loginPageBody,
httpclient, context, "Two")) {
-
- assertEquals(HttpStatus.SC_MOVED_TEMPORARILY,
response.getStatusLine().getStatusCode());
- location =
response.getFirstHeader(HttpHeaders.LOCATION).getValue();
- }
- assertTrue(location.endsWith("/account"));
-
- try (CloseableHttpResponse response =
- httpclient.execute(new HttpGet(WA_ADDRESS +
"/tenants/Two/account"), context)) {
-
- assertEquals(HttpStatus.SC_OK,
response.getStatusLine().getStatusCode());
-
assertTrue(EntityUtils.toString(response.getEntity()).contains(MT_USERNAME));
- }
- }
- }
-}
diff --git a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
index 24a5a8c1f6..7d7d7f1670 100644
--- a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
+++ b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
@@ -112,6 +112,8 @@ public class RouteProvider {
protected final String anonymousKey;
+ protected final String domain;
+
protected final boolean useGZIPCompression;
protected SyncopeClient client;
@@ -123,12 +125,14 @@ public class RouteProvider {
final ConfigurableApplicationContext ctx,
final String anonymousUser,
final String anonymousKey,
+ final String domain,
final boolean useGZIPCompression) {
this.serviceOps = serviceOps;
this.ctx = ctx;
this.anonymousUser = anonymousUser;
this.anonymousKey = anonymousKey;
+ this.domain = domain;
this.useGZIPCompression = useGZIPCompression;
}
@@ -573,6 +577,7 @@ public class RouteProvider {
try {
client = new SyncopeClientFactoryBean().
setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
+ setDomain(domain).
setUseCompression(useGZIPCompression).
create(new
BasicAuthenticationHandler(anonymousUser, anonymousKey));
} catch (Exception e) {
diff --git a/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
b/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
index ad541235a3..c7c0ffdff7 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
@@ -23,6 +23,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.SyncopeProperties;
import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apereo.cas.client.Protocol;
@@ -366,6 +367,8 @@ public class SRAProperties extends SyncopeProperties {
}
}
+ private String domain = SyncopeConstants.MASTER_DOMAIN;
+
private final Global global = new Global();
private AMType amType = AMType.OIDC;
@@ -378,6 +381,14 @@ public class SRAProperties extends SyncopeProperties {
private final CAS cas = new CAS();
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(final String domain) {
+ this.domain = domain;
+ }
+
public Global getGlobal() {
return global;
}
diff --git
a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
index bc0eac198d..44eb1c5036 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
@@ -79,6 +79,7 @@ public class SyncopeSRAApplication {
ctx,
props.getAnonymousUser(),
props.getAnonymousKey(),
+ props.getDomain(),
props.isUseGZIPCompression());
}
@@ -116,21 +117,22 @@ public class SyncopeSRAApplication {
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
- public KeymasterStart keymasterStart() {
- return new KeymasterStart(NetworkService.Type.SRA);
+ public KeymasterStart keymasterStart(final SRAProperties props) {
+ return new
KeymasterStart.Builder(NetworkService.Type.SRA).domain(props.getDomain()).build();
}
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
- public KeymasterStop keymasterStop() {
- return new KeymasterStop(NetworkService.Type.SRA);
+ public KeymasterStop keymasterStop(final SRAProperties props) {
+ return new
KeymasterStop.Builder(NetworkService.Type.SRA).domain(props.getDomain()).build();
}
@Bean
public WebExceptionHandler syncopeSRAWebExceptionHandler(
@Qualifier("routeProvider") final RouteProvider routeProvider,
final SRAProperties props) {
+
return new SyncopeSRAWebExceptionHandler(routeProvider, props);
}
}
diff --git a/sra/src/main/resources/sra.properties
b/sra/src/main/resources/sra.properties
index 7b1208157d..914deb22d0 100644
--- a/sra/src/main/resources/sra.properties
+++ b/sra/src/main/resources/sra.properties
@@ -37,6 +37,7 @@ logging.config=classpath:log4j2.xml
sra.anonymousUser=${anonymousUser}
sra.anonymousKey=${anonymousKey}
+sra.domain=Master
sra.useGZIPCompression=true
sra.global.error=/error
diff --git a/src/main/asciidoc/reference-guide/concepts/domains.adoc
b/src/main/asciidoc/reference-guide/concepts/domains.adoc
index 379cbe2964..dc327114f0 100644
--- a/src/main/asciidoc/reference-guide/concepts/domains.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/domains.adoc
@@ -26,9 +26,8 @@ External Resources, Policies, Tasks, etc. from different
domains (e.g. tenants)
By default, a single `Master` domain is defined, which also bears the
configuration for additional domains.
-Every domain besides `Master` is mapped one-to-one with a
-https://apereo.github.io/cas/8.0.x/multitenancy/Multitenancy-Overview.html[CAS
tenant^] having the same identifier; this
-allows for <<web-access>> configuration to relate only to the given domain's
database instance.
+Each <<secure-remote-access>> and <<web-access>> instance is configured to
bind to a given domain. +
+This allows to provide segregated configuration for all access-related
features.
[.text-center]
image::domains.png[title="Domains",alt="Domains"]
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
index dea90b075a..b3cada90b1 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.wa.bootstrap;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.wa.bootstrap.mapping.AttrReleaseMapper;
import org.apache.syncope.wa.bootstrap.mapping.AttrRepoPropertySourceMapper;
import org.apache.syncope.wa.bootstrap.mapping.AuthModulePropertySourceMapper;
@@ -48,6 +49,9 @@ public class WABootstrapConfiguration {
@Value("${wa.anonymousKey}")
private String anonymousKey;
+ @Value("${wa.domain:" + SyncopeConstants.MASTER_DOMAIN + "}")
+ private String domain;
+
@Value("${wa.useGZIPCompression:true}")
private boolean useGZIPCompression;
@@ -56,7 +60,7 @@ public class WABootstrapConfiguration {
@Bean
public WARestClient waRestClient() {
- return new WARestClient(anonymousUser, anonymousKey,
useGZIPCompression, serviceDiscoveryAddress);
+ return new WARestClient(anonymousUser, anonymousKey, domain,
useGZIPCompression, serviceDiscoveryAddress);
}
}
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
index 9735b3e6ca..3aef58a981 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.wa.bootstrap;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.SyncopeProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -26,8 +27,18 @@ public class WAProperties extends SyncopeProperties {
private static final long serialVersionUID = 7925827623055998239L;
+ private String domain = SyncopeConstants.MASTER_DOMAIN;
+
private int contextRefreshDelaySeconds = 15;
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(final String domain) {
+ this.domain = domain;
+ }
+
public int getContextRefreshDelaySeconds() {
return contextRefreshDelaySeconds;
}
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
index 12920a4aef..aa1dba9a68 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
@@ -29,7 +29,6 @@ import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
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.SyncopeConstants;
import org.apereo.cas.util.spring.ApplicationContextProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,6 +42,8 @@ public class WARestClient {
protected final String anonymousKey;
+ protected final String domain;
+
protected final boolean useGZIPCompression;
protected final String serviceDiscoveryAddress;
@@ -54,11 +55,13 @@ public class WARestClient {
public WARestClient(
final String anonymousUser,
final String anonymousKey,
+ final String domain,
final boolean useGZIPCompression,
final String serviceDiscoveryAddress) {
this.anonymousUser = anonymousUser;
this.anonymousKey = anonymousKey;
+ this.domain = domain;
this.useGZIPCompression = useGZIPCompression;
this.serviceDiscoveryAddress = serviceDiscoveryAddress;
}
@@ -90,38 +93,24 @@ public class WARestClient {
return Optional.empty();
}
- public boolean isReady() {
- try {
- return getCore().isPresent();
- } catch (Exception e) {
- LOG.trace("While checking Core's availability: {}",
e.getMessage());
- }
- return false;
- }
-
- public Optional<SyncopeClient> getSyncopeClient(final String domain) {
- return getCore().flatMap(core -> {
- try {
- return Optional.of(new SyncopeClientFactoryBean().
- setAddress(core.getAddress()).
- setUseCompression(useGZIPCompression).
- setDomain(domain).
- create(new BasicAuthenticationHandler(anonymousUser,
anonymousKey)));
- } catch (Exception e) {
- LOG.error("Could not init SyncopeClient", e);
- return Optional.empty();
- }
- });
- }
-
public SyncopeClient getSyncopeClient() {
synchronized (this) {
if (client == null) {
- client =
getSyncopeClient(SyncopeConstants.MASTER_DOMAIN).orElse(null);
+ getCore().ifPresent(core -> {
+ try {
+ client = new SyncopeClientFactoryBean().
+ setAddress(core.getAddress()).
+ setDomain(domain).
+ setUseCompression(useGZIPCompression).
+ create(new
BasicAuthenticationHandler(anonymousUser, anonymousKey));
+ } catch (Exception e) {
+ LOG.error("Could not init SyncopeClient", e);
+ }
+ });
}
- }
- return client;
+ return client;
+ }
}
@SuppressWarnings("unchecked")
@@ -132,4 +121,13 @@ public class WARestClient {
return (T) services.computeIfAbsent(serviceClass, k ->
getSyncopeClient().getService(k));
}
+
+ public boolean isReady() {
+ try {
+ return getCore().isPresent();
+ } catch (Exception e) {
+ LOG.trace("While checking Core's availability: {}",
e.getMessage());
+ }
+ return false;
+ }
}
diff --git
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
index abb4ae129d..e4b2400e9e 100644
---
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
+++
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
@@ -32,7 +32,6 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.keymaster.client.api.DomainOps;
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;
@@ -40,8 +39,6 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.wa.bootstrap.WAProperties;
import org.apache.syncope.wa.bootstrap.WARestClient;
import org.apache.syncope.wa.bootstrap.mapping.AttrReleaseMapper;
-import org.apache.syncope.wa.bootstrap.mapping.AttrRepoPropertySourceMapper;
-import org.apache.syncope.wa.bootstrap.mapping.AuthModulePropertySourceMapper;
import org.apache.syncope.wa.starter.actuate.SyncopeCoreHealthIndicator;
import org.apache.syncope.wa.starter.actuate.SyncopeWAInfoContributor;
import org.apache.syncope.wa.starter.audit.WAAuditTrailManager;
@@ -65,7 +62,6 @@ import
org.apache.syncope.wa.starter.mapping.SAML2SPClientAppTOMapper;
import org.apache.syncope.wa.starter.mapping.TicketExpirationMapper;
import org.apache.syncope.wa.starter.mapping.TimeBasedAccessMapper;
import
org.apache.syncope.wa.starter.mfa.WAMultifactorAuthenticationTrustStorage;
-import org.apache.syncope.wa.starter.multitenancy.WATenantsManager;
import
org.apache.syncope.wa.starter.oidc.WAOidcJsonWebKeystoreGeneratorService;
import org.apache.syncope.wa.starter.pac4j.saml.WASAML2ClientCustomizer;
import
org.apache.syncope.wa.starter.saml.idp.metadata.WASamlIdPMetadataCacheRefresher;
@@ -86,7 +82,6 @@ import org.apereo.cas.configuration.support.JpaBeans;
import org.apereo.cas.consent.ConsentRepositoryBuilder;
import org.apereo.cas.gauth.CasGoogleAuthenticator;
import
org.apereo.cas.gauth.credential.LdapGoogleAuthenticatorTokenCredentialRepository;
-import org.apereo.cas.multitenancy.TenantsManager;
import org.apereo.cas.oidc.jwks.generator.OidcJsonWebKeystoreGeneratorService;
import
org.apereo.cas.otp.repository.credentials.OneTimeTokenCredentialRepository;
import org.apereo.cas.pm.LdapPasswordManagementService;
@@ -607,34 +602,17 @@ public class WAContext {
return new InMemoryUserDetailsManager(user);
}
- @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
- @ConditionalOnProperty(
- prefix = "cas.multitenancy.core", name = "enabled", havingValue =
"true", matchIfMissing = false)
- @Bean(name = TenantsManager.BEAN_NAME)
- public TenantsManager tenantsManager(
- final DomainOps domainOps,
- final WARestClient waRestClient,
- final AuthModulePropertySourceMapper
authModulePropertySourceMapper,
- final AttrRepoPropertySourceMapper attrRepoPropertySourceMapper) {
-
- return new WATenantsManager(
- domainOps,
- waRestClient,
- authModulePropertySourceMapper,
- attrRepoPropertySourceMapper);
- }
-
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
- public KeymasterStart keymasterStart() {
- return new KeymasterStart(NetworkService.Type.WA);
+ public KeymasterStart keymasterStart(final WAProperties waProperties) {
+ return new
KeymasterStart.Builder(NetworkService.Type.WA).domain(waProperties.getDomain()).build();
}
@ConditionalOnProperty(
prefix = "keymaster", name = "enableAutoRegistration", havingValue
= "true", matchIfMissing = true)
@Bean
- public KeymasterStop keymasterStop() {
- return new KeymasterStop(NetworkService.Type.WA);
+ public KeymasterStop keymasterStop(final WAProperties waProperties) {
+ return new
KeymasterStop.Builder(NetworkService.Type.WA).domain(waProperties.getDomain()).build();
}
}
diff --git
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/multitenancy/WATenantsManager.java
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/multitenancy/WATenantsManager.java
deleted file mode 100644
index b400b9dc6d..0000000000
---
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/multitenancy/WATenantsManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.wa.starter.multitenancy;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.keymaster.client.api.DomainOps;
-import org.apache.syncope.common.rest.api.service.AttrRepoService;
-import org.apache.syncope.common.rest.api.service.AuthModuleService;
-import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
-import org.apache.syncope.wa.bootstrap.WAPropertySourceLocator;
-import org.apache.syncope.wa.bootstrap.WARestClient;
-import org.apache.syncope.wa.bootstrap.mapping.AttrRepoPropertySourceMapper;
-import org.apache.syncope.wa.bootstrap.mapping.AuthModulePropertySourceMapper;
-import org.apereo.cas.multitenancy.DefaultTenantAuthenticationPolicy;
-import org.apereo.cas.multitenancy.DefaultTenantDelegatedAuthenticationPolicy;
-import org.apereo.cas.multitenancy.TenantDefinition;
-import org.apereo.cas.multitenancy.TenantsManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class WATenantsManager implements TenantsManager {
-
- protected static final Logger LOG =
LoggerFactory.getLogger(WATenantsManager.class);
-
- protected final DomainOps domainOps;
-
- protected final WARestClient waRestClient;
-
- protected final AuthModulePropertySourceMapper
authModulePropertySourceMapper;
-
- protected final AttrRepoPropertySourceMapper attrRepoPropertySourceMapper;
-
- public WATenantsManager(
- final DomainOps domainOps,
- final WARestClient waRestClient,
- final AuthModulePropertySourceMapper
authModulePropertySourceMapper,
- final AttrRepoPropertySourceMapper attrRepoPropertySourceMapper) {
-
- this.domainOps = domainOps;
- this.waRestClient = waRestClient;
- this.authModulePropertySourceMapper = authModulePropertySourceMapper;
- this.attrRepoPropertySourceMapper = attrRepoPropertySourceMapper;
- }
-
- protected TenantDefinition buildTenantDefinition(final SyncopeClient
syncopeClient) {
- TenantDefinition tenantDefinition = new TenantDefinition();
- tenantDefinition.setId(syncopeClient.getDomain());
-
- Map<String, Object> properties = new TreeMap<>();
- Map<String, Integer> prefixes = new HashMap<>();
-
- DefaultTenantAuthenticationPolicy authPolicy = new
DefaultTenantAuthenticationPolicy();
- tenantDefinition.setAuthenticationPolicy(authPolicy);
-
- DefaultTenantDelegatedAuthenticationPolicy delegatedAuthPolicy =
- new DefaultTenantDelegatedAuthenticationPolicy();
- tenantDefinition.setDelegatedAuthenticationPolicy(delegatedAuthPolicy);
-
- authPolicy.setAuthenticationHandlers(new ArrayList<>());
- delegatedAuthPolicy.setAllowedProviders(new ArrayList<>());
-
syncopeClient.getService(AuthModuleService.class).list().forEach(authModuleTO
-> {
- LOG.debug("Mapping auth module {} ", authModuleTO.getKey());
-
- Map<String, Object> map = authModuleTO.getConf().map(authModuleTO,
authModulePropertySourceMapper);
- properties.putAll(WAPropertySourceLocator.index(map, prefixes));
-
- if (map.keySet().stream().anyMatch(k -> k.contains("pac4j"))) {
-
delegatedAuthPolicy.getAllowedProviders().add(authModuleTO.getKey());
- } else {
-
authPolicy.getAuthenticationHandlers().add(authModuleTO.getKey());
- }
- });
-
- authPolicy.setAttributeRepositories(new ArrayList<>());
-
syncopeClient.getService(AttrRepoService.class).list().forEach(attrRepoTO -> {
- LOG.debug("Mapping attr repo {} ", attrRepoTO.getKey());
-
- Map<String, Object> map = attrRepoTO.getConf().map(attrRepoTO,
attrRepoPropertySourceMapper);
- properties.putAll(WAPropertySourceLocator.index(map, prefixes));
-
- authPolicy.getAttributeRepositories().add(attrRepoTO.getKey());
- });
-
- syncopeClient.getService(WAConfigService.class).list().
- forEach(attr -> properties.put(attr.getSchema(),
String.join(",", attr.getValues())));
-
- tenantDefinition.setProperties(properties);
- LOG.debug("Collected Tenant {} properties: {}",
tenantDefinition.getId(), tenantDefinition.getProperties());
-
- return tenantDefinition;
- }
-
- @Override
- public Optional<TenantDefinition> findTenant(final String tenantId) {
- return
waRestClient.getSyncopeClient(tenantId).map(this::buildTenantDefinition);
- }
-
- @Override
- public List<TenantDefinition> findTenants() {
- List<TenantDefinition> tenants = new ArrayList<>();
- domainOps.list().forEach(domain ->
findTenant(domain.getKey()).ifPresent(tenants::add));
- return tenants;
- }
-
- @Override
- public TenantDefinition save(final TenantDefinition tenantDefinition) {
- // Syncope Domains are managed by Core
- return tenantDefinition;
- }
-
- @Override
- public boolean delete(final String tenantId) {
- // Syncope Domains are managed by Core
- return false;
- }
-
- @Override
- public void load() {
- // nothing to do
- }
-}
diff --git a/wa/starter/src/main/resources/wa.properties
b/wa/starter/src/main/resources/wa.properties
index 21fc8b05f3..c2f8bb9981 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -37,7 +37,7 @@
spring.web.resources.static-locations=classpath:/thymeleaf/static,classpath:/syn
cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
management.endpoints.access.default=UNRESTRICTED
-management.endpoints.web.exposure.include=info,health,env,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent,multitenancy
+management.endpoints.web.exposure.include=info,health,env,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent
management.endpoint.health.show-details=ALWAYS
management.endpoint.env.show-values=WHEN_AUTHORIZED
spring.cloud.discovery.client.health-indicator.enabled=false
@@ -64,6 +64,7 @@ service.discovery.address=http://localhost:8080/syncope-wa/
wa.anonymousUser=${anonymousUser}
wa.anonymousKey=${anonymousKey}
+wa.domain=Master
wa.useGZIPCompression=true
wa.contextRefreshDelaySeconds=15
diff --git a/wa/starter/src/test/resources/debug/wa-debug.properties
b/wa/starter/src/test/resources/debug/wa-debug.properties
index 6f14c491ba..7343d1e3e4 100644
--- a/wa/starter/src/test/resources/debug/wa-debug.properties
+++ b/wa/starter/src/test/resources/debug/wa-debug.properties
@@ -21,7 +21,7 @@ keymaster.address=http://localhost:9080/syncope/rest/keymaster
keymaster.username=${anonymousUser}
keymaster.password=${anonymousKey}
-management.endpoints.web.exposure.include=info,health,env,beans,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent,multitenancy
+management.endpoints.web.exposure.include=info,health,env,beans,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes,attributeConsent
cas.server.name=http://localhost:8080
cas.server.prefix=${cas.server.name}/syncope-wa