diff --git a/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java b/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java new file mode 100644 index 00000000..1ab96689 --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java @@ -0,0 +1,32 @@ +package fr.xephi.authme.events; + +import org.bukkit.entity.Player; + +/** + * Event fired when a player has been unregistered. + */ +public abstract class AbstractUnregisterEvent extends CustomEvent { + + private final Player player; + + /** + * Constructor for a player that has unregistered himself. + * + * @param player the player + */ + public AbstractUnregisterEvent(Player player, boolean isAsync) { + super(isAsync); + this.player = player; + } + + /** + * Returns the player that has been unregistered. + *

+ * This may be {@code null}! Please refer to the implementations of this class for details. + * + * @return the unregistered player, or null + */ + public Player getPlayer() { + return player; + } +} diff --git a/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java b/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java new file mode 100644 index 00000000..f12e5116 --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java @@ -0,0 +1,67 @@ +package fr.xephi.authme.events; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Event fired after a player has been unregistered from an external source (by an admin or via the API). + *

+ * Note that only the {@code playerName} is guaranteed to be not {@code null} in any case. + *

+ * The {@code player} may be null if a name is supplied which has never been online on the server – + * due to migrations, data removal, etc. it is possible that a user exists in the database for which the + * server knows no {@link Player} object. + *

+ * If a player is unregistered via an API call, the {@code initiator} is null as the action has not been + * started by any {@link CommandSender}. Otherwise, the {@code initiator} is the user who performed the + * command to unregister the player name. + */ +public class UnregisterByAdminEvent extends AbstractUnregisterEvent { + + private static final HandlerList handlers = new HandlerList(); + private final String playerName; + private final CommandSender initiator; + + /** + * Constructor. + * + * @param player the player (may be null - see class JavaDoc) + * @param playerName the name of the player that was unregistered + * @param isAsync whether or not the event is async + * @param initiator the initiator of the unregister process (may be null - see class JavaDoc) + */ + public UnregisterByAdminEvent(Player player, String playerName, boolean isAsync, CommandSender initiator) { + super(player, isAsync); + this.playerName = playerName; + this.initiator = initiator; + } + + /** + * @return the name of the player that was unregistered + */ + public String getPlayerName() { + return playerName; + } + + /** + * @return the user who requested to unregister the name, or null if not applicable + */ + public CommandSender getInitiator() { + return initiator; + } + + /** + * Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link org.bukkit.event.Event}. + * + * @return The list of handlers + */ + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java b/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java new file mode 100644 index 00000000..bd28c50e --- /dev/null +++ b/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; + +/** + * Event fired after a player has unregistered himself. + */ +public class UnregisterByPlayerEvent extends AbstractUnregisterEvent { + + private static final HandlerList handlers = new HandlerList(); + + /** + * Constructor. + * + * @param player the player (never null) + */ + public UnregisterByPlayerEvent(Player player, boolean isAsync) { + super(player, isAsync); + } + + /** + * Return the list of handlers, equivalent to {@link #getHandlers()} and required by {@link org.bukkit.event.Event}. + * + * @return The list of handlers + */ + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java index eabd902e..f0332238 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -5,6 +5,8 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.UnregisterByAdminEvent; +import fr.xephi.authme.events.UnregisterByPlayerEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupType; @@ -66,6 +68,7 @@ public class AsynchronousUnregister implements AsynchronousProcess { if (dataSource.removeAuth(name)) { performUnregister(name, player); ConsoleLogger.info(name + " unregistered himself"); + bukkitService.createAndCallEvent(isAsync -> new UnregisterByPlayerEvent(player, isAsync)); } else { service.send(player, MessageKey.ERROR); } @@ -86,6 +89,8 @@ public class AsynchronousUnregister implements AsynchronousProcess { public void adminUnregister(CommandSender initiator, String name, Player player) { if (dataSource.removeAuth(name)) { performUnregister(name, player); + bukkitService.createAndCallEvent(isAsync -> new UnregisterByAdminEvent(player, name, isAsync, initiator)); + if (initiator == null) { ConsoleLogger.info(name + " was unregistered"); } else { diff --git a/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java index 26433a45..9ff0faee 100644 --- a/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java +++ b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java @@ -5,6 +5,7 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.AbstractUnregisterEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupType; @@ -19,10 +20,15 @@ import org.bukkit.entity.Player; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.function.Function; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -81,7 +87,7 @@ public class AsynchronousUnregisterTest { // then verify(service).send(player, MessageKey.WRONG_PASSWORD); verify(passwordSecurity).comparePassword(userPassword, password, name); - verifyZeroInteractions(dataSource, limboService, authGroupHandler, teleportationService); + verifyZeroInteractions(dataSource, limboService, authGroupHandler, teleportationService, bukkitService); verify(player, only()).getName(); } @@ -112,6 +118,7 @@ public class AsynchronousUnregisterTest { verify(teleportationService).teleportOnJoin(player); verify(authGroupHandler).setGroup(player, AuthGroupType.UNREGISTERED); verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Runnable.class)); + verifyCalledUnregisterEventFor(player); } @Test @@ -141,6 +148,7 @@ public class AsynchronousUnregisterTest { verify(teleportationService).teleportOnJoin(player); verify(authGroupHandler).setGroup(player, AuthGroupType.UNREGISTERED); verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Runnable.class)); + verifyCalledUnregisterEventFor(player); } @Test @@ -169,6 +177,7 @@ public class AsynchronousUnregisterTest { verify(authGroupHandler).setGroup(player, AuthGroupType.UNREGISTERED); verifyZeroInteractions(teleportationService, limboService); verify(bukkitService, never()).runTask(any(Runnable.class)); + verifyCalledUnregisterEventFor(player); } @Test @@ -218,6 +227,7 @@ public class AsynchronousUnregisterTest { verify(dataSource).removeAuth(name); verify(playerCache).removePlayer(name); verifyZeroInteractions(teleportationService, authGroupHandler); + verifyCalledUnregisterEventFor(player); } // Initiator known and Player object available @@ -242,6 +252,7 @@ public class AsynchronousUnregisterTest { verify(teleportationService).teleportOnJoin(player); verify(authGroupHandler).setGroup(player, AuthGroupType.UNREGISTERED); verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Runnable.class)); + verifyCalledUnregisterEventFor(player); } @Test @@ -257,6 +268,7 @@ public class AsynchronousUnregisterTest { verify(dataSource).removeAuth(name); verify(playerCache).removePlayer(name); verifyZeroInteractions(authGroupHandler, teleportationService); + verifyCalledUnregisterEventFor(null); } @Test @@ -272,6 +284,15 @@ public class AsynchronousUnregisterTest { // then verify(dataSource).removeAuth(name); verify(service).send(initiator, MessageKey.ERROR); - verifyZeroInteractions(playerCache, teleportationService, authGroupHandler); + verifyZeroInteractions(playerCache, teleportationService, authGroupHandler, bukkitService); + } + + @SuppressWarnings("unchecked") + private void verifyCalledUnregisterEventFor(Player player) { + ArgumentCaptor> eventFunctionCaptor = + ArgumentCaptor.forClass(Function.class); + verify(bukkitService).createAndCallEvent(eventFunctionCaptor.capture()); + AbstractUnregisterEvent event = eventFunctionCaptor.getValue().apply(true); + assertThat(event.getPlayer(), equalTo(player)); } }