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 2023/02/21 01:16:08 UTC

[james-project] 05/09: JAMES-3885 Step to adapt forward to a username change

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 bd0aec8909294491813f032e9df53f92f1736659
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 16 15:36:00 2023 +0700

    JAMES-3885 Step to adapt forward to a username change
    
     - Migrates old user forwards to the new. This allow managing those
     from the new user account, thus deleting forwards done prior to
     the username change.
     - Set up forwards from the old user to the new
---
 .../james/rrt/ForwardUsernameChangeTaskStep.java   |  86 ++++++++++++++++
 .../memory/ForwardUsernameChangeTaskStepTest.java  | 110 +++++++++++++++++++++
 2 files changed, 196 insertions(+)

diff --git a/server/data/data-api/src/main/java/org/apache/james/rrt/ForwardUsernameChangeTaskStep.java b/server/data/data-api/src/main/java/org/apache/james/rrt/ForwardUsernameChangeTaskStep.java
new file mode 100644
index 0000000000..2319f24d09
--- /dev/null
+++ b/server/data/data-api/src/main/java/org/apache/james/rrt/ForwardUsernameChangeTaskStep.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.rrt;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.rrt.api.RecipientRewriteTable;
+import org.apache.james.rrt.lib.Mapping;
+import org.apache.james.rrt.lib.MappingSource;
+import org.apache.james.user.api.UsernameChangeTaskStep;
+import org.apache.james.util.ReactorUtils;
+import org.reactivestreams.Publisher;
+
+import com.github.fge.lambdas.Throwing;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class ForwardUsernameChangeTaskStep implements UsernameChangeTaskStep {
+    private final RecipientRewriteTable rrt;
+
+    @Inject
+    public ForwardUsernameChangeTaskStep(RecipientRewriteTable rrt) {
+        this.rrt = rrt;
+    }
+
+    @Override
+    public Publisher<Void> changeUsername(Username oldUsername, Username newUsername) {
+        MappingSource oldSource = MappingSource.fromUser(oldUsername);
+        MappingSource newSource = MappingSource.fromUser(newUsername);
+
+        return migrateExistingForwards(oldSource, newSource)
+            .then(redirectMailsToTheNewUsername(newUsername, oldSource))
+            .subscribeOn(ReactorUtils.BLOCKING_CALL_WRAPPER);
+    }
+
+    private Mono<Void> redirectMailsToTheNewUsername(Username newUsername, MappingSource oldSource) {
+        return Mono.fromRunnable(Throwing.runnable(() -> rrt.addForwardMapping(oldSource, newUsername.asString())));
+    }
+
+    private Mono<Void> migrateExistingForwards(MappingSource oldSource, MappingSource newSource) {
+        return Mono.fromCallable(() -> rrt.getStoredMappings(oldSource))
+            .flatMapMany(mappings -> Flux.fromStream(mappings.asStream()))
+            .filter(mapping -> mapping.getType().equals(Mapping.Type.Forward))
+            .concatMap(mapping -> migrateExistingForward(oldSource, newSource, mapping))
+            .then();
+    }
+
+    private Mono<Object> migrateExistingForward(MappingSource oldSource, MappingSource newSource, org.apache.james.rrt.lib.Mapping mapping) {
+        if (mapping.getType().equals(Mapping.Type.Forward) && mapping.getMappingValue().equals(oldSource.asString())) {
+            // self redirection to keep a copy
+            return Mono.fromRunnable(Throwing.runnable(() -> rrt.addForwardMapping(newSource, newSource.asString())))
+                .then(Mono.fromRunnable(Throwing.runnable(() -> rrt.removeMapping(oldSource, mapping))));
+        }
+        return Mono.fromRunnable(Throwing.runnable(() -> rrt.addForwardMapping(newSource, mapping.getMappingValue())))
+            .then(Mono.fromRunnable(Throwing.runnable(() -> rrt.removeMapping(oldSource, mapping))));
+    }
+
+    @Override
+    public StepName name() {
+        return new StepName("ForwardUsernameChangeTaskStep");
+    }
+
+    @Override
+    public int priority() {
+        return 10;
+    }
+}
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/ForwardUsernameChangeTaskStepTest.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/ForwardUsernameChangeTaskStepTest.java
new file mode 100644
index 0000000000..63d2cf2bf8
--- /dev/null
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/ForwardUsernameChangeTaskStepTest.java
@@ -0,0 +1,110 @@
+package org.apache.james.rrt.memory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
+import org.apache.james.UserEntityValidator;
+import org.apache.james.core.Domain;
+import org.apache.james.core.Username;
+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.ForwardUsernameChangeTaskStep;
+import org.apache.james.rrt.lib.Mapping;
+import org.apache.james.rrt.lib.MappingSource;
+import org.apache.james.rrt.lib.MappingsImpl;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import reactor.core.publisher.Mono;
+
+class ForwardUsernameChangeTaskStepTest {
+    private static final Username BOB_OLD = Username.of("bob-old@domain.tld");
+    private static final Username BOB_NEW = Username.of("bob-new@domain.tld");
+    private MemoryRecipientRewriteTable rrt;
+    private ForwardUsernameChangeTaskStep testee;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        DNSService dnsService = mock(DNSService.class);
+        MemoryDomainList domainList = new MemoryDomainList(dnsService);
+        domainList.configure(DomainListConfiguration.DEFAULT);
+        domainList.addDomain(Domain.of("domain.tld"));
+        rrt = new MemoryRecipientRewriteTable();
+        rrt.setUsersRepository(MemoryUsersRepository.withVirtualHosting(domainList));
+        rrt.setUserEntityValidator(UserEntityValidator.NOOP);
+        rrt.setDomainList(domainList);
+        rrt.configure(new BaseHierarchicalConfiguration());
+        testee = new ForwardUsernameChangeTaskStep(rrt);
+    }
+
+    @Test
+    void shouldCreateForwardFromOldToNewUser() {
+        Mono.from(testee.changeUsername(BOB_OLD, BOB_NEW)).block();
+
+        assertThat(rrt.getAllMappings())
+            .hasSize(1)
+            .containsEntry(MappingSource.fromUser(BOB_OLD),
+                MappingsImpl.builder()
+                    .add(Mapping.forward(BOB_NEW.asString()))
+                    .build());
+    }
+
+    @Test
+    void shouldMigratePreviousForwards() throws Exception {
+        rrt.addForwardMapping(MappingSource.fromUser(BOB_OLD), "alice@domain.tld");
+
+        Mono.from(testee.changeUsername(BOB_OLD, BOB_NEW)).block();
+
+        assertThat(rrt.getAllMappings())
+            .hasSize(2)
+            .containsEntry(MappingSource.fromUser(BOB_OLD),
+                MappingsImpl.builder()
+                    .add(Mapping.forward(BOB_NEW.asString()))
+                    .build())
+            .containsEntry(MappingSource.fromUser(BOB_NEW),
+                MappingsImpl.builder()
+                    .add(Mapping.forward("alice@domain.tld"))
+                    .build());
+    }
+
+    @Test
+    void shouldNotAlterDestinationForwards() throws Exception {
+        rrt.addForwardMapping(MappingSource.fromUser(BOB_NEW), "alice@domain.tld");
+
+        Mono.from(testee.changeUsername(BOB_OLD, BOB_NEW)).block();
+
+        assertThat(rrt.getAllMappings())
+            .hasSize(2)
+            .containsEntry(MappingSource.fromUser(BOB_OLD),
+                MappingsImpl.builder()
+                    .add(Mapping.forward(BOB_NEW.asString()))
+                    .build())
+            .containsEntry(MappingSource.fromUser(BOB_NEW),
+                MappingsImpl.builder()
+                    .add(Mapping.forward("alice@domain.tld"))
+                    .build());
+    }
+
+    @Test
+    void shouldPreserveKeepACopy() throws Exception {
+        rrt.addForwardMapping(MappingSource.fromUser(BOB_OLD), "alice@domain.tld");
+        rrt.addForwardMapping(MappingSource.fromUser(BOB_OLD), BOB_OLD.asString());
+
+        Mono.from(testee.changeUsername(BOB_OLD, BOB_NEW)).block();
+
+        assertThat(rrt.getAllMappings())
+            .hasSize(2)
+            .containsEntry(MappingSource.fromUser(BOB_OLD),
+                MappingsImpl.builder()
+                    .add(Mapping.forward(BOB_NEW.asString()))
+                    .build())
+            .containsEntry(MappingSource.fromUser(BOB_NEW),
+                MappingsImpl.builder()
+                    .add(Mapping.forward(BOB_NEW.asString()))
+                    .add(Mapping.forward("alice@domain.tld"))
+                    .build());
+    }
+}


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