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

Check restriction status only on cache #2358

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/main/java/fr/xephi/authme/data/auth/PlayerCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
public class PlayerCache {

private final Map<String, PlayerAuth> cache = new ConcurrentHashMap<>();
private final Map<String, RegistrationStatus> registeredCache = new ConcurrentHashMap<>();

PlayerCache() {
}
Expand All @@ -20,6 +21,7 @@ public class PlayerCache {
* @param auth the player auth object to save
*/
public void updatePlayer(PlayerAuth auth) {
registeredCache.put(auth.getNickname().toLowerCase(), RegistrationStatus.REGISTERED);
cache.put(auth.getNickname().toLowerCase(), auth);
}

Expand All @@ -30,6 +32,7 @@ public void updatePlayer(PlayerAuth auth) {
*/
public void removePlayer(String user) {
cache.remove(user.toLowerCase());
registeredCache.remove(user.toLowerCase());
}

/**
Expand All @@ -43,6 +46,35 @@ public boolean isAuthenticated(String user) {
return cache.containsKey(user.toLowerCase());
}

/**
* Add a registration entry to the cache for active use later like the player active playing.
*
* @param user player name
* @param status registration status
*/
public void addRegistrationStatus(String user, RegistrationStatus status) {
registeredCache.put(user.toLowerCase(), status);
}

/**
* Update the status for existing entries like currently active users
* @param user player name
* @param status newest query result
*/
public void updateRegistrationStatus(String user, RegistrationStatus status) {
registeredCache.replace(user, status);
}

/**
* Checks if there is cached result with the player having an account.
* <b>Warning: This shouldn't be used for authentication, because the result could be outdated.</b>
* @param user player name
* @return Cached result about being registered or unregistered and UNKNOWN if there is no cache entry
*/
public RegistrationStatus getRegistrationStatus(String user) {
return registeredCache.getOrDefault(user.toLowerCase(), RegistrationStatus.UNKNOWN);
}

/**
* Returns the PlayerAuth associated with the given user, if available.
*
Expand All @@ -66,8 +98,13 @@ public int getLogged() {
*
* @return all player auths inside the player cache
*/
public Map<String, PlayerAuth> getCache() {
public Map<String, PlayerAuth> getAuthCache() {
return this.cache;
}

public enum RegistrationStatus {
REGISTERED,
UNREGISTERED,
UNKNOWN
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ public List<PlayerAuth> getAllAuths() {

@Override
public List<String> getLoggedPlayersWithEmptyMail() {
return playerCache.getCache().values().stream()
return playerCache.getAuthCache().values().stream()
.filter(auth -> Utils.isEmailEmpty(auth.getEmail()))
.map(PlayerAuth::getRealName)
.collect(Collectors.toList());
Expand Down
37 changes: 22 additions & 15 deletions src/main/java/fr/xephi/authme/listener/ListenerService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.xephi.authme.listener;

import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.auth.PlayerCache.RegistrationStatus;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.service.ValidationService;
Expand All @@ -17,7 +18,7 @@
/**
* Service class for the AuthMe listeners to determine whether an event should be canceled.
*/
class ListenerService implements SettingsDependent {
public class ListenerService implements SettingsDependent {

private final DataSource dataSource;
private final PlayerCache playerCache;
Expand Down Expand Up @@ -77,28 +78,34 @@ public boolean shouldCancelEvent(PlayerEvent event) {
* @return true if the associated event should be canceled, false otherwise
*/
public boolean shouldCancelEvent(Player player) {
return player != null && !checkAuth(player.getName()) && !PlayerUtils.isNpc(player);
}

@Override
public void reload(Settings settings) {
isRegistrationForced = settings.getProperty(RegistrationSettings.FORCE);
return player != null && !PlayerUtils.isNpc(player) && shouldRestrictPlayer(player.getName());
}

/**
* Checks whether the player is allowed to perform actions (i.e. whether he is logged in
* or if other settings permit playing).
* Check if restriction are required for the given player name. The check will be performed against the local
* cache. This means changes from other sources like web services will have a delay to it.
*
* @param name the name of the player to verify
* @return true if the player may play, false otherwise
* @param name player name
* @return true if the player needs to be restricted
*/
private boolean checkAuth(String name) {
public boolean shouldRestrictPlayer(String name) {
if (validationService.isUnrestricted(name) || playerCache.isAuthenticated(name)) {
return true;
return false;
}
if (!isRegistrationForced && !dataSource.isAuthAvailable(name)) {

if (isRegistrationForced) {
// registration always required to play - so restrict everything
return true;
}
return false;

// registration not enforced, but registered players needs to be restricted if not logged in
// if there is no data fall back to safer alternative to prevent any leakage
final RegistrationStatus status = playerCache.getRegistrationStatus(name);
return status != RegistrationStatus.UNREGISTERED;
}

@Override
public void reload(Settings settings) {
isRegistrationForced = settings.getProperty(RegistrationSettings.FORCE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.StructureModifier;

import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.listener.ListenerService;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

class InventoryPacketAdapter extends PacketAdapter {

private static final int PLAYER_INVENTORY = 0;
Expand All @@ -49,13 +50,12 @@ class InventoryPacketAdapter extends PacketAdapter {
private static final int HOTBAR_SIZE = 9;

private final ConsoleLogger logger = ConsoleLoggerFactory.get(InventoryPacketAdapter.class);
private final PlayerCache playerCache;
private final DataSource dataSource;

InventoryPacketAdapter(AuthMe plugin, PlayerCache playerCache, DataSource dataSource) {
private final ListenerService listenerService;

InventoryPacketAdapter(AuthMe plugin, ListenerService listenerService) {
super(plugin, PacketType.Play.Server.SET_SLOT, PacketType.Play.Server.WINDOW_ITEMS);
this.playerCache = playerCache;
this.dataSource = dataSource;
this.listenerService = listenerService;
}

@Override
Expand All @@ -64,7 +64,7 @@ public void onPacketSending(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();

int windowId = packet.getIntegers().read(0);
if (windowId == PLAYER_INVENTORY && shouldHideInventory(player.getName())) {
if (windowId == PLAYER_INVENTORY && listenerService.shouldRestrictPlayer(player.getName())) {
packetEvent.setCancelled(true);
}
}
Expand All @@ -78,14 +78,10 @@ public void register(BukkitService bukkitService) {
ProtocolLibrary.getProtocolManager().addPacketListener(this);

bukkitService.getOnlinePlayers().stream()
.filter(player -> shouldHideInventory(player.getName()))
.filter(player -> listenerService.shouldRestrictPlayer(player.getName()))
.forEach(this::sendBlankInventoryPacket);
}

private boolean shouldHideInventory(String playerName) {
return !playerCache.isAuthenticated(playerName) && dataSource.isAuthAvailable(playerName);
}

public void unregister() {
ProtocolLibrary.getProtocolManager().removePacketListener(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package fr.xephi.authme.listener.protocollib;

import ch.jalu.injector.annotations.NoFieldScan;

import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.listener.ListenerService;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.entity.Player;

import javax.inject.Inject;

import org.bukkit.entity.Player;

@NoFieldScan
public class ProtocolLibService implements SettingsDependent {

Expand All @@ -31,16 +33,16 @@ public class ProtocolLibService implements SettingsDependent {
private boolean isEnabled;
private final AuthMe plugin;
private final BukkitService bukkitService;
private final ListenerService listenerService;
private final PlayerCache playerCache;
private final DataSource dataSource;

@Inject
ProtocolLibService(AuthMe plugin, Settings settings, BukkitService bukkitService, PlayerCache playerCache,
DataSource dataSource) {
ProtocolLibService(AuthMe plugin, Settings settings, BukkitService bukkitService, ListenerService listenerService,
PlayerCache playerCache) {
this.plugin = plugin;
this.bukkitService = bukkitService;
this.listenerService = listenerService;
this.playerCache = playerCache;
this.dataSource = dataSource;
reload(settings);
}

Expand All @@ -66,7 +68,7 @@ public void setup() {
if (protectInvBeforeLogin) {
if (inventoryPacketAdapter == null) {
// register the packet listener and start hiding it for all already online players (reload)
inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache, dataSource);
inventoryPacketAdapter = new InventoryPacketAdapter(plugin, listenerService);
inventoryPacketAdapter.register(bukkitService);
}
} else if (inventoryPacketAdapter != null) {
Expand All @@ -76,7 +78,7 @@ public void setup() {

if (denyTabCompleteBeforeLogin) {
if (tabCompletePacketAdapter == null) {
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache);
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, listenerService);
tabCompletePacketAdapter.register();
}
} else if (tabCompletePacketAdapter != null) {
Expand Down Expand Up @@ -118,8 +120,8 @@ public void sendBlankInventoryPacket(Player player) {
public void reload(Settings settings) {
boolean oldProtectInventory = this.protectInvBeforeLogin;

this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);

//it was true and will be deactivated now, so we need to restore the inventory for every player
if (oldProtectInventory && !protectInvBeforeLogin && inventoryPacketAdapter != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@
import com.comphenix.protocol.reflect.FieldAccessException;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.listener.ListenerService;
import fr.xephi.authme.output.ConsoleLoggerFactory;

class TabCompletePacketAdapter extends PacketAdapter {

private final ConsoleLogger logger = ConsoleLoggerFactory.get(TabCompletePacketAdapter.class);
private final PlayerCache playerCache;

TabCompletePacketAdapter(AuthMe plugin, PlayerCache playerCache) {
private final ListenerService listenerService;

TabCompletePacketAdapter(AuthMe plugin, ListenerService listenerService) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE);
this.playerCache = playerCache;
this.listenerService = listenerService;
}

@Override
public void onPacketReceiving(PacketEvent event) {
if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) {
try {
if (!playerCache.isAuthenticated(event.getPlayer().getName())) {
String playerName = event.getPlayer().getName();
if (listenerService.shouldRestrictPlayer(playerName)) {
event.setCancelled(true);
}
} catch (FieldAccessException e) {
Expand Down
30 changes: 20 additions & 10 deletions src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.ProxySessionManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.auth.PlayerCache.RegistrationStatus;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.ProtectInventoryEvent;
Expand Down Expand Up @@ -73,6 +75,9 @@ public class AsynchronousJoin implements AsynchronousProcess {
@Inject
private SessionService sessionService;

@Inject
private PlayerCache playerCache;

@Inject
private ProxySessionManager proxySessionManager;

Expand Down Expand Up @@ -112,17 +117,10 @@ public void processJoin(final Player player) {
}

final boolean isAuthAvailable = database.isAuthAvailable(name);

RegistrationStatus status = isAuthAvailable ? RegistrationStatus.REGISTERED : RegistrationStatus.UNREGISTERED;
playerCache.addRegistrationStatus(name, status);
if (isAuthAvailable) {
// Protect inventory
if (service.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) {
ProtectInventoryEvent ev = bukkitService.createAndCallEvent(
isAsync -> new ProtectInventoryEvent(player, isAsync));
if (ev.isCancelled()) {
player.updateInventory();
logger.fine("ProtectInventoryEvent has been cancelled for " + player.getName() + "...");
}
}
protectInventory(player);

// Session logic
if (sessionService.canResumeSession(player)) {
Expand Down Expand Up @@ -154,6 +152,18 @@ public void processJoin(final Player player) {
processJoinSync(player, isAuthAvailable);
}

private void protectInventory(Player player) {
// Protect inventory
if (service.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) {
ProtectInventoryEvent ev = bukkitService.createAndCallEvent(
isAsync -> new ProtectInventoryEvent(player, isAsync));
if (ev.isCancelled()) {
player.updateInventory();
logger.fine("ProtectInventoryEvent has been cancelled for " + player.getName() + "...");
}
}
}

private void handlePlayerWithUnmetNameRestriction(Player player, String ip) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR));
Expand Down
Loading