You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2023/11/10 02:38:20 UTC

(james-project) branch master updated: JAMES-3897: Crowdsec Extension Test (#1779)

This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 180c0ab4a3 JAMES-3897: Crowdsec Extension Test (#1779)
180c0ab4a3 is described below

commit 180c0ab4a3c3c3cbbae9f83b8933d116ed3ecbe1
Author: vtbui <87...@users.noreply.github.com>
AuthorDate: Fri Nov 10 09:38:14 2023 +0700

    JAMES-3897: Crowdsec Extension Test (#1779)
---
 third-party/crowdsec/pom.xml                       |  91 ++++++++++++++
 .../org/apache/james/model/CrowdsecDecision.java   | 137 +++++++++++++++++++++
 .../james/model/CrowdsecDecisionDeserializer.java  |  51 ++++++++
 .../java/org/apache/james/CrowdsecExtension.java   |  66 ++++++++++
 .../apache/james/DockerCrowdsecExtensionTest.java  |  75 +++++++++++
 third-party/pom.xml                                |   3 +-
 6 files changed, 422 insertions(+), 1 deletion(-)

diff --git a/third-party/crowdsec/pom.xml b/third-party/crowdsec/pom.xml
new file mode 100644
index 0000000000..65f039d456
--- /dev/null
+++ b/third-party/crowdsec/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>third-party</artifactId>
+        <version>3.9.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>crowdsec</artifactId>
+    <name>Apache James :: Third Party :: Crowdsec</name>
+    <description>Crowdsec Java client (HTTP) and testing utilities</description>
+
+    <properties>
+        <james.baseVersion>${project.version}</james.baseVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-common</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.fge</groupId>
+            <artifactId>throwing-lambdas</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-configuration2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>james-server-testing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>RELEASE</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecision.java b/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecision.java
new file mode 100644
index 0000000000..b3a3f2ea7c
--- /dev/null
+++ b/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecision.java
@@ -0,0 +1,137 @@
+/****************************************************************
+ * 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.model;
+
+import java.time.Duration;
+import java.util.Objects;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.base.MoreObjects;
+
+@JsonDeserialize(
+    using = CrowdsecDecisionDeserializer.class
+)
+public class CrowdsecDecision {
+    private static final Logger LOGGER = LoggerFactory.getLogger(CrowdsecDecision.class);
+    public static final String BAN = "ban";
+
+    private final Duration duration;
+    private final long id;
+    private final String origin;
+    private final String scenario;
+    private final String scope;
+    private final String type;
+    private final String value;
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    private CrowdsecDecision(Duration duration, long id, String origin, String scenario, String scope, String type, String value) {
+        this.id = id;
+        this.origin = origin;
+        this.scenario = scenario;
+        this.scope = scope;
+        this.type = type;
+        this.value = value;
+        this.duration = duration;
+    }
+
+    public final boolean equals(Object o) {
+        if (!(o instanceof CrowdsecDecision)) {
+            return false;
+        } else {
+            CrowdsecDecision that = (CrowdsecDecision)o;
+            return Objects.equals(this.id, that.id) && Objects.equals(this.duration, that.duration) && Objects.equals(this.origin, that.origin) && Objects.equals(this.scenario, that.scenario) && Objects.equals(this.scope, that.scope) && Objects.equals(this.type, that.type) && Objects.equals(this.value, that.value);
+        }
+    }
+
+    public final int hashCode() {
+        return Objects.hash(this.duration, this.id, this.origin, this.scenario, this.scope, this.type, this.value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("duration", duration)
+            .add("id", id)
+            .add("origin", origin)
+            .add("scenario", scenario)
+            .add("scope", scope)
+            .add("type", type)
+            .add("value", value)
+            .toString();
+    }
+
+    public static class Builder {
+        private Duration duration;
+        private long id;
+        private String origin;
+        private String scenario;
+        private String scope;
+        private String type;
+        private String value;
+
+        public Builder() {
+        }
+
+        public Builder duration(Duration duration) {
+            this.duration = duration;
+            return this;
+        }
+
+        public Builder id(long id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder origin(String origin) {
+            this.origin = origin;
+            return this;
+        }
+
+        public Builder scenario(String scenario) {
+            this.scenario = scenario;
+            return this;
+        }
+
+        public Builder scope(String scope) {
+            this.scope = scope;
+            return this;
+        }
+
+        public Builder type(String type) {
+            this.type = type;
+            return this;
+        }
+
+        public Builder value(String value) {
+            this.value = value;
+            return this;
+        }
+
+        public CrowdsecDecision build() {
+            return new CrowdsecDecision(this.duration, this.id, this.origin, this.scenario, this.scope, this.type, this.value);
+        }
+    }
+}
diff --git a/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecisionDeserializer.java b/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecisionDeserializer.java
new file mode 100644
index 0000000000..5d0aa03477
--- /dev/null
+++ b/third-party/crowdsec/src/main/java/org/apache/james/model/CrowdsecDecisionDeserializer.java
@@ -0,0 +1,51 @@
+/****************************************************************
+ * 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.model;
+
+import java.io.IOException;
+import java.time.Duration;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+public class CrowdsecDecisionDeserializer extends StdDeserializer<CrowdsecDecision> {
+    public CrowdsecDecisionDeserializer() {
+        this(null);
+    }
+
+    protected CrowdsecDecisionDeserializer(Class<?> vc) {
+        super(vc);
+    }
+
+    public CrowdsecDecision deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException {
+        JsonNode node = jp.getCodec().readTree(jp);
+        return CrowdsecDecision.builder()
+            .duration(Duration.parse("PT" + node.get("duration").asText()))
+            .id(node.get("id").asLong())
+            .origin(node.get("origin").asText())
+            .scenario(node.get("scenario").asText())
+            .scope(node.get("scope").asText())
+            .type(node.get("type").asText())
+            .value(node.get("value").asText())
+            .build();
+    }
+}
diff --git a/third-party/crowdsec/src/test/java/org/apache/james/CrowdsecExtension.java b/third-party/crowdsec/src/test/java/org/apache/james/CrowdsecExtension.java
new file mode 100644
index 0000000000..45edba2068
--- /dev/null
+++ b/third-party/crowdsec/src/test/java/org/apache/james/CrowdsecExtension.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.UUID;
+
+import org.apache.james.util.docker.RateLimiters;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
+
+public class CrowdsecExtension implements GuiceModuleTestExtension {
+    public static final Duration STARTUP_TIMEOUT = Duration.ofMinutes(5);
+    public static final int CROWDSEC_PORT = 8080;
+    public static final String CROWDSEC_IMAGE = "crowdsecurity/crowdsec:v1.5.4";
+
+    private final GenericContainer<?> crowdsecContainer;
+
+    public CrowdsecExtension() {
+        this.crowdsecContainer = new GenericContainer<>(CROWDSEC_IMAGE)
+            .withCreateContainerCmdModifier(cmd -> cmd.withName("james-crowdsec-test-" + UUID.randomUUID()))
+            .withExposedPorts(CROWDSEC_PORT)
+            .withStartupTimeout(STARTUP_TIMEOUT)
+            .waitingFor(new HostPortWaitStrategy().withRateLimiter(RateLimiters.TWENTIES_PER_SECOND));;
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext context) {
+        if (!crowdsecContainer.isRunning()) {
+            crowdsecContainer.start();
+        }
+    }
+
+    @Override
+    public void afterEach(ExtensionContext extensionContext) throws IOException, InterruptedException {
+        crowdsecContainer.execInContainer("cscli", "decision", "delete", "--all");
+    }
+
+    @Override
+    public void afterAll(ExtensionContext extensionContext) {
+
+    }
+
+    public GenericContainer<?> getCrowdsecContainer() {
+        return crowdsecContainer;
+    }
+}
diff --git a/third-party/crowdsec/src/test/java/org/apache/james/DockerCrowdsecExtensionTest.java b/third-party/crowdsec/src/test/java/org/apache/james/DockerCrowdsecExtensionTest.java
new file mode 100644
index 0000000000..38463805a1
--- /dev/null
+++ b/third-party/crowdsec/src/test/java/org/apache/james/DockerCrowdsecExtensionTest.java
@@ -0,0 +1,75 @@
+/****************************************************************
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.testcontainers.containers.Container;
+
+class DockerCrowdsecExtensionTest {
+    @RegisterExtension
+    static CrowdsecExtension crowdsecExtension = new CrowdsecExtension();
+
+    @AfterEach
+    public void tearDown() throws Exception {
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli", "decision", "delete", "--all");
+    }
+
+    @Test
+    void addDecisionWithAnIPTest() throws IOException, InterruptedException {
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "add", "-i", "192.168.0.4");
+        Container.ExecResult result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout().contains("Ip:192.168.0.4") && result.getStdout().contains("manual 'ban' from 'localhost'")).isTrue();
+    }
+
+    @Test
+    void addDecisionWithAnIPRangeTest() throws IOException, InterruptedException {
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "add", "-r", "192.168.0.0/16");
+        Container.ExecResult result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout().contains("Range:192.168.0.0/16") && result.getStdout().contains("manual 'ban' from 'localhost'")).isTrue();
+    }
+
+    @Test
+    void deleteDecisionWithAnIPTest() throws IOException, InterruptedException {
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "add", "-i", "192.168.0.4");
+        Container.ExecResult result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout().contains("Ip:192.168.0.4") && result.getStdout().contains("manual 'ban' from 'localhost'")).isTrue();
+
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "delete", "-i", "192.168.0.4");
+        result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout()).contains("No active decisions");
+    }
+
+    @Test
+    void deleteDecisionWithAnIPRangeTest() throws IOException, InterruptedException {
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "add", "-r", "192.168.0.0/16");
+        Container.ExecResult result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout().contains("Range:192.168.0.0/16") && result.getStdout().contains("manual 'ban' from 'localhost'")).isTrue();
+
+        crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "delete", "-r", "192.168.0.0/16");
+        result = crowdsecExtension.getCrowdsecContainer().execInContainer("cscli" , "decision", "list");
+        assertThat(result.getStdout()).contains("No active decisions");
+    }
+}
diff --git a/third-party/pom.xml b/third-party/pom.xml
index e7d3a55cba..e7a107bb0b 100644
--- a/third-party/pom.xml
+++ b/third-party/pom.xml
@@ -34,9 +34,10 @@
 
     <modules>
         <module>clamav</module>
+        <module>crowdsec</module>
         <module>elasticsearch</module>
         <module>linshare</module>
         <module>rspamd</module>
         <module>spamassassin</module>
     </modules>
-</project>
\ No newline at end of file
+</project>


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