You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/10/08 02:27:49 UTC

[james-project] branch master updated: JAMES-3657 Generalise entity validation Upon creation (#681)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new b4b0a9a  JAMES-3657 Generalise entity validation Upon creation (#681)
b4b0a9a is described below

commit b4b0a9abe74b3dce63b8928c2f5bb929a3325f2e
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Fri Oct 8 09:27:45 2021 +0700

    JAMES-3657 Generalise entity validation Upon creation (#681)
---
 .../james/cli/DataCommandsIntegrationTest.java     | 21 +++++++++++
 .../java/org/apache/james/CoreDataModule.java}     | 33 ++++++++---------
 server/container/guice/data-cassandra/pom.xml      |  4 +++
 .../modules/data/CassandraDomainListModule.java    | 13 ++-----
 .../apache/james/modules/data/JPADataModule.java   |  4 ++-
 .../james/modules/data/JPADomainListModule.java    | 10 ------
 .../james/modules/data/MemoryDataModule.java       | 16 ++++-----
 .../james/modules/server/DataRoutesModules.java    | 17 ---------
 server/container/spring/pom.xml                    |  4 +++
 .../resource/SpringUserEntityValidator.java}       | 41 ++++++++++++++--------
 .../apache/james/spring-mailbox-authenticator.xml  |  2 ++
 .../james/rrt/api/MappingConflictException.java}   | 13 +++----
 .../CassandraRecipientRewriteTableTest.java        |  8 +++++
 .../james/rrt/cassandra/CassandraStepdefs.java     |  8 ++++-
 server/data/data-file/pom.xml                      | 10 ++++++
 .../rrt/file/XMLRecipientRewriteTableTest.java     |  7 ++++
 server/data/data-jpa/pom.xml                       |  5 +++
 .../rrt/jpa/JPARecipientRewriteTableTest.java      |  5 +++
 .../java/org/apache/james/rrt/jpa/JPAStepdefs.java |  2 ++
 .../james}/AggregateUserEntityValidator.java       |  2 +-
 .../apache/james}/DefaultUserEntityValidator.java  |  2 +-
 .../RecipientRewriteTableUserEntityValidator.java  |  2 +-
 .../org/apache/james}/UserEntityValidator.java     |  2 +-
 .../rrt/lib/AbstractRecipientRewriteTable.java     | 41 ++++++++++++++++++++++
 .../apache/james/user/lib/UsersRepositoryImpl.java | 30 +++++++++++++---
 .../rrt/lib/RecipientRewriteTableContract.java     |  3 +-
 .../rrt/lib/AliasReverseResolverImplTest.java      |  4 +++
 .../apache/james/rrt/lib/CanSendFromImplTest.java  |  4 +++
 .../apache/james/rrt/memory/InMemoryStepdefs.java  |  8 ++++-
 .../memory/MemoryRecipientRewriteTableTest.java    | 14 +++++++-
 .../methods/SetMessagesCreationProcessorTest.java  |  4 +++
 .../methods/SetMessagesUpdateProcessorTest.java    |  4 +++
 .../apache/james/lmtpserver/LmtpServerTest.java    |  4 ++-
 .../mailbox/DistributedPop3ServerTest.java         |  2 ++
 .../apache/james/pop3server/POP3ServerTest.java    |  3 ++
 .../java/org/apache/james/smtpserver/DSNTest.java  |  2 ++
 .../apache/james/smtpserver/SMTPServerTest.java    |  2 ++
 .../apache/james/webadmin/routes/AliasRoutes.java  | 27 ++------------
 .../apache/james/webadmin/routes/GroupsRoutes.java | 39 +++++---------------
 .../apache/james/webadmin/service/UserService.java | 14 +-------
 .../james/webadmin/routes/AliasRoutesTest.java     | 16 +++++----
 .../james/webadmin/routes/GroupsRoutesTest.java    | 14 +++++---
 .../james/webadmin/routes/MappingRoutesTest.java   |  6 +++-
 .../james/webadmin/routes/UserRoutesTest.java      | 29 +++++++++------
 44 files changed, 307 insertions(+), 194 deletions(-)

