You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/03/26 02:41:32 UTC

[james-project] 10/16: JAMES-3088 Tests and fix for the missing domain checks

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

commit e8578196791abf54b0d99ff577e81e6d68676d3f
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Mar 5 15:03:24 2020 +0700

    JAMES-3088 Tests and fix for the missing domain checks
---
 .../user/cassandra/CassandraUsersRepository.java   |   7 +-
 .../apache/james/user/jpa/JPAUsersRepository.java  |   6 +
 .../user/ldap/ReadOnlyUsersLDAPRepository.java     |  23 +-
 .../ReadOnlyUsersLDAPRepositoryInvalidDnTest.java  |   2 +-
 .../user/ldap/ReadOnlyUsersLDAPRepositoryTest.java | 416 +---------
 .../james/user/lib/AbstractUsersRepository.java    |  18 +-
 .../user/lib/AbstractUsersRepositoryContract.java  | 873 ++++++++++++---------
 .../james/user/memory/MemoryUsersRepository.java   |   4 +
 .../user/memory/MemoryUsersRepositoryTest.java     |  11 +-
 .../james/webadmin/routes/AliasRoutesTest.java     |   2 -
 .../james/webadmin/routes/ForwardRoutesTest.java   |   2 -
 .../routes/DeletedMessagesVaultRoutesTest.java     |   3 +-
 12 files changed, 550 insertions(+), 817 deletions(-)

diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepository.java b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepository.java
index fe17614..a903f34 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepository.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepository.java
@@ -115,7 +115,7 @@ public class CassandraUsersRepository extends AbstractUsersRepository {
     }
 
     @Override
