You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2023/02/23 01:10:25 UTC
[james-project] branch master updated: JAMES-3885 Migrate users filters
This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new bae6c512d4 JAMES-3885 Migrate users filters
bae6c512d4 is described below
commit bae6c512d4f021e7ecfa7edb4322d0ca926fa364
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed Feb 22 11:15:58 2023 +0700
JAMES-3885 Migrate users filters
---
.../docs/modules/ROOT/pages/operate/webadmin.adoc | 1 +
.../james/modules/data/CassandraJmapModule.java | 9 +-
.../james/modules/data/MemoryDataJmapModule.java | 5 +
.../impl/FilterUsernameChangeTaskStep.java | 62 +++++++++++
.../impl/FilterUsernameChangeTaskStepTest.java | 113 +++++++++++++++++++++
.../MemoryUsernameChangeIntegrationTest.java | 62 ++++++++++-
src/site/markdown/server/manage-webadmin.md | 1 +
7 files changed, 250 insertions(+), 3 deletions(-)
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
index 616489147c..229b6f2ad3 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
@@ -633,6 +633,7 @@ a task].
Implemented migration steps are:
- `ForwardUsernameChangeTaskStep`: creates forward from old user to new user and migrates existing forwards
+ - `FilterUsernameChangeTaskStep`: migrates users filtering rules
- `DelegationUsernameChangeTaskStep`: migrates delegations where the impacted user is either delegatee or delegator
Response codes:
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
index 9fd16bf28f..77e64336b9 100644
--- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
@@ -27,6 +27,7 @@ import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTOModule;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.apache.james.jmap.api.filtering.FilteringManagement;
import org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.jmap.api.filtering.impl.FilterUsernameChangeTaskStep;
import org.apache.james.jmap.api.identity.CustomIdentityDAO;
import org.apache.james.jmap.api.projections.EmailQueryView;
import org.apache.james.jmap.api.projections.MessageFastViewProjection;
@@ -50,6 +51,7 @@ import org.apache.james.jmap.cassandra.upload.CassandraUploadRepository;
import org.apache.james.jmap.cassandra.upload.UploadConfiguration;
import org.apache.james.jmap.cassandra.upload.UploadDAO;
import org.apache.james.jmap.cassandra.upload.UploadModule;
+import org.apache.james.user.api.UsernameChangeTaskStep;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
@@ -97,7 +99,12 @@ public class CassandraJmapModule extends AbstractModule {
cassandraDataDefinitions.addBinding().toInstance(CassandraPushSubscriptionModule.MODULE);
cassandraDataDefinitions.addBinding().toInstance(CassandraCustomIdentityModule.MODULE());
- Multibinder<EventDTOModule<? extends Event, ? extends EventDTO>> eventDTOModuleBinder = Multibinder.newSetBinder(binder(), new TypeLiteral<EventDTOModule<? extends Event, ? extends EventDTO>>() {});
+ Multibinder<EventDTOModule<? extends Event, ? extends EventDTO>> eventDTOModuleBinder = Multibinder.newSetBinder(binder(), new TypeLiteral<>() {});
eventDTOModuleBinder.addBinding().toInstance(FilteringRuleSetDefineDTOModules.FILTERING_RULE_SET_DEFINED);
+
+
+ Multibinder.newSetBinder(binder(), UsernameChangeTaskStep.class)
+ .addBinding()
+ .to(FilterUsernameChangeTaskStep.class);
}
}
diff --git a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
index f60c3919e5..55ba982be9 100644
--- a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
+++ b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
@@ -23,6 +23,7 @@ import org.apache.james.core.healthcheck.HealthCheck;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.apache.james.jmap.api.filtering.FilteringManagement;
import org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.jmap.api.filtering.impl.FilterUsernameChangeTaskStep;
import org.apache.james.jmap.api.identity.CustomIdentityDAO;
import org.apache.james.jmap.api.projections.EmailQueryView;
import org.apache.james.jmap.api.projections.MessageFastViewProjection;
@@ -36,6 +37,7 @@ import org.apache.james.jmap.memory.upload.InMemoryUploadRepository;
import org.apache.james.mailbox.extractor.TextExtractor;
import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
import org.apache.james.mailbox.store.extractor.JsoupTextExtractor;
+import org.apache.james.user.api.UsernameChangeTaskStep;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
@@ -70,5 +72,8 @@ public class MemoryDataJmapModule extends AbstractModule {
Multibinder.newSetBinder(binder(), HealthCheck.class)
.addBinding()
.to(MessageFastViewProjectionHealthCheck.class);
+ Multibinder.newSetBinder(binder(), UsernameChangeTaskStep.class)
+ .addBinding()
+ .to(FilterUsernameChangeTaskStep.class);
}
}
diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStep.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStep.java
new file mode 100644
index 0000000000..822a8fa58d
--- /dev/null
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStep.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.jmap.api.filtering.impl;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.Version;
+import org.apache.james.user.api.UsernameChangeTaskStep;
+import org.reactivestreams.Publisher;
+
+import reactor.core.publisher.Mono;
+
+public class FilterUsernameChangeTaskStep implements UsernameChangeTaskStep {
+ public static final Optional<Version> NO_VERSION = Optional.empty();
+
+ private final FilteringManagement filteringManagement;
+
+ @Inject
+ public FilterUsernameChangeTaskStep(FilteringManagement filteringManagement) {
+ this.filteringManagement = filteringManagement;
+ }
+
+ @Override
+ public StepName name() {
+ return new StepName("FilterUsernameChangeTaskStep");
+ }
+
+ @Override
+ public int priority() {
+ return 4;
+ }
+
+ @Override
+ public Publisher<Void> changeUsername(Username oldUsername, Username newUsername) {
+ return Mono.from(filteringManagement.listRulesForUser(oldUsername))
+ .filter(rules -> !rules.getRules().isEmpty())
+ .flatMap(rules -> Mono.from(filteringManagement.defineRulesForUser(newUsername, rules.getRules(), NO_VERSION))
+ .then(Mono.from(filteringManagement.clearRulesForUser(oldUsername))))
+ .then();
+ }
+}
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStepTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStepTest.java
new file mode 100644
index 0000000000..6a4c89a289
--- /dev/null
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/impl/FilterUsernameChangeTaskStepTest.java
@@ -0,0 +1,113 @@
+/****************************************************************
+ * 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.jmap.api.filtering.impl;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStoreExtension;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.Version;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(InMemoryEventStoreExtension.class)
+public class FilterUsernameChangeTaskStepTest {
+ private static final Username BOB = Username.of("bob");
+ private static final Username ALICE = Username.of("alice");
+ private static final Optional<Version> NO_VERSION = Optional.empty();
+
+ private static final String NAME = "a name";
+ private static final Rule.Condition CONDITION = Rule.Condition.of(Rule.Condition.Field.CC, Rule.Condition.Comparator.CONTAINS, "something");
+ private static final Rule.Action ACTION = Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds("id-01"));
+ private static final Rule.Builder RULE_BUILDER = Rule.builder().name(NAME).condition(CONDITION).action(ACTION);
+ private static final Rule RULE_1 = RULE_BUILDER.id(Rule.Id.of("1")).build();
+ private static final Rule RULE_2 = RULE_BUILDER.id(Rule.Id.of("2")).build();
+
+ private FilterUsernameChangeTaskStep testee;
+ private EventSourcingFilteringManagement filteringManagement;
+
+ @BeforeEach
+ void setup(EventStore eventStore) {
+ filteringManagement = new EventSourcingFilteringManagement(eventStore);
+ testee = new FilterUsernameChangeTaskStep(filteringManagement);
+ }
+
+ @Test
+ void shouldMigrateFilters() {
+ Mono.from(filteringManagement.defineRulesForUser(BOB, NO_VERSION, RULE_1))
+ .block();
+
+ Mono.from(testee.changeUsername(BOB, ALICE))
+ .block();
+
+ assertThat(Mono.from(filteringManagement.listRulesForUser(ALICE))
+ .block().getRules())
+ .containsOnly(RULE_1);
+ }
+
+ @Test
+ void shouldRemoveFiltersFromOriginalAccount() {
+ Mono.from(filteringManagement.defineRulesForUser(BOB, NO_VERSION, RULE_1))
+ .block();
+
+ Mono.from(testee.changeUsername(BOB, ALICE))
+ .block();
+
+ assertThat(Mono.from(filteringManagement.listRulesForUser(BOB))
+ .block().getRules())
+ .isEmpty();
+ }
+
+ @Test
+ void shouldOverrideFiltersFromDestinationAccount() {
+ Mono.from(filteringManagement.defineRulesForUser(BOB, NO_VERSION, RULE_1))
+ .block();
+ Mono.from(filteringManagement.defineRulesForUser(ALICE, NO_VERSION, RULE_2))
+ .block();
+
+ Mono.from(testee.changeUsername(BOB, ALICE))
+ .block();
+
+ assertThat(Mono.from(filteringManagement.listRulesForUser(ALICE))
+ .block().getRules())
+ .containsOnly(RULE_1);
+ }
+
+ @Test
+ void shouldNotOverrideFiltersFromDestinationAccountWhenNoDataInSourceAccount() {
+ Mono.from(filteringManagement.defineRulesForUser(ALICE, NO_VERSION, RULE_2))
+ .block();
+
+ Mono.from(testee.changeUsername(BOB, ALICE))
+ .block();
+
+ assertThat(Mono.from(filteringManagement.listRulesForUser(ALICE))
+ .block().getRules())
+ .containsOnly(RULE_2);
+ }
+}
diff --git a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryUsernameChangeIntegrationTest.java b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryUsernameChangeIntegrationTest.java
index a16b75b904..3da89cc460 100644
--- a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryUsernameChangeIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/MemoryUsernameChangeIntegrationTest.java
@@ -19,7 +19,6 @@
package org.apache.james.webadmin.integration.memory;
-import static io.restassured.RestAssured.given;
import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
import static org.apache.james.jmap.JMAPTestingConstants.ALICE;
import static org.apache.james.jmap.JMAPTestingConstants.ALICE_PASSWORD;
@@ -34,28 +33,66 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasSize;
import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
import org.apache.james.GuiceJamesServer;
import org.apache.james.JamesServerBuilder;
import org.apache.james.JamesServerExtension;
import org.apache.james.MemoryJamesConfiguration;
import org.apache.james.MemoryJamesServerMain;
+import org.apache.james.core.Username;
+import org.apache.james.jmap.api.filtering.FilteringManagement;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.Rules;
+import org.apache.james.jmap.api.filtering.Version;
import org.apache.james.jmap.draft.JmapGuiceProbe;
import org.apache.james.modules.TestJMAPServerModule;
import org.apache.james.probe.DataProbe;
import org.apache.james.util.Port;
import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.GuiceProbe;
import org.apache.james.utils.WebAdminGuiceProbe;
import org.apache.james.webadmin.WebAdminUtils;
-import org.apache.james.webadmin.routes.TasksRoutes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import com.google.inject.multibindings.Multibinder;
+
import io.restassured.RestAssured;
import io.restassured.specification.RequestSpecification;
+import reactor.core.publisher.Mono;
class MemoryUsernameChangeIntegrationTest {
+ public static final class FilterProbe implements GuiceProbe {
+ private final FilteringManagement filteringManagement;
+
+ @Inject
+ public FilterProbe(FilteringManagement filteringManagement) {
+ this.filteringManagement = filteringManagement;
+ }
+
+ public void defineRulesForUser(Username username, List<Rule> rules, Optional<Version> ifInState) {
+ Mono.from(filteringManagement.defineRulesForUser(username, rules, ifInState))
+ .block();
+ }
+
+ public Rules listRulesForUser(Username username) {
+ return Mono.from(filteringManagement.listRulesForUser(username))
+ .block();
+ }
+ }
+
+ private static final String NAME = "a name";
+ private static final Rule.Condition CONDITION = Rule.Condition.of(Rule.Condition.Field.CC, Rule.Condition.Comparator.CONTAINS, "something");
+ private static final Rule.Action ACTION = Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds("id-01"));
+ private static final Rule.Builder RULE_BUILDER = Rule.builder().name(NAME).condition(CONDITION).action(ACTION);
+ private static final Rule RULE_1 = RULE_BUILDER.id(Rule.Id.of("1")).build();
+ private static final Rule RULE_2 = RULE_BUILDER.id(Rule.Id.of("2")).build();
+ private static final Optional<Version> NO_VERSION = Optional.empty();
+
@RegisterExtension
static JamesServerExtension jamesServerExtension = new JamesServerBuilder<MemoryJamesConfiguration>(tmpDir ->
MemoryJamesConfiguration.builder()
@@ -64,6 +101,8 @@ class MemoryUsernameChangeIntegrationTest {
.usersRepository(DEFAULT)
.build())
.server(configuration -> MemoryJamesServerMain.createServer(configuration)
+ .overrideWith(binder -> Multibinder.newSetBinder(binder, GuiceProbe.class)
+ .addBinding().to(FilterProbe.class))
.overrideWith(new TestJMAPServerModule()))
.build();
@@ -126,4 +165,23 @@ class MemoryUsernameChangeIntegrationTest {
.body(".", hasSize(1))
.body("[0]", is(CEDRIC.asString()));
}
+
+ @Test
+ void shouldAdaptFilters(GuiceJamesServer server) {
+ FilterProbe filterProbe = server.getProbe(FilterProbe.class);
+ filterProbe.defineRulesForUser(ALICE, List.of(RULE_1), NO_VERSION);
+
+ String taskId = webAdminApi
+ .queryParam("action", "rename")
+ .post("/users/" + ALICE.asString() + "/rename/" + BOB.asString())
+ .jsonPath()
+ .get("taskId");
+
+ webAdminApi.get("/tasks/" + taskId + "/await");
+
+ assertThat(filterProbe.listRulesForUser(BOB).getRules())
+ .containsOnly(RULE_1);
+ assertThat(filterProbe.listRulesForUser(ALICE).getRules())
+ .isEmpty();
+ }
}
diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 07f89767d3..2338b0e334 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -485,6 +485,7 @@ Would migrate account data from `oldUser` to `newUser`.
Implemented migration steps are:
- `ForwardUsernameChangeTaskStep`: creates forward from old user to new user and migrates existing forwards
+ - `FilterUsernameChangeTaskStep`: migrates users filtering rules
- `DelegationUsernameChangeTaskStep`: migrates delegations where the impacted user is either delegatee or delegator
Response codes:
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org