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