-    public User getUserByName(Username name) {
+    public User getUserByName(Username name) throws UsersRepositoryException {
         return executor.executeSingleRow(
                 getUserStatement.bind()
                     .setString(NAME, name.asString()))
@@ -126,6 +126,7 @@ public class CassandraUsersRepository extends AbstractUsersRepository {
 
     @Override
     public void updateUser(User user) throws UsersRepositoryException {
+        assertDomainPartValid(user.getUserName());
         Preconditions.checkArgument(user instanceof DefaultUser);
         DefaultUser defaultUser = (DefaultUser) user;
         boolean executed = executor.executeReturnApplied(
@@ -143,6 +144,8 @@ public class CassandraUsersRepository extends AbstractUsersRepository {
 
     @Override
     public void removeUser(Username name) throws UsersRepositoryException {
+        assertDomainPartValid(name);
+
         boolean executed = executor.executeReturnApplied(
             removeUserStatement.bind()
                 .setString(NAME, name.asString()))
@@ -154,7 +157,7 @@ public class CassandraUsersRepository extends AbstractUsersRepository {
     }
 
     @Override
-    public boolean contains(Username name) {
+    public boolean contains(Username name) throws UsersRepositoryException {
         return getUserByName(name) != null;
     }
 
diff --git a/server/data/data-jpa/src/main/java/org/apache/james/user/jpa/JPAUsersRepository.java b/server/data/data-jpa/src/main/java/org/apache/james/user/jpa/JPAUsersRepository.java
index e949a52..3de0be5 100644
--- a/server/data/data-jpa/src/main/java/org/apache/james/user/jpa/JPAUsersRepository.java
+++ b/server/data/data-jpa/src/main/java/org/apache/james/user/jpa/JPAUsersRepository.java
@@ -46,6 +46,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
 
 /**
  * JPA based UserRepository
@@ -111,6 +112,9 @@ public class JPAUsersRepository extends AbstractUsersRepository {
      */
     @Override
     public void updateUser(User user) throws UsersRepositoryException {
+        Preconditions.checkNotNull(user);
+        assertDomainPartValid(user.getUserName());
+
         EntityManager entityManager = entityManagerFactory.createEntityManager();
 
         final EntityTransaction transaction = entityManager.getTransaction();
@@ -142,6 +146,8 @@ public class JPAUsersRepository extends AbstractUsersRepository {
      */
     @Override
     public void removeUser(Username name) throws UsersRepositoryException {
+        assertDomainPartValid(name);
+
         EntityManager entityManager = entityManagerFactory.createEntityManager();
 
         final EntityTransaction transaction = entityManager.getTransaction();
diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepository.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepository.java
index c1ec084..3889fb2 100644
--- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepository.java
+++ b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepository.java
@@ -628,9 +628,7 @@ public class ReadOnlyUsersLDAPRepository extends AbstractUsersRepository impleme
 
     @Override
     public void removeUser(Username name) throws UsersRepositoryException {
-        LOGGER.warn("This user-repository is read-only. Modifications are not permitted.");
-        throw new UsersRepositoryException(
-                "This user-repository is read-only. Modifications are not permitted.");
+        throw new UsersRepositoryException("This user-repository is read-only. Modifications are not permitted.");
 
     }
 
@@ -641,17 +639,8 @@ public class ReadOnlyUsersLDAPRepository extends AbstractUsersRepository impleme
     }
 
     @Override
-    public void addUser(Username username, String password) throws UsersRepositoryException {
-        LOGGER.error("This user-repository is read-only. Modifications are not permitted.");
-        throw new UsersRepositoryException(
-                "This user-repository is read-only. Modifications are not permitted.");
-    }
-
-    @Override
     public void updateUser(User user) throws UsersRepositoryException {
-        LOGGER.error("This user-repository is read-only. Modifications are not permitted.");
-        throw new UsersRepositoryException(
-                "This user-repository is read-only. Modifications are not permitted.");
+        throw new UsersRepositoryException("This user-repository is read-only. Modifications are not permitted.");
     }
 
     /**
@@ -664,13 +653,13 @@ public class ReadOnlyUsersLDAPRepository extends AbstractUsersRepository impleme
 
     @Override
     protected void doAddUser(Username username, String password) throws UsersRepositoryException {
-        LOGGER.error("This user-repository is read-only. Modifications are not permitted.");
-        throw new UsersRepositoryException(
-                "This user-repository is read-only. Modifications are not permitted.");
+        throw new UsersRepositoryException("This user-repository is read-only. Modifications are not permitted.");
     }
 
     @Override
-    public boolean isAdministrator(Username username) {
+    public boolean isAdministrator(Username username) throws UsersRepositoryException {
+        assertValid(username);
+
         if (ldapConfiguration.getAdministratorId().isPresent()) {
             return ldapConfiguration.getAdministratorId().get().equals(username);
         }
diff --git a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryInvalidDnTest.java b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryInvalidDnTest.java
index d558dd7..18f3c80 100644
--- a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryInvalidDnTest.java
+++ b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryInvalidDnTest.java
@@ -53,7 +53,7 @@ class ReadOnlyUsersLDAPRepositoryInvalidDnTest {
 
     @AfterAll
     static void afterAll() {
-        ldapContainer.start();
+        ldapContainer.stop();
     }
 
     @BeforeEach
diff --git a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryTest.java b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryTest.java
index 8fbaa63..44491b8 100644
--- a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryTest.java
+++ b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/ReadOnlyUsersLDAPRepositoryTest.java
@@ -34,13 +34,11 @@ import org.apache.commons.configuration2.plist.PropertyListConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
 import org.apache.james.core.Username;
 import org.apache.james.domainlist.api.DomainList;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import org.apache.james.domainlist.api.mock.SimpleDomainList;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import org.apache.james.user.lib.AbstractUsersRepository;
 import org.apache.james.user.lib.AbstractUsersRepositoryContract;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Nested;
@@ -49,8 +47,6 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
-
 class ReadOnlyUsersLDAPRepositoryTest {
 
     static final Logger LOGGER = LoggerFactory.getLogger(ReadOnlyUsersLDAPRepositoryTest.class);
@@ -70,11 +66,11 @@ class ReadOnlyUsersLDAPRepositoryTest {
 
     @AfterAll
     static void afterAll() {
-        ldapContainer.start();
+        ldapContainer.stop();
     }
 
     @Nested
-    class WhenEnableVirtualHosting implements AbstractUsersRepositoryContract.WithVirtualHostingContract {
+    class WhenEnableVirtualHosting implements AbstractUsersRepositoryContract.WithVirtualHostingReadOnlyContract {
         @RegisterExtension
         UserRepositoryExtension extension = UserRepositoryExtension.withVirtualHost();
 
@@ -90,9 +86,8 @@ class ReadOnlyUsersLDAPRepositoryTest {
             return usersRepository;
         }
 
-        @Override
         @Test
-        public void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
+        void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
             assertThat(testee().isAdministrator(testSystem.getAdmin())).isTrue();
         }
 
@@ -122,11 +117,6 @@ class ReadOnlyUsersLDAPRepositoryTest {
         }
 
         @Test
-        void unknownUserShouldNotBeAbleToLogInWithVirtualHosting() throws Exception {
-            assertThat(usersRepository.test(UNKNOWN, BAD_PASSWORD)).isFalse();
-        }
-
-        @Test
         void unknownUserShouldNotBeAbleToLogInWhenPasswordIsCorrectWithVirtualHosting() throws Exception {
             assertThat(usersRepository.test(UNKNOWN, PASSWORD)).isFalse();
         }
@@ -143,219 +133,21 @@ class ReadOnlyUsersLDAPRepositoryTest {
             assertThat(usersRepository.contains(usersRepository.getUsername(JAMES_USER_MAIL.asMailAddress()))).isTrue();
         }
 
-        @Disabled("JAMES-3088 isAdministrator is case sensitive")
-        @Override
-        @Test
-        public void isAdministratorShouldBeCaseInsentive(TestSystem testSystem) throws Exception {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void isAdministratorShouldReturnFalseWhenNotConfigured(TestSystem testSystem) throws Exception {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldThrowWhenSameUsernameWithDifferentCase(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldThrowWhenAUserIsNoMoreInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
         @Disabled("JAMES-3088 Users are provisioned by default from Dockerfile, cannot setup this test case")
         @Override
         @Test
         public void listShouldReturnEmptyIteratorWhenEmptyRepository(TestSystem testSystem) {
         }
 
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void countUsersShouldReturnNumberOfUsersWhenNotEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldDisableCaseVariation(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldAllowToAuthenticateWithNewPassword(TestSystem testSystem){
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldAddAUserWhenNotEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserHasAnIncorrectCasePassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void listShouldReturnExactlyUsersInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnTrueWhenAUserHasACorrectPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldBeCaseInsentiveWhenOriginalValueLowerCased(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnLowerCaseAddedUser(TestSystem testSystem) {
-        }
-
         @Disabled("JAMES-3088 Users are provisioned by default from Dockerfile, cannot setup this test case")
         @Override
         @Test
         public void countUsersShouldReturnZeroWhenEmptyRepository() {
         }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserIsNotInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnTrueWhenAUserHasACorrectPasswordAndOtherCaseInDomain(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldDisableCaseVariationWhenOriginalValueLowerCased(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnTrueWhenAUserHasAnIncorrectCaseName(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldAddAUserWhenEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldPreserveCaseVariation(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldNotAllowToAuthenticateWithOldPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnAUserWhenContainedInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldRemoveAUserWhenPresentInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserHasAnIncorrectPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserIsRemovedFromRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void listShouldReturnLowerCaseUser(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnUserWhenDifferentCase(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldThrowWhenUserAlreadyPresentInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) {
-        }
     }
 
     @Nested
-    class WhenDisableVirtualHosting implements AbstractUsersRepositoryContract.WithOutVirtualHostingContract {
+    class WhenDisableVirtualHosting implements AbstractUsersRepositoryContract.WithOutVirtualHostingReadOnlyContract {
         @RegisterExtension
         UserRepositoryExtension extension = UserRepositoryExtension.withoutVirtualHosting();
 
@@ -396,216 +188,22 @@ class ReadOnlyUsersLDAPRepositoryTest {
             assertThat(usersRepository.contains(usersRepository.getUsername(JAMES_USER_MAIL.asMailAddress()))).isTrue();
         }
 
-        @Override
         @Test
-        public void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
+        void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
             assertThat(testee().isAdministrator(testSystem.getAdmin())).isTrue();
         }
 
-        @Disabled("JAMES-3088 isAdministrator is case sensitive")
-        @Override
-        @Test
-        public void isAdministratorShouldBeCaseInsentive(TestSystem testSystem) throws Exception {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void isAdministratorShouldReturnFalseWhenNotConfigured(TestSystem testSystem) throws Exception {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldThrowWhenSameUsernameWithDifferentCase(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldThrowWhenAUserIsNoMoreInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
         @Disabled("JAMES-3088 Users are provisioned by default from Dockerfile, cannot setup this test case")
         @Override
         @Test
         public void listShouldReturnEmptyIteratorWhenEmptyRepository(TestSystem testSystem) {
         }
 
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void countUsersShouldReturnNumberOfUsersWhenNotEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldDisableCaseVariation(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldAllowToAuthenticateWithNewPassword(TestSystem testSystem){
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldAddAUserWhenNotEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserHasAnIncorrectCasePassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void listShouldReturnExactlyUsersInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnTrueWhenAUserHasACorrectPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldBeCaseInsentiveWhenOriginalValueLowerCased(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnLowerCaseAddedUser(TestSystem testSystem) {
-        }
-
         @Disabled("JAMES-3088 Users are provisioned by default from Dockerfile, cannot setup this test case")
         @Override
         @Test
         public void countUsersShouldReturnZeroWhenEmptyRepository() {
         }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserIsNotInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldDisableCaseVariationWhenOriginalValueLowerCased(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnTrueWhenAUserHasAnIncorrectCaseName(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldAddAUserWhenEmptyRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void containsShouldPreserveCaseVariation(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void updateUserShouldNotAllowToAuthenticateWithOldPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnAUserWhenContainedInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldRemoveAUserWhenPresentInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserHasAnIncorrectPassword(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldReturnFalseWhenAUserIsRemovedFromRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void removeUserShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void listShouldReturnLowerCaseUser(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldBeCaseInsentive(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void getUserByNameShouldReturnUserWhenDifferentCase(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void addUserShouldThrowWhenUserAlreadyPresentInRepository(TestSystem testSystem) {
-        }
-
-        @Disabled("JAMES-3088 This user-repository is read-only. Modifications are not permitted.")
-        @Override
-        @Test
-        public void testShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) {
-        }
-
     }
 
     @Nested
diff --git a/server/data/data-library/src/main/java/org/apache/james/user/lib/AbstractUsersRepository.java b/server/data/data-library/src/main/java/org/apache/james/user/lib/AbstractUsersRepository.java
index b770ff1..3a25105 100644
--- a/server/data/data-library/src/main/java/org/apache/james/user/lib/AbstractUsersRepository.java
+++ b/server/data/data-library/src/main/java/org/apache/james/user/lib/AbstractUsersRepository.java
@@ -75,6 +75,11 @@ public abstract class AbstractUsersRepository implements UsersRepository, Config
 
     @Override
     public void assertValid(Username username) throws UsersRepositoryException {
+        assertDomainPartValid(username);
+        assertLocalPartValid(username);
+    }
+
+    protected void assertDomainPartValid(Username username) throws UsersRepositoryException {
         if (supportVirtualHosting()) {
             // need a @ in the username
             if (!username.hasDomainPart()) {
@@ -95,8 +100,12 @@ public abstract class AbstractUsersRepository implements UsersRepository, Config
                 throw new InvalidUsernameException("Given Username contains a @domainpart but virtualhosting support is disabled");
             }
         }
+    }
 
-        if (!isLocalPartValid(username)) {
+    private void assertLocalPartValid(Username username) throws InvalidUsernameException {
+        boolean isValid = CharMatcher.anyOf(ILLEGAL_USERNAME_CHARACTERS)
+            .matchesNoneOf(username.getLocalPart());
+        if (!isValid) {
             throw new InvalidUsernameException(String.format("Given Username '%s' should not contain any of those characters: %s",
                 username.asString(), ILLEGAL_USERNAME_CHARACTERS));
         }
@@ -135,6 +144,8 @@ public abstract class AbstractUsersRepository implements UsersRepository, Config
 
     @Override
     public boolean isAdministrator(Username username) throws UsersRepositoryException {
+        assertValid(username);
+
         return administratorId.map(id -> id.equals(username))
             .orElse(false);
     }
@@ -155,9 +166,4 @@ public abstract class AbstractUsersRepository implements UsersRepository, Config
             throw new UsersRepositoryException("Failed to compute mail address associated with the user", e);
         }
     }
-
-    private boolean isLocalPartValid(Username username) {
-        return CharMatcher.anyOf(ILLEGAL_USERNAME_CHARACTERS)
-            .matchesNoneOf(username.getLocalPart());
-    }
 }
diff --git a/server/data/data-library/src/test/java/org/apache/james/user/lib/AbstractUsersRepositoryContract.java b/server/data/data-library/src/test/java/org/apache/james/user/lib/AbstractUsersRepositoryContract.java
index 4780b02..557824f 100644
--- a/server/data/data-library/src/test/java/org/apache/james/user/lib/AbstractUsersRepositoryContract.java
+++ b/server/data/data-library/src/test/java/org/apache/james/user/lib/AbstractUsersRepositoryContract.java
@@ -19,10 +19,12 @@
 package org.apache.james.user.lib;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
 
@@ -34,6 +36,7 @@ import org.apache.james.user.api.AlreadyExistInUsersRepositoryException;
 import org.apache.james.user.api.InvalidUsernameException;
 import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.james.user.api.model.User;
+import org.apache.james.user.lib.model.DefaultUser;
 import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
@@ -89,6 +92,7 @@ public interface AbstractUsersRepositoryContract {
 
     class TestSystem {
         static final Domain DOMAIN = Domain.of("james.org");
+        static final Domain UNKNOW_DOMAIN = Domain.of("unknown.org");
 
         private final boolean supportVirtualHosting;
         private final SimpleDomainList domainList;
@@ -98,6 +102,8 @@ public interface AbstractUsersRepositoryContract {
         private final Username user3;
         private final Username admin;
         private final Username adminCaseVariation;
+        private final Username userWithUnknowDomain;
+        private final Username invalidUsername;
 
         TestSystem(boolean supportVirtualHosting) throws Exception {
             this.supportVirtualHosting = supportVirtualHosting;
@@ -108,14 +114,20 @@ public interface AbstractUsersRepositoryContract {
             user3 = toUsername("username3");
             user1CaseVariation = toUsername("uSeRnaMe");
             admin = toUsername("admin");
-            adminCaseVariation = toUsername("admin");
+            adminCaseVariation = toUsername("adMin");
+            userWithUnknowDomain = toUsername("unknown", UNKNOW_DOMAIN);
+            invalidUsername = toUsername("userContains)*(");
         }
 
         private Username toUsername(String login) {
+            return toUsername(login, DOMAIN);
+        }
+
+        private Username toUsername(String login, Domain domain) {
             if (supportVirtualHosting) {
-                return Username.of(login + '@' + DOMAIN.name());
+                return Username.fromLocalPartWithDomain(login, domain);
             } else {
-                return Username.of(login);
+                return Username.fromLocalPartWithoutDomain(login);
             }
         }
 
@@ -126,467 +138,584 @@ public interface AbstractUsersRepositoryContract {
         public Username getAdmin() {
             return admin;
         }
+
+        public Username getUserWithUnknowDomain() {
+            return userWithUnknowDomain;
+        }
     }
 
-    interface WithVirtualHostingContract extends AbstractUsersRepositoryContract {
+    AbstractUsersRepository testee();
 
+    interface ReadOnlyContract extends AbstractUsersRepositoryContract {
         @Test
-        default void testShouldReturnTrueWhenAUserHasACorrectPasswordAndOtherCaseInDomain(TestSystem testSystem) throws Exception {
-            testSystem.domainList.addDomain(Domain.of("Domain.OrG"));
-            String username = "myuser";
-            String password = "password";
-            testee().addUser(Username.of(username + "@Domain.OrG"), password);
+        default void countUsersShouldReturnZeroWhenEmptyRepository() throws UsersRepositoryException {
+            //Given
+            int expected = 0;
+            //When
+            int actual = testee().countUsers();
+            //Then
+            assertThat(actual).isEqualTo(expected);
+        }
 
-            boolean actual = testee().test(Username.of(username + "@domain.org"), password);
+        @Test
+        default void listShouldReturnEmptyIteratorWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //When
+            Iterator<Username> actual = testee().list();
+            //Then
+            assertThat(actual)
+                .toIterable()
+                .isEmpty();
+        }
 
-            assertThat(actual).isTrue();
+        @Test
+        default void isAdministratorShouldBeCaseInsentive(TestSystem testSystem) throws Exception {
+            testee().setAdministratorId(Optional.of(testSystem.admin));
+            assertThat(testee().isAdministrator(testSystem.adminCaseVariation))
+                .isTrue();
         }
 
         @Test
-        default void virtualHostedUsersRepositoryShouldUseFullMailAddressAsUsername() throws Exception {
-            // Some implementations do not support changing virtual hosting value
-            Assumptions.assumeTrue(testee().supportVirtualHosting());
+        default void testShouldReturnFalseWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //When
+            boolean actual = testee().test(testSystem.user1, "password");
+            //Then
+            assertThat(actual).isFalse();
+        }
 
-            assertThat(testee().getUsername(new MailAddress("local@domain"))).isEqualTo(Username.of("local@domain"));
+        @ParameterizedTest
+        @MethodSource("illegalCharacters")
+        default void assertValidShouldThrowWhenUsernameLocalPartWithIllegalCharacter(String illegalCharacter) {
+            assertThatThrownBy(() -> testee().assertValid(Username.of("a" + illegalCharacter + "a")))
+                .isInstanceOf(InvalidUsernameException.class);
+        }
+
+        static Stream<Arguments> illegalCharacters() {
+            return Stream.of(
+                "\"",
+                "(",
+                ")",
+                ",",
+                ":",
+                ";",
+                "<",
+                ">",
+                "@",
+                "[",
+                "\\",
+                "]",
+                " ")
+                .map(Arguments::of);
         }
+    }
+
+    interface ReadWriteContract extends AbstractUsersRepositoryContract {
 
         @Test
-        default void getMailAddressForShouldBeIdentityWhenVirtualHosting() throws Exception {
-            // Some implementations do not support changing virtual hosting value
-            Assumptions.assumeTrue(testee().supportVirtualHosting());
+        default void countUsersShouldReturnNumberOfUsersWhenNotEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            List<Username> keys = Arrays.asList(testSystem.user1, testSystem.user2, testSystem.user3);
+            for (Username username : keys) {
+                testee().addUser(username, username.asString());
+            }
+            //When
+            int actual = testee().countUsers();
+            //Then
+            assertThat(actual).isEqualTo(keys.size());
+        }
 
-            String username = "user@domain";
-            assertThat(testee().getMailAddressFor(Username.of(username)))
-                .isEqualTo(username);
+        @Test
+        default void listShouldReturnExactlyUsersInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            List<Username> keys = Arrays.asList(testSystem.user1, testSystem.user2, testSystem.user3);
+            for (Username username : keys) {
+                testee().addUser(username, username.asString());
+            }
+            //When
+            Iterator<Username> actual = testee().list();
+            //Then
+            assertThat(actual)
+                .toIterable()
+                .containsOnly(testSystem.user1, testSystem.user2, testSystem.user3);
         }
 
         @Test
-        default void getUserShouldBeCaseInsensitive() throws Exception {
-            assertThat(testee().getUsername(new MailAddress("lowerUPPER", TestSystem.DOMAIN)))
-                .isEqualTo(Username.fromLocalPartWithDomain("lowerupper", TestSystem.DOMAIN));
+        default void addUserShouldAddAUserWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //When
+            testee().addUser(testSystem.user2, "password2");
+            //Then
+            assertThat(testee().contains(testSystem.user2)).isTrue();
         }
-    }
 
-    interface WithOutVirtualHostingContract extends AbstractUsersRepositoryContract {
         @Test
-        default void nonVirtualHostedUsersRepositoryShouldUseLocalPartAsUsername() throws Exception {
-            // Some implementations do not support changing virtual hosting value
-            Assumptions.assumeFalse(testee().supportVirtualHosting());
+        default void containsShouldPreserveCaseVariation(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-            assertThat(testee().getUsername(new MailAddress("local@domain"))).isEqualTo(Username.of("local"));
+            assertThat(testee().contains(testSystem.user1CaseVariation)).isTrue();
         }
 
         @Test
-        default void getMailAddressForShouldAppendDefaultDomainWhenNoVirtualHosting(TestSystem testSystem) throws Exception {
-            // Some implementations do not support changing virtual hosting value
-            Assumptions.assumeFalse(testee().supportVirtualHosting());
+        default void containsShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-            String username = "user";
-            assertThat(testee().getMailAddressFor(Username.of(username)))
-                .isEqualTo(new MailAddress(username, testSystem.domainList.getDefaultDomain()));
+            assertThat(testee().contains(testSystem.user1)).isTrue();
         }
 
         @Test
-        default void getUserShouldBeCaseInsensitive() throws Exception {
-            assertThat(testee().getUsername(new MailAddress("lowerUPPER", TestSystem.DOMAIN)))
-                .isEqualTo(Username.fromLocalPartWithoutDomain("lowerupper"));
+        default void containsShouldBeCaseInsentiveWhenOriginalValueLowerCased(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1, "password2");
+
+            assertThat(testee().contains(testSystem.user1CaseVariation)).isTrue();
         }
 
-    }
+        @Test
+        default void addUserShouldDisableCaseVariationWhenOriginalValueLowerCased(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1, "password2");
 
-    AbstractUsersRepository testee();
+            assertThatThrownBy(() -> testee().addUser(testSystem.user1CaseVariation, "pass"))
+                .isInstanceOf(UsersRepositoryException.class);
+        }
 
-    @Test
-    default void countUsersShouldReturnZeroWhenEmptyRepository() throws UsersRepositoryException {
-        //Given
-        int expected = 0;
-        //When
-        int actual = testee().countUsers();
-        //Then
-        assertThat(actual).isEqualTo(expected);
-    }
+        @Test
+        default void addUserShouldDisableCaseVariation(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-    @Test
-    default void countUsersShouldReturnNumberOfUsersWhenNotEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        ArrayList<Username> keys = new ArrayList<>(3);
-        keys.add(testSystem.user1);
-        keys.add(testSystem.user2);
-        keys.add(testSystem.user3);
-        for (Username username : keys) {
-            testee().addUser(username, username.asString());
-        }
-        //When
-        int actual = testee().countUsers();
-        //Then
-        assertThat(actual).isEqualTo(keys.size());
-    }
+            assertThatThrownBy(() -> testee().addUser(testSystem.user1, "pass"))
+                .isInstanceOf(UsersRepositoryException.class);
+        }
 
-    @Test
-    default void listShouldReturnEmptyIteratorWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //When
-        Iterator<Username> actual = testee().list();
-        //Then
-        assertThat(actual)
-            .toIterable()
-            .isEmpty();
-    }
+        @Test
+        default void listShouldReturnLowerCaseUser(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-    @Test
-    default void listShouldReturnExactlyUsersInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        ArrayList<Username> keys = new ArrayList<>(3);
-        keys.add(testSystem.user1);
-        keys.add(testSystem.user2);
-        keys.add(testSystem.user3);
-        for (Username username : keys) {
-            testee().addUser(username, username.asString());
-        }
-        //When
-        Iterator<Username> actual = testee().list();
-        //Then
-        assertThat(actual)
-            .toIterable()
-            .containsOnly(testSystem.user1, testSystem.user2, testSystem.user3);
-    }
+            assertThat(testee().list())
+                .toIterable()
+                .containsExactly(testSystem.user1);
+        }
 
-    @Test
-    default void addUserShouldAddAUserWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //When
-        testee().addUser(testSystem.user2, "password2");
-        //Then
-        assertThat(testee().contains(testSystem.user2)).isTrue();
-    }
+        @Test
+        default void removeUserShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-    @Test
-    default void containsShouldPreserveCaseVariation(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+            testee().removeUser(testSystem.user1);
 
-        assertThat(testee().contains(testSystem.user1CaseVariation)).isTrue();
-    }
+            assertThat(testee().list())
+                .toIterable()
+                .isEmpty();
+        }
 
-    @Test
-    default void containsShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+        @Test
+        default void removeUserShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1, "password2");
 
-        assertThat(testee().contains(testSystem.user1)).isTrue();
-    }
+            testee().removeUser(testSystem.user1CaseVariation);
 
-    @Test
-    default void containsShouldBeCaseInsentiveWhenOriginalValueLowerCased(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1, "password2");
+            assertThat(testee().list())
+                .toIterable()
+                .isEmpty();
+        }
 
-        assertThat(testee().contains(testSystem.user1CaseVariation)).isTrue();
-    }
+        @Test
+        default void getUserByNameShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1, "password2");
 
-    @Test
-    default void addUserShouldDisableCaseVariationWhenOriginalValueLowerCased(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1, "password2");
+            assertThat(testee().getUserByName(testSystem.user1CaseVariation).getUserName())
+                .isEqualTo(testSystem.user1);
+        }
 
-        assertThatThrownBy(() -> testee().addUser(testSystem.user1CaseVariation, "pass"))
-            .isInstanceOf(UsersRepositoryException.class);
-    }
+        @Test
+        default void getUserByNameShouldReturnLowerCaseAddedUser(TestSystem testSystem) throws UsersRepositoryException {
+            testee().addUser(testSystem.user1CaseVariation, "password2");
 
-    @Test
-    default void addUserShouldDisableCaseVariation(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+            assertThat(testee().getUserByName(testSystem.user1).getUserName())
+                .isEqualTo(testSystem.user1);
+        }
 
-        assertThatThrownBy(() -> testee().addUser(testSystem.user1, "pass"))
-            .isInstanceOf(UsersRepositoryException.class);
-    }
 
-    @Test
-    default void listShouldReturnLowerCaseUser(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+        @Test
+        default void testShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) throws UsersRepositoryException {
+            String password = "password2";
+            testee().addUser(testSystem.user1CaseVariation, password);
 
-        assertThat(testee().list())
-            .toIterable()
-            .containsExactly(testSystem.user1);
-    }
+            assertThat(testee().test(testSystem.user1, password))
+                .isTrue();
+        }
 
-    @Test
-    default void removeUserShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+        @Test
+        default void testShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
+            String password = "password2";
+            testee().addUser(testSystem.user1, password);
 
-        testee().removeUser(testSystem.user1);
+            assertThat(testee().test(testSystem.user1CaseVariation, password))
+                .isTrue();
+        }
 
-        assertThat(testee().list())
-            .toIterable()
-            .isEmpty();
-    }
+        @Test
+        default void addUserShouldAddAUserWhenNotEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user2, "password2");
+            //When
+            testee().addUser(testSystem.user3, "password3");
+            //Then
+            assertThat(testee().contains(testSystem.user3)).isTrue();
+        }
 
-    @Test
-    default void removeUserShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1, "password2");
+        @Test
+        default void addUserShouldThrowWhenSameUsernameWithDifferentCase(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.toUsername("myUsername"), "password");
+            //When
+            assertThatThrownBy(() -> testee().addUser(testSystem.toUsername("MyUsername"), "password"))
+                .isInstanceOf(AlreadyExistInUsersRepositoryException.class);
+        }
 
-        testee().removeUser(testSystem.user1CaseVariation);
+        @Test
+        default void addUserShouldThrowWhenUserAlreadyPresentInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            assertThatThrownBy(() -> testee().addUser(testSystem.user1, "password2"))
+                .isInstanceOf(AlreadyExistInUsersRepositoryException.class);
+        }
 
-        assertThat(testee().list())
-            .toIterable()
-            .isEmpty();
-    }
+        @Test
+        default void getUserByNameShouldReturnAUserWhenContainedInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            User actual = testee().getUserByName(testSystem.user1);
+            //Then
+            assertThat(actual).isNotNull();
+            assertThat(actual.getUserName()).isEqualTo(testSystem.user1);
+        }
 
-    @Test
-    default void getUserByNameShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1, "password2");
+        @Test
+        default void getUserByNameShouldReturnUserWhenDifferentCase(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.toUsername("username"), "password");
+            //When
+            User actual = testee().getUserByName(testSystem.toUsername("uSERNAMe"));
+            //Then
+            assertThat(actual).isNotNull();
+            assertThat(actual.getUserName()).isEqualTo(testSystem.user1);
+        }
 
-        assertThat(testee().getUserByName(testSystem.user1CaseVariation).getUserName())
-            .isEqualTo(testSystem.user1);
-    }
+        @Test
+        default void testShouldReturnTrueWhenAUserHasACorrectPassword(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            boolean actual = testee().test(testSystem.user1, "password");
+            //Then
+            assertThat(actual).isTrue();
+        }
 
-    @Test
-    default void getUserByNameShouldReturnLowerCaseAddedUser(TestSystem testSystem) throws UsersRepositoryException {
-        testee().addUser(testSystem.user1CaseVariation, "password2");
+        @Test
+        default void testShouldReturnFalseWhenAUserHasAnIncorrectPassword(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            boolean actual = testee().test(testSystem.user1, "password2");
+            //Then
+            assertThat(actual).isFalse();
+        }
 
-        assertThat(testee().getUserByName(testSystem.user1).getUserName())
-            .isEqualTo(testSystem.user1);
-    }
+        @Test
+        default void testShouldReturnFalseWhenAUserHasAnIncorrectCasePassword(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            boolean actual = testee().test(testSystem.user1, "Password");
+            //Then
+            assertThat(actual).isFalse();
+        }
 
-    @Test
-    default void getUserShouldBeCaseInsentive(TestSystem testSystem) throws Exception {
-        assertThat(testee().getUsername(testSystem.user1CaseVariation.asMailAddress()))
-            .isEqualTo(testSystem.user1);
-    }
+        @Test
+        default void testShouldReturnFalseWhenAUserIsNotInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.toUsername("username"), "password");
+            //When
+            boolean actual = testee().test(testSystem.toUsername("username2"), "password");
+            //Then
+            assertThat(actual).isFalse();
+        }
 
-    @Test
-    default void isAdministratorShouldBeCaseInsentive(TestSystem testSystem) throws Exception {
-        testee().setAdministratorId(Optional.of(testSystem.admin));
-        assertThat(testee().isAdministrator(testSystem.adminCaseVariation))
-            .isTrue();
-    }
+        @Test
+        default void testShouldReturnTrueWhenAUserHasAnIncorrectCaseName(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.toUsername("username"), "password");
+            //When
+            boolean actual = testee().test(testSystem.toUsername("userName"), "password");
+            //Then
+            assertThat(actual).isTrue();
+        }
 
-    @Test
-    default void testShouldBeCaseInsentiveOnCaseVariationUser(TestSystem testSystem) throws UsersRepositoryException {
-        String password = "password2";
-        testee().addUser(testSystem.user1CaseVariation, password);
 
-        assertThat(testee().test(testSystem.user1, password))
-            .isTrue();
-    }
+        @Test
+        default void testShouldReturnFalseWhenAUserIsRemovedFromRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            testee().removeUser(testSystem.user1);
+            //When
+            boolean actual = testee().test(testSystem.user1, "password");
+            //Then
+            assertThat(actual).isFalse();
+        }
 
-    @Test
-    default void testShouldBeCaseInsentive(TestSystem testSystem) throws UsersRepositoryException {
-        String password = "password2";
-        testee().addUser(testSystem.user1, password);
+        @Test
+        default void removeUserShouldRemoveAUserWhenPresentInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            //When
+            testee().removeUser(testSystem.user1);
+            //Then
+            assertThat(testee().contains(testSystem.user1)).isFalse();
+        }
 
-        assertThat(testee().test(testSystem.user1CaseVariation, password))
-            .isTrue();
-    }
+        @Test
+        default void updateUserShouldAllowToAuthenticateWithNewPassword(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            User user = testee().getUserByName(testSystem.user1);
+            user.setPassword("newpass");
+            //When
+            testee().updateUser(user);
+            //Then
+            assertThat(testee().test(testSystem.user1, "newpass")).isTrue();
+        }
 
-    @Test
-    default void addUserShouldAddAUserWhenNotEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user2, "password2");
-        //When
-        testee().addUser(testSystem.user3, "password3");
-        //Then
-        assertThat(testee().contains(testSystem.user3)).isTrue();
-    }
+        @Test
+        default void updateUserShouldNotAllowToAuthenticateWithOldPassword(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            User user = testee().getUserByName(testSystem.user1);
+            user.setPassword("newpass");
+            //When
+            testee().updateUser(user);
+            //Then
+            assertThat(testee().test(testSystem.user1, "password")).isFalse();
+        }
 
-    @Test
-    default void addUserShouldThrowWhenSameUsernameWithDifferentCase(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.toUsername("myUsername"), "password");
-        //When
-        assertThatThrownBy(() -> testee().addUser(testSystem.toUsername("MyUsername"), "password"))
-            .isInstanceOf(AlreadyExistInUsersRepositoryException.class);
-    }
+        @Test
+        default void updateUserShouldThrowWhenAUserIsNoMoreInRepository(TestSystem testSystem) throws UsersRepositoryException {
+            //Given
+            testee().addUser(testSystem.user1, "password");
+            User user = testee().getUserByName(testSystem.user1);
+            testee().removeUser(testSystem.user1);
+            //When
+            assertThatThrownBy(() -> testee().updateUser(user))
+                .isInstanceOf(UsersRepositoryException.class);
+        }
 
-    @Test
-    default void addUserShouldThrowWhenUserAlreadyPresentInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        assertThatThrownBy(() -> testee().addUser(testSystem.user1, "password2"))
-            .isInstanceOf(AlreadyExistInUsersRepositoryException.class);
-    }
+        @Test
+        default void removeUserShouldThrowWhenUserNotInRepository(TestSystem testSystem) {
+            //When
+            assertThatThrownBy(() -> testee().removeUser(testSystem.user1))
+                .isInstanceOf(UsersRepositoryException.class);
+        }
 
-    @Test
-    default void getUserByNameShouldReturnAUserWhenContainedInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        User actual = testee().getUserByName(testSystem.user1);
-        //Then
-        assertThat(actual).isNotNull();
-        assertThat(actual.getUserName()).isEqualTo(testSystem.user1);
-    }
+        @Test
+        default void isAdministratorShouldReturnFalseWhenNotConfigured(TestSystem testSystem) throws Exception {
+            testee().setAdministratorId(Optional.empty());
 
-    @Test
-    default void getUserByNameShouldReturnUserWhenDifferentCase(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.toUsername("username"), "password");
-        //When
-        User actual = testee().getUserByName(testSystem.toUsername("uSERNAMe"));
-        //Then
-        assertThat(actual).isNotNull();
-        assertThat(actual.getUserName()).isEqualTo(testSystem.user1);
-    }
+            assertThat(testee().isAdministrator(testSystem.admin)).isFalse();
+        }
 
-    @Test
-    default void testShouldReturnTrueWhenAUserHasACorrectPassword(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        boolean actual = testee().test(testSystem.user1, "password");
-        //Then
-        assertThat(actual).isTrue();
-    }
+        @Test
+        default void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
+            testee().setAdministratorId(Optional.of(testSystem.admin));
 
-    @Test
-    default void testShouldReturnFalseWhenAUserHasAnIncorrectPassword(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        boolean actual = testee().test(testSystem.user1, "password2");
-        //Then
-        assertThat(actual).isFalse();
-    }
+            assertThat(testee().isAdministrator(testSystem.admin)).isTrue();
+        }
 
-    @Test
-    default void testShouldReturnFalseWhenAUserHasAnIncorrectCasePassword(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        boolean actual = testee().test(testSystem.user1, "Password");
-        //Then
-        assertThat(actual).isFalse();
-    }
+        @Test
+        default void isAdministratorShouldReturnFalseWhenConfiguredAndUserIsNotAdmin(TestSystem testSystem) throws Exception {
+            testee().setAdministratorId(Optional.of(testSystem.admin));
 
-    @Test
-    default void testShouldReturnFalseWhenAUserIsNotInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.toUsername("username"), "password");
-        //When
-        boolean actual = testee().test(testSystem.toUsername("username2"), "password");
-        //Then
-        assertThat(actual).isFalse();
+            assertThat(testee().isAdministrator(testSystem.user1)).isFalse();
+        }
     }
 
-    @Test
-    default void testShouldReturnTrueWhenAUserHasAnIncorrectCaseName(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.toUsername("username"), "password");
-        //When
-        boolean actual = testee().test(testSystem.toUsername("userName"), "password");
-        //Then
-        assertThat(actual).isTrue();
-    }
+    interface WithVirtualHostingReadWriteContract extends ReadWriteContract {
 
-    @Test
-    default void testShouldReturnFalseWhenEmptyRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //When
-        boolean actual = testee().test(testSystem.user1, "password");
-        //Then
-        assertThat(actual).isFalse();
-    }
+        @Test
+        default void testShouldReturnTrueWhenAUserHasACorrectPasswordAndOtherCaseInDomain(TestSystem testSystem) throws Exception {
+            testSystem.domainList.addDomain(Domain.of("Domain.OrG"));
+            String username = "myuser";
+            String password = "password";
+            testee().addUser(Username.of(username + "@Domain.OrG"), password);
 
-    @Test
-    default void testShouldReturnFalseWhenAUserIsRemovedFromRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        testee().removeUser(testSystem.user1);
-        //When
-        boolean actual = testee().test(testSystem.user1, "password");
-        //Then
-        assertThat(actual).isFalse();
-    }
+            boolean actual = testee().test(Username.of(username + "@domain.org"), password);
 
-    @Test
-    default void removeUserShouldRemoveAUserWhenPresentInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        //When
-        testee().removeUser(testSystem.user1);
-        //Then
-        assertThat(testee().contains(testSystem.user1)).isFalse();
-    }
+            assertThat(actual).isTrue();
+        }
 
-    @Test
-    default void removeUserShouldThrowWhenUserNotInRepository(TestSystem testSystem) {
-        //When
-        assertThatThrownBy(() -> testee().removeUser(testSystem.user1))
-            .isInstanceOf(UsersRepositoryException.class);
-    }
+        @Test
+        default void addUserShouldThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().addUser(testSystem.userWithUnknowDomain, "password"))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Domain does not exist in DomainList");
+        }
 
-    @Test
-    default void updateUserShouldAllowToAuthenticateWithNewPassword(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        User user = testee().getUserByName(testSystem.user1);
-        user.setPassword("newpass");
-        //When
-        testee().updateUser(user);
-        //Then
-        assertThat(testee().test(testSystem.user1, "newpass")).isTrue();
-    }
+        @Test
+        default void addUserShouldThrowWhenInvalidUser(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().addUser(testSystem.invalidUsername, "password"))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessageContaining("should not contain any of those characters");
+        }
 
-    @Test
-    default void updateUserShouldNotAllowToAuthenticateWithOldPassword(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        User user = testee().getUserByName(testSystem.user1);
-        user.setPassword("newpass");
-        //When
-        testee().updateUser(user);
-        //Then
-        assertThat(testee().test(testSystem.user1, "password")).isFalse();
-    }
+        @Test
+        default void updateUserShouldThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().updateUser(new DefaultUser(testSystem.userWithUnknowDomain, "hasAlg")))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Domain does not exist in DomainList");
+        }
 
-    @Test
-    default void updateUserShouldThrowWhenAUserIsNoMoreInRepository(TestSystem testSystem) throws UsersRepositoryException {
-        //Given
-        testee().addUser(testSystem.user1, "password");
-        User user = testee().getUserByName(testSystem.user1);
-        testee().removeUser(testSystem.user1);
-        //When
-        assertThatThrownBy(() -> testee().updateUser(user))
-            .isInstanceOf(UsersRepositoryException.class);
-    }
+        @Test
+        default void updateUserShouldNotThrowInvalidUsernameExceptionWhenInvalidUser(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().updateUser(new DefaultUser(testSystem.invalidUsername, "hasAlg")))
+                .isNotInstanceOf(InvalidUsernameException.class);
+        }
 
-    @Test
-    default void isAdministratorShouldReturnFalseWhenNotConfigured(TestSystem testSystem) throws Exception {
-        testee().setAdministratorId(Optional.empty());
+        @Test
+        default void removeUserShouldThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().removeUser(testSystem.userWithUnknowDomain))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Domain does not exist in DomainList");
+        }
 
-        assertThat(testee().isAdministrator(testSystem.admin)).isFalse();
+        @Test
+        default void removeUserShouldNotThrowInvalidUsernameExceptionWhenInvalidUser(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().removeUser(testSystem.invalidUsername))
+                .isNotInstanceOf(InvalidUsernameException.class);
+        }
     }
 
-    @Test
-    default void isAdministratorShouldReturnTrueWhenConfiguredAndUserIsAdmin(TestSystem testSystem) throws Exception {
-        testee().setAdministratorId(Optional.of(testSystem.admin));
+    interface WithVirtualHostingReadOnlyContract extends ReadOnlyContract {
+
+        @Test
+        default void getUserByNameShouldNotThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatCode(() -> testee().getUserByName(testSystem.userWithUnknowDomain))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void containsShouldNotThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatCode(() -> testee().contains(testSystem.userWithUnknowDomain))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void testShouldNotThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatCode(() -> testee().test(testSystem.userWithUnknowDomain, "password"))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void isAdministratorShouldThrowWhenUserDoesNotBelongToDomainList(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().isAdministrator(testSystem.userWithUnknowDomain))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Domain does not exist in DomainList");
+        }
+
+        @Test
+        default void virtualHostedUsersRepositoryShouldUseFullMailAddressAsUsername() throws Exception {
+            // Some implementations do not support changing virtual hosting value
+            Assumptions.assumeTrue(testee().supportVirtualHosting());
+
+            assertThat(testee().getUsername(new MailAddress("local@domain"))).isEqualTo(Username.of("local@domain"));
+        }
+
+        @Test
+        default void getMailAddressForShouldBeIdentityWhenVirtualHosting() throws Exception {
+            // Some implementations do not support changing virtual hosting value
+            Assumptions.assumeTrue(testee().supportVirtualHosting());
+
+            String username = "user@domain";
+            assertThat(testee().getMailAddressFor(Username.of(username)))
+                .isEqualTo(username);
+        }
+
+        @Test
+        default void getUserShouldBeCaseInsensitive() throws Exception {
+            assertThat(testee().getUsername(new MailAddress("lowerUPPER", TestSystem.DOMAIN)))
+                .isEqualTo(Username.fromLocalPartWithDomain("lowerupper", TestSystem.DOMAIN));
+        }
+
+        @Test
+        default void assertDomainPartValidShouldThrowWhenDomainPartIsMissing() throws Exception {
+            Username withoutDomainPart = Username.fromLocalPartWithoutDomain("localPartOnly");
+
+            assertThatThrownBy(() -> testee().assertDomainPartValid(withoutDomainPart))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Given Username needs to contain a @domainpart");
+        }
+
+        @Test
+        default void assertDomainPartValidShouldThrowWhenDomainPartIsNotManaged(TestSystem testSystem) {
+            assertThatThrownBy(() -> testee().assertDomainPartValid(testSystem.userWithUnknowDomain))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Domain does not exist in DomainList");
+        }
+
+        @Test
+        default void assertDomainPartValidShouldNotThrowWhenDomainPartIsManaged() {
+            Username userWithManagedDomain = Username.fromLocalPartWithDomain(
+                "localPart",
+                TestSystem.DOMAIN);
 
-        assertThat(testee().isAdministrator(testSystem.admin)).isTrue();
+            assertThatCode(() -> testee().assertDomainPartValid(userWithManagedDomain))
+                .doesNotThrowAnyException();
+        }
     }
 
-    @Test
-    default void isAdministratorShouldReturnFalseWhenConfiguredAndUserIsNotAdmin(TestSystem testSystem) throws Exception {
-        testee().setAdministratorId(Optional.of(testSystem.admin));
+    interface WithOutVirtualHostingReadOnlyContract extends ReadOnlyContract {
+        @Test
+        default void nonVirtualHostedUsersRepositoryShouldUseLocalPartAsUsername() throws Exception {
+            // Some implementations do not support changing virtual hosting value
+            Assumptions.assumeFalse(testee().supportVirtualHosting());
+
+            assertThat(testee().getUsername(new MailAddress("local@domain"))).isEqualTo(Username.of("local"));
+        }
+
+        @Test
+        default void getMailAddressForShouldAppendDefaultDomainWhenNoVirtualHosting(TestSystem testSystem) throws Exception {
+            // Some implementations do not support changing virtual hosting value
+            Assumptions.assumeFalse(testee().supportVirtualHosting());
+
+            String username = "user";
+            assertThat(testee().getMailAddressFor(Username.of(username)))
+                .isEqualTo(new MailAddress(username, testSystem.domainList.getDefaultDomain()));
+        }
+
+        @Test
+        default void getUserShouldBeCaseInsensitive() throws Exception {
+            assertThat(testee().getUsername(new MailAddress("lowerUPPER", TestSystem.DOMAIN)))
+                .isEqualTo(Username.fromLocalPartWithoutDomain("lowerupper"));
+        }
+
+        @Test
+        default void assertDomainPartValidShouldThrowWhenDomainPartIsPresent() {
+            Username withDomainPart = Username.fromLocalPartWithDomain(
+                "localPart",
+                TestSystem.DOMAIN);
+
+            assertThatThrownBy(() -> testee().assertDomainPartValid(withDomainPart))
+                .isInstanceOf(InvalidUsernameException.class)
+                .hasMessage("Given Username contains a @domainpart but virtualhosting support is disabled");
+        }
 
-        assertThat(testee().isAdministrator(testSystem.user1)).isFalse();
+        @Test
+        default void assertDomainPartValidShouldNotThrowWhenDomainPartIsMissing() {
+            Username withOutDomainPart = Username.fromLocalPartWithoutDomain("localPartOnly");
+
+            assertThatCode(() -> testee().assertDomainPartValid(withOutDomainPart))
+                .doesNotThrowAnyException();
+        }
     }
 
-    @ParameterizedTest
-    @MethodSource("illegalCharacters")
-    default void assertValidShouldThrowWhenUsernameLocalPartWithIllegalCharacter(String illegalCharacter) {
-        assertThatThrownBy(() -> testee().assertValid(Username.of("a" + illegalCharacter + "a")))
-            .isInstanceOf(InvalidUsernameException.class);
+    interface WithVirtualHostingContract extends WithVirtualHostingReadOnlyContract, WithVirtualHostingReadWriteContract {
     }
 
-    static Stream<Arguments> illegalCharacters() {
-        return Stream.of(
-            "\"",
-            "(",
-            ")",
-            ",",
-            ":",
-            ";",
-            "<",
-            ">",
-            "@",
-            "[",
-            "\\",
-            "]",
-            " ")
-            .map(Arguments::of);
+    interface WithOutVirtualHostingContract extends WithOutVirtualHostingReadOnlyContract, ReadWriteContract {
     }
 }
diff --git a/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryUsersRepository.java b/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryUsersRepository.java
index 2853514..b80c387 100644
--- a/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryUsersRepository.java
+++ b/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryUsersRepository.java
@@ -81,6 +81,8 @@ public class MemoryUsersRepository extends AbstractUsersRepository {
 
     @Override
     public void updateUser(User user) throws UsersRepositoryException {
+        assertDomainPartValid(user.getUserName());
+
         User existingUser = getUserByName(user.getUserName());
         if (existingUser == null) {
             throw new UsersRepositoryException("Please provide an existing user to update");
@@ -90,6 +92,8 @@ public class MemoryUsersRepository extends AbstractUsersRepository {
 
     @Override
     public void removeUser(Username name) throws UsersRepositoryException {
+        assertDomainPartValid(name);
+
         if (userByName.remove(name.asString()) == null) {
             throw new UsersRepositoryException("unable to remove unknown user " + name.asString());
         }
diff --git a/server/data/data-memory/src/test/java/org/apache/james/user/memory/MemoryUsersRepositoryTest.java b/server/data/data-memory/src/test/java/org/apache/james/user/memory/MemoryUsersRepositoryTest.java
index cbae5da..894f7b4 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/user/memory/MemoryUsersRepositoryTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/user/memory/MemoryUsersRepositoryTest.java
@@ -36,6 +36,9 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 
 class MemoryUsersRepositoryTest {
 
+    private static final String LOCALHOST = "localhost";
+    private static final String LOCALHOST_ADDRESS = "127.0.0.1";
+
     @Nested
     class WhenEnableVirtualHosting implements AbstractUsersRepositoryContract.WithVirtualHostingContract {
         @RegisterExtension
@@ -62,8 +65,8 @@ class MemoryUsersRepositoryTest {
         @Test
         void assertValidShouldNotThrowWhenDomainPartAndVirtualHosting() throws Exception {
             MemoryDomainList domainList = new MemoryDomainList(new InMemoryDNSService()
-                .registerMxRecord("localhost", "127.0.0.1")
-                .registerMxRecord("127.0.0.1", "127.0.0.1"));
+                .registerMxRecord(LOCALHOST, LOCALHOST_ADDRESS)
+                .registerMxRecord(LOCALHOST_ADDRESS, LOCALHOST_ADDRESS));
             domainList.setAutoDetect(false);
             domainList.setAutoDetectIP(false);
             domainList.addDomain(Domain.of("domain.tld"));
@@ -77,8 +80,8 @@ class MemoryUsersRepositoryTest {
         @Test
         void assertValidShouldNotThrowWhenDomainPartAndDomainNotFound() throws Exception {
             MemoryDomainList domainList = new MemoryDomainList(new InMemoryDNSService()
-                .registerMxRecord("localhost", "127.0.0.1")
-                .registerMxRecord("127.0.0.1", "127.0.0.1"));
+                .registerMxRecord(LOCALHOST, LOCALHOST_ADDRESS)
+                .registerMxRecord(LOCALHOST_ADDRESS, LOCALHOST_ADDRESS));
             domainList.setAutoDetect(false);
             domainList.setAutoDetectIP(false);
 
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 2d29cfd..d17afd2 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,7 +35,6 @@ import static org.mockito.Mockito.spy;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -119,7 +118,6 @@ class AliasRoutesTest {
             memoryRecipientRewriteTable.setDomainList(domainList);
 
             usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
-            usersRepository.configure(new BaseHierarchicalConfiguration());
 
             usersRepository.addUser(Username.of(BOB), BOB_PASSWORD);
             usersRepository.addUser(Username.of(BOB_WITH_SLASH), BOB_WITH_SLASH_PASSWORD);
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
index bcecf27..8425f92 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/ForwardRoutesTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.spy;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.james.core.Domain;
 import org.apache.james.core.Username;
 import org.apache.james.dnsservice.api.DNSService;
@@ -118,7 +117,6 @@ class ForwardRoutesTest {
             MappingSourceModule mappingSourceModule = new MappingSourceModule();
 
             usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
-            usersRepository.configure(new BaseHierarchicalConfiguration());
 
             usersRepository.addUser(Username.of(BOB), BOB_PASSWORD);
             usersRepository.addUser(Username.of(ALICE), ALICE_PASSWORD);
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
index 50a0cec..5990161 100644
--- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
@@ -71,7 +71,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
 
-import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.BucketName;
 import org.apache.james.blob.api.HashBlobId;
@@ -129,6 +128,7 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
 import com.google.common.collect.ImmutableList;
+
 import io.restassured.RestAssured;
 import io.restassured.filter.log.LogDetail;
 import reactor.core.publisher.Flux;
@@ -211,7 +211,6 @@ class DeletedMessagesVaultRoutesTest {
         domainList.addDomain(DOMAIN);
 
         MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList);
-        usersRepository.configure(new BaseHierarchicalConfiguration());
 
         usersRepository.addUser(USERNAME, "userPassword");
 


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