diff --git a/server/apps/cli-integration-tests/src/test/java/org/apache/james/cli/DataCommandsIntegrationTest.java b/server/apps/cli-integration-tests/src/test/java/org/apache/james/cli/DataCommandsIntegrationTest.java
index 09de657..7b1c3da 100644
--- a/server/apps/cli-integration-tests/src/test/java/org/apache/james/cli/DataCommandsIntegrationTest.java
+++ b/server/apps/cli-integration-tests/src/test/java/org/apache/james/cli/DataCommandsIntegrationTest.java
@@ -42,6 +42,7 @@ class DataCommandsIntegrationTest {
     public static final String DOMAIN = "domain.com";
     public static final String USER = "chibenwa";
     public static final String MAIL_ADDRESS = USER + "@" + DOMAIN;
+    public static final String OTHER_MAIL_ADDRESS = USER + "other@" + DOMAIN;
     public static final String PASSWORD = "12345";
     private OutputCapture outputCapture;
 
@@ -105,6 +106,26 @@ class DataCommandsIntegrationTest {
     }
 
     @Test
+    void addUserShouldFailWhenGroupConflict() throws Exception {
+        dataProbe.addDomain(DOMAIN);
+        dataProbe.addUser(OTHER_MAIL_ADDRESS, "pass");
+        dataProbe.addGroupAliasMapping(MAIL_ADDRESS, OTHER_MAIL_ADDRESS);
+
+        assertThatThrownBy(() -> ServerCmd.doMain(new String[] {"-h", "127.0.0.1", "-p", "9999", "ADDUSER", MAIL_ADDRESS, PASSWORD}))
+            .hasMessageContaining("'chibenwa@domain.com' already have associated mappings: group:chibenwaother@domain.com");
+    }
+
+    @Test
+    void addUserShouldFailWhenAliasConflict() throws Exception {
+        dataProbe.addDomain(DOMAIN);
+        dataProbe.addUser(OTHER_MAIL_ADDRESS, "pass");
+        dataProbe.addUserAliasMapping(USER, DOMAIN, OTHER_MAIL_ADDRESS);
+
+        assertThatThrownBy(() -> ServerCmd.doMain(new String[] {"-h", "127.0.0.1", "-p", "9999", "ADDUSER", MAIL_ADDRESS, PASSWORD}))
+            .hasMessageContaining("'chibenwa@domain.com' already have associated mappings: alias:chibenwaother@domain.com");
+    }
+
+    @Test
     void removeUserShouldWork() throws Exception {
         dataProbe.fluent()
             .addDomain(DOMAIN)
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java b/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
similarity index 68%
copy from server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java
copy to server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
index 544807b..6657b55 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java
+++ b/server/container/guice/common/src/main/java/org/apache/james/CoreDataModule.java
@@ -16,39 +16,36 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.modules.data;
+
+package org.apache.james;
+
+import java.util.Set;
 
 import org.apache.commons.configuration2.ex.ConfigurationException;
-import org.apache.james.domainlist.api.DomainList;
-import org.apache.james.domainlist.jpa.JPADomainList;
 import org.apache.james.domainlist.lib.DomainListConfiguration;
 import org.apache.james.server.core.configuration.ConfigurationProvider;
-import org.apache.james.utils.InitializationOperation;
-import org.apache.james.utils.InitilizationOperationBuilder;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
-import com.google.inject.Scopes;
 import com.google.inject.Singleton;
-import com.google.inject.multibindings.ProvidesIntoSet;
+import com.google.inject.multibindings.Multibinder;
 
-public class JPADomainListModule extends AbstractModule {
+public class CoreDataModule extends AbstractModule {
     @Override
-    public void configure() {
-        bind(JPADomainList.class).in(Scopes.SINGLETON);
-        bind(DomainList.class).to(JPADomainList.class);
+    protected void configure() {
+        Multibinder.newSetBinder(binder(), UserEntityValidator.class).addBinding().to(DefaultUserEntityValidator.class);
+        Multibinder.newSetBinder(binder(), UserEntityValidator.class).addBinding().to(RecipientRewriteTableUserEntityValidator.class);
     }
 
     @Provides
     @Singleton
-    public DomainListConfiguration provideDomainListConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
-        return DomainListConfiguration.from(configurationProvider.getConfiguration("domainlist"));
+    UserEntityValidator userEntityValidator(Set<UserEntityValidator> validatorSet) {
+        return new AggregateUserEntityValidator(validatorSet);
     }
 
-    @ProvidesIntoSet
-    InitializationOperation configureDomainList(DomainListConfiguration configuration, JPADomainList jpaDomainList) {
-        return InitilizationOperationBuilder
-            .forClass(JPADomainList.class)
-            .init(() -> jpaDomainList.configure(configuration));
+    @Provides
+    @Singleton
+    public DomainListConfiguration provideDomainListConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
+        return DomainListConfiguration.from(configurationProvider.getConfiguration("domainlist"));
     }
 }
diff --git a/server/container/guice/data-cassandra/pom.xml b/server/container/guice/data-cassandra/pom.xml
index 08ff850..570fb6a 100644
--- a/server/container/guice/data-cassandra/pom.xml
+++ b/server/container/guice/data-cassandra/pom.xml
@@ -49,6 +49,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-configuration</artifactId>
         </dependency>
         <dependency>
diff --git a/server/container/guice/data-cassandra/src/main/java/org/apache/james/modules/data/CassandraDomainListModule.java b/server/container/guice/data-cassandra/src/main/java/org/apache/james/modules/data/CassandraDomainListModule.java
index e9cb213..cccd155 100644
--- a/server/container/guice/data-cassandra/src/main/java/org/apache/james/modules/data/CassandraDomainListModule.java
+++ b/server/container/guice/data-cassandra/src/main/java/org/apache/james/modules/data/CassandraDomainListModule.java
@@ -19,19 +19,16 @@
 
 package org.apache.james.modules.data;
 
-import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.CoreDataModule;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.cassandra.CassandraDomainList;
 import org.apache.james.domainlist.lib.DomainListConfiguration;
-import org.apache.james.server.core.configuration.ConfigurationProvider;
 import org.apache.james.utils.InitializationOperation;
 import org.apache.james.utils.InitilizationOperationBuilder;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
 import com.google.inject.Scopes;
-import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
 import com.google.inject.multibindings.ProvidesIntoSet;
 
@@ -39,17 +36,13 @@ public class CassandraDomainListModule extends AbstractModule {
 
     @Override
     public void configure() {
+        install(new CoreDataModule());
+
         bind(CassandraDomainList.class).in(Scopes.SINGLETON);
         bind(DomainList.class).to(CassandraDomainList.class);
         Multibinder.newSetBinder(binder(), CassandraModule.class).addBinding().toInstance(org.apache.james.domainlist.cassandra.CassandraDomainListModule.MODULE);
     }
 
-    @Provides
-    @Singleton
-    public DomainListConfiguration provideDomainListConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
-        return DomainListConfiguration.from(configurationProvider.getConfiguration("domainlist"));
-    }
-
     @ProvidesIntoSet
     InitializationOperation configureDomainList(DomainListConfiguration configuration, CassandraDomainList cassandraDomainList) {
         return InitilizationOperationBuilder
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
index 4082070..ff1b84b 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
+++ b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
@@ -18,14 +18,16 @@
  ****************************************************************/
 package org.apache.james.modules.data;
 
+import org.apache.james.CoreDataModule;
+
 import com.google.inject.AbstractModule;
 
 public class JPADataModule extends AbstractModule {
     @Override
     protected void configure() {
+        install(new CoreDataModule());
         install(new JPADomainListModule());
         install(new JPARecipientRewriteTableModule());
         install(new JPAMailRepositoryModule());
     }
-
 }
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java
index 544807b..116fd4b 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java
+++ b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADomainListModule.java
@@ -18,18 +18,14 @@
  ****************************************************************/
 package org.apache.james.modules.data;
 
-import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.jpa.JPADomainList;
 import org.apache.james.domainlist.lib.DomainListConfiguration;
-import org.apache.james.server.core.configuration.ConfigurationProvider;
 import org.apache.james.utils.InitializationOperation;
 import org.apache.james.utils.InitilizationOperationBuilder;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
 import com.google.inject.Scopes;
-import com.google.inject.Singleton;
 import com.google.inject.multibindings.ProvidesIntoSet;
 
 public class JPADomainListModule extends AbstractModule {
@@ -39,12 +35,6 @@ public class JPADomainListModule extends AbstractModule {
         bind(DomainList.class).to(JPADomainList.class);
     }
 
-    @Provides
-    @Singleton
-    public DomainListConfiguration provideDomainListConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
-        return DomainListConfiguration.from(configurationProvider.getConfiguration("domainlist"));
-    }
-
     @ProvidesIntoSet
     InitializationOperation configureDomainList(DomainListConfiguration configuration, JPADomainList jpaDomainList) {
         return InitilizationOperationBuilder
diff --git a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataModule.java b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
index df658fe..0b1937c 100644
--- a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
+++ b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
@@ -20,7 +20,8 @@
 package org.apache.james.modules.data;
 
 import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
-import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.CoreDataModule;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.dlp.api.DLPConfigurationStore;
 import org.apache.james.dlp.eventsourcing.EventSourcingDLPConfigurationStore;
 import org.apache.james.domainlist.api.DomainList;
@@ -55,6 +56,7 @@ public class MemoryDataModule extends AbstractModule {
     @Override
     protected void configure() {
         install(new SieveFileRepositoryModule());
+        install(new CoreDataModule());
 
         bind(EventSourcingDLPConfigurationStore.class).in(Scopes.SINGLETON);
         bind(DLPConfigurationStore.class).to(EventSourcingDLPConfigurationStore.class);
@@ -88,14 +90,10 @@ public class MemoryDataModule extends AbstractModule {
 
     @Provides
     @Singleton
-    public MemoryUsersRepository providesUsersRepository(DomainList domainList) {
-        return MemoryUsersRepository.withVirtualHosting(domainList);
-    }
-
-    @Provides
-    @Singleton
-    public DomainListConfiguration provideDomainListConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
-        return DomainListConfiguration.from(configurationProvider.getConfiguration("domainlist"));
+    public MemoryUsersRepository providesUsersRepository(DomainList domainList, UserEntityValidator validator) {
+        MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
+        usersRepository.setValidator(validator);
+        return usersRepository;
     }
 
     @ProvidesIntoSet
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
index ef49eac..1014264 100644
--- a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
@@ -19,8 +19,6 @@
 
 package org.apache.james.modules.server;
 
-import java.util.Set;
-
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.dto.MappingSourceModule;
 import org.apache.james.webadmin.mdc.RequestLogger;
@@ -34,15 +32,9 @@ import org.apache.james.webadmin.routes.MappingRoutes;
 import org.apache.james.webadmin.routes.RegexMappingRoutes;
 import org.apache.james.webadmin.routes.UserCreationRequestLogger;
 import org.apache.james.webadmin.routes.UserRoutes;
-import org.apache.james.webadmin.service.AggregateUserEntityValidator;
-import org.apache.james.webadmin.service.DefaultUserEntityValidator;
-import org.apache.james.webadmin.service.RecipientRewriteTableUserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator;
 import org.apache.james.webadmin.utils.JsonTransformerModule;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
-import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
 
 public class DataRoutesModules extends AbstractModule {
@@ -64,14 +56,5 @@ public class DataRoutesModules extends AbstractModule {
         jsonTransformerModuleMultibinder.addBinding().to(MappingSourceModule.class);
 
         Multibinder.newSetBinder(binder(), RequestLogger.class).addBinding().to(UserCreationRequestLogger.class);
-
-        Multibinder.newSetBinder(binder(), UserEntityValidator.class).addBinding().to(DefaultUserEntityValidator.class);
-        Multibinder.newSetBinder(binder(), UserEntityValidator.class).addBinding().to(RecipientRewriteTableUserEntityValidator.class);
-    }
-
-    @Provides
-    @Singleton
-    UserEntityValidator userEntityValidator(Set<UserEntityValidator> validatorSet) {
-        return new AggregateUserEntityValidator(validatorSet);
     }
 }
diff --git a/server/container/spring/pom.xml b/server/container/spring/pom.xml
index 36add15..78e01a5 100644
--- a/server/container/spring/pom.xml
+++ b/server/container/spring/pom.xml
@@ -55,6 +55,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-library</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-filesystem-api</artifactId>
         </dependency>
         <dependency>
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java b/server/container/spring/src/main/java/org/apache/james/container/spring/resource/SpringUserEntityValidator.java
similarity index 56%
copy from server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java
copy to server/container/spring/src/main/java/org/apache/james/container/spring/resource/SpringUserEntityValidator.java
index b08a0e6..97a434d 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java
+++ b/server/container/spring/src/main/java/org/apache/james/container/spring/resource/SpringUserEntityValidator.java
@@ -16,34 +16,47 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-
-package org.apache.james.webadmin.service;
+package org.apache.james.container.spring.resource;
 
 import java.util.Optional;
 import java.util.Set;
 
 import javax.inject.Inject;
 
+import org.apache.james.DefaultUserEntityValidator;
+import org.apache.james.RecipientRewriteTableUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Username;
+import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.user.api.UsersRepository;
-import org.apache.james.user.api.UsersRepositoryException;
 
-public class DefaultUserEntityValidator implements UserEntityValidator {
-    private final UsersRepository usersRepository;
+public class SpringUserEntityValidator implements UserEntityValidator {
+    private UsersRepository usersRepository;
+    private RecipientRewriteTable rrt;
+    private UserEntityValidator delegate;
 
     @Inject
-    public DefaultUserEntityValidator(UsersRepository usersRepository) {
+    public void setUsersRepository(UsersRepository usersRepository) {
         this.usersRepository = usersRepository;
+        if (rrt != null) {
+            delegate = UserEntityValidator.aggregate(
+                new DefaultUserEntityValidator(usersRepository),
+                new RecipientRewriteTableUserEntityValidator(rrt));
+        }
     }
 
-    @Override
-    public Optional<ValidationFailure> canCreate(Username username, Set<EntityType> ignoredTypes) throws UsersRepositoryException {
-        if (ignoredTypes.contains(EntityType.USER)) {
-            return Optional.empty();
-        }
-        if (usersRepository.contains(username)) {
-            return Optional.of(new ValidationFailure("'" + username.asString() + "' user already exist"));
+    @Inject
+    public void setRrt(RecipientRewriteTable rrt) {
+        this.rrt = rrt;
+        if (usersRepository != null) {
+            delegate = UserEntityValidator.aggregate(
+                new DefaultUserEntityValidator(usersRepository),
+                new RecipientRewriteTableUserEntityValidator(rrt));
         }
-        return Optional.empty();
+    }
+
+    @Override
+    public Optional<ValidationFailure> canCreate(Username username, Set<EntityType> ignoredTypes) throws Exception {
+        return delegate.canCreate(username, ignoredTypes);
     }
 }
diff --git a/server/container/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-authenticator.xml b/server/container/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-authenticator.xml
index 89501ef..6006264 100644
--- a/server/container/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-authenticator.xml
+++ b/server/container/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-authenticator.xml
@@ -29,4 +29,6 @@
 
     <bean id="authenticator" class="org.apache.james.adapter.mailbox.UserRepositoryAuthenticator"/>
 
+    <bean id="user-entity-validator" class="org.apache.james.container.spring.resource.SpringUserEntityValidator"/>
+
 </beans>
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java b/server/data/data-api/src/main/java/org/apache/james/rrt/api/MappingConflictException.java
similarity index 78%
copy from server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
copy to server/data/data-api/src/main/java/org/apache/james/rrt/api/MappingConflictException.java
index 4082070..cbb0323 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPADataModule.java
+++ b/server/data/data-api/src/main/java/org/apache/james/rrt/api/MappingConflictException.java
@@ -16,16 +16,11 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.modules.data;
+package org.apache.james.rrt.api;
 
-import com.google.inject.AbstractModule;
+public class MappingConflictException extends RecipientRewriteTableException {
 
-public class JPADataModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        install(new JPADomainListModule());
-        install(new JPARecipientRewriteTableModule());
-        install(new JPAMailRepositoryModule());
+    public MappingConflictException(String msg) {
+        super(msg);
     }
-
 }
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableTest.java b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableTest.java
index 29d4696..fb4028c 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableTest.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableTest.java
@@ -24,8 +24,13 @@ import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
+import org.apache.james.domainlist.api.mock.SimpleDomainList;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.apache.james.user.cassandra.CassandraUsersDAO;
+import org.apache.james.user.cassandra.CassandraUsersRepositoryModule;
+import org.apache.james.user.lib.UsersRepositoryImpl;
+import org.apache.james.user.lib.model.Algorithm;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.extension.RegisterExtension;
@@ -33,6 +38,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 class CassandraRecipientRewriteTableTest implements RecipientRewriteTableContract {
     static final CassandraModule MODULE = CassandraModule.aggregateModules(
         CassandraRRTModule.MODULE,
+        CassandraUsersRepositoryModule.MODULE,
         CassandraSchemaVersionModule.MODULE);
 
     @RegisterExtension
@@ -60,6 +66,8 @@ class CassandraRecipientRewriteTableTest implements RecipientRewriteTableContrac
     @Override
     public void createRecipientRewriteTable() {
         recipientRewriteTable = new CassandraRecipientRewriteTable(recipientRewriteTableDAO, mappingsSourcesDAO);
+        recipientRewriteTable.setUsersRepository(new UsersRepositoryImpl<>(new SimpleDomainList(),
+            new CassandraUsersDAO(new Algorithm.DefaultFactory(), cassandraCluster.getCassandraCluster().getConf())));
     }
 
     @Override
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraStepdefs.java b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraStepdefs.java
index 6298ee1..39ecf57 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraStepdefs.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraStepdefs.java
@@ -25,6 +25,10 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableFixture;
 import org.apache.james.rrt.lib.RewriteTablesStepdefs;
+import org.apache.james.user.cassandra.CassandraUsersDAO;
+import org.apache.james.user.cassandra.CassandraUsersRepositoryModule;
+import org.apache.james.user.lib.UsersRepositoryImpl;
+import org.apache.james.user.lib.model.Algorithm;
 import org.junit.Rule;
 
 import com.github.fge.lambdas.Throwing;
@@ -48,7 +52,7 @@ public class CassandraStepdefs {
     @Before
     public void setup() {
         cassandra = CassandraCluster.create(
-            CassandraModule.aggregateModules(CassandraRRTModule.MODULE, CassandraSchemaVersionModule.MODULE),
+            CassandraModule.aggregateModules(CassandraRRTModule.MODULE, CassandraUsersRepositoryModule.MODULE, CassandraSchemaVersionModule.MODULE),
             cassandraServer.getHost());
         mainStepdefs.setUp(Throwing.supplier(this::getRecipientRewriteTable).sneakyThrow());
     }
@@ -62,6 +66,8 @@ public class CassandraStepdefs {
         CassandraRecipientRewriteTable rrt = new CassandraRecipientRewriteTable(
             new CassandraRecipientRewriteTableDAO(cassandra.getConf()),
             new CassandraMappingsSourcesDAO(cassandra.getConf()));
+        rrt.setUsersRepository(new UsersRepositoryImpl<>(RecipientRewriteTableFixture.domainListForCucumberTests(),
+            new CassandraUsersDAO(new Algorithm.DefaultFactory(), cassandra.getConf())));
         rrt.setDomainList(RecipientRewriteTableFixture.domainListForCucumberTests());
         return rrt;
     }
diff --git a/server/data/data-file/pom.xml b/server/data/data-file/pom.xml
index 4e8cef5..2f88a1b 100644
--- a/server/data/data-file/pom.xml
+++ b/server/data/data-file/pom.xml
@@ -57,6 +57,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-dnsservice-api</artifactId>
         </dependency>
         <dependency>
@@ -130,6 +135,11 @@
             <artifactId>commons-configuration2</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
         </dependency>
diff --git a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
index 32bfc8a..4a206f9 100644
--- a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
+++ b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
@@ -18,10 +18,15 @@
  ****************************************************************/
 package org.apache.james.rrt.file;
 
+import static org.mockito.Mockito.mock;
+
 import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
+import org.apache.james.UserEntityValidator;
+import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -47,6 +52,8 @@ class XMLRecipientRewriteTableTest implements RecipientRewriteTableContract {
     @Override
     public void createRecipientRewriteTable() {
         recipientRewriteTable = new XMLRecipientRewriteTable();
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
+        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(mock(DomainList.class)));
     }
 
     @Override
diff --git a/server/data/data-jpa/pom.xml b/server/data/data-jpa/pom.xml
index 71b601b..e2bb79f 100644
--- a/server/data/data-jpa/pom.xml
+++ b/server/data/data-jpa/pom.xml
@@ -120,6 +120,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
         </dependency>
diff --git a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
index 777ebf7..2f60f58 100644
--- a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
+++ b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
@@ -18,10 +18,14 @@
  ****************************************************************/
 package org.apache.james.rrt.jpa;
 
+import static org.mockito.Mockito.mock;
+
 import org.apache.james.backends.jpa.JpaTestCluster;
+import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.rrt.jpa.model.JPARecipientRewrite;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.apache.james.user.jpa.JPAUsersRepository;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
@@ -45,6 +49,7 @@ class JPARecipientRewriteTableTest implements RecipientRewriteTableContract {
     public void createRecipientRewriteTable() {
         JPARecipientRewriteTable localVirtualUserTable = new JPARecipientRewriteTable();
         localVirtualUserTable.setEntityManagerFactory(JPA_TEST_CLUSTER.getEntityManagerFactory());
+        localVirtualUserTable.setUsersRepository(new JPAUsersRepository(mock(DomainList.class)));
         recipientRewriteTable = localVirtualUserTable;
     }
 
diff --git a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPAStepdefs.java b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPAStepdefs.java
index 20b24e5..3908dfe 100644
--- a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPAStepdefs.java
+++ b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPAStepdefs.java
@@ -23,6 +23,7 @@ import org.apache.james.rrt.jpa.model.JPARecipientRewrite;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableFixture;
 import org.apache.james.rrt.lib.RewriteTablesStepdefs;
+import org.apache.james.user.jpa.JPAUsersRepository;
 
 import com.github.fge.lambdas.Throwing;
 
@@ -52,6 +53,7 @@ public class JPAStepdefs {
     private AbstractRecipientRewriteTable getRecipientRewriteTable() throws Exception {
         JPARecipientRewriteTable localVirtualUserTable = new JPARecipientRewriteTable();
         localVirtualUserTable.setEntityManagerFactory(JPA_TEST_CLUSTER.getEntityManagerFactory());
+        localVirtualUserTable.setUsersRepository(new JPAUsersRepository(RecipientRewriteTableFixture.domainListForCucumberTests()));
         localVirtualUserTable.setDomainList(RecipientRewriteTableFixture.domainListForCucumberTests());
         return localVirtualUserTable;
     }
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/AggregateUserEntityValidator.java b/server/data/data-library/src/main/java/org/apache/james/AggregateUserEntityValidator.java
similarity index 97%
rename from server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/AggregateUserEntityValidator.java
rename to server/data/data-library/src/main/java/org/apache/james/AggregateUserEntityValidator.java
index 8bc54d2..49970d6 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/AggregateUserEntityValidator.java
+++ b/server/data/data-library/src/main/java/org/apache/james/AggregateUserEntityValidator.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.webadmin.service;
+package org.apache.james;
 
 import java.util.Optional;
 import java.util.Set;
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java b/server/data/data-library/src/main/java/org/apache/james/DefaultUserEntityValidator.java
similarity index 98%
rename from server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java
rename to server/data/data-library/src/main/java/org/apache/james/DefaultUserEntityValidator.java
index b08a0e6..78c21fd 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DefaultUserEntityValidator.java
+++ b/server/data/data-library/src/main/java/org/apache/james/DefaultUserEntityValidator.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.webadmin.service;
+package org.apache.james;
 
 import java.util.Optional;
 import java.util.Set;
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/RecipientRewriteTableUserEntityValidator.java b/server/data/data-library/src/main/java/org/apache/james/RecipientRewriteTableUserEntityValidator.java
similarity index 98%
rename from server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/RecipientRewriteTableUserEntityValidator.java
rename to server/data/data-library/src/main/java/org/apache/james/RecipientRewriteTableUserEntityValidator.java
index 2274784..77dfa94 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/RecipientRewriteTableUserEntityValidator.java
+++ b/server/data/data-library/src/main/java/org/apache/james/RecipientRewriteTableUserEntityValidator.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.webadmin.service;
+package org.apache.james;
 
 import java.util.Map;
 import java.util.Optional;
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserEntityValidator.java b/server/data/data-library/src/main/java/org/apache/james/UserEntityValidator.java
similarity index 98%
rename from server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserEntityValidator.java
rename to server/data/data-library/src/main/java/org/apache/james/UserEntityValidator.java
index 5ebadf8..e683431 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserEntityValidator.java
+++ b/server/data/data-library/src/main/java/org/apache/james/UserEntityValidator.java
@@ -17,7 +17,7 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.webadmin.service;
+package org.apache.james;
 
 import java.util.Objects;
 import java.util.Optional;
diff --git a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTable.java b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTable.java
index b4b66b8..10f0009 100644
--- a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTable.java
+++ b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTable.java
@@ -18,6 +18,9 @@
  ****************************************************************/
 package org.apache.james.rrt.lib;
 
+import static org.apache.james.UserEntityValidator.EntityType.ALIAS;
+import static org.apache.james.UserEntityValidator.EntityType.GROUP;
+
 import java.util.EnumSet;
 import java.util.Map;
 import java.util.Optional;
@@ -32,6 +35,8 @@ import javax.mail.internet.AddressException;
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.apache.james.RecipientRewriteTableUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -41,27 +46,43 @@ import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.rrt.api.InvalidRegexException;
 import org.apache.james.rrt.api.LoopDetectedException;
 import org.apache.james.rrt.api.MappingAlreadyExistsException;
+import org.apache.james.rrt.api.MappingConflictException;
 import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
 import org.apache.james.rrt.api.SameSourceAndDestinationException;
 import org.apache.james.rrt.api.SourceDomainIsNotInDomainListException;
 import org.apache.james.rrt.lib.Mapping.Type;
+import org.apache.james.user.api.UsersRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
 
 public abstract class AbstractRecipientRewriteTable implements RecipientRewriteTable, Configurable {
     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRecipientRewriteTable.class);
 
     private RecipientRewriteTableConfiguration configuration;
+    private UserEntityValidator userEntityValidator;
+    private UsersRepository usersRepository;
     private DomainList domainList;
 
     public void setConfiguration(RecipientRewriteTableConfiguration configuration) {
         Preconditions.checkState(this.configuration == null, "A configuration cannot be set twice");
         this.configuration = configuration;
+        this.userEntityValidator = new RecipientRewriteTableUserEntityValidator(this);
+    }
+
+    @Inject
+    public void setUsersRepository(UsersRepository usersRepository) {
+        this.usersRepository = usersRepository;
+    }
+
+    @Inject
+    public void setUserEntityValidator(UserEntityValidator userEntityValidator) {
+        this.userEntityValidator = userEntityValidator;
     }
 
     @Inject
@@ -301,6 +322,8 @@ public abstract class AbstractRecipientRewriteTable implements RecipientRewriteT
             .appendDomainFromThrowingSupplierIfNone(this::defaultDomain);
 
         checkHasValidAddress(mapping);
+        source.asMailAddress()
+            .ifPresent(Throwing.consumer(this::ensureGroupNotShadowingAnotherAddress).sneakyThrow());
         checkDuplicateMapping(source, mapping);
         checkDomainMappingSourceIsManaged(source);
 
@@ -309,6 +332,22 @@ public abstract class AbstractRecipientRewriteTable implements RecipientRewriteT
         addMapping(source, mapping);
     }
 
+    private void ensureGroupNotShadowingAnotherAddress(MailAddress groupAddress) throws Exception {
+        ensureNoConflict(GROUP, groupAddress);
+    }
+
+    private void ensureAliasNotShadowingAnotherAddress(MailAddress groupAddress) throws Exception {
+        ensureNoConflict(ALIAS, groupAddress);
+    }
+
+    private void ensureNoConflict(UserEntityValidator.EntityType entity, MailAddress groupAddress) throws Exception {
+        Username username = usersRepository.getUsername(groupAddress);
+        Optional<UserEntityValidator.ValidationFailure> validationFailure = userEntityValidator.canCreate(username, ImmutableSet.of(entity));
+        if (validationFailure.isPresent()) {
+            throw new MappingConflictException(validationFailure.get().errorMessage());
+        }
+    }
+
     @Override
     public void removeGroupMapping(MappingSource source, String address) throws RecipientRewriteTableException {
         Mapping mapping = Mapping.group(address)
@@ -325,6 +364,8 @@ public abstract class AbstractRecipientRewriteTable implements RecipientRewriteT
 
         checkHasValidAddress(mapping);
         checkDuplicateMapping(source, mapping);
+        source.asMailAddress()
+            .ifPresent(Throwing.consumer(this::ensureAliasNotShadowingAnotherAddress).sneakyThrow());
         checkNotSameSourceAndDestination(source, address);
         checkDomainMappingSourceIsManaged(source);
 
diff --git a/server/data/data-library/src/main/java/org/apache/james/user/lib/UsersRepositoryImpl.java b/server/data/data-library/src/main/java/org/apache/james/user/lib/UsersRepositoryImpl.java
index d499e74..f39c2a6 100644
--- a/server/data/data-library/src/main/java/org/apache/james/user/lib/UsersRepositoryImpl.java
+++ b/server/data/data-library/src/main/java/org/apache/james/user/lib/UsersRepositoryImpl.java
@@ -27,6 +27,8 @@ import javax.inject.Inject;
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.configuration2.tree.ImmutableNode;
+import org.apache.james.DefaultUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -50,11 +52,18 @@ public class UsersRepositoryImpl<T extends UsersDAO> implements UsersRepository,
     protected final T usersDAO;
     private boolean virtualHosting;
     private Optional<Username> administratorId;
+    private UserEntityValidator validator;
 
     @Inject
     public UsersRepositoryImpl(DomainList domainList, T usersDAO) {
         this.domainList = domainList;
         this.usersDAO = usersDAO;
+        this.validator = new DefaultUserEntityValidator(this);
+    }
+
+    @Inject
+    public void setValidator(UserEntityValidator validator) {
+        this.validator = validator;
     }
 
     @Override
@@ -108,11 +117,22 @@ public class UsersRepositoryImpl<T extends UsersDAO> implements UsersRepository,
 
     @Override
     public void addUser(Username username, String password) throws UsersRepositoryException {
-        if (!contains(username)) {
-            assertValid(username);
-            usersDAO.addUser(username, password);
-        } else {
-            throw new AlreadyExistInUsersRepositoryException("User with username " + username + " already exists!");
+        ensureNoConflict(username);
+        assertValid(username);
+        usersDAO.addUser(username, password);
+    }
+
+    private void ensureNoConflict(Username username) throws UsersRepositoryException {
+        try {
+            Optional<UserEntityValidator.ValidationFailure> validationFailure = validator.canCreate(username);
+            if (validationFailure.isPresent()) {
+                throw new AlreadyExistInUsersRepositoryException(validationFailure.get().errorMessage());
+            }
+        } catch (Exception e) {
+            if (e instanceof UsersRepositoryException) {
+                throw (UsersRepositoryException) e;
+            }
+            throw new UsersRepositoryException("Unexpected exception", e);
         }
     }
 
diff --git a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
index 8522b52..fcfb49e 100644
--- a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
+++ b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
@@ -54,7 +54,8 @@ public interface RecipientRewriteTableContract {
     Domain NOT_SUPPORTED_DOMAIN = Domain.of("notAManagedDomain");
     MappingSource SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST = MappingSource.fromUser(USER, NOT_SUPPORTED_DOMAIN);
 
-    void createRecipientRewriteTable();
+    void createRecipientRewriteTable() throws Exception;
+
     AbstractRecipientRewriteTable virtualUserTable();
 
     default void setUp() throws Exception {
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/AliasReverseResolverImplTest.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/AliasReverseResolverImplTest.java
index 1bbca6e..b05d17f 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/AliasReverseResolverImplTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/AliasReverseResolverImplTest.java
@@ -21,6 +21,7 @@ package org.apache.james.rrt.lib;
 
 import static org.mockito.Mockito.mock;
 
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -29,6 +30,7 @@ import org.apache.james.domainlist.memory.MemoryDomainList;
 import org.apache.james.rrt.api.AliasReverseResolver;
 import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
 import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.junit.jupiter.api.BeforeEach;
 
 public class AliasReverseResolverImplTest implements AliasReverseResolverContract {
@@ -46,6 +48,8 @@ public class AliasReverseResolverImplTest implements AliasReverseResolverContrac
         domainList.addDomain(DOMAIN);
         domainList.addDomain(OTHER_DOMAIN);
         recipientRewriteTable.setDomainList(domainList);
+        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
         recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
 
         this.aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/CanSendFromImplTest.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/CanSendFromImplTest.java
index 8d9b47e..3ca74ae 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/CanSendFromImplTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/lib/CanSendFromImplTest.java
@@ -21,6 +21,7 @@ package org.apache.james.rrt.lib;
 import static org.mockito.Mockito.mock;
 
 import org.apache.commons.lang3.NotImplementedException;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -30,6 +31,7 @@ import org.apache.james.rrt.api.AliasReverseResolver;
 import org.apache.james.rrt.api.CanSendFrom;
 import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
 import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.junit.jupiter.api.BeforeEach;
 
 public class CanSendFromImplTest implements CanSendFromContract {
@@ -48,6 +50,8 @@ public class CanSendFromImplTest implements CanSendFromContract {
         domainList.addDomain(OTHER_DOMAIN);
         recipientRewriteTable.setDomainList(domainList);
         recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
+        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
 
         AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
         canSendFrom = new CanSendFromImpl(recipientRewriteTable, aliasReverseResolver);
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/InMemoryStepdefs.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/InMemoryStepdefs.java
index edac008..8e9e2f6 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/InMemoryStepdefs.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/InMemoryStepdefs.java
@@ -19,9 +19,12 @@
 
 package org.apache.james.rrt.memory;
 
+import org.apache.james.UserEntityValidator;
+import org.apache.james.domainlist.api.mock.SimpleDomainList;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableFixture;
 import org.apache.james.rrt.lib.RewriteTablesStepdefs;
+import org.apache.james.user.memory.MemoryUsersRepository;
 
 import com.github.fge.lambdas.Throwing;
 
@@ -42,7 +45,10 @@ public class InMemoryStepdefs {
 
     private AbstractRecipientRewriteTable getRecipientRewriteTable() throws Exception {
         MemoryRecipientRewriteTable rrt = new MemoryRecipientRewriteTable();
-        rrt.setDomainList(RecipientRewriteTableFixture.domainListForCucumberTests());
+        SimpleDomainList domainList = RecipientRewriteTableFixture.domainListForCucumberTests();
+        rrt.setDomainList(domainList);
+        rrt.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        rrt.setUserEntityValidator(UserEntityValidator.NOOP);
         return rrt;
     }
 }
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
index 76aff5e..f94b86b 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
@@ -19,8 +19,15 @@
 
 package org.apache.james.rrt.memory;
 
+import static org.mockito.Mockito.mock;
+
+import org.apache.james.UserEntityValidator;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.lib.DomainListConfiguration;
+import org.apache.james.domainlist.memory.MemoryDomainList;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
 import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
@@ -39,8 +46,13 @@ class MemoryRecipientRewriteTableTest implements RecipientRewriteTableContract {
     }
 
     @Override
-    public void createRecipientRewriteTable() {
+    public void createRecipientRewriteTable() throws Exception {
+        DNSService dnsService = mock(DNSService.class);
+        MemoryDomainList domainList = new MemoryDomainList(dnsService);
+        domainList.configure(DomainListConfiguration.DEFAULT);
         recipientRewriteTable = new MemoryRecipientRewriteTable();
+        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
     }
 
     @Override
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
index 7527710..a8eb038 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
@@ -35,6 +35,7 @@ import java.util.Optional;
 import java.util.function.Supplier;
 
 import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -81,6 +82,7 @@ import org.apache.james.rrt.lib.CanSendFromImpl;
 import org.apache.james.rrt.lib.Mapping;
 import org.apache.james.rrt.lib.MappingSource;
 import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.util.html.HtmlTextExtractor;
 import org.apache.james.util.mime.MessageContentExtractor;
 import org.apache.mailet.Mail;
@@ -156,6 +158,8 @@ public class SetMessagesCreationProcessorTest {
         domainList.configure(DomainListConfiguration.DEFAULT);
         domainList.addDomain(Domain.of("example.com"));
         domainList.addDomain(Domain.of("other.org"));
+        recipientRewriteTable.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
         recipientRewriteTable.setDomainList(domainList);
         recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
         AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
index 0e5d893..ef88f4a 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.jmap.draft.methods;
 
+import static org.apache.james.user.memory.MemoryUsersRepository.withVirtualHosting;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -33,6 +34,7 @@ import java.util.function.Supplier;
 import javax.mail.internet.AddressException;
 
 import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -162,6 +164,8 @@ public class SetMessagesUpdateProcessorTest {
         domainList.configure(DomainListConfiguration.DEFAULT);
         domainList.addDomain(Domain.of("example.com"));
         domainList.addDomain(Domain.of("other.org"));
+        recipientRewriteTable.setUsersRepository(withVirtualHosting(domainList));
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
         recipientRewriteTable.setDomainList(domainList);
         recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
         AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
diff --git a/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java
index 51b8b4e..154682d 100644
--- a/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java
+++ b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/LmtpServerTest.java
@@ -37,6 +37,7 @@ import java.util.EnumSet;
 import java.util.List;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -157,7 +158,8 @@ class LmtpServerTest {
             .put(binder -> binder.bind(FileSystem.class).toInstance(fileSystem))
             .put(binder -> binder.bind(DNSService.class).toInstance(dnsService))
             .put(binder -> binder.bind(UsersRepository.class).toInstance(usersRepository))
-            .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class));
+            .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP));
     }
 
     private LMTPServerFactory createLMTPServer(MockProtocolHandlerLoader loader, String configuration) throws Exception {
diff --git a/server/protocols/protocols-pop3-distributed/src/test/java/org/apache/james/pop3server/mailbox/DistributedPop3ServerTest.java b/server/protocols/protocols-pop3-distributed/src/test/java/org/apache/james/pop3server/mailbox/DistributedPop3ServerTest.java
index c8c314e..987a074 100644
--- a/server/protocols/protocols-pop3-distributed/src/test/java/org/apache/james/pop3server/mailbox/DistributedPop3ServerTest.java
+++ b/server/protocols/protocols-pop3-distributed/src/test/java/org/apache/james/pop3server/mailbox/DistributedPop3ServerTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.pop3server.mailbox;
 
+import org.apache.james.UserEntityValidator;
 import org.apache.james.filesystem.api.FileSystem;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MessageIdManager;
@@ -74,6 +75,7 @@ public class DistributedPop3ServerTest extends POP3ServerTest {
             .put(binder -> binder.bind(MessageId.Factory.class).toInstance(memoryIntegrationResources.getMessageIdFactory()))
             .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class))
             .put(binder -> binder.bind(Pop3MetadataStore.class).toInstance(metadataStore))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP))
             .build();
     }
 }
diff --git a/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java b/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
index 7cc04db..6cef267 100644
--- a/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
+++ b/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
@@ -38,6 +38,7 @@ import org.apache.commons.net.pop3.POP3Client;
 import org.apache.commons.net.pop3.POP3MessageInfo;
 import org.apache.commons.net.pop3.POP3Reply;
 import org.apache.commons.net.pop3.POP3SClient;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Username;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.filesystem.api.FileSystem;
@@ -915,7 +916,9 @@ public class POP3ServerTest {
             .put(binder -> binder.bind(MailboxManager.class).annotatedWith(Names.named("mailboxmanager")).toInstance(mailboxManager))
             .put(binder -> binder.bind(FileSystem.class).toInstance(fileSystem))
             .put(binder -> binder.bind(MetricFactory.class).toInstance(new RecordingMetricFactory()))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP))
             .put(binder -> binder.bind(MailboxAdapterFactory.class).to(DefaultMailboxAdapterFactory.class))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP))
             .build();
     }
 
diff --git a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/DSNTest.java b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/DSNTest.java
index f3f13cb..d2d0285 100644
--- a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/DSNTest.java
+++ b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/DSNTest.java
@@ -33,6 +33,7 @@ import java.util.EnumSet;
 
 import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.commons.net.smtp.SMTPClient;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -175,6 +176,7 @@ class DSNTest {
             .put(binder -> binder.bind(DNSService.class).toInstance(dnsServer))
             .put(binder -> binder.bind(UsersRepository.class).toInstance(usersRepository))
             .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP))
             .build();
     }
 
diff --git a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
index acf718f..df69548 100644
--- a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
+++ b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
@@ -48,6 +48,7 @@ import org.apache.commons.net.ProtocolCommandEvent;
 import org.apache.commons.net.ProtocolCommandListener;
 import org.apache.commons.net.smtp.SMTPClient;
 import org.apache.commons.net.smtp.SMTPReply;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -300,6 +301,7 @@ public class SMTPServerTest {
             .put(binder -> binder.bind(DNSService.class).toInstance(dnsServer))
             .put(binder -> binder.bind(UsersRepository.class).toInstance(usersRepository))
             .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class))
+            .put(binder -> binder.bind(UserEntityValidator.class).toInstance(UserEntityValidator.NOOP))
             .build();
     }
 
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/AliasRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/AliasRoutes.java
index 9109916..e91b603 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/AliasRoutes.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/AliasRoutes.java
@@ -20,12 +20,10 @@
 package org.apache.james.webadmin.routes;
 
 import static org.apache.james.webadmin.Constants.SEPARATOR;
-import static org.apache.james.webadmin.service.UserEntityValidator.EntityType.ALIAS;
 import static spark.Spark.halt;
 
 import java.util.Comparator;
 import java.util.List;
-import java.util.Optional;
 
 import javax.inject.Inject;
 import javax.ws.rs.DELETE;
@@ -41,18 +39,16 @@ import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.api.DomainListException;
 import org.apache.james.rrt.api.LoopDetectedException;
 import org.apache.james.rrt.api.MappingAlreadyExistsException;
+import org.apache.james.rrt.api.MappingConflictException;
 import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
 import org.apache.james.rrt.api.SameSourceAndDestinationException;
 import org.apache.james.rrt.api.SourceDomainIsNotInDomainListException;
 import org.apache.james.rrt.lib.Mapping;
 import org.apache.james.rrt.lib.MappingSource;
-import org.apache.james.user.api.UsersRepository;
 import org.apache.james.webadmin.Constants;
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.dto.AliasSourcesResponse;
-import org.apache.james.webadmin.service.UserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator.ValidationFailure;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.eclipse.jetty.http.HttpStatus;
@@ -88,17 +84,13 @@ public class AliasRoutes implements Routes {
         "Mail addresses not matching this criteria will be rejected.";
     private static final String ADDRESS_TYPE = "alias";
 
-    private final UsersRepository usersRepository;
-    private final UserEntityValidator userEntityValidator;
     private final DomainList domainList;
     private final JsonTransformer jsonTransformer;
     private final RecipientRewriteTable recipientRewriteTable;
 
     @Inject
     @VisibleForTesting
-    AliasRoutes(RecipientRewriteTable recipientRewriteTable, UsersRepository usersRepository, UserEntityValidator userEntityValidator, DomainList domainList, JsonTransformer jsonTransformer) {
-        this.usersRepository = usersRepository;
-        this.userEntityValidator = userEntityValidator;
+    AliasRoutes(RecipientRewriteTable recipientRewriteTable, DomainList domainList, JsonTransformer jsonTransformer) {
         this.domainList = domainList;
         this.jsonTransformer = jsonTransformer;
         this.recipientRewriteTable = recipientRewriteTable;
@@ -156,7 +148,6 @@ public class AliasRoutes implements Routes {
     })
     public HaltException addAlias(Request request, Response response) throws Exception {
         MailAddress aliasSourceAddress = MailAddressParser.parseMailAddress(request.params(ALIAS_SOURCE_ADDRESS), ADDRESS_TYPE);
-        ensureUserDoesNotExist(aliasSourceAddress);
         MailAddress destinationAddress = MailAddressParser.parseMailAddress(request.params(ALIAS_DESTINATION_ADDRESS), ADDRESS_TYPE);
         ensureDomainIsSupported(destinationAddress.getDomain());
         MappingSource source = MappingSource.fromUser(Username.fromMailAddress(aliasSourceAddress));
@@ -175,7 +166,7 @@ public class AliasRoutes implements Routes {
                 .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
                 .message(e.getMessage())
                 .haltError();
-        } catch (LoopDetectedException e) {
+        } catch (LoopDetectedException | MappingConflictException e) {
             throw ErrorResponder.builder()
                 .statusCode(HttpStatus.CONFLICT_409)
                 .type(ErrorResponder.ErrorType.WRONG_STATE)
@@ -194,18 +185,6 @@ public class AliasRoutes implements Routes {
         }
     }
 
-    private void ensureUserDoesNotExist(MailAddress mailAddress) throws Exception {
-        Username username = usersRepository.getUsername(mailAddress);
-        Optional<ValidationFailure> validationFailure = userEntityValidator.canCreate(username, ImmutableSet.of(ALIAS));
-        if (validationFailure.isPresent()) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.CONFLICT_409)
-                .type(ErrorResponder.ErrorType.WRONG_STATE)
-                .message(validationFailure.get().errorMessage())
-                .haltError();
-        }
-    }
-
     @DELETE
     @Path(ROOT_PATH + "/{" + ALIAS_DESTINATION_ADDRESS + "}/sources/{" + ALIAS_SOURCE_ADDRESS + "}")
     @ApiOperation(value = "remove an alias from a destination address")
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/GroupsRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/GroupsRoutes.java
index ec1eca6..651ec1f 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/GroupsRoutes.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/GroupsRoutes.java
@@ -20,7 +20,6 @@
 package org.apache.james.webadmin.routes;
 
 import static org.apache.james.webadmin.Constants.SEPARATOR;
-import static org.apache.james.webadmin.service.UserEntityValidator.EntityType.GROUP;
 import static spark.Spark.halt;
 
 import java.util.List;
@@ -38,17 +37,15 @@ import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
 import org.apache.james.rrt.api.LoopDetectedException;
 import org.apache.james.rrt.api.MappingAlreadyExistsException;
+import org.apache.james.rrt.api.MappingConflictException;
 import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
 import org.apache.james.rrt.api.SourceDomainIsNotInDomainListException;
 import org.apache.james.rrt.lib.Mapping;
 import org.apache.james.rrt.lib.MappingSource;
 import org.apache.james.rrt.lib.Mappings;
-import org.apache.james.user.api.UsersRepository;
 import org.apache.james.webadmin.Constants;
 import org.apache.james.webadmin.Routes;
-import org.apache.james.webadmin.service.UserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator.ValidationFailure;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.ErrorResponder.ErrorType;
 import org.apache.james.webadmin.utils.JsonTransformer;
@@ -56,7 +53,6 @@ import org.eclipse.jetty.http.HttpStatus;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 
 import io.swagger.annotations.Api;
@@ -86,17 +82,12 @@ public class GroupsRoutes implements Routes {
     private static final String GROUP_ADDRESS_TYPE = "group";
     private static final String USER_ADDRESS_TYPE = "group member";
 
-    private final UsersRepository usersRepository;
-    private final UserEntityValidator userEntityValidator;
     private final JsonTransformer jsonTransformer;
     private final RecipientRewriteTable recipientRewriteTable;
 
     @Inject
     @VisibleForTesting
-    GroupsRoutes(RecipientRewriteTable recipientRewriteTable, UsersRepository usersRepository,
-                 UserEntityValidator userEntityValidator, JsonTransformer jsonTransformer) {
-        this.usersRepository = usersRepository;
-        this.userEntityValidator = userEntityValidator;
+    GroupsRoutes(RecipientRewriteTable recipientRewriteTable, JsonTransformer jsonTransformer) {
         this.jsonTransformer = jsonTransformer;
         this.recipientRewriteTable = recipientRewriteTable;
     }
@@ -151,7 +142,6 @@ public class GroupsRoutes implements Routes {
     public HaltException addToGroup(Request request, Response response) throws Exception {
         MailAddress groupAddress = MailAddressParser.parseMailAddress(request.params(GROUP_ADDRESS), GROUP_ADDRESS_TYPE);
         Domain domain = groupAddress.getDomain();
-        ensureNotShadowingAnotherAddress(groupAddress);
         MailAddress userAddress = MailAddressParser.parseMailAddress(request.params(USER_ADDRESS), USER_ADDRESS_TYPE);
         MappingSource source = MappingSource.fromUser(Username.fromLocalPartWithDomain(groupAddress.getLocalPart(), domain));
         addGroupMember(source, userAddress);
@@ -163,16 +153,16 @@ public class GroupsRoutes implements Routes {
             recipientRewriteTable.addGroupMapping(source, userAddress.asString());
         } catch (MappingAlreadyExistsException e) {
             // do nothing
-        } catch (SourceDomainIsNotInDomainListException e) {
+        } catch (MappingConflictException | LoopDetectedException e) {
             throw ErrorResponder.builder()
-                .statusCode(HttpStatus.BAD_REQUEST_400)
-                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .statusCode(HttpStatus.CONFLICT_409)
+                .type(ErrorType.WRONG_STATE)
                 .message(e.getMessage())
                 .haltError();
-        } catch (LoopDetectedException e) {
+        } catch (SourceDomainIsNotInDomainListException e) {
             throw ErrorResponder.builder()
-                .statusCode(HttpStatus.CONFLICT_409)
-                .type(ErrorResponder.ErrorType.WRONG_STATE)
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
                 .message(e.getMessage())
                 .haltError();
         } catch (RecipientRewriteTableException e) {
@@ -184,19 +174,6 @@ public class GroupsRoutes implements Routes {
         }
     }
 
-    private void ensureNotShadowingAnotherAddress(MailAddress groupAddress) throws Exception {
-        Username username = usersRepository.getUsername(groupAddress);
-        Optional<ValidationFailure> validationFailure = userEntityValidator.canCreate(username, ImmutableSet.of(GROUP));
-        if (validationFailure.isPresent()) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.CONFLICT_409)
-                .type(ErrorType.WRONG_STATE)
-                .message(validationFailure.get().errorMessage())
-                .haltError();
-        }
-    }
-
-
     @DELETE
     @Path(ROOT_PATH + "/{" + GROUP_ADDRESS + "}/{" + USER_ADDRESS + "}")
     @ApiOperation(value = "remove a member from a group")
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
index 38cbabb..7904a48 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/UserService.java
@@ -26,24 +26,20 @@ import java.util.stream.Stream;
 import javax.inject.Inject;
 
 import org.apache.james.core.Username;
-import org.apache.james.user.api.AlreadyExistInUsersRepositoryException;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.james.user.api.model.User;
 import org.apache.james.util.streams.Iterators;
 import org.apache.james.webadmin.dto.UserResponse;
-import org.apache.james.webadmin.service.UserEntityValidator.ValidationFailure;
 
 import com.google.common.collect.ImmutableList;
 
 public class UserService {
     private final UsersRepository usersRepository;
-    private final UserEntityValidator userEntityValidator;
 
     @Inject
-    public UserService(UsersRepository usersRepository, UserEntityValidator userEntityValidator) {
+    public UserService(UsersRepository usersRepository) {
         this.usersRepository = usersRepository;
-        this.userEntityValidator = userEntityValidator;
     }
 
     public List<UserResponse> getUsers() throws UsersRepositoryException {
@@ -62,10 +58,6 @@ public class UserService {
     public void upsertUser(Username username, char[] password) throws Exception {
         User user = usersRepository.getUserByName(username);
         if (user == null) {
-            Optional<ValidationFailure> validationFailure = userEntityValidator.canCreate(username);
-            if (validationFailure.isPresent()) {
-                throw new AlreadyExistInUsersRepositoryException(validationFailure.get().errorMessage());
-            }
             usersRepository.addUser(username, new String(password));
         } else {
             user.setPassword(new String(password));
@@ -78,10 +70,6 @@ public class UserService {
     }
 
     public void insertUser(Username username, char[] password) throws Exception {
-        Optional<ValidationFailure> validationFailure = userEntityValidator.canCreate(username);
-        if (validationFailure.isPresent()) {
-            throw new AlreadyExistInUsersRepositoryException(validationFailure.get().errorMessage());
-        }
         usersRepository.addUser(username, new String(password));
     }
 
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
index 7d8d09b..a46738a 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/AliasRoutesTest.java
@@ -35,6 +35,9 @@ import static org.mockito.Mockito.spy;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.james.DefaultUserEntityValidator;
+import org.apache.james.RecipientRewriteTableUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -50,9 +53,6 @@ import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.dto.MappingSourceModule;
-import org.apache.james.webadmin.service.DefaultUserEntityValidator;
-import org.apache.james.webadmin.service.RecipientRewriteTableUserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.eclipse.jetty.http.HttpStatus;
 import org.junit.jupiter.api.AfterEach;
@@ -124,12 +124,13 @@ class AliasRoutesTest {
             usersRepository.addUser(Username.of(BOB_WITH_SLASH), BOB_WITH_SLASH_PASSWORD);
             usersRepository.addUser(Username.of(ALICE), ALICE_PASSWORD);
 
-
             UserEntityValidator validator = UserEntityValidator.aggregate(
                 new DefaultUserEntityValidator(usersRepository),
                 new RecipientRewriteTableUserEntityValidator(memoryRecipientRewriteTable));
+            memoryRecipientRewriteTable.setUserEntityValidator(validator);
+            memoryRecipientRewriteTable.setUsersRepository(usersRepository);
 
-            createServer(new AliasRoutes(memoryRecipientRewriteTable, usersRepository, validator, domainList, new JsonTransformer(module)),
+            createServer(new AliasRoutes(memoryRecipientRewriteTable, domainList, new JsonTransformer(module)),
                 new AddressMappingRoutes(memoryRecipientRewriteTable));
         }
 
@@ -457,7 +458,6 @@ class AliasRoutesTest {
             memoryRecipientRewriteTable.addDomainAliasMapping(MappingSource.fromDomain(ALIAS_DOMAIN), DOMAIN);
             memoryRecipientRewriteTable.addDomainMapping(MappingSource.fromDomain(DOMAIN_MAPPING), DOMAIN);
         }
-
     }
 
     @Nested
@@ -472,8 +472,10 @@ class AliasRoutesTest {
             UsersRepository userRepository = mock(UsersRepository.class);
             domainList = mock(DomainList.class);
             memoryRecipientRewriteTable.setDomainList(domainList);
+            memoryRecipientRewriteTable.setUsersRepository(userRepository);
+            memoryRecipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
             Mockito.when(domainList.containsDomain(any())).thenReturn(true);
-            createServer(new AliasRoutes(memoryRecipientRewriteTable, userRepository, UserEntityValidator.NOOP, domainList, new JsonTransformer()),
+            createServer(new AliasRoutes(memoryRecipientRewriteTable, domainList, new JsonTransformer()),
                 new AddressMappingRoutes(memoryRecipientRewriteTable));
         }
 
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
index d461c93..3ee2edd 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/GroupsRoutesTest.java
@@ -35,6 +35,9 @@ import static org.mockito.Mockito.spy;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.james.DefaultUserEntityValidator;
+import org.apache.james.RecipientRewriteTableUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -52,9 +55,6 @@ import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.dto.MappingSourceModule;
-import org.apache.james.webadmin.service.DefaultUserEntityValidator;
-import org.apache.james.webadmin.service.RecipientRewriteTableUserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.eclipse.jetty.http.HttpStatus;
 import org.junit.jupiter.api.AfterEach;
@@ -119,7 +119,9 @@ class GroupsRoutesTest {
             UserEntityValidator validator = UserEntityValidator.aggregate(
                 new DefaultUserEntityValidator(usersRepository),
                 new RecipientRewriteTableUserEntityValidator(memoryRecipientRewriteTable));
-            createServer(new GroupsRoutes(memoryRecipientRewriteTable, usersRepository, validator, new JsonTransformer(mappingSourceModule)),
+            memoryRecipientRewriteTable.setUserEntityValidator(validator);
+            memoryRecipientRewriteTable.setUsersRepository(usersRepository);
+            createServer(new GroupsRoutes(memoryRecipientRewriteTable, new JsonTransformer(mappingSourceModule)),
                 new AddressMappingRoutes(memoryRecipientRewriteTable));
         }
 
@@ -498,7 +500,9 @@ class GroupsRoutesTest {
             domainList = mock(DomainList.class);
             memoryRecipientRewriteTable.setDomainList(domainList);
             Mockito.when(domainList.containsDomain(any())).thenReturn(true);
-            createServer(new GroupsRoutes(memoryRecipientRewriteTable, userRepository, UserEntityValidator.NOOP, new JsonTransformer()),
+            memoryRecipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
+            memoryRecipientRewriteTable.setUsersRepository(userRepository);
+            createServer(new GroupsRoutes(memoryRecipientRewriteTable, new JsonTransformer()),
                 new AddressMappingRoutes(memoryRecipientRewriteTable));
         }
 
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
index dce8772..334c66e 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/MappingRoutesTest.java
@@ -24,6 +24,7 @@ import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
 import static org.hamcrest.CoreMatchers.is;
 import static org.mockito.Mockito.mock;
 
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
@@ -34,6 +35,7 @@ import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
 import org.apache.james.rrt.lib.MappingSource;
 import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
+import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.utils.JsonTransformer;
@@ -43,7 +45,6 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import io.restassured.RestAssured;
-import io.restassured.filter.log.LogDetail;
 import io.restassured.http.ContentType;
 import net.javacrumbs.jsonunit.core.Option;
 
@@ -76,6 +77,9 @@ class MappingRoutesTest {
 
         recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
         recipientRewriteTable.setDomainList(domainList);
+        MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
+        recipientRewriteTable.setUserEntityValidator(UserEntityValidator.NOOP);
+        recipientRewriteTable.setUsersRepository(usersRepository);
 
         webAdminServer = WebAdminUtils.createWebAdminServer(new MappingRoutes(jsonTransformer, recipientRewriteTable))
             .start();
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
index 065d93b..57997d9 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/UserRoutesTest.java
@@ -36,6 +36,9 @@ import java.util.Map;
 import java.util.stream.Stream;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.james.DefaultUserEntityValidator;
+import org.apache.james.RecipientRewriteTableUserEntityValidator;
+import org.apache.james.UserEntityValidator;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.domainlist.api.DomainListException;
@@ -55,9 +58,6 @@ import org.apache.james.user.api.model.User;
 import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
-import org.apache.james.webadmin.service.DefaultUserEntityValidator;
-import org.apache.james.webadmin.service.RecipientRewriteTableUserEntityValidator;
-import org.apache.james.webadmin.service.UserEntityValidator;
 import org.apache.james.webadmin.service.UserService;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
@@ -117,11 +117,17 @@ class UserRoutesTest {
             this.recipientRewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED);
             this.aliasReverseResolver = new AliasReverseResolverImpl(recipientRewriteTable);
             this.canSendFrom = new CanSendFromImpl(recipientRewriteTable, aliasReverseResolver);
+            UserEntityValidator validator = UserEntityValidator.aggregate(
+                new DefaultUserEntityValidator(this.usersRepository),
+                new RecipientRewriteTableUserEntityValidator(recipientRewriteTable));
+            this.usersRepository.setValidator(validator);
+            recipientRewriteTable.setUsersRepository(usersRepository);
+            recipientRewriteTable.setUserEntityValidator(validator);
         }
 
         @Override
         public void beforeEach(ExtensionContext extensionContext) {
-            webAdminServer = startServer(usersRepository, recipientRewriteTable);
+            webAdminServer = startServer(usersRepository);
         }
 
         @Override
@@ -148,11 +154,8 @@ class UserRoutesTest {
             throw new RuntimeException("Unknown parameter type: " + parameterType);
         }
 
-        private WebAdminServer startServer(UsersRepository usersRepository, RecipientRewriteTable recipientRewriteTable) {
-            UserEntityValidator validator = UserEntityValidator.aggregate(
-                new DefaultUserEntityValidator(usersRepository),
-                new RecipientRewriteTableUserEntityValidator(recipientRewriteTable));
-            WebAdminServer server = WebAdminUtils.createWebAdminServer(new UserRoutes(new UserService(usersRepository, validator), canSendFrom, new JsonTransformer()))
+        private WebAdminServer startServer(UsersRepository usersRepository) {
+            WebAdminServer server = WebAdminUtils.createWebAdminServer(new UserRoutes(new UserService(usersRepository), canSendFrom, new JsonTransformer()))
                 .start();
 
             RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(server)
@@ -415,7 +418,9 @@ class UserRoutesTest {
 
             @Test
             default void putShouldFailOnRepositoryExceptionOnGetUserByName(UsersRepository usersRepository) throws Exception {
-                when(usersRepository.contains(USERNAME_WITH_DOMAIN)).thenThrow(new UsersRepositoryException("message"));
+                doThrow(new UsersRepositoryException("message"))
+                    .when(usersRepository)
+                    .contains(any(Username.class));
 
                 given()
                     .body("{\"password\":\"password\"}")
@@ -475,7 +480,9 @@ class UserRoutesTest {
 
             @Test
             default void putShouldFailOnUnknownExceptionOnGetUserByName(UsersRepository usersRepository) throws Exception {
-                when(usersRepository.contains(USERNAME_WITH_DOMAIN)).thenThrow(new RuntimeException());
+                doThrow(new RuntimeException())
+                    .when(usersRepository)
+                    .contains(any(Username.class));
 
                 given()
                     .body("{\"password\":\"password\"}")

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org