Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alts registration per server #248

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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. <br>
* <br>
* 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
* <br>
* 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<R extends Record14<
Expand Down Expand Up @@ -111,7 +110,11 @@ public Field<ScopeType> scopeType() {
}

public Field<UUID> uuid() {
return Objects.requireNonNull(fieldSupplier.field11(), "uuid field does not exist");
return fieldSupplier.field11();
}

public Field<NetworkAddress> address() {
return fieldSupplier.field12();
}

}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -19,6 +19,7 @@

package space.arim.libertybans.core.selector;

import java.util.List;
import java.util.Set;

import space.arim.dazzleconf.annote.ConfComments;
Expand Down Expand Up @@ -138,4 +139,29 @@ interface AltAccountExpiration {
long expirationTimeDays();

}

SnakeAmazing marked this conversation as resolved.
Show resolved Hide resolved
@ConfKey("alts-registry")
@SubSection
AltsRegistry altsRegistry();

@ConfHeader({"Controls if all servers should register the IP address of the player connecting."})
interface AltsRegistry {

@ConfComments({"The server names in this list will be excluded from associating the IP address of the player connecting.",
"Please note that the server's name in the list should be the same as the ones in your proxy configuration.",
"This is intended to be used by LibertyBans proxy installations.",
"If you are planning to use this feature, you MUST enable the 'enforce-server-switch' option."
})
@ConfKey("servers-without-ip-registration")
@DefaultStrings({"auth"})
List<String> 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, ",
"set this to false and add the authentication servers' names in the list above.",
"If this is a backend server, set it to false; if it's an authentication server, set to true."})
@ConfKey("should-register-on-connection)")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove ) in should-register-on-connection)

@ConfDefault.DefaultBoolean(true)
boolean shouldRegisterOnConnection();
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -22,6 +22,8 @@
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jooq.DSLContext;
import space.arim.libertybans.api.NetworkAddress;
import space.arim.libertybans.api.PunishmentType;
import space.arim.libertybans.api.punish.Punishment;
Expand Down Expand Up @@ -74,14 +76,17 @@ CentralisedFuture<Component> executeAndCheckConnection(UUID uuid, String name, N
Set<ServerScope> scopes, SelectorImpl selector) {
return queryExecutor.get().queryWithRetry((context, transaction) -> {
Instant currentTime = time.currentTimestamp();
EnforcementConfig config = configs.getMainConfig().enforcement();

Association association = new Association(uuid, context);
association.associateCurrentName(name, currentTime);
association.associateCurrentAddress(address, currentTime);
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) {
Expand Down Expand Up @@ -113,4 +118,42 @@ CentralisedFuture<Component> executeAndCheckConnection(UUID uuid, String name, N
return futuresFactory.completedFuture(null);
});
}

public CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, String name, NetworkAddress address,
String destinationServer, ServerScope serverScope,
SelectorImpl selector) {
if (!configs.getMainConfig().platforms().proxies().enforceServerSwitch()) {
return null;
}

return queryExecutor.get().queryWithRetry((context, transaction) -> {
Instant currentTime = time.currentTimestamp();
var altsRegistry = configs.getMainConfig().enforcement().altsRegistry();
boolean registerOnConnection = altsRegistry.shouldRegisterOnConnection();
List<String> serversWithoutAssociation = altsRegistry.serversWithoutRegistration();

if (!registerOnConnection && !serversWithoutAssociation.contains(destinationServer)) {
doAssociation(uuid, name, address, currentTime, context);
}

return selector.selectionByApplicabilityBuilder(uuid, address)
.type(PunishmentType.BAN)
.scopes(SelectionPredicate.matchingOnly(serverScope))
.build()
.findFirstSpecificPunishment(context, () -> currentTime, SortPunishments.LATEST_END_DATE_FIRST);
}).thenCompose((punishment) -> {
if (punishment != null) {
return formatter.getPunishmentMessage(punishment);
} else {
return futuresFactory.completedFuture(null);
}
});
}

