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 2018/06/20 02:08:31 UTC

[2/3] james-project git commit: JAMES-2428 [DLP] Webadmin DLP markdown documentation

JAMES-2428 [DLP] Webadmin DLP markdown documentation


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/55cd111c
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/55cd111c
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/55cd111c

Branch: refs/heads/master
Commit: 55cd111c6ef2e5db545bd1101a8034515f69e623
Parents: dec6e89
Author: duc <dt...@linagora.com>
Authored: Fri Jun 15 15:13:47 2018 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Jun 20 09:08:10 2018 +0700

----------------------------------------------------------------------
 .../main/java/org/apache/james/core/Domain.java |   5 +-
 .../apache/james/CassandraJamesServerMain.java  |   4 +-
 .../org/apache/james/MemoryJamesServerMain.java |   4 +-
 .../james/modules/server/DLPRoutesModule.java   |  35 +++
 .../james/modules/server/DataRoutesModules.java |   2 -
 .../james/webadmin/utils/ErrorResponder.java    |   2 +-
 .../james/webadmin/utils/JsonExtractor.java     |   3 +-
 .../org/apache/james/webadmin/DLPModule.java    |  36 ---
 .../james/webadmin/dto/DLPConfigurationDTO.java |  20 +-
 .../webadmin/dto/DLPConfigurationItemDTO.java   |  55 ++---
 .../webadmin/routes/DLPConfigurationRoutes.java |  67 +++---
 .../dto/DLPConfigurationItemDTOTest.java        |  49 ++--
 .../routes/DLPConfigurationRoutesTest.java      | 225 +++++++++++++++----
 src/site/markdown/server/manage-webadmin.md     | 115 ++++++++++
 14 files changed, 440 insertions(+), 182 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/core/src/main/java/org/apache/james/core/Domain.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/james/core/Domain.java b/core/src/main/java/org/apache/james/core/Domain.java
