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:30 UTC

[1/3] james-project git commit: JAMES-2428 [DLP] Web admin endpoint to update DLP configuration

Repository: james-project
Updated Branches:
  refs/heads/master 23d22a0e9 -> 536f3caab


JAMES-2428 [DLP] Web admin endpoint to update DLP configuration


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

Branch: refs/heads/master
Commit: dec6e89024f78f3ddbe7e24157d9f7d991381d54
Parents: 23d22a0
Author: duc <dt...@linagora.com>
Authored: Thu Jun 14 15:49:47 2018 +0700
Committer: duc <dt...@linagora.com>
Committed: Tue Jun 19 16:59:52 2018 +0700

----------------------------------------------------------------------
 .../james/modules/server/DataRoutesModules.java |   2 +
 .../james/webadmin/utils/JsonExtractor.java     |   1 +
 server/protocols/webadmin/webadmin-data/pom.xml |  19 +
 .../org/apache/james/webadmin/DLPModule.java    |  36 ++
 .../james/webadmin/dto/DLPConfigurationDTO.java |  64 +++
 .../webadmin/dto/DLPConfigurationItemDTO.java   | 127 +++++
 .../webadmin/routes/DLPConfigurationRoutes.java | 217 +++++++
 .../dto/DLPConfigurationItemDTOTest.java        | 105 ++++
 .../routes/DLPConfigurationRoutesTest.java      | 571 +++++++++++++++++++
 9 files changed, 1142 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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 2ae64ec..2cb95c5 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,6 +20,7 @@
 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;
@@ -33,6 +34,7 @@ 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/dec6e890/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 3f62d89..fcecc3d 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,6 +20,7 @@
 package org.apache.james.webadmin.utils;
 
 import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 
 import com.fasterxml.jackson.databind.Module;

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/server/protocols/webadmin/webadmin-data/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-data/pom.xml b/server/protocols/webadmin/webadmin-data/pom.xml
index e6c876b..f4b1d20 100644
--- a/server/protocols/webadmin/webadmin-data/pom.xml
+++ b/server/protocols/webadmin/webadmin-data/pom.xml
@@ -35,6 +35,11 @@
     <dependencies>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>james-core</artifactId>
         </dependency>
         <dependency>
@@ -67,6 +72,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-guava</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -80,6 +89,16 @@
             <artifactId>javax.inject</artifactId>
         </dependency>
         <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-fluent</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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