private void doAssociation(UUID uuid, String name, NetworkAddress address,
Instant currentTime, DSLContext context) {
Association association = new Association(uuid, context);
association.associateCurrentName(name, currentTime);
association.associateCurrentAddress(address, currentTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public interface Guardian {
* Adds the uuid and name to the local fast cache, queries for an applicable ban, and formats the
* ban reason as the punishment message.
*
* @param uuid the player's uuid
* @param name the player's name
* @param address the player's network address
* @param uuid the player's uuid
* @param name the player's name
* @param address the player's network address
* @return a future which yields the punishment message if denied, else null if allowed
*/
CentralisedFuture<@Nullable Component> executeAndCheckConnection(UUID uuid, String name, NetworkAddress address);
Expand All @@ -63,11 +63,27 @@ public interface Guardian {
* Queries for an applicable ban, and formats the ban reason as the punishment message.
*
* @param uuid the player's uuid
* @param name the player's name
* @param address the player's network address
* @param destinationServer the player's destination server
* @return a future which yields the punishment message if denied, else null if allowed
*/
CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, InetAddress address, String destinationServer);
CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, String name, NetworkAddress address, String destinationServer);

/**
* Enforces a server switch, returning a punishment message if denied, null if allowed. <br>
* <br>
* Queries for an applicable ban, and formats the ban reason as the punishment message.
*
* @param uuid the player's uuid
* @param name the player's name
* @param address the player's network address
* @param destinationServer the player's destination server
* @return a future which yields the punishment message if denied, else null if allowed
*/
default CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, String name, InetAddress address, String destinationServer) {
return checkServerSwitch(uuid, name, NetworkAddress.of(address), destinationServer);
}

/**
* Enforces a chat message or executed command, returning a punishment message if denied, null if allowed. <br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import space.arim.libertybans.api.NetworkAddress;
import space.arim.libertybans.api.PunishmentType;
import space.arim.libertybans.api.scope.ScopeManager;
import space.arim.libertybans.api.select.SortPunishments;
import space.arim.libertybans.core.config.Configs;
import space.arim.libertybans.core.config.InternalFormatter;
import space.arim.libertybans.core.selector.cache.MuteCache;
import space.arim.libertybans.core.uuid.UUIDManager;
import space.arim.omnibus.util.concurrent.CentralisedFuture;
import space.arim.omnibus.util.concurrent.FactoryOfTheFuture;

import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -98,23 +95,12 @@ private static <R> Function<Throwable, R> timeoutHandler(String where) {
}

@Override
public CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, InetAddress address,
public CentralisedFuture<@Nullable Component> checkServerSwitch(UUID uuid, String name, NetworkAddress address,
String destinationServer) {
if (!configs.getMainConfig().platforms().proxies().enforceServerSwitch()) {
return futuresFactory.completedFuture(null);
}
return selector
.selectionByApplicabilityBuilder(uuid, address)
.type(PunishmentType.BAN)
.scope(scopeManager.specificScope(destinationServer))
.build()
.getFirstSpecificPunishment(SortPunishments.LATEST_END_DATE_FIRST)
.thenCompose((punishment) -> {
if (punishment.isEmpty()) {
return futuresFactory.completedFuture(null);
}
return formatter.getPunishmentMessage(punishment.get());
})
.executeAndCheckServerSwitch(
uuid, name, address, destinationServer
)
.toCompletableFuture()
.orTimeout(12, TimeUnit.SECONDS)
.exceptionally(timeoutHandler("server switch"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,26 @@ public interface InternalSelector extends PunishmentSelector {
/**
* Checks a player connection's in a single connection query, enforcing any applicable bans,
* connection limits, and dealing out alt checks
*
* @param uuid the player uuid
* @param name the player name
* @param address the player address
* @param scopes the server scopes to include in the selection query
*
* @param uuid the player uuid
* @param name the player name
* @param address the player address
* @param scopes the server scopes to include in the selection query
* @return a future which yields the denial message, or null if there is none
*/
CentralisedFuture<Component> executeAndCheckConnection(UUID uuid, String name, NetworkAddress address,
Set<ServerScope> scopes);

/**
* Checks a player connection's in a single connection query, enforcing any applicable bans if
* enforce server switch is enabled.
*
* @param uuid the player uuid
* @param name the player name
* @param address the player address
* @param destinationServer the server the player is switching to
* @return a future which yields the denial message, or null if there is none
*/
CentralisedFuture<Component> executeAndCheckServerSwitch(UUID uuid, String name, NetworkAddress address,
String destinationServer);
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
);
}

Expand All @@ -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;
}

}
Loading
Loading