index 808de41..1bcea59 100644
--- a/core/src/main/java/org/apache/james/core/Domain.java
+++ b/core/src/main/java/org/apache/james/core/Domain.java
@@ -37,8 +37,9 @@ public class Domain implements Serializable {
     }
 
     public static Domain of(String domain) {
-        Preconditions.checkNotNull(domain);
-        Preconditions.checkArgument(!domain.isEmpty() && !domain.contains("@"));
+        Preconditions.checkNotNull(domain, "Domain can not be null");
+        Preconditions.checkArgument(!domain.isEmpty() && !domain.contains("@"),
+            "Domain can not be empty nor contain `@`");
         return new Domain(domain);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
index 035a272..4878e1e 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
@@ -44,6 +44,7 @@ import org.apache.james.modules.protocols.ProtocolHandlerModule;
 import org.apache.james.modules.protocols.SMTPServerModule;
 import org.apache.james.modules.server.ActiveMQQueueModule;
 import org.apache.james.modules.server.CassandraRoutesModule;
+import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
 import org.apache.james.modules.server.ElasticSearchMetricReporterModule;
 import org.apache.james.modules.server.JMXServerModule;
@@ -67,7 +68,8 @@ public class CassandraJamesServerMain {
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
         new SwaggerRoutesModule(),
-        new WebAdminServerModule());
+        new WebAdminServerModule(),
+        new DLPRoutesModule());
 
     public static final Module PROTOCOLS = Modules.combine(
         new CassandraJmapModule(),

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
----------------------------------------------------------------------
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 99783fd..fc80fe6 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -33,6 +33,7 @@ import org.apache.james.modules.protocols.POP3ServerModule;
 import org.apache.james.modules.protocols.ProtocolHandlerModule;
 import org.apache.james.modules.protocols.SMTPServerModule;
 import org.apache.james.modules.server.CamelMailetContainerModule;
+import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
 import org.apache.james.modules.server.JMXServerModule;
 import org.apache.james.modules.server.MailQueueRoutesModule;
@@ -56,7 +57,8 @@ public class MemoryJamesServerMain {
         new MailboxRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
-        new SwaggerRoutesModule());
+        new SwaggerRoutesModule(),
+        new DLPRoutesModule());
 
     public static final Module PROTOCOLS = Modules.combine(
         new IMAPServerModule(),

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DLPRoutesModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DLPRoutesModule.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DLPRoutesModule.java
new file mode 100644
index 0000000..96fcdde
--- /dev/null
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DLPRoutesModule.java
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.DLPConfigurationRoutes;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+
+public class DLPRoutesModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        Multibinder.newSetBinder(binder(), Routes.class)
+            .addBinding()
+            .to(DLPConfigurationRoutes.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
index 2cb95c5..2ae64ec 100644
--- a/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
+++ b/server/container/guice/protocols/webadmin-data/src/main/java/org/apache/james/modules/server/DataRoutesModules.java
@@ -20,7 +20,6 @@
 package org.apache.james.modules.server;
 
 import org.apache.james.webadmin.Routes;
-import org.apache.james.webadmin.routes.DLPConfigurationRoutes;
 import org.apache.james.webadmin.routes.DomainsRoutes;
 import org.apache.james.webadmin.routes.ForwardRoutes;
 import org.apache.james.webadmin.routes.GroupsRoutes;
@@ -34,7 +33,6 @@ public class DataRoutesModules extends AbstractModule {
     @Override
     protected void configure() {
         Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class);
-        routesMultibinder.addBinding().to(DLPConfigurationRoutes.class);
         routesMultibinder.addBinding().to(DomainsRoutes.class);
         routesMultibinder.addBinding().to(ForwardRoutes.class);
         routesMultibinder.addBinding().to(GroupsRoutes.class);

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
index 73dde47..e72cbec 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/ErrorResponder.java
@@ -115,7 +115,7 @@ public class ErrorResponder {
             cause.map(Throwable::getMessage)));
     }
 
-    static class ErrorDetail {
+    public static class ErrorDetail {
         private final int statusCode;
         private final String type;
         private final String message;

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
index fcecc3d..83be263 100644
--- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
+++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/utils/JsonExtractor.java
@@ -20,11 +20,11 @@
 package org.apache.james.webadmin.utils;
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.List;
 
 import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.google.common.collect.ImmutableList;
 
@@ -40,6 +40,7 @@ public class JsonExtractor<RequestT> {
     public JsonExtractor(Class<RequestT> type, List<Module> modules) {
         this.objectMapper = new ObjectMapper()
             .registerModule(new Jdk8Module())
+            .registerModule(new GuavaModule())
             .registerModules(modules);
         this.type = type;
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/DLPModule.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/DLPModule.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/DLPModule.java
deleted file mode 100644
index 0363ca3..0000000
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/DLPModule.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/****************************************************************
- * 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.webadmin;
-
-import org.apache.james.webadmin.utils.JsonTransformerModule;
-
-import com.fasterxml.jackson.databind.Module;
-import com.fasterxml.jackson.datatype.guava.GuavaModule;
-
-public class DLPModule implements JsonTransformerModule {
-
-    public DLPModule() {
-    }
-
-    @Override
-    public Module asJacksonModule() {
-        return new GuavaModule();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java
index d1265a6..5849f71 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.james.dlp.api.DLPConfigurationItem;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.github.steveash.guavate.Guavate;
 import com.google.common.base.Preconditions;
@@ -31,15 +32,6 @@ import com.google.common.collect.ImmutableList;
 
 public class DLPConfigurationDTO {
 
-    public static List<DLPConfigurationItem> toDLPConfigurations(DLPConfigurationDTO dto) {
-        Preconditions.checkNotNull(dto);
-
-        return dto.rules
-            .stream()
-            .map(DLPConfigurationItemDTO::toDLPConfiguration)
-            .collect(Guavate.toImmutableList());
-    }
-
     public static DLPConfigurationDTO toDTO(List<DLPConfigurationItem> dlpConfigurations) {
         Preconditions.checkNotNull(dlpConfigurations);
 
@@ -54,11 +46,19 @@ public class DLPConfigurationDTO {
 
     @JsonCreator
     public DLPConfigurationDTO(
-        @JsonProperty("rules") ImmutableList<DLPConfigurationItemDTO> rules) {
+            @JsonProperty("rules") ImmutableList<DLPConfigurationItemDTO> rules) {
         this.rules = rules;
     }
 
     public ImmutableList<DLPConfigurationItemDTO> getRules() {
         return rules;
     }
+
+    @JsonIgnore
+    public List<DLPConfigurationItem> toDLPConfigurations() {
+        return rules
+            .stream()
+            .map(DLPConfigurationItemDTO::toDLPConfiguration)
+            .collect(Guavate.toImmutableList());
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java
index 502615a..5f3da5f 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java
@@ -19,58 +19,47 @@
 
 package org.apache.james.webadmin.dto;
 
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 
 import org.apache.james.dlp.api.DLPConfigurationItem;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.github.steveash.guavate.Guavate;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 public class DLPConfigurationItemDTO {
 
     @VisibleForTesting
-    static DLPConfigurationItem toDLPConfiguration(DLPConfigurationItemDTO dto) {
-        return DLPConfigurationItem.builder()
-            .id(DLPConfigurationItem.Id.of(dto.id))
-            .expression(dto.expression)
-            .explanation(dto.explanation)
-            .targetsSender(dto.targetsSender)
-            .targetsRecipients(dto.targetsRecipients)
-            .targetsContent(dto.targetsContent)
-            .build();
-    }
-
-    @VisibleForTesting
     static DLPConfigurationItemDTO toDTO(DLPConfigurationItem dlpConfiguration) {
         DLPConfigurationItem.Targets targets = dlpConfiguration.getTargets();
         return new DLPConfigurationItemDTO(
             dlpConfiguration.getId().asString(),
-            dlpConfiguration.getRegexp(),
+            dlpConfiguration.getRegexp().pattern(),
             dlpConfiguration.getExplanation(),
-            Optional.of(targets.isSenderTargeted()),
-            Optional.of(targets.isRecipientTargeted()),
-            Optional.of(targets.isContentTargeted()));
+            targets.isSenderTargeted(),
+            targets.isRecipientTargeted(),
+            targets.isRecipientTargeted());
     }
 
+
+
     private final String id;
     private final String expression;
     private final Optional<String> explanation;
-    private final Optional<Boolean> targetsSender;
-    private final Optional<Boolean> targetsRecipients;
-    private final Optional<Boolean> targetsContent;
+    private final boolean targetsSender;
+    private final boolean targetsRecipients;
+    private final boolean targetsContent;
 
     @JsonCreator
     public DLPConfigurationItemDTO(@JsonProperty("id") String id,
                                    @JsonProperty("expression") String expression,
                                    @JsonProperty("explanation") Optional<String> explanation,
-                                   @JsonProperty("targetsSender") Optional<Boolean> targetsSender,
-                                   @JsonProperty("targetsRecipients") Optional<Boolean> targetsRecipients,
-                                   @JsonProperty("targetsContent") Optional<Boolean> targetsContent) {
+                                   @JsonProperty("targetsSender") boolean targetsSender,
+                                   @JsonProperty("targetsRecipients") boolean targetsRecipients,
+                                   @JsonProperty("targetsContent") boolean targetsContent) {
         Preconditions.checkNotNull(id, "'id' is mandatory");
         Preconditions.checkNotNull(expression, "'expression' is mandatory");
         this.id = id;
@@ -93,18 +82,30 @@ public class DLPConfigurationItemDTO {
         return explanation;
     }
 
-    public Optional<Boolean> getTargetsSender() {
+    public boolean getTargetsSender() {
         return targetsSender;
     }
 
-    public Optional<Boolean> getTargetsRecipients() {
+    public boolean getTargetsRecipients() {
         return targetsRecipients;
     }
 
-    public Optional<Boolean> getTargetsContent() {
+    public boolean getTargetsContent() {
         return targetsContent;
     }
 
+    @JsonIgnore
+    public DLPConfigurationItem toDLPConfiguration() {
+        return DLPConfigurationItem.builder()
+            .id(DLPConfigurationItem.Id.of(id))
+            .expression(expression)
+            .explanation(explanation)
+            .targetsSender(targetsSender)
+            .targetsRecipients(targetsRecipients)
+            .targetsContent(targetsContent)
+            .build();
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof DLPConfigurationItemDTO) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java
index 73b1d33..c735337 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java
@@ -46,11 +46,12 @@ import org.apache.james.webadmin.utils.JsonExtractor;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.eclipse.jetty.http.HttpStatus;
 
-import com.fasterxml.jackson.datatype.guava.GuavaModule;
 import com.github.steveash.guavate.Guavate;
+
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
@@ -59,6 +60,9 @@ import spark.Request;
 import spark.Service;
 
 @Api(tags = "DLPRules")
+@ApiModel(description = "DLP (stands for Data Leak Prevention) is supported by James. A DLP matcher will, on incoming emails, " +
+        "execute regular expressions on email sender, recipients or content, in order to report suspicious emails to" +
+        "an administrator. WebAdmin can be used to manage these DLP rules on a per sender-domain basis.")
 @Path(DLPConfigurationRoutes.BASE_PATH)
 @Produces(JSON_CONTENT_TYPE)
 public class DLPConfigurationRoutes implements Routes {
@@ -73,46 +77,47 @@ public class DLPConfigurationRoutes implements Routes {
     private final JsonExtractor<DLPConfigurationDTO> jsonExtractor;
     private final DomainList domainList;
 
-    private Service service;
-
     @Inject
     public DLPConfigurationRoutes(DLPConfigurationStore dlpConfigurationStore, DomainList domainList, JsonTransformer jsonTransformer) {
         this.dlpConfigurationStore = dlpConfigurationStore;
         this.domainList = domainList;
         this.jsonTransformer = jsonTransformer;
-        this.jsonExtractor = new JsonExtractor<>(DLPConfigurationDTO.class, new GuavaModule());
+        this.jsonExtractor = new JsonExtractor<>(DLPConfigurationDTO.class);
     }
 
     @Override
     public void define(Service service) {
-        this.service = service;
 
-        defineStore();
+        defineStore(service);
 
-        defineList();
+        defineList(service);
 
-        defineClear();
+        defineClear(service);
     }
 
     @PUT
     @Path("/{senderDomain}")
-    @ApiOperation(value = "Store a list of dlp configs for given senderDomain")
+    @ApiOperation(value = "Store a DLP configuration for given senderDomain")
     @ApiImplicitParams({
-        @ApiImplicitParam(required = true, dataType = "string", name = "senderDomain", paramType = "path")
+        @ApiImplicitParam(required = true, dataType = "string", name = "senderDomain", paramType = "path"),
+        @ApiImplicitParam(required = true, dataType = "org.apache.james.webadmin.dto.DLPConfigurationDTO", paramType = "body")
     })
     @ApiResponses(value = {
-        @ApiResponse(code = HttpStatus.NO_CONTENT_204, message = "OK. dlp config is stored."),
-        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain or payload in request"),
-        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist."),
+        @ApiResponse(code = HttpStatus.NO_CONTENT_204, message = "OK. DLP configuration is stored."),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain or payload in request",
+            response = ErrorResponder.ErrorDetail.class),
+        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist.",
+            response = ErrorResponder.ErrorDetail.class),
         @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
-            message = "Internal server error - Something went bad on the server side.")
+            message = "Internal server error - Something went bad on the server side.",
+            response = ErrorResponder.ErrorDetail.class)
     })
-    public void defineStore() {
+    public void defineStore(Service service) {
         service.put(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
             Domain senderDomain = parseDomain(request);
             DLPConfigurationDTO dto = jsonExtractor.parse(request.body());
 
-            dlpConfigurationStore.store(senderDomain, DLPConfigurationDTO.toDLPConfigurations(dto));
+            dlpConfigurationStore.store(senderDomain, dto.toDLPConfigurations());
 
             response.status(HttpStatus.NO_CONTENT_204);
             return EMPTY_BODY;
@@ -121,18 +126,21 @@ public class DLPConfigurationRoutes implements Routes {
 
     @GET
     @Path("/{senderDomain}")
-    @ApiOperation(value = "Retrieve a list of dlp configs for given senderDomain")
+    @ApiOperation(value = "Return a DLP configuration for given senderDomain")
     @ApiImplicitParams({
         @ApiImplicitParam(required = true, dataType = "string", name = "senderDomain", paramType = "path")
     })
     @ApiResponses(value = {
-        @ApiResponse(code = HttpStatus.OK_200, message = "OK. dlp configs returned"),
-        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain in request"),
-        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist."),
+        @ApiResponse(code = HttpStatus.OK_200, message = "OK. DLP configuration is returned", response = DLPConfigurationDTO.class),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain in request",
+            response = ErrorResponder.ErrorDetail.class),
+        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist.",
+            response = ErrorResponder.ErrorDetail.class),
         @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
-            message = "Internal server error - Something went bad on the server side.")
+            message = "Internal server error - Something went bad on the server side.",
+            response = ErrorResponder.ErrorDetail.class)
     })
-    public void defineList() {
+    public void defineList(Service service) {
         service.get(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
             Domain senderDomain = parseDomain(request);
             List<DLPConfigurationItem> dlpConfigurations = dlpConfigurationStore
@@ -148,18 +156,21 @@ public class DLPConfigurationRoutes implements Routes {
 
     @DELETE
     @Path("/{senderDomain}")
-    @ApiOperation(value = "Clear all dlp configs for given senderDomain")
+    @ApiOperation(value = "Clear a DLP configuration for a given senderDomain")
     @ApiImplicitParams({
         @ApiImplicitParam(required = true, dataType = "string", name = "senderDomain", paramType = "path")
     })
     @ApiResponses(value = {
-        @ApiResponse(code = HttpStatus.NO_CONTENT_204, message = "OK. dlp configs are cleared"),
-        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain in request"),
-        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist."),
+        @ApiResponse(code = HttpStatus.NO_CONTENT_204, message = "OK. DLP configuration is cleared"),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Invalid senderDomain in request",
+            response = ErrorResponder.ErrorDetail.class),
+        @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "The domain does not exist.",
+            response = ErrorResponder.ErrorDetail.class),
         @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
-            message = "Internal server error - Something went bad on the server side.")
+            message = "Internal server error - Something went bad on the server side.",
+            response = ErrorResponder.ErrorDetail.class)
     })
-    public void defineClear() {
+    public void defineClear(Service service) {
         service.delete(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
             Domain senderDomain = parseDomain(request);
             dlpConfigurationStore.clear(senderDomain);

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java
index a4b0da8..1c8f1f6 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java
@@ -21,15 +21,12 @@ package org.apache.james.webadmin.dto;
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import java.util.List;
 import java.util.Optional;
 
 import org.apache.james.dlp.api.DLPConfigurationItem;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.Test;
 
-import com.google.common.collect.ImmutableList;
-
 class DLPConfigurationItemDTOTest {
 
     private static final String ID = "id";
@@ -39,41 +36,41 @@ class DLPConfigurationItemDTOTest {
     private static final String NULL_EXPRESSION = null;
 
     @Test
-    void toDTOsShouldBeSetAllFields() {
+    void toDTOsShouldSetAllFields() {
         DLPConfigurationItemDTO dto = DLPConfigurationItemDTO.toDTO(
             DLPConfigurationItem.builder()
                 .id(DLPConfigurationItem.Id.of(ID))
                 .expression(EXPRESSION)
                 .explanation(EXPLANATION)
-                .targetsSender(Optional.of(true))
-                .targetsRecipients(Optional.of(true))
-                .targetsContent(Optional.of(true))
+                .targetsSender(true)
+                .targetsRecipients(true)
+                .targetsContent(true)
                 .build());
 
         SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(dto.getId()).isEqualTo(ID);
             softly.assertThat(dto.getExpression()).isEqualTo(EXPRESSION);
             softly.assertThat(dto.getExplanation().get()).isEqualTo(EXPLANATION);
-            softly.assertThat(dto.getTargetsSender().get()).isTrue();
-            softly.assertThat(dto.getTargetsRecipients().get()).isTrue();
-            softly.assertThat(dto.getTargetsContent().get()).isTrue();
+            softly.assertThat(dto.getTargetsSender()).isTrue();
+            softly.assertThat(dto.getTargetsRecipients()).isTrue();
+            softly.assertThat(dto.getTargetsContent()).isTrue();
         });
     }
 
     @Test
-    void toDLPConfigurationsShouldBeSetAllFields() {
-        DLPConfigurationItem item = DLPConfigurationItemDTO.toDLPConfiguration(
-            new DLPConfigurationItemDTO(
-                ID,
-                EXPRESSION,
-                Optional.of(EXPLANATION),
-                Optional.of(true),
-                Optional.of(true),
-                Optional.of(true)));
+    void toDLPConfigurationsShouldSetAllFields() {
+        DLPConfigurationItemDTO itemDTO = new DLPConfigurationItemDTO(
+            ID,
+            EXPRESSION,
+            Optional.of(EXPLANATION),
+            true,
+            true,
+            true);
+        DLPConfigurationItem item = itemDTO.toDLPConfiguration();
 
         SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(item.getId().asString()).isEqualTo(ID);
-            softly.assertThat(item.getRegexp()).isEqualTo(EXPRESSION);
+            softly.assertThat(item.getRegexp().pattern()).isEqualTo(EXPRESSION);
             softly.assertThat(item.getExplanation().get()).isEqualTo(EXPLANATION);
             softly.assertThat(item.getTargets().isSenderTargeted()).isTrue();
             softly.assertThat(item.getTargets().isRecipientTargeted()).isTrue();
@@ -86,9 +83,9 @@ class DLPConfigurationItemDTOTest {
         assertThatThrownBy(() -> new DLPConfigurationItemDTO(NULL_ID,
                 EXPRESSION,
                 Optional.of(EXPLANATION),
-                Optional.of(true),
-                Optional.of(true),
-                Optional.of(true)))
+                true,
+                true,
+                true))
             .isInstanceOf(NullPointerException.class);
     }
 
@@ -97,9 +94,9 @@ class DLPConfigurationItemDTOTest {
         assertThatThrownBy(() -> new DLPConfigurationItemDTO(ID,
                 NULL_EXPRESSION,
                 Optional.of(EXPLANATION),
-                Optional.of(true),
-                Optional.of(true),
-                Optional.of(true)))
+                true,
+                true,
+                true))
             .isInstanceOf(NullPointerException.class);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java
index 74d3468..49b6614 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java
@@ -31,10 +31,8 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 
 import java.net.InetAddress;
-import java.util.List;
 
 import org.apache.james.core.Domain;
-import org.apache.james.dlp.api.DLPConfigurationItem;
 import org.apache.james.dlp.api.DLPConfigurationStore;
 import org.apache.james.dlp.eventsourcing.EventSourcingDLPConfigurationStore;
 import org.apache.james.dnsservice.api.DNSService;
@@ -42,7 +40,6 @@ import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.memory.MemoryDomainList;
 import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStore;
 import org.apache.james.metrics.logger.DefaultMetricFactory;
-import org.apache.james.webadmin.DLPModule;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.utils.JsonTransformer;
@@ -52,7 +49,6 @@ import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
-import com.google.common.collect.ImmutableList;
 import com.jayway.restassured.RestAssured;
 import com.jayway.restassured.http.ContentType;
 import com.jayway.restassured.specification.RequestSpecification;
@@ -64,35 +60,13 @@ class DLPConfigurationRoutesTest {
     private static final String DOMAIN_2 = "apache.org";
     private static final Domain SENDER_DOMAIN_2 = Domain.of(DOMAIN_2);
 
-    private static final DLPConfigurationItem CONFIGURATION_ITEM_1 = DLPConfigurationItem.builder()
-        .id(DLPConfigurationItem.Id.of("1"))
-        .explanation("explanation 1")
-        .expression(DEFAULT_DOMAIN)
-        .targetsSender()
-        .targetsRecipients()
-        .targetsContent()
-        .build();
-    private static final DLPConfigurationItem CONFIGURATION_ITEM_2 = DLPConfigurationItem.builder()
-        .id(DLPConfigurationItem.Id.of("2"))
-        .expression(DEFAULT_DOMAIN)
-        .targetsSender()
-        .build();
-
-    private static final List<DLPConfigurationItem> CONFIGURATION_ITEMS_FOR_DOMAIN_2 = ImmutableList.of(DLPConfigurationItem.builder()
-        .id(DLPConfigurationItem.Id.of("3"))
-        .expression(DOMAIN_2)
-        .targetsSender()
-        .build());
-
-    private static final List<DLPConfigurationItem> CONFIGURATION_ITEMS = ImmutableList.of(CONFIGURATION_ITEM_1, CONFIGURATION_ITEM_2);
-
     private WebAdminServer webAdminServer;
     private EventSourcingDLPConfigurationStore dlpStore;
 
     private void createServer(DLPConfigurationStore dlpConfigurationStore, DomainList domainList) throws Exception {
         webAdminServer = WebAdminUtils.createWebAdminServer(
             new DefaultMetricFactory(),
-            new DLPConfigurationRoutes(dlpConfigurationStore, domainList, new JsonTransformer(new DLPModule())));
+            new DLPConfigurationRoutes(dlpConfigurationStore, domainList, new JsonTransformer()));
         webAdminServer.configure(NO_CONFIGURATION);
         webAdminServer.await();
 
@@ -247,13 +221,13 @@ class DLPConfigurationRoutesTest {
         @Test
         void putShouldReturnBadRequestWhenIdIsNotSpecified() {
             String body =
-                "{" +
+                "{\"rules\": [{" +
                 "  \"expression\": \"expression 4\"," +
                 "  \"explanation\": \"explanation 4\"," +
                 "  \"targetsSender\": false," +
                 "  \"targetsRecipients\": false," +
                 "  \"targetsContent\": false" +
-                "}";
+                "}]}";
 
             given()
                 .body(body)
@@ -264,19 +238,20 @@ class DLPConfigurationRoutesTest {
                 .contentType(JSON_CONTENT_TYPE)
                 .body("statusCode", is(400))
                 .body("type", is("InvalidArgument"))
-                .body("message", is("JSON payload of the request is not valid"));
+                .body("message", is("JSON payload of the request is not valid"))
+                .body("details", is("Instantiation of [simple type, class org.apache.james.webadmin.dto.DLPConfigurationItemDTO] value failed: 'id' is mandatory (through reference chain: org.apache.james.webadmin.dto.DLPConfigurationDTO[\"rules\"])"));
         }
 
         @Test
         void putShouldReturnBadRequestWhenExpressionIsNotSpecified() {
             String body =
-                "{" +
+                "{\"rules\": [{" +
                 "  \"id\": \"5\"," +
                 "  \"explanation\": \"explanation 5\"," +
                 "  \"targetsSender\": false," +
                 "  \"targetsRecipients\": false," +
                 "  \"targetsContent\": false" +
-                "}";
+                "}]}";
 
             given()
                 .body(body)
@@ -287,7 +262,8 @@ class DLPConfigurationRoutesTest {
                 .contentType(JSON_CONTENT_TYPE)
                 .body("statusCode", is(400))
                 .body("type", is("InvalidArgument"))
-                .body("message", is("JSON payload of the request is not valid"));
+                .body("message", is("JSON payload of the request is not valid"))
+                .body("details", is("Instantiation of [simple type, class org.apache.james.webadmin.dto.DLPConfigurationItemDTO] value failed: 'expression' is mandatory (through reference chain: org.apache.james.webadmin.dto.DLPConfigurationDTO[\"rules\"])"));
         }
 
         @Test
@@ -351,7 +327,8 @@ class DLPConfigurationRoutesTest {
                 .contentType(JSON_CONTENT_TYPE)
                 .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
                 .body("type", is("InvalidArgument"))
-                .body("message", is("Invalid request for domain: dr@strange.com"));
+                .body("message", is("Invalid request for domain: dr@strange.com"))
+                .body("details", is("Domain can not be empty nor contain `@`"));
         }
     }
 
@@ -360,7 +337,31 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void deleteShouldRemoveTheConfigurations() {
-            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"expression 1\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"expression 2\"," +
+                "    \"explanation\": \"explanation 2\"," +
+                "    \"targetsSender\": false," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             when()
                 .delete(DEFAULT_DOMAIN)
@@ -380,8 +381,48 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void deleteShouldRemoveOnlyConfigurationsFromCorrespondingDomain() {
-            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
-            dlpStore.store(SENDER_DOMAIN_2, CONFIGURATION_ITEMS_FOR_DOMAIN_2);
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"expression 1\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"expression 2\"," +
+                "    \"explanation\": \"explanation 2\"," +
+                "    \"targetsSender\": false," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+
+            String storeDomain2Body =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"3\"," +
+                "    \"expression\": \"apache.org\"," +
+                "    \"targetsSender\": true" +
+                "  }" +
+                "]}";
+
+            given()
+                .body(storeDomain2Body)
+            .when()
+                .put(DOMAIN_2)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             when()
                 .delete(DEFAULT_DOMAIN)
@@ -441,7 +482,8 @@ class DLPConfigurationRoutesTest {
                 .contentType(JSON_CONTENT_TYPE)
                 .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
                 .body("type", is("InvalidArgument"))
-                .body("message", is("Invalid request for domain: dr@strange.com"));
+                .body("message", is("Invalid request for domain: dr@strange.com"))
+                .body("details", is("Domain can not be empty nor contain `@`"));
         }
     }
 
@@ -450,7 +492,24 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void getShouldReturnOK() {
-            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"expression 1\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }" +
+                "]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             when()
                 .get(DEFAULT_DOMAIN)
@@ -461,7 +520,31 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void getShouldReturnABody() {
-            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 2\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             String body = when()
                 .get(DEFAULT_DOMAIN)
@@ -485,7 +568,7 @@ class DLPConfigurationRoutesTest {
                 "  {" +
                 "    \"id\": \"2\"," +
                 "    \"expression\": \"james.org\"," +
-                "    \"explanation\": null," +
+                "    \"explanation\": \"explanation 2\"," +
                 "    \"targetsSender\": true," +
                 "    \"targetsRecipients\": false," +
                 "    \"targetsContent\": false" +
@@ -495,7 +578,14 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void getShouldReturnAnEmptyBodyWhenDLPStoreIsEmpty() {
-            dlpStore.store(SENDER_DOMAIN, ImmutableList.of());
+            String storeBody = "{\"rules\": []}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             String body = when()
                 .get(DEFAULT_DOMAIN)
@@ -511,8 +601,48 @@ class DLPConfigurationRoutesTest {
 
         @Test
         void getShouldReturnOnlyConfigurationsFromCorrespondingDomain() {
-            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
-            dlpStore.store(SENDER_DOMAIN_2, CONFIGURATION_ITEMS_FOR_DOMAIN_2);
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 2\"," +
+                "    \"targetsSender\": false," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+
+            String storeDomain2Body =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"3\"," +
+                "    \"expression\": \"apache.org\"," +
+                "    \"targetsSender\": true" +
+                "  }" +
+                "]}";
+
+            given()
+                .body(storeDomain2Body)
+            .when()
+                .put(DOMAIN_2)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
 
             String body = when()
                 .get(DEFAULT_DOMAIN)
@@ -536,8 +666,8 @@ class DLPConfigurationRoutesTest {
                 "  {" +
                 "    \"id\": \"2\"," +
                 "    \"expression\": \"james.org\"," +
-                "    \"explanation\": null," +
-                "    \"targetsSender\": true," +
+                "    \"explanation\": \"explanation 2\"," +
+                "    \"targetsSender\": false," +
                 "    \"targetsRecipients\": false," +
                 "    \"targetsContent\": false" +
                 "  }" +
@@ -565,7 +695,8 @@ class DLPConfigurationRoutesTest {
                 .contentType(JSON_CONTENT_TYPE)
                 .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
                 .body("type", is("InvalidArgument"))
-                .body("message", is("Invalid request for domain: dr@strange.com"));
+                .body("message", is("Invalid request for domain: dr@strange.com"))
+                .body("details", is("Domain can not be empty nor contain `@`"));
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/55cd111c/src/site/markdown/server/manage-webadmin.md
----------------------------------------------------------------------
diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 1a574d3..7ba63af 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -33,6 +33,7 @@ In case of any error, the system will return an error message which is json form
  - [Creating address forwards](#Creating_address_forwards)
  - [Administrating mail repositories](#Administrating_mail_repositories)
  - [Administrating mail queues](#Administrating_mail_queues)
+ - [Administrating DLP Configuration](#Administrating_dlp_configuration)
  - [Task management](#Task_management)
 
 ## Administrating domains
@@ -1754,6 +1755,120 @@ Response codes:
  - 404: The mail queue does not exist
  - 500: Internal error
 
+## Administrating DLP Configuration
+
+DLP (stands for Data Leak Prevention) is supported by James. A DLP matcher will, on incoming emails,
+execute regular expressions on email sender, recipients or content, in order to report suspicious emails to
+an administrator. WebAdmin can be used to manage these DLP rules on a per `senderDomain` basis.
+
+`senderDomain` is domain of the sender of incoming emails, for example: `apache.org`, `james.org`,...
+Each `senderDomain` correspond to a distinct DLP configuration.
+
+- [List DLP configuration by sender domain](List_dlp_configuration_by_sender_domain)
+- [Store DLP configuration by sender domain](Store_dlp_configuration_by_sender_domain)
+- [Remove DLP configuration by sender domain](Remove_dlp_configuration_by_sender_domain)
+
+### List DLP configuration by sender domain
+
+Retrieve a DLP configuration for corresponding `senderDomain`, a configuration contains list of configuration items
+
+```
+curl -XGET http://ip:port/dlp/rules/senderDomain
+```
+
+Response codes:
+
+ - 200: A list of dlp configuration items is returned
+ - 400: Invalid senderDomain or payload in request
+ - 404: The domain does not exist.
+ - 500: Internal error
+
+This is an example of returned body. The rules field is a list of rules as described below.
+
+```
+{"rules : [
+  {
+    "id": "1",
+    "expression": "james.org",
+    "explanation": "Find senders or recipients containing james[any char]org",
+    "targetsSender": true,
+    "targetsRecipients": true,
+    "targetsContent": false
+  },
+  {
+    "id": "2",
+    "expression": "Find senders containing apache[any char]org",
+    "explanation": "apache.org",
+    "targetsSender": true,
+    "targetsRecipients": false,
+    "targetsContent": false
+  }
+]}
+```
+
+### Store DLP configuration by sender domain
+
+Store a DLP configuration for corresponding `senderDomain`, if any item of DLP configuration in the request is stored before, 
+it will not be stored anymore
+
+```
+curl -XPUT http://ip:port/dlp/rules/senderDomain
+```
+
+The body can contain a list of DLP configuration items formed by those fields: 
+- `id`(String) is mandatory, unique identifier of the configuration item
+- `expression`(String) is mandatory, regular expression to match contents of targets
+- `explanation`(String) is optional, description of the configuration item
+- `targetsSender`(boolean) is optional and defaults to false. If true, `expression` will be applied to Sender and to From headers of the mail
+- `targetsContent`(boolean) is optional and defaults to false. If true, `expression` will be applied to Subject headers and textual bodies (text/plain and text/html) of the mail
+- `targetsRecipients`(boolean) is optional and defaults to false. If true, `expression` will be applied to recipients of the mail
+
+This is an example of returned body. The rules field is a list of rules as described below.
+
+```
+{"rules": [
+  {
+    "id": "1",
+    "expression": "james.org",
+    "explanation": "Find senders or recipients containing james[any char]org",
+    "targetsSender": true,
+    "targetsRecipients": true,
+    "targetsContent": false
+  },
+  {
+    "id": "2",
+    "expression": "Find senders containing apache[any char]org",
+    "explanation": "apache.org",
+    "targetsSender": true,
+    "targetsRecipients": false,
+    "targetsContent": false
+  }
+]}
+```
+
+Response codes:
+
+ - 204: List of dlp configuration items is stored
+ - 400: Invalid senderDomain or payload in request
+ - 404: The domain does not exist.
+ - 500: Internal error
+
+### Remove DLP configuration by sender domain
+
+Remove a DLP configuration for corresponding `senderDomain`
+
+```
+curl -XDELETE http://ip:port/dlp/rules/senderDomain
+```
+
+Response codes:
+
+ - 204: DLP configuration is removed
+ - 400: Invalid senderDomain or payload in request
+ - 404: The domain does not exist.
+ - 500: Internal error
+
+
 ## Task management
 
 Some webadmin features schedules tasks. The task management API allow to monitor and manage the execution of the following tasks.


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