JAMES-3946 DropList Cassandra and Memory app (#2263)
diff --git a/server/apps/distributed-app/docs/modules/ROOT/nav.adoc b/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
index bf98a74..60b767f 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/nav.adoc
@@ -34,6 +34,7 @@
**** xref:configure/batchsizes.adoc[batchsizes.properties]
**** xref:configure/dns.adoc[dnsservice.xml]
**** xref:configure/domainlist.adoc[domainlist.xml]
+**** xref:configure/droplists.adoc[DropLists]
**** xref:configure/healthcheck.adoc[healthcheck.properties]
**** xref:configure/mailetcontainer.adoc[mailetcontainer.xml]
**** xref:configure/mailets.adoc[Packaged Mailets]
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc
new file mode 100644
index 0000000..e63a846
--- /dev/null
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/droplists.adoc
@@ -0,0 +1,32 @@
+= Distributed James Server — DropLists
+:navtitle: DropLists
+
+The DropList, also known as the mail blacklist, is a collection of
+domains and email addresses that are denied from sending emails within the system.
+It is disabled by default.
+To enable it, modify the `droplists.properties` file and include the `IsInDropList` matcher in the `mailetcontainer.xml`.
+To disable it, adjust the `droplists.properties` file and remove the `IsInDropList` matcher from the `mailetcontainer.xml`.
+
+.droplists.properties content
+|===
+| Property name | explanation
+
+| enabled
+| Boolean. Governs whether DropLists should be enabled. Defaults to `false`.
+|===
+
+== Enabling Matcher
+
+Plug the `IsInDropList` matcher within `mailetcontainer.xml` :
+
+....
+<mailet match="org.apache.james.transport.matchers.IsInDropList" class="ToProcessor">
+ <processor>transport</processor>
+</mailet>
+....
+
+== DropList management
+
+DropList management, including adding and deleting entries, is performed through the WebAdmin REST API.
+
+See xref:operate/webadmin.adoc#_administrating_droplists[WebAdmin DropLists].
\ No newline at end of file
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
index 2fa9d90..bc9b0f9 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/index.adoc
@@ -66,6 +66,7 @@
** xref:configure/collecting-contacts.adoc[This page] documents contact collection
** xref:configure/collecting-events.adoc[This page] documents event collection
** xref:configure/dsn.adoc[this page] specified how to support SMTP Delivery Submission Notification (link:https://tools.ietf.org/html/rfc3461[RFC-3461])
+** xref:configure/droplists.adoc[This page] allows configuring drop lists.
== System properties
diff --git a/server/apps/distributed-app/sample-configuration/droplists.properties b/server/apps/distributed-app/sample-configuration/droplists.properties
new file mode 100644
index 0000000..bbc2756
--- /dev/null
+++ b/server/apps/distributed-app/sample-configuration/droplists.properties
@@ -0,0 +1,3 @@
+# Configuration file for DropLists
+
+enabled=false
\ No newline at end of file
diff --git a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
index eb4bbce..c4b6436 100644
--- a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
+++ b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesConfiguration.java
@@ -52,6 +52,7 @@
private Optional<VaultConfiguration> vaultConfiguration;
private Optional<Boolean> jmapEnabled;
private Optional<Boolean> quotaCompatibilityMode;
+ private Optional<Boolean> dropListsEnabled;
private Builder() {
searchConfiguration = Optional.empty();
@@ -64,6 +65,7 @@
vaultConfiguration = Optional.empty();
jmapEnabled = Optional.empty();
quotaCompatibilityMode = Optional.empty();
+ dropListsEnabled = Optional.empty();
}
public Builder workingDirectory(String path) {
@@ -134,6 +136,11 @@
return this;
}
+ public Builder enableDropLists() {
+ this.dropListsEnabled = Optional.of(true);
+ return this;
+ }
+
public CassandraRabbitMQJamesConfiguration build() {
ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
@@ -190,6 +197,16 @@
}
});
+ boolean dropListsEnabled = this.dropListsEnabled.orElseGet(() -> {
+ try {
+ return propertiesProvider.getConfiguration("droplists").getBoolean("enabled", false);
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
return new CassandraRabbitMQJamesConfiguration(
configurationPath,
directories,
@@ -199,7 +216,8 @@
mailQueueChoice,
mailQueueViewChoice, vaultConfiguration,
jmapEnabled,
- quotaCompatibilityMode);
+ quotaCompatibilityMode,
+ dropListsEnabled);
}
}
@@ -217,12 +235,13 @@
private final VaultConfiguration vaultConfiguration;
private final boolean jmapEnabled;
private final boolean quotaCompatibilityMode;
+ private final boolean dropListsEnabled;
public CassandraRabbitMQJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories,
BlobStoreConfiguration blobStoreConfiguration, SearchConfiguration searchConfiguration,
UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation, MailQueueChoice mailQueueChoice,
MailQueueViewChoice mailQueueViewChoice, VaultConfiguration vaultConfiguration,
- boolean jmapEnabled, boolean quotaCompatibilityMode) {
+ boolean jmapEnabled, boolean quotaCompatibilityMode, boolean dropListsEnabled) {
this.configurationPath = configurationPath;
this.directories = directories;
this.blobStoreConfiguration = blobStoreConfiguration;
@@ -233,6 +252,7 @@
this.vaultConfiguration = vaultConfiguration;
this.jmapEnabled = jmapEnabled;
this.quotaCompatibilityMode = quotaCompatibilityMode;
+ this.dropListsEnabled = dropListsEnabled;
}
public MailQueueViewChoice getMailQueueViewChoice() {
@@ -276,4 +296,8 @@
public boolean isQuotaCompatibilityMode() {
return quotaCompatibilityMode;
}
+
+ public boolean isDropListsEnabled() {
+ return dropListsEnabled;
+ }
}
diff --git a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
index aebbf32..e0c750f 100644
--- a/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
+++ b/server/apps/distributed-app/src/main/java/org/apache/james/CassandraRabbitMQJamesServerMain.java
@@ -41,6 +41,7 @@
import org.apache.james.modules.data.CassandraDLPConfigurationStoreModule;
import org.apache.james.modules.data.CassandraDelegationStoreModule;
import org.apache.james.modules.data.CassandraDomainListModule;
+import org.apache.james.modules.data.CassandraDropListsModule;
import org.apache.james.modules.data.CassandraJmapModule;
import org.apache.james.modules.data.CassandraRecipientRewriteTableModule;
import org.apache.james.modules.data.CassandraSieveQuotaLegacyModule;
@@ -75,6 +76,7 @@
import org.apache.james.modules.server.DKIMMailetModule;
import org.apache.james.modules.server.DLPRoutesModule;
import org.apache.james.modules.server.DataRoutesModules;
+import org.apache.james.modules.server.DropListsRoutesModule;
import org.apache.james.modules.server.InconsistencyQuotasSolvingRoutesModule;
import org.apache.james.modules.server.JMXServerModule;
import org.apache.james.modules.server.JmapTasksModule;
@@ -209,7 +211,8 @@
.chooseModules(configuration.getUsersRepositoryImplementation()))
.combineWith(chooseDeletedMessageVault(configuration.getVaultConfiguration()))
.combineWith(chooseQuotaModule(configuration))
- .overrideWith(chooseJmapModules(configuration));
+ .overrideWith(chooseJmapModules(configuration))
+ .overrideWith(chooseDropListsModule(configuration));
}
private static Module chooseMailQueue(CassandraRabbitMQJamesConfiguration configuration) {
@@ -258,4 +261,14 @@
return Modules.combine(new CassandraMailboxQuotaModule(), new CassandraSieveQuotaModule());
}
}
+
+ private static Module chooseDropListsModule(CassandraRabbitMQJamesConfiguration configuration) {
+ if (configuration.isDropListsEnabled()) {
+ return Modules.combine(new CassandraDropListsModule(), new DropListsRoutesModule());
+ }
+ return binder -> {
+
+ };
+ }
+
}
diff --git a/server/apps/memory-app/sample-configuration/droplists.properties b/server/apps/memory-app/sample-configuration/droplists.properties
new file mode 100644
index 0000000..bbc2756
--- /dev/null
+++ b/server/apps/memory-app/sample-configuration/droplists.properties
@@ -0,0 +1,3 @@
+# Configuration file for DropLists
+
+enabled=false
\ No newline at end of file
diff --git a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
index 88bfff3..aa0e08f 100644
--- a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
+++ b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesConfiguration.java
@@ -41,12 +41,14 @@
private Optional<ConfigurationPath> configurationPath;
private Optional<UsersRepositoryModuleChooser.Implementation> usersRepositoryImplementation;
private Optional<Boolean> jmapEnabled;
+ private Optional<Boolean> dropListsEnabled;
private Builder() {
rootDirectory = Optional.empty();
configurationPath = Optional.empty();
usersRepositoryImplementation = Optional.empty();
jmapEnabled = Optional.empty();
+ dropListsEnabled = Optional.empty();
}
public Builder workingDirectory(String path) {
@@ -87,6 +89,11 @@
return this;
}
+ public Builder enableDropLists() {
+ this.dropListsEnabled = Optional.of(true);
+ return this;
+ }
+
public MemoryJamesConfiguration build() {
ConfigurationPath configurationPath = this.configurationPath.orElse(new ConfigurationPath(FileSystem.FILE_PROTOCOL_AND_CONF));
JamesServerResourceLoader directories = new JamesServerResourceLoader(rootDirectory
@@ -112,10 +119,22 @@
}
});
+ boolean dropListsEnabled = this.dropListsEnabled.orElseGet(() -> {
+ PropertiesProvider propertiesProvider = new PropertiesProvider(fileSystem, configurationPath);
+ try {
+ return propertiesProvider.getConfiguration("droplists").getBoolean("enabled", false);
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+
return new MemoryJamesConfiguration(
configurationPath,
directories,
- usersRepositoryChoice, jmapEnabled);
+ usersRepositoryChoice, jmapEnabled, dropListsEnabled);
}
}
@@ -127,12 +146,16 @@
private final JamesDirectoriesProvider directories;
private final UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation;
private final boolean jmapEnabled;
+ private final boolean dropListsEnabled;
- public MemoryJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories, UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation, boolean jmapEnabled) {
+ public MemoryJamesConfiguration(ConfigurationPath configurationPath, JamesDirectoriesProvider directories,
+ UsersRepositoryModuleChooser.Implementation usersRepositoryImplementation,
+ boolean jmapEnabled, boolean dropListsEnabled) {
this.configurationPath = configurationPath;
this.directories = directories;
this.usersRepositoryImplementation = usersRepositoryImplementation;
this.jmapEnabled = jmapEnabled;
+ this.dropListsEnabled = dropListsEnabled;
}
@Override
@@ -152,4 +175,8 @@
public boolean isJmapEnabled() {
return jmapEnabled;
}
+
+ public boolean isDropListsEnabled() {
+ return dropListsEnabled;
+ }
}
diff --git a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 959c3fe..f1797b5 100644
--- a/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/apps/memory-app/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -34,6 +34,7 @@
import org.apache.james.modules.data.MemoryDataJmapModule;
import org.apache.james.modules.data.MemoryDataModule;
import org.apache.james.modules.data.MemoryDelegationStoreModule;
+import org.apache.james.modules.data.MemoryDropListsModule;
import org.apache.james.modules.data.MemoryUsersRepositoryModule;
import org.apache.james.modules.eventstore.MemoryEventStoreModule;
import org.apache.james.modules.mailbox.MemoryMailboxModule;
@@ -49,6 +50,7 @@
import org.apache.james.modules.server.DKIMMailetModule;
import org.apache.james.modules.server.DLPRoutesModule;
import org.apache.james.modules.server.DataRoutesModules;
+import org.apache.james.modules.server.DropListsRoutesModule;
import org.apache.james.modules.server.InconsistencyQuotasSolvingRoutesModule;
import org.apache.james.modules.server.JMXServerModule;
import org.apache.james.modules.server.JmapTasksModule;
@@ -174,7 +176,8 @@
.combineWith(IN_MEMORY_SERVER_AGGREGATE_MODULE)
.combineWith(new UsersRepositoryModuleChooser(new MemoryUsersRepositoryModule())
.chooseModules(configuration.getUsersRepositoryImplementation()))
- .combineWith(chooseJmapModule(configuration));
+ .combineWith(chooseJmapModule(configuration))
+ .combineWith(chooseDropListsModule(configuration));
}
private static Module chooseJmapModule(MemoryJamesConfiguration configuration) {
@@ -186,4 +189,13 @@
};
}
+ private static Module chooseDropListsModule(MemoryJamesConfiguration configuration) {
+ if (configuration.isDropListsEnabled()) {
+ return Modules.combine(new MemoryDropListsModule(), new DropListsRoutesModule());
+ }
+ return binder -> {
+
+ };
+ }
+
}
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java
new file mode 100644
index 0000000..450cc85
--- /dev/null
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraDropListsModule.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.modules.data;
+
+import org.apache.james.droplists.api.DropList;
+import org.apache.james.droplists.cassandra.CassandraDropList;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+
+public class CassandraDropListsModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(DropList.class).to(CassandraDropList.class).in(Scopes.SINGLETON);
+ }
+}
\ No newline at end of file
diff --git a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java
new file mode 100644
index 0000000..7810e73
--- /dev/null
+++ b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDropListsModule.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.modules.data;
+
+import org.apache.james.droplists.api.DropList;
+import org.apache.james.droplists.memory.MemoryDropList;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+
+public class MemoryDropListsModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DropList.class).to(MemoryDropList.class).in(Scopes.SINGLETON);
+ }
+}
\ No newline at end of file
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java
new file mode 100644
index 0000000..5a06422
--- /dev/null
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DropListsRoutesModule.java
@@ -0,0 +1,34 @@
+/****************************************************************
+ * 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.modules.server;
+
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.routes.DropListRoutes;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+
+public class DropListsRoutesModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ Multibinder.newSetBinder(binder(), Routes.class).addBinding()
+ .to(DropListRoutes.class);
+ }
+}