This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch droplist in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/droplist by this push: new ae8bfc4a35 JAMES-3946 add a memory implementation of the DropList (#2197) ae8bfc4a35 is described below commit ae8bfc4a35535a8f69147025ed1df28e368013ac Author: Maksim <85022218+maxxx...@users.noreply.github.com> AuthorDate: Tue Apr 16 12:00:22 2024 +0300 JAMES-3946 add a memory implementation of the DropList (#2197) --- .../apache/james/droplists/api/DropListEntry.java | 71 ++++---- .../james/droplists/api/DropListContract.java | 190 +++++++++++++++++++++ .../james/droplists/api/DropListEntryTest.java | 128 +++++--------- .../james/droplists/memory/MemoryDropList.java | 97 +++++++++++ .../james/droplists/memory/MemoryDropListTest.java | 39 +++++ 5 files changed, 407 insertions(+), 118 deletions(-) diff --git a/server/data/data-api/src/main/java/org/apache/james/droplists/api/DropListEntry.java b/server/data/data-api/src/main/java/org/apache/james/droplists/api/DropListEntry.java index ab73cc4e51..7bec40408f 100644 --- a/server/data/data-api/src/main/java/org/apache/james/droplists/api/DropListEntry.java +++ b/server/data/data-api/src/main/java/org/apache/james/droplists/api/DropListEntry.java @@ -18,12 +18,16 @@ ****************************************************************/ package org.apache.james.droplists.api; -import static org.apache.james.core.Domain.MAXIMUM_DOMAIN_LENGTH; +import static org.apache.james.droplists.api.OwnerScope.DOMAIN; import static org.apache.james.droplists.api.OwnerScope.GLOBAL; +import static org.apache.james.droplists.api.OwnerScope.USER; import java.util.Objects; import java.util.Optional; +import org.apache.james.core.Domain; +import org.apache.james.core.MailAddress; + import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -34,53 +38,58 @@ public class DropListEntry { } public static class Builder { - private Optional<OwnerScope> ownerScope = Optional.empty(); - private String owner; + private OwnerScope ownerScope; + private Optional<String> owner = Optional.empty(); private DeniedEntityType deniedEntityType; private String deniedEntity; - public Builder ownerScope(OwnerScope ownerScope) { - this.ownerScope = Optional.ofNullable(ownerScope); + public Builder userOwner(MailAddress mailAddress) { + Preconditions.checkNotNull(mailAddress); + this.owner = Optional.of(mailAddress.toString()); + this.ownerScope = USER; + return this; + } + + public Builder domainOwner(Domain domain) { + Preconditions.checkNotNull(domain); + this.owner = Optional.of(domain.asString()); + this.ownerScope = DOMAIN; return this; } - public Builder owner(String owner) { - Preconditions.checkNotNull(owner); - this.owner = owner; + public Builder forAll() { + this.ownerScope = GLOBAL; return this; } - public Builder deniedEntityType(DeniedEntityType deniedEntityType) { - Preconditions.checkNotNull(deniedEntityType); - this.deniedEntityType = deniedEntityType; + public Builder denyDomain(Domain domain) { + Preconditions.checkNotNull(domain); + this.deniedEntity = domain.asString(); + this.deniedEntityType = DeniedEntityType.DOMAIN; return this; } - public Builder deniedEntity(String deniedEntity) { - Preconditions.checkNotNull(deniedEntity); - this.deniedEntity = deniedEntity; + public Builder denyAddress(MailAddress mailAddress) { + Preconditions.checkNotNull(mailAddress); + this.deniedEntity = mailAddress.toString(); + this.deniedEntityType = DeniedEntityType.ADDRESS; return this; } public DropListEntry build() { - OwnerScope scope = ownerScope.orElse(GLOBAL); - Preconditions.checkArgument(owner != null && !owner.trim().isBlank(), "owner must not be null, empty, or blank"); - Preconditions.checkArgument(owner.length() <= MAXIMUM_DOMAIN_LENGTH, - "owner length should not be longer than %s characters", MAXIMUM_DOMAIN_LENGTH); Preconditions.checkArgument(deniedEntityType != null, "`deniedEntityType` is mandatory"); + Preconditions.checkArgument(ownerScope != null, "`ownerScope` is mandatory"); Preconditions.checkArgument(deniedEntity != null && !deniedEntity.isBlank(), "`deniedEntity` must not be null, empty, or blank"); - Preconditions.checkArgument(deniedEntity.length() <= MAXIMUM_DOMAIN_LENGTH, - "deniedEntity length should not be longer than %s characters", MAXIMUM_DOMAIN_LENGTH); - return new DropListEntry(scope, owner, deniedEntityType, deniedEntity); + return new DropListEntry(ownerScope, owner, deniedEntityType, deniedEntity); } } private final OwnerScope ownerScope; - private final String owner; + private final Optional<String> owner; private final DeniedEntityType deniedEntityType; private final String deniedEntity; - private DropListEntry(OwnerScope ownerScope, String owner, DeniedEntityType deniedEntityType, String deniedEntity) { + private DropListEntry(OwnerScope ownerScope, Optional<String> owner, DeniedEntityType deniedEntityType, String deniedEntity) { this.ownerScope = ownerScope; this.owner = owner; this.deniedEntityType = deniedEntityType; @@ -92,7 +101,7 @@ public class DropListEntry { } public String getOwner() { - return owner; + return owner.orElse(""); } public DeniedEntityType getDeniedEntityType() { @@ -121,11 +130,11 @@ public class DropListEntry { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("ownerScope", ownerScope) - .add("owner", owner) - .add("deniedType", deniedEntityType) - .add("deniedEntity", deniedEntity) - .toString(); + MoreObjects.ToStringHelper result = MoreObjects.toStringHelper(this) + .add("ownerScope", ownerScope); + owner.ifPresent(o -> result.add("owner", o)); + result.add("deniedType", deniedEntityType) + .add("deniedEntity", deniedEntity); + return result.toString(); } -} +} \ No newline at end of file diff --git a/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListContract.java b/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListContract.java new file mode 100644 index 0000000000..a4993ff973 --- /dev/null +++ b/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListContract.java @@ -0,0 +1,190 @@ +/**************************************************************** + * 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.droplists.api; + +import static org.apache.james.droplists.api.OwnerScope.GLOBAL; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.stream.Stream; + +import jakarta.mail.internet.AddressException; + +import org.apache.james.core.Domain; +import org.apache.james.core.MailAddress; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface DropListContract { + + DropList dropList(); + + @Test + default void shouldAddEntry() throws AddressException { + DropListEntry dropListEntry = DropListEntry.builder() + .forAll() + .denyAddress(new MailAddress("den...@denied.com")) + .build(); + + Mono<Void> result = dropList().add(dropListEntry); + + assertThat(dropList().list(GLOBAL, dropListEntry.getOwner()).collectList().block().size()).isEqualTo(1); + assertThat(result).isEqualTo(Mono.empty()); + } + + @Test + default void shouldRemoveEntry() throws AddressException { + DropListEntry dropListEntry = DropListEntry.builder() + .forAll() + .denyAddress(new MailAddress("den...@denied.com")) + .build(); + + dropList().add(dropListEntry); + + Mono<Void> result = dropList().remove(dropListEntry); + + assertThat(dropList().list(GLOBAL, dropListEntry.getOwner()).collectList().block().size()).isZero(); + assertThat(result).isEqualTo(Mono.empty()); + } + + @Test + default void shouldThrowWhenAddOnNullDropListEntry() { + assertThatThrownBy(() -> dropList().add(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenRemoveOnNullDropListEntry() { + assertThatThrownBy(() -> dropList().remove(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenListOnNullScope() { + assertThatThrownBy(() -> dropList().list(null, "owner")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenListOnNullOwner() { + assertThatThrownBy(() -> dropList().list(GLOBAL, null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenQueryOnNullScope() { + assertThatThrownBy(() -> dropList().query(null, "owner", new MailAddress("sen...@example.com"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenQueryOnNullOwner() { + assertThatThrownBy(() -> dropList().query(GLOBAL, null, new MailAddress("sen...@example.com"))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + default void shouldThrowWhenQueryOnNullSender() { + assertThatThrownBy(() -> dropList().query(GLOBAL, "owner", null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideParametersForGetEntryListTest") + default void shouldGetEntryListForSpecifiedScopeAndOwner(DropListEntry dropListEntry) { + dropList().add(dropListEntry); + + Flux<DropListEntry> result = dropList().list(dropListEntry.getOwnerScope(), dropListEntry.getOwner()); + + assertThat(result.collectList().block().size()).isEqualTo(1); + } + + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideParametersForReturnAllowedTest") + default void shouldReturnAllowed(DropListEntry dropListEntry, MailAddress senderMailAddress) { + dropList().add(dropListEntry); + + Mono<DropList.Status> result = dropList().query(dropListEntry.getOwnerScope(), dropListEntry.getOwner(), senderMailAddress); + + assertThat(result.block()).isEqualTo(DropList.Status.ALLOWED); + } + + @ParameterizedTest(name = "{index} {0}") + @MethodSource("provideParametersForReturnBlockedTest") + default void shouldReturnBlocked(DropListEntry dropListEntry, MailAddress senderMailAddress) { + dropList().add(dropListEntry); + + Mono<DropList.Status> result = dropList().query(dropListEntry.getOwnerScope(), dropListEntry.getOwner(), senderMailAddress); + + assertThat(result.block()).isEqualTo(DropList.Status.BLOCKED); + } + + static Stream<DropListEntry> getDropListTestEntries() throws AddressException { + return Stream.of( + DropListEntry.builder() + .forAll() + .denyAddress(new MailAddress("den...@denied.com")) + .build(), + DropListEntry.builder() + .forAll() + .denyDomain(Domain.of("denied.com")) + .build(), + DropListEntry.builder() + .domainOwner(Domain.of("example.com")) + .denyAddress(new MailAddress("den...@denied.com")) + .build(), + DropListEntry.builder() + .domainOwner(Domain.of("example.com")) + .denyDomain(Domain.of("denied.com")) + .build(), + DropListEntry.builder() + .userOwner(new MailAddress("ow...@example.com")) + .denyAddress(new MailAddress("den...@denied.com")) + .build(), + DropListEntry.builder() + .userOwner(new MailAddress("ow...@example.com")) + .denyDomain(Domain.of("denied.com")) + .build()); + } + + static Stream<Arguments> provideParametersForGetEntryListTest() throws AddressException { + return getDropListTestEntries().map(Arguments::of); + } + + static Stream<Arguments> provideParametersForReturnAllowedTest() throws AddressException { + MailAddress allowedSenderAddress = new MailAddress("allo...@allowed.com"); + return getDropListTestEntries().map(dropListEntry -> Arguments.of(dropListEntry, allowedSenderAddress)); + } + + static Stream<Arguments> provideParametersForReturnBlockedTest() throws AddressException { + + MailAddress deniedSenderAddress = new MailAddress("den...@denied.com"); + MailAddress deniedSenderDomain = new MailAddress("allo...@denied.com"); + return getDropListTestEntries().map(dropListEntry -> + dropListEntry.getDeniedEntityType().equals(DeniedEntityType.DOMAIN) ? + Arguments.of(dropListEntry, deniedSenderDomain) : + Arguments.of(dropListEntry, deniedSenderAddress)); + } +} diff --git a/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListEntryTest.java b/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListEntryTest.java index 14e6ff5e60..79d6f2a4d3 100644 --- a/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListEntryTest.java +++ b/server/data/data-api/src/test/java/org/apache/james/droplists/api/DropListEntryTest.java @@ -21,15 +21,16 @@ package org.apache.james.droplists.api; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import org.apache.commons.lang3.StringUtils; +import jakarta.mail.internet.AddressException; + +import org.apache.james.core.Domain; +import org.apache.james.core.MailAddress; import org.junit.jupiter.api.Test; import nl.jqno.equalsverifier.EqualsVerifier; class DropListEntryTest { - private final String LONG_ENTITY = StringUtils.repeat('x', 254); - @Test void shouldRespectEqualsContract() { EqualsVerifier.forClass(DropListEntry.class) @@ -37,152 +38,105 @@ class DropListEntryTest { } @Test - void shouldThrowOnWithoutOwner() { - DropListEntry.Builder builder = DropListEntry.builder() - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("deniedEntity"); + void shouldThrowOnWhenBuilderIsEmpty() { + DropListEntry.Builder builder = DropListEntry.builder(); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class); } @Test - void shouldThrowOnEmptyOwner() { + void shouldThrowOnWithoutOwnerScope() { DropListEntry.Builder builder = DropListEntry.builder() - .owner("") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("deniedEntity"); + .denyDomain(Domain.of("denied.com")); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class); } @Test - void shouldThrowOnBlankOwner() { - DropListEntry.Builder builder = DropListEntry.builder() - .owner(" ") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("deniedEntity"); + void shouldThrowOnNullUserOwner() { + DropListEntry.Builder builder = DropListEntry.builder(); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.userOwner(null)) + .isInstanceOf(NullPointerException.class); } @Test - void shouldThrowOnNullOwner() { + void shouldThrowOnNullDomainOwner() { DropListEntry.Builder builder = DropListEntry.builder(); - assertThatThrownBy(() -> builder.owner(null)) + assertThatThrownBy(() -> builder.domainOwner(null)) .isInstanceOf(NullPointerException.class); } @Test void shouldThrowOnWithoutDeniedEntity() { DropListEntry.Builder builder = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity(""); + .forAll(); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class); } @Test - void shouldThrowOnEmptyDeniedEntity() { + void shouldThrowOnNullDeniedDomain() { DropListEntry.Builder builder = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity(""); + .forAll(); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - void shouldThrowOnBlankDeniedEntity() { - DropListEntry.Builder builder = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity(" "); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.denyDomain(null)) + .isInstanceOf(NullPointerException.class); } @Test - void shouldThrowOnNullDeniedEntity() { + void shouldThrowOnNullDeniedMailAddress() { DropListEntry.Builder builder = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity(" "); + .forAll(); - assertThatThrownBy(() -> builder.deniedEntity(null)) + assertThatThrownBy(() -> builder.denyAddress(null)) .isInstanceOf(NullPointerException.class); } @Test - void shouldDefaultGlobalOwnerScopeWhenNotSpecified() { + void shouldGlobalOwnerScopeBeSetWhenForAllIsCalled() { DropListEntry dropListEntry = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("entity") + .forAll() + .denyDomain(Domain.of("denied.com")) .build(); assertThat(dropListEntry.getOwnerScope()).isEqualTo(OwnerScope.GLOBAL); } @Test - void shouldDefaultGlobalOwnerScopeOnNull() { + void shouldEmptyOwnerBeSetWhenForAllIsCalled() throws AddressException { DropListEntry dropListEntry = DropListEntry.builder() - .ownerScope(null) - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("entity") + .forAll() + .denyAddress(new MailAddress("den...@example.com")) .build(); - assertThat(dropListEntry.getOwnerScope()).isEqualTo(OwnerScope.GLOBAL); - } - - @Test - void shouldThrowOnNullDeniedEntityType() { - DropListEntry.Builder builder = DropListEntry.builder(); - - assertThatThrownBy(() -> builder.deniedEntityType(null)) - .isInstanceOf(NullPointerException.class); + assertThat(dropListEntry.getOwner()).isEmpty(); } @Test - void shouldThrowWhenDeniedEntityTooLong() { - DropListEntry.Builder builder = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity(LONG_ENTITY); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - void shouldThrowWhenOwnerEntityTooLong() { - DropListEntry.Builder builder = DropListEntry.builder() - .owner(LONG_ENTITY) - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("deniedEntity"); + void shouldReturnDropListEntryAsString() throws AddressException { + String expectedString = "DropListEntry{ownerScope=USER, owner=ow...@example.com, deniedType=DOMAIN, deniedEntity=denied.com}"; + DropListEntry dropListEntry = DropListEntry.builder() + .userOwner(new MailAddress("ow...@example.com")) + .denyDomain(Domain.of("denied.com")) + .build(); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class); + assertThat(dropListEntry).hasToString(expectedString); } @Test - void shouldReturnDropListEntryAsString() { - String expectedString = "DropListEntry{ownerScope=GLOBAL, owner=owner, deniedType=DOMAIN, deniedEntity=entity}"; + void shouldReturnDropListEntryAsStringWithoutOwnerWhenScopeGlobal() { + String expectedString = "DropListEntry{ownerScope=GLOBAL, deniedType=DOMAIN, deniedEntity=denied.com}"; DropListEntry dropListEntry = DropListEntry.builder() - .owner("owner") - .deniedEntityType(DeniedEntityType.DOMAIN) - .deniedEntity("entity") + .forAll() + .denyDomain(Domain.of("denied.com")) .build(); - assertThat(expectedString).isEqualTo(dropListEntry.toString()); + assertThat(dropListEntry).hasToString(expectedString); } } \ No newline at end of file diff --git a/server/data/data-memory/src/main/java/org/apache/james/droplists/memory/MemoryDropList.java b/server/data/data-memory/src/main/java/org/apache/james/droplists/memory/MemoryDropList.java new file mode 100644 index 0000000000..b413d4f919 --- /dev/null +++ b/server/data/data-memory/src/main/java/org/apache/james/droplists/memory/MemoryDropList.java @@ -0,0 +1,97 @@ +/**************************************************************** + * 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.droplists.memory; + +import org.apache.james.core.MailAddress; +import org.apache.james.droplists.api.DeniedEntityType; +import org.apache.james.droplists.api.DropList; +import org.apache.james.droplists.api.DropListEntry; +import org.apache.james.droplists.api.OwnerScope; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class MemoryDropList implements DropList { + + private final Multimap<OwnerScope, DropListEntry> globalDropList = Multimaps.synchronizedMultimap(HashMultimap.create()); + private final Multimap<OwnerScope, DropListEntry> domainDropList = Multimaps.synchronizedMultimap(HashMultimap.create()); + private final Multimap<OwnerScope, DropListEntry> userDropList = Multimaps.synchronizedMultimap(HashMultimap.create()); + + @Override + public Mono<Void> add(DropListEntry entry) { + Preconditions.checkArgument(entry != null); + OwnerScope ownerScope = entry.getOwnerScope(); + Multimap<OwnerScope, DropListEntry> selectedDropList = getDropListByScope(ownerScope); + selectedDropList.put(ownerScope, entry); + return Mono.empty(); + } + + @Override + public Mono<Void> remove(DropListEntry entry) { + Preconditions.checkArgument(entry != null); + OwnerScope ownerScope = entry.getOwnerScope(); + Multimap<OwnerScope, DropListEntry> selectedDropList = getDropListByScope(ownerScope); + selectedDropList.remove(ownerScope, entry); + return Mono.empty(); + } + + @Override + public Flux<DropListEntry> list(OwnerScope ownerScope, String owner) { + Preconditions.checkArgument(ownerScope != null); + Preconditions.checkArgument(owner != null); + Multimap<OwnerScope, DropListEntry> selectedDropList = getDropListByScope(ownerScope); + return Flux.fromIterable(selectedDropList.get(ownerScope)) + .filter(entry -> entry.getOwner().equals(owner)); + } + + @Override + public Mono<Status> query(OwnerScope ownerScope, String owner, MailAddress sender) { + Preconditions.checkArgument(ownerScope != null); + Preconditions.checkArgument(owner != null); + Preconditions.checkArgument(sender != null); + Multimap<OwnerScope, DropListEntry> selectedDropList = getDropListByScope(ownerScope); + boolean isBlocked = selectedDropList.get(ownerScope).stream() + .anyMatch(entry -> isEntryMatchingOwner(owner, entry) && isEntryMatchingDeniedEntity(sender, entry)); + + return Mono.just(isBlocked ? Status.BLOCKED : Status.ALLOWED); + } + + private Multimap<OwnerScope, DropListEntry> getDropListByScope(OwnerScope ownerScope) { + return switch (ownerScope) { + case GLOBAL -> globalDropList; + case DOMAIN -> domainDropList; + case USER -> userDropList; + }; + } + + private boolean isEntryMatchingOwner(String owner, DropListEntry entry) { + return entry.getOwner().equals(owner); + } + + private boolean isEntryMatchingDeniedEntity(MailAddress sender, DropListEntry entry) { + String entityFromSender = entry.getDeniedEntityType() == DeniedEntityType.DOMAIN ? sender.getDomain().asString() : sender.asString(); + + return entry.getDeniedEntity().equals(entityFromSender); + } +} diff --git a/server/data/data-memory/src/test/java/org/apache/james/droplists/memory/MemoryDropListTest.java b/server/data/data-memory/src/test/java/org/apache/james/droplists/memory/MemoryDropListTest.java new file mode 100644 index 0000000000..d78cfcca82 --- /dev/null +++ b/server/data/data-memory/src/test/java/org/apache/james/droplists/memory/MemoryDropListTest.java @@ -0,0 +1,39 @@ +/**************************************************************** + * 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.droplists.memory; + +import org.apache.james.droplists.api.DropList; +import org.apache.james.droplists.api.DropListContract; +import org.junit.jupiter.api.BeforeEach; + +class MemoryDropListTest implements DropListContract { + + DropList dropList; + + @BeforeEach + void setup() { + dropList = new MemoryDropList(); + } + + @Override + public DropList dropList() { + return dropList; + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org