new file mode 100644
index 0000000..0363ca3
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/DLPModule.java
@@ -0,0 +1,36 @@
+/****************************************************************
+ * 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/dec6e890/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
new file mode 100644
index 0000000..d1265a6
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationDTO.java
@@ -0,0 +1,64 @@
+/****************************************************************
+ * 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.dto;
+
+import java.util.List;
+
+import org.apache.james.dlp.api.DLPConfigurationItem;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+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);
+
+        return new DLPConfigurationDTO(
+            dlpConfigurations
+                .stream()
+                .map(DLPConfigurationItemDTO::toDTO)
+                .collect(Guavate.toImmutableList()));
+    }
+
+    private final ImmutableList<DLPConfigurationItemDTO> rules;
+
+    @JsonCreator
+    public DLPConfigurationDTO(
+        @JsonProperty("rules") ImmutableList<DLPConfigurationItemDTO> rules) {
+        this.rules = rules;
+    }
+
+    public ImmutableList<DLPConfigurationItemDTO> getRules() {
+        return rules;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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
new file mode 100644
index 0000000..502615a
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTO.java
@@ -0,0 +1,127 @@
+/****************************************************************
+ * 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.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.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.getExplanation(),
+            Optional.of(targets.isSenderTargeted()),
+            Optional.of(targets.isRecipientTargeted()),
+            Optional.of(targets.isContentTargeted()));
+    }
+
+    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;
+
+    @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) {
+        Preconditions.checkNotNull(id, "'id' is mandatory");
+        Preconditions.checkNotNull(expression, "'expression' is mandatory");
+        this.id = id;
+        this.expression = expression;
+        this.explanation = explanation;
+        this.targetsSender = targetsSender;
+        this.targetsRecipients = targetsRecipients;
+        this.targetsContent = targetsContent;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getExpression() {
+        return expression;
+    }
+
+    public Optional<String> getExplanation() {
+        return explanation;
+    }
+
+    public Optional<Boolean> getTargetsSender() {
+        return targetsSender;
+    }
+
+    public Optional<Boolean> getTargetsRecipients() {
+        return targetsRecipients;
+    }
+
+    public Optional<Boolean> getTargetsContent() {
+        return targetsContent;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof DLPConfigurationItemDTO) {
+            DLPConfigurationItemDTO that = (DLPConfigurationItemDTO) o;
+
+            return Objects.equals(this.id, that.id)
+                && Objects.equals(this.expression, that.expression)
+                && Objects.equals(this.explanation, that.explanation)
+                && Objects.equals(this.targetsSender, that.targetsSender)
+                && Objects.equals(this.targetsRecipients, that.targetsRecipients)
+                && Objects.equals(this.targetsContent, that.targetsContent);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(id, expression, explanation, targetsSender, targetsRecipients, targetsContent);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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
new file mode 100644
index 0000000..73b1d33
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DLPConfigurationRoutes.java
@@ -0,0 +1,217 @@
+/****************************************************************
+ * 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.routes;
+
+import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
+import static org.apache.james.webadmin.Constants.EMPTY_BODY;
+import static org.apache.james.webadmin.Constants.JSON_CONTENT_TYPE;
+import static org.apache.james.webadmin.Constants.SEPARATOR;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.apache.james.core.Domain;
+import org.apache.james.dlp.api.DLPConfigurationItem;
+import org.apache.james.dlp.api.DLPConfigurationStore;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.domainlist.api.DomainListException;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.dto.DLPConfigurationDTO;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.ErrorResponder.ErrorType;
+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.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import spark.HaltException;
+import spark.Request;
+import spark.Service;
+
+@Api(tags = "DLPRules")
+@Path(DLPConfigurationRoutes.BASE_PATH)
+@Produces(JSON_CONTENT_TYPE)
+public class DLPConfigurationRoutes implements Routes {
+
+    static final String BASE_PATH = "/dlp/rules";
+
+    private static final String DOMAIN_NAME = ":senderDomain";
+    private static final String SPECIFIC_DLP_RULE_DOMAIN = BASE_PATH + SEPARATOR + DOMAIN_NAME;
+
+    private final JsonTransformer jsonTransformer;
+    private final DLPConfigurationStore dlpConfigurationStore;
+    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());
+    }
+
+    @Override
+    public void define(Service service) {
+        this.service = service;
+
+        defineStore();
+
+        defineList();
+
+        defineClear();
+    }
+
+    @PUT
+    @Path("/{senderDomain}")
+    @ApiOperation(value = "Store a list of dlp configs for given senderDomain")
+    @ApiImplicitParams({
+        @ApiImplicitParam(required = true, dataType = "string", name = "senderDomain", paramType = "path")
+    })
+    @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.INTERNAL_SERVER_ERROR_500,
+            message = "Internal server error - Something went bad on the server side.")
+    })
+    public void defineStore() {
+        service.put(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
+            Domain senderDomain = parseDomain(request);
+            DLPConfigurationDTO dto = jsonExtractor.parse(request.body());
+
+            dlpConfigurationStore.store(senderDomain, DLPConfigurationDTO.toDLPConfigurations(dto));
+
+            response.status(HttpStatus.NO_CONTENT_204);
+            return EMPTY_BODY;
+        });
+    }
+
+    @GET
+    @Path("/{senderDomain}")
+    @ApiOperation(value = "Retrieve a list of dlp configs 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.INTERNAL_SERVER_ERROR_500,
+            message = "Internal server error - Something went bad on the server side.")
+    })
+    public void defineList() {
+        service.get(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
+            Domain senderDomain = parseDomain(request);
+            List<DLPConfigurationItem> dlpConfigurations = dlpConfigurationStore
+                .list(senderDomain)
+                .collect(Guavate.toImmutableList());
+
+            DLPConfigurationDTO dto = DLPConfigurationDTO.toDTO(dlpConfigurations);
+            response.status(HttpStatus.OK_200);
+            response.header(CONTENT_TYPE, JSON_CONTENT_TYPE);
+            return dto;
+        }, jsonTransformer);
+    }
+
+    @DELETE
+    @Path("/{senderDomain}")
+    @ApiOperation(value = "Clear all dlp configs for 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.INTERNAL_SERVER_ERROR_500,
+            message = "Internal server error - Something went bad on the server side.")
+    })
+    public void defineClear() {
+        service.delete(SPECIFIC_DLP_RULE_DOMAIN, (request, response) -> {
+            Domain senderDomain = parseDomain(request);
+            dlpConfigurationStore.clear(senderDomain);
+
+            response.status(HttpStatus.NO_CONTENT_204);
+            return EMPTY_BODY;
+        }, jsonTransformer);
+    }
+
+    private Domain parseDomain(Request request) {
+        String domainName = request.params(DOMAIN_NAME);
+        try {
+            Domain domain = Domain.of(domainName);
+            validateDomainInList(domain);
+
+            return domain;
+        } catch (IllegalArgumentException e) {
+            throw invalidDomain(String.format("Invalid request for domain: %s", domainName), e);
+        } catch (DomainListException e) {
+            throw serverError(String.format("Cannot recognize domain: %s in domain list", domainName), e);
+        }
+    }
+
+    private void validateDomainInList(Domain domain) throws DomainListException {
+        if (!domainList.containsDomain(domain)) {
+            throw notFound(String.format("'%s' is not managed by this James server", domain.name()));
+        }
+    }
+
+    private HaltException invalidDomain(String message, Exception e) {
+        return ErrorResponder.builder()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .type(ErrorType.INVALID_ARGUMENT)
+            .message(message)
+            .cause(e)
+            .haltError();
+    }
+
+    private HaltException serverError(String message, Exception e) {
+        return ErrorResponder.builder()
+            .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+            .type(ErrorType.SERVER_ERROR)
+            .message(message)
+            .cause(e)
+            .haltError();
+    }
+
+    private HaltException notFound(String message) {
+        return ErrorResponder.builder()
+            .statusCode(HttpStatus.NOT_FOUND_404)
+            .type(ErrorType.INVALID_ARGUMENT)
+            .message(message)
+            .haltError();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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
new file mode 100644
index 0000000..a4b0da8
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/dto/DLPConfigurationItemDTOTest.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * 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.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";
+    private static final String EXPRESSION = "expression";
+    private static final String EXPLANATION = "explanation";
+    private static final String NULL_ID = null;
+    private static final String NULL_EXPRESSION = null;
+
+    @Test
+    void toDTOsShouldBeSetAllFields() {
+        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))
+                .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();
+        });
+    }
+
+    @Test
+    void toDLPConfigurationsShouldBeSetAllFields() {
+        DLPConfigurationItem item = DLPConfigurationItemDTO.toDLPConfiguration(
+            new DLPConfigurationItemDTO(
+                ID,
+                EXPRESSION,
+                Optional.of(EXPLANATION),
+                Optional.of(true),
+                Optional.of(true),
+                Optional.of(true)));
+
+        SoftAssertions.assertSoftly(softly -> {
+            softly.assertThat(item.getId().asString()).isEqualTo(ID);
+            softly.assertThat(item.getRegexp()).isEqualTo(EXPRESSION);
+            softly.assertThat(item.getExplanation().get()).isEqualTo(EXPLANATION);
+            softly.assertThat(item.getTargets().isSenderTargeted()).isTrue();
+            softly.assertThat(item.getTargets().isRecipientTargeted()).isTrue();
+            softly.assertThat(item.getTargets().isContentTargeted()).isTrue();
+        });
+    }
+
+    @Test
+    void constructorShouldThrowWhenIdIsNull() {
+        assertThatThrownBy(() -> new DLPConfigurationItemDTO(NULL_ID,
+                EXPRESSION,
+                Optional.of(EXPLANATION),
+                Optional.of(true),
+                Optional.of(true),
+                Optional.of(true)))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenExpressionIsNull() {
+        assertThatThrownBy(() -> new DLPConfigurationItemDTO(ID,
+                NULL_EXPRESSION,
+                Optional.of(EXPLANATION),
+                Optional.of(true),
+                Optional.of(true),
+                Optional.of(true)))
+            .isInstanceOf(NullPointerException.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/dec6e890/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
new file mode 100644
index 0000000..74d3468
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DLPConfigurationRoutesTest.java
@@ -0,0 +1,571 @@
+/****************************************************************
+ * 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.routes;
+
+import static com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.RestAssured.requestSpecification;
+import static com.jayway.restassured.RestAssured.when;
+import static com.jayway.restassured.RestAssured.with;
+import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
+import static org.apache.james.webadmin.Constants.JSON_CONTENT_TYPE;
+import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+import static org.hamcrest.Matchers.is;
+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;
+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;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.BeforeEach;
+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;
+
+class DLPConfigurationRoutesTest {
+
+    private static final String DEFAULT_DOMAIN = "james.org";
+    private static final Domain SENDER_DOMAIN = Domain.of(DEFAULT_DOMAIN);
+    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())));
+        webAdminServer.configure(NO_CONFIGURATION);
+        webAdminServer.await();
+
+        requestSpecification = buildRequestSpecification(webAdminServer);
+    }
+
+    RequestSpecification buildRequestSpecification(WebAdminServer server) {
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+
+        return WebAdminUtils
+                .buildRequestSpecification(server)
+                .setBasePath(DLPConfigurationRoutes.BASE_PATH)
+                .build();
+    }
+
+
+    @BeforeEach
+    void setup() throws Exception {
+        DNSService dnsService = mock(DNSService.class);
+        Mockito.when(dnsService.getHostName(any())).thenReturn("localhost");
+        Mockito.when(dnsService.getLocalHost()).thenReturn(InetAddress.getByName("localhost"));
+
+        MemoryDomainList domainList = new MemoryDomainList(dnsService);
+        domainList.setAutoDetectIP(false);
+        domainList.setAutoDetect(false);
+        domainList.addDomain(SENDER_DOMAIN);
+        domainList.addDomain(SENDER_DOMAIN_2);
+
+        dlpStore = new EventSourcingDLPConfigurationStore(new InMemoryEventStore());
+        createServer(dlpStore, domainList);
+    }
+
+
+    @Nested
+    class DefineStore {
+
+        @Test
+        void putShouldStoreTheConfigurations() {
+            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 retrievedBody = with()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBody).isEqualTo(storeBody);
+        }
+
+        @Test
+        void putShouldStoreTheConfigurationsWhenTargetsAreNotSpecified() {
+            String storeBody =
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"3\"," +
+                "    \"expression\": \"expression 3\"," +
+                "    \"explanation\": \"explanation 3\"" +
+                "  }]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+            String retrievedBody = with()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBody)
+                .isEqualTo(
+                    "{\"rules\": [" +
+                    "  {" +
+                    "    \"id\": \"3\"," +
+                    "    \"expression\": \"expression 3\"," +
+                    "    \"explanation\": \"explanation 3\"," +
+                    "    \"targetsSender\": false," +
+                    "    \"targetsRecipients\": false," +
+                    "    \"targetsContent\": false" +
+                    "  }" +
+                    "]}");
+        }
+
+        @Test
+        void putShouldStoreTheConfigurationsWhenExplanationNotSpecified() {
+            String storeBody =
+                "{\"rules\": [{" +
+                "  \"id\": \"3\"," +
+                "  \"expression\": \"expression 3\"" +
+                "}]}";
+
+            given()
+                .body(storeBody)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+            String retrievedBody = with()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBody)
+                .isEqualTo(
+                    "{\"rules\": [" +
+                    "  {" +
+                    "    \"id\": \"3\"," +
+                    "    \"expression\": \"expression 3\"," +
+                    "    \"explanation\": null," +
+                    "    \"targetsSender\": false," +
+                    "    \"targetsRecipients\": false," +
+                    "    \"targetsContent\": false" +
+                    "  }" +
+                    "]}");
+        }
+
+        @Test
+        void putShouldReturnBadRequestWhenIdIsNotSpecified() {
+            String body =
+                "{" +
+                "  \"expression\": \"expression 4\"," +
+                "  \"explanation\": \"explanation 4\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": false" +
+                "}";
+
+            given()
+                .body(body)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .contentType(JSON_CONTENT_TYPE)
+                .body("statusCode", is(400))
+                .body("type", is("InvalidArgument"))
+                .body("message", is("JSON payload of the request is not valid"));
+        }
+
+        @Test
+        void putShouldReturnBadRequestWhenExpressionIsNotSpecified() {
+            String body =
+                "{" +
+                "  \"id\": \"5\"," +
+                "  \"explanation\": \"explanation 5\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": false" +
+                "}";
+
+            given()
+                .body(body)
+            .when()
+                .put(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .contentType(JSON_CONTENT_TYPE)
+                .body("statusCode", is(400))
+                .body("type", is("InvalidArgument"))
+                .body("message", is("JSON payload of the request is not valid"));
+        }
+
+        @Test
+        void putShouldReturnNotFoundWhenDomainNotInList() {
+            String body =
+                "[{" +
+                "  \"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(body)
+            .when()
+                .put("strange.com")
+            .then()
+                .statusCode(HttpStatus.NOT_FOUND_404)
+                .contentType(JSON_CONTENT_TYPE)
+                .body("statusCode", is(HttpStatus.NOT_FOUND_404))
+                .body("type", is("InvalidArgument"))
+                .body("message", is("'strange.com' is not managed by this James server"));
+        }
+
+        @Test
+        void putShouldReturnBadRequestWhenDomainIsNotValid() {
+            String body =
+                "[{" +
+                "  \"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(body)
+            .when()
+                .put("dr@strange.com")
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .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"));
+        }
+    }
+
+    @Nested
+    class DefineClear {
+
+        @Test
+        void deleteShouldRemoveTheConfigurations() {
+            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+
+            when()
+                .delete(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+            String retrievedBody = with()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBody).isEqualTo("{\"rules\":[]}");
+        }
+
+        @Test
+        void deleteShouldRemoveOnlyConfigurationsFromCorrespondingDomain() {
+            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+            dlpStore.store(SENDER_DOMAIN_2, CONFIGURATION_ITEMS_FOR_DOMAIN_2);
+
+            when()
+                .delete(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.NO_CONTENT_204);
+
+            String retrievedBody = with()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBody).isEqualTo("{\"rules\":[]}");
+
+            String retrievedBodyDomain2 = when()
+                .get(DOMAIN_2)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body().asString();
+
+            assertThatJson(retrievedBodyDomain2)
+                .isEqualTo(
+                    "{\"rules\": [" +
+                    "    {" +
+                    "        \"id\": \"3\"," +
+                    "        \"expression\": \"apache.org\"," +
+                    "        \"explanation\": null," +
+                    "        \"targetsSender\": true," +
+                    "        \"targetsRecipients\": false," +
+                    "        \"targetsContent\": false" +
+                    "    }" +
+                    "]}");
+        }
+
+        @Test
+        void deleteShouldReturnNotFoundWhenDomainNotInList() {
+            when()
+                .delete("strange.com")
+            .then()
+                .statusCode(HttpStatus.NOT_FOUND_404)
+                .contentType(JSON_CONTENT_TYPE)
+                .body("statusCode", is(HttpStatus.NOT_FOUND_404))
+                .body("type", is("InvalidArgument"))
+                .body("message", is("'strange.com' is not managed by this James server"));
+        }
+
+        @Test
+        void deleteShouldReturnBadRequestWhenDomainIsNotValid() {
+            when()
+                .delete("dr@strange.com")
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .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"));
+        }
+    }
+
+    @Nested
+    class DefineList {
+
+        @Test
+        void getShouldReturnOK() {
+            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+
+            when()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON);
+        }
+
+        @Test
+        void getShouldReturnABody() {
+            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+
+            String body = when()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body()
+                .asString();
+
+            assertThatJson(body).isEqualTo(
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": null," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }" +
+                "]}");
+        }
+
+        @Test
+        void getShouldReturnAnEmptyBodyWhenDLPStoreIsEmpty() {
+            dlpStore.store(SENDER_DOMAIN, ImmutableList.of());
+
+            String body = when()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body()
+                .asString();
+
+            assertThatJson(body).isEqualTo("{\"rules\":[]}");
+        }
+
+        @Test
+        void getShouldReturnOnlyConfigurationsFromCorrespondingDomain() {
+            dlpStore.store(SENDER_DOMAIN, CONFIGURATION_ITEMS);
+            dlpStore.store(SENDER_DOMAIN_2, CONFIGURATION_ITEMS_FOR_DOMAIN_2);
+
+            String body = when()
+                .get(DEFAULT_DOMAIN)
+            .then()
+                .statusCode(HttpStatus.OK_200)
+                .contentType(ContentType.JSON)
+                .extract()
+                .body()
+                .asString();
+
+            assertThatJson(body).isEqualTo(
+                "{\"rules\": [" +
+                "  {" +
+                "    \"id\": \"1\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": \"explanation 1\"," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": true," +
+                "    \"targetsContent\": true" +
+                "  }," +
+                "  {" +
+                "    \"id\": \"2\"," +
+                "    \"expression\": \"james.org\"," +
+                "    \"explanation\": null," +
+                "    \"targetsSender\": true," +
+                "    \"targetsRecipients\": false," +
+                "    \"targetsContent\": false" +
+                "  }" +
+                "]}");
+        }
+
+        @Test
+        void getShouldReturnNotFoundWhenDomainNotInList() {
+            when()
+                .get("strange.com")
+            .then()
+                .statusCode(HttpStatus.NOT_FOUND_404)
+                .contentType(JSON_CONTENT_TYPE)
+                .body("statusCode", is(HttpStatus.NOT_FOUND_404))
+                .body("type", is("InvalidArgument"))
+                .body("message", is("'strange.com' is not managed by this James server"));
+        }
+
+        @Test
+        void getShouldReturnBadRequestWhenDomainIsNotValid() {
+            when()
+                .get("dr@strange.com")
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .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"));
+        }
+    }
+}
\ No newline at end of file


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


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

Posted by bt...@apache.org.
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


[3/3] james-project git commit: JAMES-2430 Write an integration test for DLP matcher

Posted by bt...@apache.org.
JAMES-2430 Write an integration test for DLP matcher


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

Branch: refs/heads/master
Commit: 536f3caab4744f777cd449a0c94ce08476bae4d3
Parents: 55cd111
Author: benwa <bt...@linagora.com>
Authored: Fri Jun 15 15:48:25 2018 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Jun 20 09:08:10 2018 +0700

----------------------------------------------------------------------
 .../james/modules/data/MemoryDataModule.java    |   3 +
 .../transport/mailets/DlpIntegrationTest.java   | 265 +++++++++++++++++++
 .../mailets/ToSenderDomainRepository.java       |   9 +-
 .../james/transport/matchers/dlp/Dlp.java       |   7 +-
 4 files changed, 280 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/536f3caa/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/data/MemoryDataModule.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
index 90fdd43..44e7496 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/data/MemoryDataModule.java
@@ -64,6 +64,9 @@ public class MemoryDataModule extends AbstractModule {
         bind(MemoryUsersRepository.class).toInstance(MemoryUsersRepository.withVirtualHosting());
         bind(UsersRepository.class).to(MemoryUsersRepository.class);
 
+        bind(EventSourcingDLPConfigurationStore.class).in(Scopes.SINGLETON);
+        bind(DLPConfigurationStore.class).to(EventSourcingDLPConfigurationStore.class);
+
         Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(MemoryDataConfigurationPerformer.class);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/536f3caa/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/DlpIntegrationTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/DlpIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/DlpIntegrationTest.java
new file mode 100644
index 0000000..0762995
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/DlpIntegrationTest.java
@@ -0,0 +1,265 @@
+/****************************************************************
+ * 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.transport.mailets;
+
+import static com.jayway.restassured.RestAssured.given;
+import static org.apache.james.mailets.configuration.Constants.DEFAULT_DOMAIN;
+import static org.apache.james.mailets.configuration.Constants.FROM;
+import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP;
+import static org.apache.james.mailets.configuration.Constants.PASSWORD;
+import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
+import static org.apache.james.mailets.configuration.Constants.RECIPIENT2;
+import static org.apache.james.mailets.configuration.Constants.SMTP_PORT;
+import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
+
+import java.util.Optional;
+
+import org.apache.james.MemoryJamesServerMain;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.jwt.JwtConfiguration;
+import org.apache.james.mailets.TemporaryJamesServer;
+import org.apache.james.mailets.configuration.MailetConfiguration;
+import org.apache.james.mailets.configuration.MailetContainer;
+import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
+import org.apache.james.transport.matchers.All;
+import org.apache.james.transport.matchers.dlp.Dlp;
+import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.IMAPMessageReader;
+import org.apache.james.utils.SMTPMessageSender;
+import org.apache.james.utils.WebAdminGuiceProbe;
+import org.apache.james.webadmin.WebAdminConfiguration;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.authentication.AuthenticationFilter;
+import org.apache.james.webadmin.authentication.NoAuthenticationFilter;
+import org.apache.mailet.base.test.FakeMail;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import com.google.inject.util.Modules;
+import com.jayway.restassured.specification.RequestSpecification;
+
+public class DlpIntegrationTest {
+    public static final String REPOSITORY_PREFIX = "file://var/mail/dlp/quarantine/";
+    public static final JwtConfiguration NO_JWT_CONFIGURATION = new JwtConfiguration(Optional.empty());
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder();
+
+    @Rule
+    public IMAPMessageReader imapMessageReader = new IMAPMessageReader();
+    @Rule
+    public SMTPMessageSender messageSender = new SMTPMessageSender(DEFAULT_DOMAIN);
+
+    private TemporaryJamesServer jamesServer;
+    private RequestSpecification specification;
+
+    private void createJamesServer(MailetConfiguration.Builder dlpMailet) throws Exception {
+        MailetContainer.Builder mailets = TemporaryJamesServer.DEFAULT_MAILET_CONTAINER_CONFIGURATION
+            .putProcessor(
+                ProcessorConfiguration.transport()
+                    .addMailet(MailetConfiguration.BCC_STRIPPER)
+                    .addMailet(dlpMailet)
+                    .addMailet(MailetConfiguration.builder()
+                        .matcher(All.class)
+                        .mailet(Null.class)));
+
+        jamesServer = TemporaryJamesServer.builder()
+            .withBase(Modules.override(
+                    MemoryJamesServerMain.SMTP_AND_IMAP_MODULE,
+                    MemoryJamesServerMain.WEBADMIN)
+                .with(
+                    binder -> binder.bind(JwtConfiguration.class).toInstance(NO_JWT_CONFIGURATION),
+                    binder -> binder.bind(AuthenticationFilter.class).to(NoAuthenticationFilter.class),
+                    binder -> binder.bind(WebAdminConfiguration.class).toInstance(WebAdminConfiguration.TEST_CONFIGURATION)))
+            .withMailetContainer(mailets)
+            .build(folder);
+
+        jamesServer.getProbe(DataProbeImpl.class)
+            .fluent()
+            .addDomain(DEFAULT_DOMAIN)
+            .addUser(FROM, PASSWORD)
+            .addUser(RECIPIENT, PASSWORD)
+            .addUser(RECIPIENT2, PASSWORD);
+        WebAdminGuiceProbe webAdminGuiceProbe = jamesServer.getProbe(WebAdminGuiceProbe.class);
+        webAdminGuiceProbe.await();
+        specification = WebAdminUtils.buildRequestSpecification(webAdminGuiceProbe.getWebAdminPort()).build();
+    }
+
+    @After
+    public void tearDown() {
+        jamesServer.shutdown();
+    }
+
+    @Test
+    public void dlpShouldStoreMatchingEmails() throws Exception {
+        createJamesServer(MailetConfiguration.builder()
+            .matcher(Dlp.class)
+            .mailet(ToSenderDomainRepository.class)
+            .addProperty(ToSenderDomainRepository.URL_PREFIX, REPOSITORY_PREFIX));
+
+        given()
+            .spec(specification)
+            .body("{\"rules\":[{" +
+                "  \"id\": \"1\"," +
+                "  \"expression\": \"match me\"," +
+                "  \"explanation\": \"A simple DLP rule.\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": true" +
+                "}]}")
+            .put("/dlp/rules/" + DEFAULT_DOMAIN);
+
+        messageSender.connect(LOCALHOST_IP, SMTP_PORT)
+            .sendMessage(FakeMail.builder()
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addToRecipient(RECIPIENT)
+                    .setSender(FROM)
+                    .setText("match me"))
+                .sender(FROM)
+                .recipient(RECIPIENT));
+
+        awaitAtMostOneMinute.until(() -> containsExactlyOneMail(MailRepositoryUrl.from(REPOSITORY_PREFIX + DEFAULT_DOMAIN)));
+    }
+
+    @Test
+    public void dlpShouldNotCreateRepositoryWhenNotAllowed() throws Exception {
+        createJamesServer(MailetConfiguration.builder()
+            .matcher(Dlp.class)
+            .mailet(ToSenderDomainRepository.class)
+            .addProperty(ToSenderDomainRepository.URL_PREFIX, REPOSITORY_PREFIX)
+            .addProperty(ToSenderDomainRepository.ALLOW_REPOSITORY_CREATION, "false"));
+
+        given()
+            .spec(specification)
+            .body("{\"rules\":[[{" +
+                "  \"id\": \"1\"," +
+                "  \"expression\": \"match me\"," +
+                "  \"explanation\": \"A simple DLP rule.\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": true" +
+                "}]}")
+            .put("/dlp/rules/" + DEFAULT_DOMAIN);
+
+        messageSender.connect(LOCALHOST_IP, SMTP_PORT)
+            .sendMessage(FakeMail.builder()
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addToRecipient(RECIPIENT)
+                    .setSender(FROM)
+                    .setText("match me"))
+                .sender(FROM)
+                .recipient(RECIPIENT));
+
+        MailRepositoryUrl repositoryUrl = MailRepositoryUrl.from(REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+        given()
+            .spec(specification)
+        .get("/mailRepositories/" + repositoryUrl.urlEncoded() + "/mails")
+            .then()
+            .statusCode(HttpStatus.NOT_FOUND_404);
+    }
+
+    @Test
+    public void dlpShouldCreateRepositoryWhenAllowed() throws Exception {
+        createJamesServer(MailetConfiguration.builder()
+            .matcher(Dlp.class)
+            .mailet(ToSenderDomainRepository.class)
+            .addProperty(ToSenderDomainRepository.URL_PREFIX, REPOSITORY_PREFIX)
+            .addProperty(ToSenderDomainRepository.ALLOW_REPOSITORY_CREATION, "true"));
+        MailRepositoryUrl repositoryUrl = MailRepositoryUrl.from(REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+
+        given()
+            .spec(specification)
+            .body("{\"rules\":[{" +
+                "  \"id\": \"1\"," +
+                "  \"expression\": \"match me\"," +
+                "  \"explanation\": \"A simple DLP rule.\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": true" +
+                "}]}")
+            .put("/dlp/rules/" + DEFAULT_DOMAIN);
+
+        messageSender.connect(LOCALHOST_IP, SMTP_PORT)
+            .sendMessage(FakeMail.builder()
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addToRecipient(RECIPIENT)
+                    .setSender(FROM)
+                    .setText("match me"))
+                .sender(FROM)
+                .recipient(RECIPIENT));
+
+        awaitAtMostOneMinute.until(() -> containsExactlyOneMail(repositoryUrl));
+    }
+
+    @Test
+    public void dlpShouldStoreMailWhenNotAllowedButRepositoryExists() throws Exception {
+        createJamesServer(MailetConfiguration.builder()
+            .matcher(Dlp.class)
+            .mailet(ToSenderDomainRepository.class)
+            .addProperty(ToSenderDomainRepository.URL_PREFIX, REPOSITORY_PREFIX)
+            .addProperty(ToSenderDomainRepository.ALLOW_REPOSITORY_CREATION, "false"));
+
+        MailRepositoryUrl repositoryUrl = MailRepositoryUrl.from(REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+        given()
+            .spec(specification)
+            .put("/mailRepositories/" + repositoryUrl.urlEncoded());
+
+        given()
+            .spec(specification)
+            .body("{\"rules\":[{" +
+                "  \"id\": \"1\"," +
+                "  \"expression\": \"match me\"," +
+                "  \"explanation\": \"A simple DLP rule.\"," +
+                "  \"targetsSender\": false," +
+                "  \"targetsRecipients\": false," +
+                "  \"targetsContent\": true" +
+                "}]}")
+            .put("/dlp/rules/" + DEFAULT_DOMAIN);
+
+        messageSender.connect(LOCALHOST_IP, SMTP_PORT)
+            .sendMessage(FakeMail.builder()
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addToRecipient(RECIPIENT)
+                    .setSender(FROM)
+                    .setText("match me"))
+                .sender(FROM)
+                .recipient(RECIPIENT));
+
+        awaitAtMostOneMinute.until(() -> containsExactlyOneMail(repositoryUrl));
+    }
+
+    private boolean containsExactlyOneMail(MailRepositoryUrl repositoryUrl) {
+        try {
+            return given()
+                .spec(specification)
+                .get("/mailRepositories/" + repositoryUrl.urlEncoded() + "/mails")
+                .prettyPeek()
+                .jsonPath()
+                .getList(".")
+                .size() == 1;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/536f3caa/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
index 5992f85..2dce4e6 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
@@ -61,6 +61,9 @@ import com.github.fge.lambdas.consumers.ThrowingConsumer;
  * &lt;/mailet&gt;
  */
 public class ToSenderDomainRepository extends GenericMailet {
+    public static final String URL_PREFIX = "urlPrefix";
+    public static final String PASS_THROUGH = "passThrough";
+    public static final String ALLOW_REPOSITORY_CREATION = "allowRepositoryCreation";
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ToSenderDomainRepository.class);
     private static final boolean DEFAULT_CONSUME = false;
