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