From 8ad0d65276702f4810e5113430bbed9f69f8843a Mon Sep 17 00:00:00 2001 From: A248 Date: Mon, 4 Mar 2024 19:01:27 -0500 Subject: [PATCH] Accomodate unrecorded IP address in applicability calculation Plus a couple tweaks to code style --- .../api/select/PunishmentSelector.java | 5 +- .../database/sql/ApplicableViewFields.java | 9 ++- .../core/selector/EnforcementConfig.java | 6 +- .../libertybans/core/selector/Gatekeeper.java | 13 ++-- .../SelectionByApplicabilityBuilderImpl.java | 18 ++++- .../SelectionByApplicabilityImpl.java | 75 ++++++++++++++++++- 6 files changed, 108 insertions(+), 18 deletions(-) diff --git a/bans-api/src/main/java/space/arim/libertybans/api/select/PunishmentSelector.java b/bans-api/src/main/java/space/arim/libertybans/api/select/PunishmentSelector.java index 0e2bb1c44..bf6709bfa 100644 --- a/bans-api/src/main/java/space/arim/libertybans/api/select/PunishmentSelector.java +++ b/bans-api/src/main/java/space/arim/libertybans/api/select/PunishmentSelector.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2022 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -77,6 +77,9 @@ public interface PunishmentSelector { * the player's UUID and IP. It may be, the player's IP is banned, the player's UUID is banned, * or the player has played on a banned IP and that IP is banned while strict address enforcement is enabled.
*
+ * For maximum accuracy, the UUID and IP address pair should be taken from an actual user who has logged on before. + * Due to specifics in terms of how applicability is computed, some punishments + *
* By default, the server's configured address strictness is used, but this may be changed if desired. * * @param uuid the uuid of the user for whom to select applicable punishments diff --git a/bans-core/src/main/java/space/arim/libertybans/core/database/sql/ApplicableViewFields.java b/bans-core/src/main/java/space/arim/libertybans/core/database/sql/ApplicableViewFields.java index 2933361c4..9f42c0a15 100644 --- a/bans-core/src/main/java/space/arim/libertybans/core/database/sql/ApplicableViewFields.java +++ b/bans-core/src/main/java/space/arim/libertybans/core/database/sql/ApplicableViewFields.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2023 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -31,7 +31,6 @@ import space.arim.libertybans.core.scope.ScopeType; import java.time.Instant; -import java.util.Objects; import java.util.UUID; public record ApplicableViewFields scopeType() { } public Field uuid() { - return Objects.requireNonNull(fieldSupplier.field11(), "uuid field does not exist"); + return fieldSupplier.field11(); + } + + public Field address() { + return fieldSupplier.field12(); } } diff --git a/bans-core/src/main/java/space/arim/libertybans/core/selector/EnforcementConfig.java b/bans-core/src/main/java/space/arim/libertybans/core/selector/EnforcementConfig.java index 348e4a823..98aeb8981 100644 --- a/bans-core/src/main/java/space/arim/libertybans/core/selector/EnforcementConfig.java +++ b/bans-core/src/main/java/space/arim/libertybans/core/selector/EnforcementConfig.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2022 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -152,8 +152,8 @@ interface AltsRegistry { "If you are planning to use this feature, make sure to have enabled 'enforce-server-switch' option." }) @ConfKey("servers-without-ip-registration") - @DefaultStrings("") - List servers(); + @DefaultStrings({"auth"}) + List serversWithoutRegistration(); @ConfComments({"If you want to register the IP address of the player connecting, set this to true.", "If you are running a Proxy and don't want to register the IP when players connect, ", diff --git a/bans-core/src/main/java/space/arim/libertybans/core/selector/Gatekeeper.java b/bans-core/src/main/java/space/arim/libertybans/core/selector/Gatekeeper.java index 210a4df50..40c44999b 100644 --- a/bans-core/src/main/java/space/arim/libertybans/core/selector/Gatekeeper.java +++ b/bans-core/src/main/java/space/arim/libertybans/core/selector/Gatekeeper.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2023 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -78,13 +78,15 @@ CentralisedFuture executeAndCheckConnection(UUID uuid, String name, N Instant currentTime = time.currentTimestamp(); EnforcementConfig config = configs.getMainConfig().enforcement(); - if (config.altsRegistry().shouldRegisterOnConnection()) { + boolean recordUserAssociation = config.altsRegistry().shouldRegisterOnConnection(); + if (recordUserAssociation) { doAssociation(uuid, name, address, currentTime, context); } Punishment ban = selector.selectionByApplicabilityBuilder(uuid, address) .type(PunishmentType.BAN) .scopes(SelectionPredicate.matchingAnyOf(scopes)) + .canAssumeUserRecorded(recordUserAssociation) .build() .findFirstSpecificPunishment(context, () -> currentTime, SortPunishments.LATEST_END_DATE_FIRST); if (ban != null) { @@ -124,10 +126,11 @@ CentralisedFuture executeAndCheckConnection(UUID uuid, String name, N } return queryExecutor.get().queryWithRetry((context, transaction) -> { - boolean shouldRegister = configs.getMainConfig().enforcement().altsRegistry().shouldRegisterOnConnection(); - List servers = configs.getMainConfig().enforcement().altsRegistry().servers(); + var altsRegistry = configs.getMainConfig().enforcement().altsRegistry(); + boolean registerOnConnection = altsRegistry.shouldRegisterOnConnection(); + List serversWithoutAssociation = altsRegistry.serversWithoutRegistration(); - if (!shouldRegister && !servers.contains(destinationServer)) { + if (!registerOnConnection && !serversWithoutAssociation.contains(destinationServer)) { doAssociation(uuid, name, address, time.currentTimestamp(), context); } diff --git a/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityBuilderImpl.java b/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityBuilderImpl.java index cbb0f6747..26551cb4b 100644 --- a/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityBuilderImpl.java +++ b/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityBuilderImpl.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2023 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -39,6 +39,7 @@ public final class SelectionByApplicabilityBuilderImpl private final NetworkAddress address; private AddressStrictness strictness; private final AddressStrictness defaultStrictness; + private boolean potentialNewEntrant; SelectionByApplicabilityBuilderImpl(SelectionResources resources, UUID uuid, NetworkAddress address, AddressStrictness defaultStrictness) { @@ -68,7 +69,7 @@ SelectionByApplicabilityBuilder yieldSelf() { @Override SelectionByApplicability buildWith(SelectionBaseImpl.Details details) { return new SelectionByApplicabilityImpl( - details, resources, uuid, address, strictness + details, resources, uuid, address, strictness, potentialNewEntrant ); } @@ -89,4 +90,17 @@ public SelectionByApplicabilityImpl build() { return (SelectionByApplicabilityImpl) super.build(); } + /** + * For internal use, to accomodate a new user whose UUID and IP address combination + * has not yet been entered to the database. Sets whether this scenario is potential. + * + * @param canAssumeUserRecorded if the user is definitely registered, set to true. True by default. + * @return this builder + */ + public SelectionByApplicabilityBuilderImpl canAssumeUserRecorded(boolean canAssumeUserRecorded) { + // A user is a potential new entrant if we can't assume they're recorded + this.potentialNewEntrant = !canAssumeUserRecorded; + return this; + } + } diff --git a/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityImpl.java b/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityImpl.java index 1fc9c87a9..3e95db55d 100644 --- a/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityImpl.java +++ b/bans-core/src/main/java/space/arim/libertybans/core/selector/SelectionByApplicabilityImpl.java @@ -1,6 +1,6 @@ /* * LibertyBans - * Copyright © 2023 Anand Beh + * Copyright © 2024 Anand Beh * * LibertyBans is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -38,6 +38,7 @@ import java.util.UUID; import static org.jooq.impl.DSL.inline; +import static org.jooq.impl.DSL.or; import static space.arim.libertybans.core.schema.tables.StrictLinks.STRICT_LINKS; public final class SelectionByApplicabilityImpl extends SelectionBaseSQL implements SelectionByApplicability { @@ -45,13 +46,16 @@ public final class SelectionByApplicabilityImpl extends SelectionBaseSQL impleme private final UUID uuid; private final NetworkAddress address; private final AddressStrictness strictness; + private final boolean potentialNewEntrant; SelectionByApplicabilityImpl(Details details, SelectionResources resources, - UUID uuid, NetworkAddress address, AddressStrictness strictness) { + UUID uuid, NetworkAddress address, AddressStrictness strictness, + boolean potentialNewEntrant) { super(details, resources); this.uuid = Objects.requireNonNull(uuid, "uuid"); this.address = Objects.requireNonNull(address, "address"); this.strictness = Objects.requireNonNull(strictness, "strictness"); + this.potentialNewEntrant = potentialNewEntrant; } @Override @@ -69,6 +73,16 @@ public AddressStrictness getAddressStrictness() { return strictness; } + /* + If the potentialNewEntrant flag is set, our task becomes significantly more complicated. + We now have to account for the enforcement of non-lenient address strictness by acting as if + the user had their IP address recorded already. This means attaching additional predication + to scans for IP-based punishments and patching over the applicability links we are accustomed + to having the database account for. + + Thanks to SnakeAmazing for providing this marvelous intellectual puzzle. + */ + @Override Query requestQuery(QueryParameters parameters) { PunishmentFields fields = null; @@ -83,21 +97,74 @@ Query requestQuery(QueryParameters parameters) { ApplicableViewFields applView = requestApplicableView(); fields = applView; table = fields.table(); + if (potentialNewEntrant) { + // appl.uuid = uuid + // OR victim_type != 'PLAYER' AND victim_address = address + // + // Note: It must be victim_address and not appl.address + // Exercise for understanding: Why? + // + yield or( + applView.uuid().eq(uuid), + applView.victimType().notEqual(inline(VictimType.PLAYER)) + .and(applView.victimAddress().eq(address)) + ); + } yield applView.uuid().eq(uuid); // appl.uuid = uuid } case STERN -> { + if (potentialNewEntrant) { + ApplicableViewFields applView = requestApplicableView(); + fields = applView; + table = fields + .table() + .leftJoin(STRICT_LINKS) + .on(applView.uuid().eq(STRICT_LINKS.UUID1)); + // appl.uuid = uuid # NORMAL + // OR victim_type != 'PLAYER' AND ( + // appl.address = address # NORMAL + STERN + // OR strict_links.uuid2 IS NOT NULL AND strict_links.uuid2 = uuid # STERN + // ) + // + // Note: The last predicate must use appl.address and not victim_address + // Exercise for understanding: Why? + // + yield or( + applView.uuid().eq(uuid), + applView.victimType().notEqual(inline(VictimType.PLAYER)).and(or( + STRICT_LINKS.UUID2.isNotNull().and(STRICT_LINKS.UUID2.eq(uuid)), + applView.address().eq(address) + )) + ); + } ApplicableViewFields applView = requestApplicableView(); fields = applView; table = fields .table() .innerJoin(STRICT_LINKS) .on(applView.uuid().eq(STRICT_LINKS.UUID1)); - // appl.uuid = strict_links.uuid1 = uuid - // OR victim_type != 'PLAYER' AND strict_links.uuid2 = uuid + // appl.uuid = strict_links.uuid1 = uuid # NORMAL + // OR victim_type != 'PLAYER' AND strict_links.uuid2 = uuid # STERN yield STRICT_LINKS.UUID1.eq(uuid).or( STRICT_LINKS.UUID2.eq(uuid).and(applView.victimType().notEqual(inline(VictimType.PLAYER)))); } case STRICT -> { + if (potentialNewEntrant) { + ApplicableViewFields applView = requestApplicableView(); + fields = applView; + table = fields + .table() + .leftJoin(STRICT_LINKS) + .on(applView.uuid().eq(STRICT_LINKS.UUID1)); + // appl.uuid = uuid # NORMAL + // OR appl.address = address # NORMAL + STRICT + // OR strict_links.uuid2 IS NOT NULL AND strict_links.uuid2 = uuid # STRICT + yield or( + applView.uuid().eq(uuid), + applView.address().eq(address), + STRICT_LINKS.UUID2.isNotNull().and(STRICT_LINKS.UUID2.eq(uuid)) + ); + } ApplicableViewFields applView = requestApplicableView(); fields = applView; table = fields