@@ -78,10 +81,10 @@ public class ToSenderDomainRepository extends GenericMailet {
 
     @Override
     public void init() throws MessagingException {
-        urlPrefix = Optional.ofNullable(getInitParameter("urlPrefix"))
+        urlPrefix = Optional.ofNullable(getInitParameter(URL_PREFIX))
             .orElseThrow(() -> new MessagingException("'urlPrefix' is a mandatory configuration property"));
-        passThrough = getInitParameter("passThrough", DEFAULT_CONSUME);
-        allowRepositoryCreation = getInitParameter("allowRepositoryCreation", DEFAULT_ALLOW_REPOSITORY_CREATION);
+        passThrough = getInitParameter(PASS_THROUGH, DEFAULT_CONSUME);
+        allowRepositoryCreation = getInitParameter(ALLOW_REPOSITORY_CREATION, DEFAULT_ALLOW_REPOSITORY_CREATION);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/536f3caa/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/Dlp.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/Dlp.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/Dlp.java
index cb7fc72..1f65082 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/Dlp.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/dlp/Dlp.java
@@ -27,6 +27,7 @@ import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
 import org.apache.james.dlp.api.DLPConfigurationItem;
+import org.apache.james.dlp.api.DLPConfigurationStore;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMatcher;
 
@@ -39,12 +40,16 @@ public class Dlp extends GenericMatcher {
 
     private final DlpRulesLoader rulesLoader;
 
-    @Inject
     @VisibleForTesting
     Dlp(DlpRulesLoader rulesLoader) {
         this.rulesLoader = rulesLoader;
     }
 
+    @Inject
+    public Dlp(DLPConfigurationStore configurationStore) {
+        this(new DlpRulesLoader.Impl(configurationStore));
+    }
+
     @Override
     public Collection<MailAddress> match(Mail mail) throws MessagingException {
         Optional<DLPConfigurationItem.Id> firstMatchingRuleId = findFirstMatchingRule(mail);


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