Remove all files

This commit is contained in:
HaHaWTH
2023-09-20 02:10:19 +08:00
parent 62f27f8231
commit ed320196fe
478 changed files with 0 additions and 94591 deletions
@@ -1,64 +0,0 @@
package fr.xephi.authme.command.executable;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.command.FoundCommandResult;
import fr.xephi.authme.command.FoundResultStatus;
import fr.xephi.authme.command.help.HelpProvider;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION;
/**
* Displays help information to a user.
*/
public class HelpCommand implements ExecutableCommand {
@Inject
private CommandMapper commandMapper;
@Inject
private HelpProvider helpProvider;
// Convention: arguments is not the actual invoked arguments but the command that was invoked,
// e.g. "/authme help register" would typically be arguments = [register], but here we pass [authme, register]
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, arguments);
FoundResultStatus resultStatus = result.getResultStatus();
if (MISSING_BASE_COMMAND.equals(resultStatus)) {
sender.sendMessage(ChatColor.DARK_RED + "Could not get base command");
return;
} else if (UNKNOWN_LABEL.equals(resultStatus)) {
if (result.getCommandDescription() == null) {
sender.sendMessage(ChatColor.DARK_RED + "Unknown command");
return;
} else {
sender.sendMessage(ChatColor.GOLD + "Assuming " + ChatColor.WHITE
+ CommandUtils.constructCommandPath(result.getCommandDescription()));
}
}
int mappedCommandLevel = result.getCommandDescription().getLabelCount();
if (mappedCommandLevel == 1) {
helpProvider.outputHelp(sender, result,
SHOW_COMMAND | SHOW_DESCRIPTION | SHOW_CHILDREN | SHOW_ALTERNATIVES);
} else {
helpProvider.outputHelp(sender, result, ALL_OPTIONS);
}
}
}
@@ -1,74 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
/**
* Shows all accounts registered by the same IP address for the given player name or IP address.
*/
public class AccountsCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private BukkitService bukkitService;
@Inject
private CommonService commonService;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// TODO #1366: last IP vs. registration IP?
final String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
// Assumption: a player name cannot contain '.'
if (playerName.contains(".")) {
bukkitService.runTaskAsynchronously(() -> {
List<String> accountList = dataSource.getAllAuthsByIp(playerName);
if (accountList.isEmpty()) {
sender.sendMessage("[AuthMe] This IP does not exist in the database.");
} else if (accountList.size() == 1) {
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
} else {
outputAccountsList(sender, playerName, accountList);
}
});
} else {
bukkitService.runTaskAsynchronously(() -> {
PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase(Locale.ROOT));
if (auth == null) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
return;
} else if (auth.getLastIp() == null) {
sender.sendMessage("No known last IP address for player");
return;
}
List<String> accountList = dataSource.getAllAuthsByIp(auth.getLastIp());
if (accountList.isEmpty()) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
} else if (accountList.size() == 1) {
sender.sendMessage("[AuthMe] " + playerName + " is a single account player");
} else {
outputAccountsList(sender, playerName, accountList);
}
});
}
}
private static void outputAccountsList(CommandSender sender, String playerName, List<String> accountList) {
sender.sendMessage("[AuthMe] " + playerName + " has " + accountList.size() + " accounts.");
String message = "[AuthMe] " + String.join(", ", accountList) + ".";
sender.sendMessage(message);
}
}
@@ -1,24 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import java.util.List;
/**
* AuthMe base command; shows the version and some command pointers.
*/
public class AuthMeCommand implements ExecutableCommand {
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.GREEN + "This server is running " + AuthMe.getPluginName() + " v"
+ AuthMe.getPluginVersion() + " b" + AuthMe.getPluginBuildNumber()+ "! " + ChatColor.RED + "<3");
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme help" + ChatColor.YELLOW
+ " to view help.");
sender.sendMessage(ChatColor.YELLOW + "Use the command " + ChatColor.GOLD + "/authme about" + ChatColor.YELLOW
+ " to view about.");
}
}
@@ -1,23 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BackupService.BackupCause;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Command to perform a backup.
*/
public class BackupCommand implements ExecutableCommand {
@Inject
private BackupService backupService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
backupService.doBackup(BackupCause.COMMAND, sender);
}
}
@@ -1,41 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Admin command for changing a player's password.
*/
public class ChangePasswordAdminCommand implements ExecutableCommand {
@Inject
private ValidationService validationService;
@Inject
private CommonService commonService;
@Inject
private Management management;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the player and password
final String playerName = arguments.get(0);
final String playerPass = arguments.get(1);
// Validate the password
ValidationResult validationResult = validationService.validatePassword(playerPass, playerName);
if (validationResult.hasError()) {
commonService.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
} else {
management.performPasswordChangeAsAdmin(sender, playerName, playerPass);
}
}
}
@@ -1,95 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.Factory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSortedMap;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.converter.Converter;
import fr.xephi.authme.datasource.converter.CrazyLoginConverter;
import fr.xephi.authme.datasource.converter.LoginSecurityConverter;
import fr.xephi.authme.datasource.converter.MySqlToSqlite;
import fr.xephi.authme.datasource.converter.RakamakConverter;
import fr.xephi.authme.datasource.converter.RoyalAuthConverter;
import fr.xephi.authme.datasource.converter.SqliteToSql;
import fr.xephi.authme.datasource.converter.VAuthConverter;
import fr.xephi.authme.datasource.converter.XAuthConverter;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Converter command: launches conversion based on its parameters.
*/
public class ConverterCommand implements ExecutableCommand {
@VisibleForTesting
static final Map<String, Class<? extends Converter>> CONVERTERS = getConverters();
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConverterCommand.class);
@Inject
private CommonService commonService;
@Inject
private BukkitService bukkitService;
@Inject
private Factory<Converter> converterFactory;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
Class<? extends Converter> converterClass = getConverterClassFromArgs(arguments);
if (converterClass == null) {
sender.sendMessage("Converters: " + String.join(", ", CONVERTERS.keySet()));
return;
}
// Get the proper converter instance
final Converter converter = converterFactory.newInstance(converterClass);
// Run the convert job
bukkitService.runTaskAsynchronously(() -> {
try {
converter.execute(sender);
} catch (Exception e) {
commonService.send(sender, MessageKey.ERROR);
logger.logException("Error during conversion:", e);
}
});
// Show a status message
sender.sendMessage("[AuthMe] Successfully started " + arguments.get(0));
}
private static Class<? extends Converter> getConverterClassFromArgs(List<String> arguments) {
return arguments.isEmpty()
? null
: CONVERTERS.get(arguments.get(0).toLowerCase(Locale.ROOT));
}
/**
* Initializes a map with all available converters.
*
* @return map with all available converters
*/
private static Map<String, Class<? extends Converter>> getConverters() {
return ImmutableSortedMap.<String, Class<? extends Converter>>naturalOrder()
.put("xauth", XAuthConverter.class)
.put("crazylogin", CrazyLoginConverter.class)
.put("rakamak", RakamakConverter.class)
.put("royalauth", RoyalAuthConverter.class)
.put("vauth", VAuthConverter.class)
.put("sqlitetosql", SqliteToSql.class)
.put("mysqltosqlite", MySqlToSqlite.class)
.put("loginsecurity", LoginSecurityConverter.class)
.build();
}
}
@@ -1,36 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.TeleportUtils;
import org.bukkit.entity.Player;
import fr.xephi.authme.AuthMe;
import javax.inject.Inject;
import java.util.List;
/**
* Teleports the player to the first spawn.
*/
public class FirstSpawnCommand extends PlayerCommand {
@Inject
private Settings settings;
@Inject
private SpawnLoader spawnLoader;
@Override
public void runCommand(Player player, List<String> arguments) {
if (spawnLoader.getFirstSpawn() == null) {
player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn");
} else {
//String name= player.getName();
if(AuthMe.SATEnabled) {
TeleportUtils.teleport(player, spawnLoader.getFirstSpawn());
} else {
player.teleport(spawnLoader.getFirstSpawn());
}
//player.teleport(spawnLoader.getFirstSpawn());
}
}
}
@@ -1,44 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.permission.PlayerPermission.CAN_LOGIN_BE_FORCED;
/**
* Forces the login of a player, i.e. logs the player in without the need of a (correct) password.
*/
public class ForceLoginCommand implements ExecutableCommand {
@Inject
private PermissionsManager permissionsManager;
@Inject
private Management management;
@Inject
private BukkitService bukkitService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the player query
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
Player player = bukkitService.getPlayerExact(playerName);
if (player == null || !player.isOnline()) {
sender.sendMessage("Player needs to be online!");
} else if (!permissionsManager.hasPermission(player, CAN_LOGIN_BE_FORCED)) {
sender.sendMessage("You cannot force login the player " + playerName + "!");
} else {
management.forceLogin(player);
sender.sendMessage("Force login for " + playerName + " performed!");
}
}
}
@@ -1,35 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import ch.jalu.datasourcecolumns.data.DataSourceValue;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Returns a player's email.
*/
public class GetEmailCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
DataSourceValue<String> email = dataSource.getEmail(playerName);
if (email.rowExists()) {
sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue());
} else {
commonService.send(sender, MessageKey.UNKNOWN_USER);
}
}
}
@@ -1,41 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
public class GetIpCommand implements ExecutableCommand {
@Inject
private BukkitService bukkitService;
@Inject
private DataSource dataSource;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String playerName = arguments.get(0);
Player player = bukkitService.getPlayerExact(playerName);
PlayerAuth auth = dataSource.getAuth(playerName);
if (player != null) {
sender.sendMessage("Current IP of " + player.getName() + " is " + PlayerUtils.getPlayerIp(player)
+ ":" + player.getAddress().getPort());
}
if (auth == null) {
String displayName = player == null ? playerName : player.getName();
sender.sendMessage(displayName + " is not registered in the database");
} else {
sender.sendMessage("Database: last IP: " + auth.getLastIp() + ", registration IP: "
+ auth.getRegistrationIp());
}
}
}
@@ -1,56 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Date;
import java.util.List;
/**
* Returns the last login date of the given user.
*/
public class LastLoginCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the player
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth == null) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
return;
}
// Get the last login date
final Long lastLogin = auth.getLastLogin();
final String lastLoginDate = lastLogin == null ? "never" : new Date(lastLogin).toString();
// Show the player status
sender.sendMessage("[AuthMe] " + playerName + " last login: " + lastLoginDate);
if (lastLogin != null) {
sender.sendMessage("[AuthMe] The player " + playerName + " last logged in "
+ createLastLoginIntervalMessage(lastLogin) + " ago");
}
sender.sendMessage("[AuthMe] Last player's IP: " + auth.getLastIp());
}
private static String createLastLoginIntervalMessage(long lastLogin) {
final long diff = System.currentTimeMillis() - lastLogin;
return (int) (diff / 86400000) + " days "
+ (int) (diff / 3600000 % 24) + " hours "
+ (int) (diff / 60000 % 60) + " mins "
+ (int) (diff / 1000 % 60) + " secs";
}
}
@@ -1,38 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.task.purge.PurgeService;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* Command for purging data of banned players. Depending on the settings
* it purges (deletes) data from third-party plugins as well.
*/
public class PurgeBannedPlayersCommand implements ExecutableCommand {
@Inject
private PurgeService purgeService;
@Inject
private BukkitService bukkitService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the list of banned players
Set<OfflinePlayer> bannedPlayers = bukkitService.getBannedPlayers();
Set<String> namedBanned = new HashSet<>(bannedPlayers.size());
for (OfflinePlayer offlinePlayer : bannedPlayers) {
namedBanned.add(offlinePlayer.getName().toLowerCase(Locale.ROOT));
}
purgeService.purgePlayers(sender, namedBanned, bannedPlayers.toArray(new OfflinePlayer[bannedPlayers.size()]));
}
}
@@ -1,51 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import com.google.common.primitives.Ints;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.task.purge.PurgeService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Calendar;
import java.util.List;
/**
* Command for purging the data of players which have not been online for a given number
* of days. Depending on the settings, this removes player data in third-party plugins as well.
*/
public class PurgeCommand implements ExecutableCommand {
private static final int MINIMUM_LAST_SEEN_DAYS = 30;
@Inject
private PurgeService purgeService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Get the days parameter
String daysStr = arguments.get(0);
// Convert the days string to an integer value, and make sure it's valid
Integer days = Ints.tryParse(daysStr);
if (days == null) {
sender.sendMessage(ChatColor.RED + "The value you've entered is invalid!");
return;
}
// Validate the value
if (days < MINIMUM_LAST_SEEN_DAYS) {
sender.sendMessage(ChatColor.RED + "You can only purge data older than "
+ MINIMUM_LAST_SEEN_DAYS + " days");
return;
}
// Create a calender instance to determine the date
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -days);
long until = calendar.getTimeInMillis();
// Run the purge
purgeService.runPurge(sender, until);
}
}
@@ -1,56 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Removes the stored last position of a user or of all.
*/
public class PurgeLastPositionCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0);
if ("*".equals(playerName)) {
for (PlayerAuth auth : dataSource.getAllAuths()) {
resetLastPosition(auth);
dataSource.updateQuitLoc(auth);
// TODO: send an update when a messaging service will be implemented (QUITLOC)
}
sender.sendMessage("All players last position locations are now reset");
} else {
// Get the user auth and make sure the user exists
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth == null) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
return;
}
resetLastPosition(auth);
dataSource.updateQuitLoc(auth);
// TODO: send an update when a messaging service will be implemented (QUITLOC)
sender.sendMessage(playerName + "'s last position location is now reset");
}
}
private static void resetLastPosition(PlayerAuth auth) {
auth.setQuitLocX(0d);
auth.setQuitLocY(0d);
auth.setQuitLocZ(0d);
auth.setWorld("world");
}
}
@@ -1,47 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.task.purge.PurgeExecutor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
import static java.util.Collections.singletonList;
/**
* Command to purge a player.
*/
public class PurgePlayerCommand implements ExecutableCommand {
@Inject
private PurgeExecutor purgeExecutor;
@Inject
private BukkitService bukkitService;
@Inject
private DataSource dataSource;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String option = arguments.size() > 1 ? arguments.get(1) : null;
bukkitService.runTaskAsynchronously(
() -> executeCommand(sender, arguments.get(0), option));
}
private void executeCommand(CommandSender sender, String name, String option) {
if ("force".equals(option) || !dataSource.isAuthAvailable(name)) {
OfflinePlayer offlinePlayer = bukkitService.getOfflinePlayer(name);
purgeExecutor.executePurge(singletonList(offlinePlayer), singletonList(name.toLowerCase(Locale.ROOT)));
sender.sendMessage("Purged data for player " + name);
} else {
sender.sendMessage("This player is still registered! Are you sure you want to proceed? "
+ "Use '/authme purgeplayer " + name + " force' to run the command anyway");
}
}
}
@@ -1,55 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static java.time.Instant.ofEpochMilli;
/**
* Command showing the most recent logged in players.
*/
public class RecentPlayersCommand implements ExecutableCommand {
/** DateTime formatter, producing Strings such as "10:42 AM, 11 Jul". */
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("hh:mm a, dd MMM");
@Inject
private DataSource dataSource;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Recently logged in players");
for (PlayerAuth auth : recentPlayers) {
sender.sendMessage(formatPlayerMessage(auth));
}
}
@VisibleForTesting
ZoneId getZoneId() {
return ZoneId.systemDefault();
}
private String formatPlayerMessage(PlayerAuth auth) {
String lastLoginText;
if (auth.getLastLogin() == null) {
lastLoginText = "never";
} else {
LocalDateTime lastLogin = LocalDateTime.ofInstant(ofEpochMilli(auth.getLastLogin()), getZoneId());
lastLoginText = DATE_FORMAT.format(lastLogin);
}
return "- " + auth.getRealName() + " (" + lastLoginText + " with IP " + auth.getLastIp() + ")";
}
}
@@ -1,85 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
/**
* Admin command to register a user.
*/
public class RegisterAdminCommand implements ExecutableCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterAdminCommand.class);
@Inject
private PasswordSecurity passwordSecurity;
@Inject
private CommonService commonService;
@Inject
private DataSource dataSource;
@Inject
private BukkitService bukkitService;
@Inject
private ValidationService validationService;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name and password
final String playerName = arguments.get(0);
final String playerPass = arguments.get(1);
final String playerNameLowerCase = playerName.toLowerCase(Locale.ROOT);
// Command logic
ValidationResult passwordValidation = validationService.validatePassword(playerPass, playerName);
if (passwordValidation.hasError()) {
commonService.send(sender, passwordValidation.getMessageKey(), passwordValidation.getArgs());
return;
}
bukkitService.runTaskOptionallyAsync(() -> {
if (dataSource.isAuthAvailable(playerNameLowerCase)) {
commonService.send(sender, MessageKey.NAME_ALREADY_REGISTERED);
return;
}
HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase);
PlayerAuth auth = PlayerAuth.builder()
.name(playerNameLowerCase)
.realName(playerName)
.password(hashedPassword)
.registrationDate(System.currentTimeMillis())
.build();
if (!dataSource.saveAuth(auth)) {
commonService.send(sender, MessageKey.ERROR);
return;
}
commonService.send(sender, MessageKey.REGISTER_SUCCESS);
logger.info(sender.getName() + " registered " + playerName);
final Player player = bukkitService.getPlayerExact(playerName);
if (player != null) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
}
});
}
}
@@ -1,77 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsWarner;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* The reload command.
*/
public class ReloadCommand implements ExecutableCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ReloadCommand.class);
@Inject
private AuthMe plugin;
@Inject
private Settings settings;
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Inject
private SettingsWarner settingsWarner;
@Inject
private SingletonStore<Reloadable> reloadableStore;
@Inject
private SingletonStore<SettingsDependent> settingsDependentStore;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
try {
settings.reload();
ConsoleLoggerFactory.reloadSettings(settings);
settingsWarner.logWarningsForMisconfigurations();
// We do not change database type for consistency issues, but we'll output a note in the logs
if (!settings.getProperty(DatabaseSettings.BACKEND).equals(dataSource.getType())) {
Utils.logAndSendMessage(sender, "Note: cannot change database type during /authme reload");
}
performReloadOnServices();
commonService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
} catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting");
logger.logException("Aborting! Encountered exception during reload of AuthMe:", e);
plugin.stopOrUnload();
}
}
private void performReloadOnServices() {
reloadableStore.retrieveAllOfType()
.forEach(r -> r.reload());
settingsDependentStore.retrieveAllOfType()
.forEach(s -> s.reload(settings));
}
}
@@ -1,78 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Admin command for setting an email to an account.
*/
public class SetEmailCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Inject
private PlayerCache playerCache;
@Inject
private BukkitService bukkitService;
@Inject
private ValidationService validationService;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name and email address
final String playerName = arguments.get(0);
final String playerEmail = arguments.get(1);
// Validate the email address
if (!validationService.validateEmail(playerEmail)) {
commonService.send(sender, MessageKey.INVALID_EMAIL);
return;
}
bukkitService.runTaskOptionallyAsync(new Runnable() {
@Override
public void run() {
// Validate the user
PlayerAuth auth = dataSource.getAuth(playerName);
if (auth == null) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
return;
} else if (!validationService.isEmailFreeForRegistration(playerEmail, sender)) {
commonService.send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR);
return;
}
// Set the email address
auth.setEmail(playerEmail);
if (!dataSource.updateEmail(auth)) {
commonService.send(sender, MessageKey.ERROR);
return;
}
// Update the player cache
if (playerCache.getAuth(playerName) != null) {
playerCache.updatePlayer(auth);
}
// Show a status message
commonService.send(sender, MessageKey.EMAIL_CHANGED_SUCCESS);
}
});
}
}
@@ -1,23 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.settings.SpawnLoader;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
public class SetFirstSpawnCommand extends PlayerCommand {
@Inject
private SpawnLoader spawnLoader;
@Override
public void runCommand(Player player, List<String> arguments) {
if (spawnLoader.setFirstSpawn(player.getLocation())) {
player.sendMessage("[AuthMe] Correctly defined new first spawn point");
} else {
player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry");
}
}
}
@@ -1,23 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.settings.SpawnLoader;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
public class SetSpawnCommand extends PlayerCommand {
@Inject
private SpawnLoader spawnLoader;
@Override
public void runCommand(Player player, List<String> arguments) {
if (spawnLoader.setSpawn(player.getLocation())) {
player.sendMessage("[AuthMe] Correctly defined new spawn point");
} else {
player.sendMessage("[AuthMe] SetSpawn has failed, please retry");
}
}
}
@@ -1,23 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.settings.SpawnLoader;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
public class SpawnCommand extends PlayerCommand {
@Inject
private SpawnLoader spawnLoader;
@Override
public void runCommand(Player player, List<String> arguments) {
if (spawnLoader.getSpawn() == null) {
player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn");
} else {
player.teleport(spawnLoader.getSpawn());
}
}
}
@@ -1,52 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.command.FoundCommandResult;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.service.AntiBotService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
/**
* Display or change the status of the antibot mod.
*/
public class SwitchAntiBotCommand implements ExecutableCommand {
@Inject
private AntiBotService antiBotService;
@Inject
private CommandMapper commandMapper;
@Inject
private HelpProvider helpProvider;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
if (arguments.isEmpty()) {
sender.sendMessage("[AuthMe] AntiBot status: " + antiBotService.getAntiBotStatus().name());
return;
}
String newState = arguments.get(0);
// Enable or disable the mod
if ("ON".equalsIgnoreCase(newState)) {
antiBotService.overrideAntiBotStatus(true);
sender.sendMessage("[AuthMe] AntiBot Manual Override: enabled!");
} else if ("OFF".equalsIgnoreCase(newState)) {
antiBotService.overrideAntiBotStatus(false);
sender.sendMessage("[AuthMe] AntiBot Manual Override: disabled!");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Invalid AntiBot mode!");
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Arrays.asList("authme", "antibot"));
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_ARGUMENTS);
sender.sendMessage(ChatColor.GOLD + "Detailed help: " + ChatColor.WHITE + "/authme help antibot");
}
}
}
@@ -1,61 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command to disable two-factor authentication for a user.
*/
public class TotpDisableAdminCommand implements ExecutableCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TotpDisableAdminCommand.class);
@Inject
private DataSource dataSource;
@Inject
private Messages messages;
@Inject
private BukkitService bukkitService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String player = arguments.get(0);
PlayerAuth auth = dataSource.getAuth(player);
if (auth == null) {
messages.send(sender, MessageKey.UNKNOWN_USER);
} else if (auth.getTotpKey() == null) {
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does not have two-factor auth enabled");
} else {
removeTotpKey(sender, player);
}
}
private void removeTotpKey(CommandSender sender, String player) {
if (dataSource.removeTotpKey(player)) {
sender.sendMessage("Disabled two-factor authentication successfully for '" + player + "'");
logger.info(sender.getName() + " disable two-factor authentication for '" + player + "'");
Player onlinePlayer = bukkitService.getPlayerExact(player);
if (onlinePlayer != null) {
messages.send(onlinePlayer, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
}
} else {
messages.send(sender, MessageKey.ERROR);
}
}
}
@@ -1,38 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Command to see whether a user has enabled two-factor authentication.
*/
public class TotpViewStatusCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private Messages messages;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String player = arguments.get(0);
PlayerAuth auth = dataSource.getAuth(player);
if (auth == null) {
messages.send(sender, MessageKey.UNKNOWN_USER);
} else if (auth.getTotpKey() == null) {
sender.sendMessage(ChatColor.RED + "Player '" + player + "' does NOT have two-factor auth enabled");
} else {
sender.sendMessage(ChatColor.DARK_GREEN + "Player '" + player + "' has enabled two-factor authentication");
}
}
}
@@ -1,49 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Admin command to unregister a player.
*/
public class UnregisterAdminCommand implements ExecutableCommand {
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Inject
private BukkitService bukkitService;
@Inject
private Management management;
UnregisterAdminCommand() {
}
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
String playerName = arguments.get(0);
// Make sure the user exists
if (!dataSource.isAuthAvailable(playerName)) {
commonService.send(sender, MessageKey.UNKNOWN_USER);
return;
}
// Get the player from the server and perform unregister
Player target = bukkitService.getPlayerExact(playerName);
management.performUnregisterByAdmin(sender, playerName, target);
}
}
@@ -1,39 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.command.help.HelpMessagesService;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.HelpTranslationGenerator;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Messages command, updates the user's help messages file with any missing files
* from the provided file in the JAR.
*/
public class UpdateHelpMessagesCommand implements ExecutableCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(UpdateHelpMessagesCommand.class);
@Inject
private HelpTranslationGenerator helpTranslationGenerator;
@Inject
private HelpMessagesService helpMessagesService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
try {
File updatedFile = helpTranslationGenerator.updateHelpFile();
sender.sendMessage("Successfully updated the help file '" + updatedFile.getName() + "'");
helpMessagesService.reloadMessagesFile();
} catch (IOException e) {
sender.sendMessage("Could not update help file: " + e.getMessage());
logger.logException("Could not update help file:", e);
}
}
}
@@ -1,94 +0,0 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class VersionCommand implements ExecutableCommand {
@Inject
private BukkitService bukkitService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
// Show some version info
sender.sendMessage(ChatColor.GOLD + "==========[ " + AuthMe.getPluginName() + " ABOUT ]==========");
sender.sendMessage(ChatColor.GOLD + "Version: " + ChatColor.WHITE + AuthMe.getPluginName()
+ " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")");
sender.sendMessage(ChatColor.GOLD + "Authors:");
Collection<Player> onlinePlayers = bukkitService.getOnlinePlayers();
printDeveloper(sender, "Gabriele C.", "sgdc3", "Project manager, Contributor", onlinePlayers);
printDeveloper(sender, "Lucas J.", "ljacqu", "Main Developer", onlinePlayers);
printDeveloper(sender, "games647", "games647", "Developer", onlinePlayers);
printDeveloper(sender, "Hex3l", "Hex3l", "Developer", onlinePlayers);
printDeveloper(sender, "krusic22", "krusic22", "Support", onlinePlayers);
sender.sendMessage(ChatColor.GOLD + "Retired authors:");
printDeveloper(sender, "Alexandre Vanhecke", "xephi59", "Original Author", onlinePlayers);
printDeveloper(sender, "Gnat008", "gnat008", "Developer, Retired", onlinePlayers);
printDeveloper(sender, "DNx5", "DNx5", "Developer, Retired", onlinePlayers);
printDeveloper(sender, "Tim Visee", "timvisee", "Developer, Retired", onlinePlayers);
sender.sendMessage(ChatColor.GOLD + "Website: " + ChatColor.WHITE
+ "https://github.com/AuthMe/AuthMeReloaded");
sender.sendMessage(ChatColor.GOLD + "License: " + ChatColor.WHITE + "GNU GPL v3.0"
+ ChatColor.GRAY + ChatColor.ITALIC + " (See LICENSE file)");
sender.sendMessage(ChatColor.GOLD + "Copyright: " + ChatColor.WHITE
+ "Copyright (c) AuthMe-Team " + new SimpleDateFormat("yyyy").format(new Date())
+ ". Released under GPL v3 License.");
}
/**
* Print a developer with proper styling.
*
* @param sender The command sender
* @param name The display name of the developer
* @param minecraftName The Minecraft username of the developer, if available
* @param function The function of the developer
* @param onlinePlayers The list of online players
*/
private static void printDeveloper(CommandSender sender, String name, String minecraftName, String function,
Collection<Player> onlinePlayers) {
// Print the name
StringBuilder msg = new StringBuilder();
msg.append(" ")
.append(ChatColor.WHITE)
.append(name);
// Append the Minecraft name
msg.append(ChatColor.GRAY).append(" // ").append(ChatColor.WHITE).append(minecraftName);
msg.append(ChatColor.GRAY).append(ChatColor.ITALIC).append(" (").append(function).append(")");
// Show the online status
if (isPlayerOnline(minecraftName, onlinePlayers)) {
msg.append(ChatColor.GREEN).append(ChatColor.ITALIC).append(" (In-Game)");
}
// Print the message
sender.sendMessage(msg.toString());
}
/**
* Check whether a player is online.
*
* @param minecraftName The Minecraft player name
* @param onlinePlayers List of online players
*
* @return True if the player is online, false otherwise
*/
private static boolean isPlayerOnline(String minecraftName, Collection<Player> onlinePlayers) {
for (Player player : onlinePlayers) {
if (player.getName().equalsIgnoreCase(minecraftName)) {
return true;
}
}
return false;
}
}
@@ -1,88 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.service.GeoIpService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.properties.ProtectionSettings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.regex.Pattern;
/**
* Shows the GeoIP information as returned by the geoIpService.
*/
class CountryLookup implements DebugSection {
private static final Pattern IS_IP_ADDR = Pattern.compile("(\\d{1,3}\\.){3}\\d{1,3}");
@Inject
private GeoIpService geoIpService;
@Inject
private DataSource dataSource;
@Inject
private ValidationService validationService;
@Override
public String getName() {
return "cty";
}
@Override
public String getDescription() {
return "Check country protection / country data";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe country lookup");
if (arguments.isEmpty()) {
sender.sendMessage("Check player: /authme debug cty Bobby");
sender.sendMessage("Check IP address: /authme debug cty 127.123.45.67");
return;
}
String argument = arguments.get(0);
if (IS_IP_ADDR.matcher(argument).matches()) {
outputInfoForIpAddr(sender, argument);
} else {
outputInfoForPlayer(sender, argument);
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.COUNTRY_LOOKUP;
}
private void outputInfoForIpAddr(CommandSender sender, String ipAddr) {
sender.sendMessage("IP '" + ipAddr + "' maps to country '" + geoIpService.getCountryCode(ipAddr)
+ "' (" + geoIpService.getCountryName(ipAddr) + ")");
if (validationService.isCountryAdmitted(ipAddr)) {
sender.sendMessage(ChatColor.DARK_GREEN + "This IP address' country is not blocked");
} else {
sender.sendMessage(ChatColor.DARK_RED + "This IP address' country is blocked from the server");
}
sender.sendMessage("Note: if " + ProtectionSettings.ENABLE_PROTECTION + " is false no country is blocked");
}
// TODO #1366: Extend with registration IP?
private void outputInfoForPlayer(CommandSender sender, String name) {
PlayerAuth auth = dataSource.getAuth(name);
if (auth == null) {
sender.sendMessage("No player with name '" + name + "'");
} else if (auth.getLastIp() == null) {
sender.sendMessage("No last IP address known for '" + name + "'");
} else {
sender.sendMessage("Player '" + name + "' has IP address " + auth.getLastIp());
outputInfoForIpAddr(sender, auth.getLastIp());
}
}
}
@@ -1,81 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
/**
* Fetches various statistics, particularly regarding in-memory data that is stored.
*/
class DataStatistics implements DebugSection {
@Inject
private PlayerCache playerCache;
@Inject
private LimboService limboService;
@Inject
private DataSource dataSource;
@Inject
private SingletonStore<Object> singletonStore;
@Override
public String getName() {
return "stats";
}
@Override
public String getDescription() {
return "Outputs general data statistics";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe statistics");
sender.sendMessage("LimboPlayers in memory: " + applyToLimboPlayersMap(limboService, Map::size));
sender.sendMessage("PlayerCache size: " + playerCache.getLogged() + " (= logged in players)");
outputDatabaseStats(sender);
outputInjectorStats(sender);
sender.sendMessage("Total logger instances: " + ConsoleLoggerFactory.getTotalLoggers());
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.DATA_STATISTICS;
}
private void outputDatabaseStats(CommandSender sender) {
sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered());
if (dataSource instanceof CacheDataSource) {
CacheDataSource cacheDataSource = (CacheDataSource) this.dataSource;
sender.sendMessage("Cached PlayerAuth objects: " + cacheDataSource.getCachedAuths().size());
}
}
private void outputInjectorStats(CommandSender sender) {
sender.sendMessage("Singleton Java classes: " + singletonStore.retrieveAllOfType().size());
sender.sendMessage(String.format("(Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
singletonStore.retrieveAllOfType(Reloadable.class).size(),
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
}
}
@@ -1,85 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.injector.factory.Factory;
import com.google.common.collect.ImmutableSet;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Debug command main.
*/
public class DebugCommand implements ExecutableCommand {
private static final Set<Class<? extends DebugSection>> SECTION_CLASSES = ImmutableSet.of(
PermissionGroups.class, DataStatistics.class, CountryLookup.class, PlayerAuthViewer.class, InputValidator.class,
LimboPlayerViewer.class, CountryLookup.class, HasPermissionChecker.class, TestEmailSender.class,
SpawnLocationViewer.class, MySqlDefaultChanger.class);
@Inject
private Factory<DebugSection> debugSectionFactory;
@Inject
private PermissionsManager permissionsManager;
private Map<String, DebugSection> sections;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
DebugSection debugSection = findDebugSection(arguments);
if (debugSection == null) {
sendAvailableSections(sender);
} else {
executeSection(debugSection, sender, arguments);
}
}
private DebugSection findDebugSection(List<String> arguments) {
if (arguments.isEmpty()) {
return null;
}
return getSections().get(arguments.get(0).toLowerCase(Locale.ROOT));
}
private void sendAvailableSections(CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "AuthMe debug utils");
sender.sendMessage("Sections available to you:");
long availableSections = getSections().values().stream()
.filter(section -> permissionsManager.hasPermission(sender, section.getRequiredPermission()))
.peek(e -> sender.sendMessage("- " + e.getName() + ": " + e.getDescription()))
.count();
if (availableSections == 0) {
sender.sendMessage(ChatColor.RED + "You don't have permission to view any debug section");
}
}
private void executeSection(DebugSection section, CommandSender sender, List<String> arguments) {
if (permissionsManager.hasPermission(sender, section.getRequiredPermission())) {
section.execute(sender, arguments.subList(1, arguments.size()));
} else {
sender.sendMessage(ChatColor.RED + "You don't have permission for this section. See /authme debug");
}
}
// Lazy getter
private Map<String, DebugSection> getSections() {
if (sections == null) {
Map<String, DebugSection> sections = new TreeMap<>();
for (Class<? extends DebugSection> sectionClass : SECTION_CLASSES) {
DebugSection section = debugSectionFactory.newInstance(sectionClass);
sections.put(section.getName(), section);
}
this.sections = sections;
}
return sections;
}
}
@@ -1,36 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.permission.PermissionNode;
import org.bukkit.command.CommandSender;
import java.util.List;
/**
* A debug section: "child" command of the debug command.
*/
interface DebugSection {
/**
* @return the name to get to this child command
*/
String getName();
/**
* @return short description of the child command
*/
String getDescription();
/**
* Executes the debug child command.
*
* @param sender the sender executing the command
* @param arguments the arguments, without the label of the child command
*/
void execute(CommandSender sender, List<String> arguments);
/**
* @return permission required to run this section
*/
PermissionNode getRequiredPermission();
}
@@ -1,130 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import org.bukkit.Location;
import java.lang.reflect.Field;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
/**
* Utilities used within the DebugSection implementations.
*/
final class DebugSectionUtils {
private static ConsoleLogger logger = ConsoleLoggerFactory.get(DebugSectionUtils.class);
private static Field limboEntriesField;
private DebugSectionUtils() {
}
/**
* Formats the given location in a human readable way. Null-safe.
*
* @param location the location to format
* @return the formatted location
*/
static String formatLocation(Location location) {
if (location == null) {
return "null";
}
String worldName = location.getWorld() == null ? "null" : location.getWorld().getName();
return formatLocation(location.getX(), location.getY(), location.getZ(), worldName);
}
/**
* Formats the given location in a human readable way.
*
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @param world the world name
* @return the formatted location
*/
static String formatLocation(double x, double y, double z, String world) {
return "(" + round(x) + ", " + round(y) + ", " + round(z) + ") in '" + world + "'";
}
/**
* Rounds the given number to two decimals.
*
* @param number the number to round
* @return the rounded number
*/
private static String round(double number) {
DecimalFormat df = new DecimalFormat("#.##");
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(number);
}
private static Field getLimboPlayerEntriesField() {
if (limboEntriesField == null) {
try {
Field field = LimboService.class.getDeclaredField("entries");
field.setAccessible(true);
limboEntriesField = field;
} catch (Exception e) {
logger.logException("Could not retrieve LimboService entries field:", e);
}
}
return limboEntriesField;
}
/**
* Applies the given function to the map in LimboService containing the LimboPlayers.
* As we don't want to expose this information in non-debug settings, this is done with reflection.
* Exceptions are generously caught and {@code null} is returned on failure.
*
* @param limboService the limbo service instance to get the map from
* @param function the function to apply to the map
* @param <U> the result type of the function
*
* @return the value of the function applied to the map, or null upon error
*/
static <U> U applyToLimboPlayersMap(LimboService limboService, Function<Map, U> function) {
Field limboPlayerEntriesField = getLimboPlayerEntriesField();
if (limboPlayerEntriesField != null) {
try {
return function.apply((Map) limboEntriesField.get(limboService));
} catch (Exception e) {
logger.logException("Could not retrieve LimboService values:", e);
}
}
return null;
}
static <T> T castToTypeOrNull(Object object, Class<T> clazz) {
return clazz.isInstance(object) ? clazz.cast(object) : null;
}
/**
* Unwraps the "cache data source" and returns the underlying source. Returns the
* same as the input argument otherwise.
*
* @param dataSource the data source to unwrap if applicable
* @return the non-cache data source
*/
static DataSource unwrapSourceFromCacheDataSource(DataSource dataSource) {
if (dataSource instanceof CacheDataSource) {
try {
Field source = CacheDataSource.class.getDeclaredField("source");
source.setAccessible(true);
return (DataSource) source.get(dataSource);
} catch (NoSuchFieldException | IllegalAccessException e) {
logger.logException("Could not get source of CacheDataSource:", e);
return null;
}
}
return dataSource;
}
}
@@ -1,138 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import com.google.common.collect.ImmutableList;
import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.DefaultPermission;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
/**
* Checks if a player has a given permission, as checked by AuthMe.
*/
class HasPermissionChecker implements DebugSection {
static final List<Class<? extends PermissionNode>> PERMISSION_NODE_CLASSES = ImmutableList.of(
AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class, DebugSectionPermissions.class);
@Inject
private PermissionsManager permissionsManager;
@Inject
private BukkitService bukkitService;
@Override
public String getName() {
return "perm";
}
@Override
public String getDescription() {
return "Checks if a player has a given permission";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe permission check");
if (arguments.size() < 2) {
sender.sendMessage("Check if a player has permission:");
sender.sendMessage("Example: /authme debug perm bobby my.perm.node");
sender.sendMessage("Permission system type used: " + permissionsManager.getPermissionSystem());
return;
}
final String playerName = arguments.get(0);
final String permissionNode = arguments.get(1);
Player player = bukkitService.getPlayerExact(playerName);
if (player == null) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
if (offlinePlayer == null) {
sender.sendMessage(ChatColor.DARK_RED + "Player '" + playerName + "' does not exist");
} else {
sender.sendMessage("Player '" + playerName + "' not online; checking with offline player");
performPermissionCheck(offlinePlayer, permissionNode, permissionsManager::hasPermissionOffline, sender);
}
} else {
performPermissionCheck(player, permissionNode, permissionsManager::hasPermission, sender);
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.HAS_PERMISSION_CHECK;
}
/**
* Performs a permission check and informs the given sender of the result. {@code permissionChecker} is the
* permission check to perform with the given {@code node} and the {@code player}.
*
* @param player the player to check a permission for
* @param node the node of the permission to check
* @param permissionChecker permission checking function
* @param sender the sender to inform of the result
* @param <P> the player type
*/
private static <P extends OfflinePlayer> void performPermissionCheck(
P player, String node, BiFunction<P, PermissionNode, Boolean> permissionChecker, CommandSender sender) {
PermissionNode permNode = getPermissionNode(sender, node);
if (permissionChecker.apply(player, permNode)) {
sender.sendMessage(ChatColor.DARK_GREEN + "Success: player '" + player.getName()
+ "' has permission '" + node + "'");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Check failed: player '" + player.getName()
+ "' does NOT have permission '" + node + "'");
}
}
/**
* Based on the given permission node (String), tries to find the according AuthMe {@link PermissionNode}
* instance, or creates a new one if not available.
*
* @param sender the sender (used to inform him if no AuthMe PermissionNode can be matched)
* @param node the node to search for
* @return the node as {@link PermissionNode} object
*/
private static PermissionNode getPermissionNode(CommandSender sender, String node) {
Optional<? extends PermissionNode> permNode = PERMISSION_NODE_CLASSES.stream()
.map(Class::getEnumConstants)
.flatMap(Arrays::stream)
.filter(perm -> perm.getNode().equals(node))
.findFirst();
if (permNode.isPresent()) {
return permNode.get();
} else {
sender.sendMessage("Did not detect AuthMe permission; using default permission = DENIED");
return createPermNode(node);
}
}
private static PermissionNode createPermNode(String node) {
return new PermissionNode() {
@Override
public String getNode() {
return node;
}
@Override
public DefaultPermission getDefaultPermission() {
return DefaultPermission.NOT_ALLOWED;
}
};
}
}
@@ -1,124 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.listener.FailedVerificationException;
import fr.xephi.authme.listener.OnJoinVerifier;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.MAIL;
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.NAME;
import static fr.xephi.authme.command.executable.authme.debug.InputValidator.ValidationObject.PASS;
/**
* Checks if a sample username, email or password is valid according to the AuthMe settings.
*/
class InputValidator implements DebugSection {
@Inject
private ValidationService validationService;
@Inject
private Messages messages;
@Inject
private OnJoinVerifier onJoinVerifier;
@Override
public String getName() {
return "valid";
}
@Override
public String getDescription() {
return "Checks if your config.yml allows a password / email";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
if (arguments.size() < 2 || !ValidationObject.matchesAny(arguments.get(0))) {
displayUsageHint(sender);
} else if (PASS.matches(arguments.get(0))) {
validatePassword(sender, arguments.get(1));
} else if (MAIL.matches(arguments.get(0))) {
validateEmail(sender, arguments.get(1));
} else if (NAME.matches(arguments.get(0))) {
validateUsername(sender, arguments.get(1));
} else {
throw new IllegalStateException("Unexpected validation object with arg[0] = '" + arguments.get(0) + "'");
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.INPUT_VALIDATOR;
}
private void displayUsageHint(CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "Validation tests");
sender.sendMessage("You can define forbidden emails and passwords in your config.yml."
+ " You can test your settings with this command.");
final String command = ChatColor.GOLD + "/authme debug valid";
sender.sendMessage(" Use " + command + " pass <pass>" + ChatColor.RESET + " to check a password");
sender.sendMessage(" Use " + command + " mail <mail>" + ChatColor.RESET + " to check an email");
sender.sendMessage(" Use " + command + " name <name>" + ChatColor.RESET + " to check a username");
}
private void validatePassword(CommandSender sender, String password) {
ValidationResult validationResult = validationService.validatePassword(password, "");
sender.sendMessage(ChatColor.BLUE + "Validation of password '" + password + "'");
if (validationResult.hasError()) {
messages.send(sender, validationResult.getMessageKey(), validationResult.getArgs());
} else {
sender.sendMessage(ChatColor.DARK_GREEN + "Valid password!");
}
}
private void validateEmail(CommandSender sender, String email) {
boolean isValidEmail = validationService.validateEmail(email);
sender.sendMessage(ChatColor.BLUE + "Validation of email '" + email + "'");
if (isValidEmail) {
sender.sendMessage(ChatColor.DARK_GREEN + "Valid email!");
} else {
sender.sendMessage(ChatColor.DARK_RED + "Email is not valid!");
}
}
private void validateUsername(CommandSender sender, String username) {
sender.sendMessage(ChatColor.BLUE + "Validation of username '" + username + "'");
try {
onJoinVerifier.checkIsValidName(username);
sender.sendMessage("Valid username!");
} catch (FailedVerificationException failedVerificationEx) {
messages.send(sender, failedVerificationEx.getReason(), failedVerificationEx.getArgs());
}
}
enum ValidationObject {
PASS, MAIL, NAME;
static boolean matchesAny(String arg) {
return Arrays.stream(values()).anyMatch(vo -> vo.matches(arg));
}
boolean matches(String arg) {
return name().equalsIgnoreCase(arg);
}
}
}
@@ -1,143 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.data.limbo.persistence.LimboPersistence;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
/**
* Shows the data stored in LimboPlayers and the equivalent properties on online players.
*/
class LimboPlayerViewer implements DebugSection {
@Inject
private LimboService limboService;
@Inject
private LimboPersistence limboPersistence;
@Inject
private BukkitService bukkitService;
@Inject
private PermissionsManager permissionsManager;
@Override
public String getName() {
return "limbo";
}
@Override
public String getDescription() {
return "View LimboPlayers and player's \"limbo stats\"";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
if (arguments.isEmpty()) {
sender.sendMessage(ChatColor.BLUE + "AuthMe limbo viewer");
sender.sendMessage("/authme debug limbo <player>: show a player's limbo info");
sender.sendMessage("Available limbo records: " + applyToLimboPlayersMap(limboService, Map::keySet));
return;
}
LimboPlayer memoryLimbo = limboService.getLimboPlayer(arguments.get(0));
Player player = bukkitService.getPlayerExact(arguments.get(0));
LimboPlayer diskLimbo = player != null ? limboPersistence.getLimboPlayer(player) : null;
if (memoryLimbo == null && player == null) {
sender.sendMessage(ChatColor.BLUE + "No AuthMe limbo data");
sender.sendMessage("No limbo data and no player online with name '" + arguments.get(0) + "'");
return;
}
sender.sendMessage(ChatColor.BLUE + "Player / limbo / disk limbo info for '" + arguments.get(0) + "'");
new InfoDisplayer(sender, player, memoryLimbo, diskLimbo)
.sendEntry("Is op", Player::isOp, LimboPlayer::isOperator)
.sendEntry("Walk speed", Player::getWalkSpeed, LimboPlayer::getWalkSpeed)
.sendEntry("Can fly", Player::getAllowFlight, LimboPlayer::isCanFly)
.sendEntry("Fly speed", Player::getFlySpeed, LimboPlayer::getFlySpeed)
.sendEntry("Location", p -> formatLocation(p.getLocation()), l -> formatLocation(l.getLocation()))
.sendEntry("Prim. group",
p -> permissionsManager.hasGroupSupport() ? permissionsManager.getPrimaryGroup(p) : "N/A",
LimboPlayer::getGroups);
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.LIMBO_PLAYER_VIEWER;
}
/**
* Displays the info for the given LimboPlayer and Player to the provided CommandSender.
*/
private static final class InfoDisplayer {
private final CommandSender sender;
private final Optional<Player> player;
private final Optional<LimboPlayer> memoryLimbo;
private final Optional<LimboPlayer> diskLimbo;
/**
* Constructor.
*
* @param sender command sender to send the information to
* @param player the player to get data from
* @param memoryLimbo the limbo player to get data from
*/
InfoDisplayer(CommandSender sender, Player player, LimboPlayer memoryLimbo, LimboPlayer diskLimbo) {
this.sender = sender;
this.player = Optional.ofNullable(player);
this.memoryLimbo = Optional.ofNullable(memoryLimbo);
this.diskLimbo = Optional.ofNullable(diskLimbo);
if (memoryLimbo == null) {
sender.sendMessage("Note: no Limbo information available");
}
if (player == null) {
sender.sendMessage("Note: player is not online");
} else if (diskLimbo == null) {
sender.sendMessage("Note: no Limbo on disk available");
}
}
/**
* Displays a piece of information to the command sender.
*
* @param title the designation of the piece of information
* @param playerGetter getter for data retrieval on Player
* @param limboGetter getter for data retrieval on the LimboPlayer
* @param <T> the data type
* @return this instance (for chaining)
*/
<T> InfoDisplayer sendEntry(String title,
Function<Player, T> playerGetter,
Function<LimboPlayer, T> limboGetter) {
sender.sendMessage(
title + ": "
+ getData(player, playerGetter)
+ " / "
+ getData(memoryLimbo, limboGetter)
+ " / "
+ getData(diskLimbo, limboGetter));
return this;
}
static <E, T> String getData(Optional<E> entity, Function<E, T> getter) {
return entity.map(getter).map(String::valueOf).orElse(" -- ");
}
}
}
@@ -1,327 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.configme.properties.Property;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.castToTypeOrNull;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.unwrapSourceFromCacheDataSource;
import static fr.xephi.authme.data.auth.PlayerAuth.DB_EMAIL_DEFAULT;
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_IP_DEFAULT;
import static fr.xephi.authme.data.auth.PlayerAuth.DB_LAST_LOGIN_DEFAULT;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getColumnDefaultValue;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.isNotNullColumn;
import static java.lang.String.format;
/**
* Convenience command to add or remove the default value of a column and its nullable status
* in the MySQL data source.
*/
class MySqlDefaultChanger implements DebugSection {
private static final String NOT_NULL_SUFFIX = ChatColor.DARK_AQUA + "@" + ChatColor.RESET;
private static final String DEFAULT_VALUE_SUFFIX = ChatColor.GOLD + "#" + ChatColor.RESET;
private ConsoleLogger logger = ConsoleLoggerFactory.get(MySqlDefaultChanger.class);
@Inject
private Settings settings;
@Inject
private DataSource dataSource;
private MySQL mySql;
@PostConstruct
void setMySqlField() {
this.mySql = castToTypeOrNull(unwrapSourceFromCacheDataSource(this.dataSource), MySQL.class);
}
@Override
public String getName() {
return "mysqldef";
}
@Override
public String getDescription() {
return "Add or remove the default value of MySQL columns";
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.MYSQL_DEFAULT_CHANGER;
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
if (mySql == null) {
sender.sendMessage("Defaults can be changed for the MySQL data source only.");
return;
}
Operation operation = matchToEnum(arguments, 0, Operation.class);
Columns column = matchToEnum(arguments, 1, Columns.class);
if (operation == Operation.DETAILS) {
showColumnDetails(sender);
} else if (operation == null || column == null) {
displayUsageHints(sender);
} else {
sender.sendMessage(ChatColor.BLUE + "[AuthMe] MySQL change '" + column + "'");
try (Connection con = getConnection(mySql)) {
switch (operation) {
case ADD:
changeColumnToNotNullWithDefault(sender, column, con);
break;
case REMOVE:
removeNotNullAndDefault(sender, column, con);
break;
default:
throw new IllegalStateException("Unknown operation '" + operation + "'");
}
} catch (SQLException | IllegalStateException e) {
logger.logException("Failed to perform MySQL default altering operation:", e);
}
}
}
/**
* Adds a default value to the column definition and adds a {@code NOT NULL} constraint for
* the specified column.
*
* @param sender the command sender initiation the action
* @param column the column to modify
* @param con connection to the database
* @throws SQLException .
*/
private void changeColumnToNotNullWithDefault(CommandSender sender, Columns column,
Connection con) throws SQLException {
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
final String columnName = settings.getProperty(column.getColumnNameProperty());
// Replace NULLs with future default value
String sql = format("UPDATE %s SET %s = ? WHERE %s IS NULL;", tableName, columnName, columnName);
int updatedRows;
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setObject(1, column.getDefaultValue());
updatedRows = pst.executeUpdate();
}
sender.sendMessage("Replaced NULLs with default value ('" + column.getDefaultValue()
+ "'), modifying " + updatedRows + " entries");
// Change column definition to NOT NULL version
try (Statement st = con.createStatement()) {
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNotNullDefinition()));
sender.sendMessage("Changed column '" + columnName + "' to have NOT NULL constraint");
}
// Log success message
logger.info("Changed MySQL column '" + columnName + "' to be NOT NULL, as initiated by '"
+ sender.getName() + "'");
}
/**
* Removes the {@code NOT NULL} constraint of a column definition and replaces rows with the
* default value to {@code NULL}.
*
* @param sender the command sender initiation the action
* @param column the column to modify
* @param con connection to the database
* @throws SQLException .
*/
private void removeNotNullAndDefault(CommandSender sender, Columns column, Connection con) throws SQLException {
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
final String columnName = settings.getProperty(column.getColumnNameProperty());
// Change column definition to nullable version
try (Statement st = con.createStatement()) {
st.execute(format("ALTER TABLE %s MODIFY %s %s", tableName, columnName, column.getNullableDefinition()));
sender.sendMessage("Changed column '" + columnName + "' to allow nulls");
}
// Replace old default value with NULL
String sql = format("UPDATE %s SET %s = NULL WHERE %s = ?;", tableName, columnName, columnName);
int updatedRows;
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setObject(1, column.getDefaultValue());
updatedRows = pst.executeUpdate();
}
sender.sendMessage("Replaced default value ('" + column.getDefaultValue()
+ "') to be NULL, modifying " + updatedRows + " entries");
// Log success message
logger.info("Changed MySQL column '" + columnName + "' to allow NULL, as initiated by '"
+ sender.getName() + "'");
}
/**
* Outputs the current definitions of all {@link Columns} which can be migrated.
*
* @param sender command sender to output the data to
*/
private void showColumnDetails(CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "MySQL column details");
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
try (Connection con = getConnection(mySql)) {
final DatabaseMetaData metaData = con.getMetaData();
for (Columns col : Columns.values()) {
String columnName = settings.getProperty(col.getColumnNameProperty());
String isNullText = isNotNullColumn(metaData, tableName, columnName) ? "NOT NULL" : "nullable";
Object defaultValue = getColumnDefaultValue(metaData, tableName, columnName);
String defaultText = defaultValue == null ? "no default" : "default: '" + defaultValue + "'";
sender.sendMessage(formatColumnWithMetadata(col, metaData, tableName)
+ " (" + columnName + "): " + isNullText + ", " + defaultText);
}
} catch (SQLException e) {
logger.logException("Failed while showing column details:", e);
sender.sendMessage("Failed while showing column details. See log for info");
}
}
/**
* Displays sample commands and the list of columns that can be changed.
*
* @param sender the sender issuing the command
*/
private void displayUsageHints(CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "MySQL column changer");
sender.sendMessage("Adds or removes a NOT NULL constraint for a column.");
sender.sendMessage("Examples: add a NOT NULL constraint with");
sender.sendMessage(" /authme debug mysqldef add <column>");
sender.sendMessage("Remove one with /authme debug mysqldef remove <column>");
sender.sendMessage("Available columns: " + constructColumnListWithMetadata());
sender.sendMessage(" " + NOT_NULL_SUFFIX + ": not-null, " + DEFAULT_VALUE_SUFFIX
+ ": has default. See /authme debug mysqldef details");
}
/**
* @return list of {@link Columns} we can toggle with suffixes indicating their NOT NULL and default value status
*/
private String constructColumnListWithMetadata() {
try (Connection con = getConnection(mySql)) {
final DatabaseMetaData metaData = con.getMetaData();
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
List<String> formattedColumns = new ArrayList<>(Columns.values().length);
for (Columns col : Columns.values()) {
formattedColumns.add(formatColumnWithMetadata(col, metaData, tableName));
}
return String.join(ChatColor.RESET + ", ", formattedColumns);
} catch (SQLException e) {
logger.logException("Failed to construct column list:", e);
return ChatColor.RED + "An error occurred! Please see the console for details.";
}
}
private String formatColumnWithMetadata(Columns column, DatabaseMetaData metaData,
String tableName) throws SQLException {
String columnName = settings.getProperty(column.getColumnNameProperty());
boolean isNotNull = isNotNullColumn(metaData, tableName, columnName);
boolean hasDefaultValue = getColumnDefaultValue(metaData, tableName, columnName) != null;
return column.name()
+ (isNotNull ? NOT_NULL_SUFFIX : "")
+ (hasDefaultValue ? DEFAULT_VALUE_SUFFIX : "");
}
/**
* Gets the Connection object from the MySQL data source.
*
* @param mySql the MySQL data source to get the connection from
* @return the connection
*/
@VisibleForTesting
Connection getConnection(MySQL mySql) {
try {
Method method = MySQL.class.getDeclaredMethod("getConnection");
method.setAccessible(true);
return (Connection) method.invoke(mySql);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalStateException("Could not get MySQL connection", e);
}
}
private static <E extends Enum<E>> E matchToEnum(List<String> arguments, int index, Class<E> clazz) {
if (arguments.size() <= index) {
return null;
}
String str = arguments.get(index);
return Arrays.stream(clazz.getEnumConstants())
.filter(e -> e.name().equalsIgnoreCase(str))
.findFirst().orElse(null);
}
private enum Operation {
ADD, REMOVE, DETAILS
}
/** MySQL columns which can be toggled between being NOT NULL and allowing NULL values. */
enum Columns {
LASTLOGIN(DatabaseSettings.MYSQL_COL_LASTLOGIN,
"BIGINT", "BIGINT NOT NULL DEFAULT 0", DB_LAST_LOGIN_DEFAULT),
LASTIP(DatabaseSettings.MYSQL_COL_LAST_IP,
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin",
"VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1'",
DB_LAST_IP_DEFAULT),
EMAIL(DatabaseSettings.MYSQL_COL_EMAIL,
"VARCHAR(255)", "VARCHAR(255) NOT NULL DEFAULT 'your@email.com'", DB_EMAIL_DEFAULT);
private final Property<String> columnNameProperty;
private final String nullableDefinition;
private final String notNullDefinition;
private final Object defaultValue;
Columns(Property<String> columnNameProperty, String nullableDefinition,
String notNullDefinition, Object defaultValue) {
this.columnNameProperty = columnNameProperty;
this.nullableDefinition = nullableDefinition;
this.notNullDefinition = notNullDefinition;
this.defaultValue = defaultValue;
}
/** @return property defining the column name in the database */
Property<String> getColumnNameProperty() {
return columnNameProperty;
}
/** @return SQL definition of the column allowing NULL values */
String getNullableDefinition() {
return nullableDefinition;
}
/** @return SQL definition of the column with a NOT NULL constraint */
String getNotNullDefinition() {
return notNullDefinition;
}
/** @return the default value used in {@link #notNullDefinition} */
Object getDefaultValue() {
return defaultValue;
}
}
}
@@ -1,56 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.limbo.UserGroup;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import static java.util.stream.Collectors.toList;
/**
* Outputs the permission groups of a player.
*/
class PermissionGroups implements DebugSection {
@Inject
private PermissionsManager permissionsManager;
@Override
public String getName() {
return "groups";
}
@Override
public String getDescription() {
return "Show permission groups a player belongs to";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe permission groups");
String name = arguments.isEmpty() ? sender.getName() : arguments.get(0);
Player player = Bukkit.getPlayer(name);
if (player == null) {
sender.sendMessage("Player " + name + " could not be found");
} else {
List<String> groupNames = permissionsManager.getGroups(player).stream()
.map(UserGroup::getGroupName)
.collect(toList());
sender.sendMessage("Player " + name + " has permission groups: " + String.join(", ", groupNames));
sender.sendMessage("Primary group is: " + permissionsManager.getGroups(player));
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.PERM_GROUPS;
}
}
@@ -1,117 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
/**
* Allows to view the data of a PlayerAuth in the database.
*/
class PlayerAuthViewer implements DebugSection {
@Inject
private DataSource dataSource;
@Override
public String getName() {
return "db";
}
@Override
public String getDescription() {
return "View player's data in the database";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
if (arguments.isEmpty()) {
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
sender.sendMessage("Enter player name to view his data in the database.");
sender.sendMessage("Example: /authme debug db Bobby");
return;
}
PlayerAuth auth = dataSource.getAuth(arguments.get(0));
if (auth == null) {
sender.sendMessage(ChatColor.BLUE + "AuthMe database viewer");
sender.sendMessage("No record exists for '" + arguments.get(0) + "'");
} else {
displayAuthToSender(auth, sender);
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.PLAYER_AUTH_VIEWER;
}
/**
* Outputs the PlayerAuth information to the given sender.
*
* @param auth the PlayerAuth to display
* @param sender the sender to send the messages to
*/
private void displayAuthToSender(PlayerAuth auth, CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
sender.sendMessage("Email: " + auth.getEmail() + ". IP: " + auth.getLastIp() + ". Group: " + auth.getGroupId());
sender.sendMessage("Quit location: "
+ formatLocation(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld()));
sender.sendMessage("Last login: " + formatDate(auth.getLastLogin()));
sender.sendMessage("Registration: " + formatDate(auth.getRegistrationDate())
+ " with IP " + auth.getRegistrationIp());
HashedPassword hashedPass = auth.getPassword();
sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6)
+ "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'");
sender.sendMessage("TOTP code (partial): '" + safeSubstring(auth.getTotpKey(), 3) + "'");
}
/**
* Fail-safe substring method. Guarantees not to show the entire String.
*
* @param str the string to transform
* @param length number of characters to show from the start of the String
* @return the first <code>length</code> characters of the string, or half of the string if it is shorter,
* or empty string if the string is null or empty
*/
private static String safeSubstring(String str, int length) {
if (StringUtils.isBlank(str)) {
return "";
} else if (str.length() < length) {
return str.substring(0, str.length() / 2) + "...";
} else {
return str.substring(0, length) + "...";
}
}
/**
* Formats the given timestamp to a human readable date.
*
* @param timestamp the timestamp to format (nullable)
* @return the formatted timestamp
*/
private static String formatDate(Long timestamp) {
if (timestamp == null) {
return "Not available (null)";
} else if (timestamp == 0) {
return "Not available (0)";
} else {
LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(date);
}
}
}
@@ -1,87 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.formatLocation;
/**
* Shows the spawn location that AuthMe is configured to use.
*/
class SpawnLocationViewer implements DebugSection {
@Inject
private SpawnLoader spawnLoader;
@Inject
private Settings settings;
@Inject
private BukkitService bukkitService;
@Override
public String getName() {
return "spawn";
}
@Override
public String getDescription() {
return "Shows the spawn location that AuthMe will use";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe spawn location viewer");
if (arguments.isEmpty()) {
showGeneralInfo(sender);
} else if ("?".equals(arguments.get(0))) {
showHelp(sender);
} else {
showPlayerSpawn(sender, arguments.get(0));
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.SPAWN_LOCATION;
}
private void showGeneralInfo(CommandSender sender) {
sender.sendMessage("Spawn priority: "
+ String.join(", ", settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)));
sender.sendMessage("AuthMe spawn location: " + formatLocation(spawnLoader.getSpawn()));
sender.sendMessage("AuthMe first spawn location: " + formatLocation(spawnLoader.getFirstSpawn()));
sender.sendMessage("AuthMe (first)spawn are only used depending on the configured priority!");
sender.sendMessage("Use '/authme debug spawn ?' for further help");
}
private void showHelp(CommandSender sender) {
sender.sendMessage("Use /authme spawn and /authme firstspawn to teleport to the spawns.");
sender.sendMessage("/authme set(first)spawn sets the (first) spawn to your current location.");
sender.sendMessage("Use /authme debug spawn <player> to view where a player would be teleported to.");
sender.sendMessage("Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Spawn-Handling");
}
private void showPlayerSpawn(CommandSender sender, String playerName) {
Player player = bukkitService.getPlayerExact(playerName);
if (player == null) {
sender.sendMessage("Player '" + playerName + "' is not online!");
} else {
Location spawn = spawnLoader.getSpawnLocation(player);
sender.sendMessage("Player '" + playerName + "' has spawn location: " + formatLocation(spawn));
sender.sendMessage("Note: this check excludes the AuthMe firstspawn.");
}
}
}
@@ -1,125 +0,0 @@
package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.datasourcecolumns.data.DataSourceValue;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSsl;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Sends out a test email.
*/
class TestEmailSender implements DebugSection {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TestEmailSender.class);
@Inject
private DataSource dataSource;
@Inject
private SendMailSsl sendMailSsl;
@Inject
private Server server;
@Override
public String getName() {
return "mail";
}
@Override
public String getDescription() {
return "Sends out a test email";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
sender.sendMessage(ChatColor.BLUE + "AuthMe test email sender");
if (!sendMailSsl.hasAllInformation()) {
sender.sendMessage(ChatColor.RED + "You haven't set all required configurations in config.yml "
+ "for sending emails. Please check your config.yml");
return;
}
String email = getEmail(sender, arguments);
// getEmail() takes care of informing the sender of the error if email == null
if (email != null) {
boolean sendMail = sendTestEmail(email);
if (sendMail) {
sender.sendMessage("Test email sent to " + email + " with success");
} else {
sender.sendMessage(ChatColor.RED + "Failed to send test mail to " + email + "; please check your logs");
}
}
}
@Override
public PermissionNode getRequiredPermission() {
return DebugSectionPermissions.TEST_EMAIL;
}
/**
* Gets the email address to use based on the sender and the arguments. If the arguments are empty,
* we attempt to retrieve the email from the sender. If there is an argument, we verify that it is
* an email address.
* {@code null} is returned if no email address could be found. This method informs the sender of
* the specific error in such cases.
*
* @param sender the command sender
* @param arguments the provided arguments
* @return the email to use, or null if none found
*/
private String getEmail(CommandSender sender, List<String> arguments) {
if (arguments.isEmpty()) {
DataSourceValue<String> emailResult = dataSource.getEmail(sender.getName());
if (!emailResult.rowExists()) {
sender.sendMessage(ChatColor.RED + "Please provide an email address, "
+ "e.g. /authme debug mail test@example.com");
return null;
}
final String email = emailResult.getValue();
if (Utils.isEmailEmpty(email)) {
sender.sendMessage(ChatColor.RED + "No email set for your account!"
+ " Please use /authme debug mail <email>");
return null;
}
return email;
} else {
String email = arguments.get(0);
if (StringUtils.isInsideString('@', email)) {
return email;
}
sender.sendMessage(ChatColor.RED + "Invalid email! Usage: /authme debug mail test@example.com");
return null;
}
}
private boolean sendTestEmail(String email) {
HtmlEmail htmlEmail;
try {
htmlEmail = sendMailSsl.initializeMail(email);
} catch (EmailException e) {
logger.logException("Failed to create email for sample email:", e);
return false;
}
htmlEmail.setSubject("AuthMe test email");
String message = "Hello there!<br />This is a sample email sent to you from a Minecraft server ("
+ server.getName() + ") via /authme debug mail. If you're seeing this, sending emails should be fine.";
return sendMailSsl.sendEmail(message, htmlEmail);
}
}
@@ -1,86 +0,0 @@
package fr.xephi.authme.command.executable.captcha;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.captcha.LoginCaptchaManager;
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
import fr.xephi.authme.data.limbo.LimboMessageType;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Captcha command, allowing a player to solve a captcha.
*/
public class CaptchaCommand extends PlayerCommand {
@Inject
private PlayerCache playerCache;
@Inject
private LoginCaptchaManager loginCaptchaManager;
@Inject
private RegistrationCaptchaManager registrationCaptchaManager;
@Inject
private CommonService commonService;
@Inject
private LimboService limboService;
@Inject
private DataSource dataSource;
@Override
public void runCommand(Player player, List<String> arguments) {
final String name = player.getName();
if (playerCache.isAuthenticated(name)) {
// No captcha is relevant if the player is logged in
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return;
}
if (loginCaptchaManager.isCaptchaRequired(name)) {
checkLoginCaptcha(player, arguments.get(0));
} else {
final boolean isPlayerRegistered = dataSource.isAuthAvailable(name);
if (!isPlayerRegistered && registrationCaptchaManager.isCaptchaRequired(name)) {
checkRegisterCaptcha(player, arguments.get(0));
} else {
MessageKey errorMessage = isPlayerRegistered ? MessageKey.USAGE_LOGIN : MessageKey.USAGE_REGISTER;
commonService.send(player, errorMessage);
}
}
}
private void checkLoginCaptcha(Player player, String captchaCode) {
final boolean isCorrectCode = loginCaptchaManager.checkCode(player, captchaCode);
if (isCorrectCode) {
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
commonService.send(player, MessageKey.LOGIN_MESSAGE);
limboService.unmuteMessageTask(player);
} else {
String newCode = loginCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
}
}
private void checkRegisterCaptcha(Player player, String captchaCode) {
final boolean isCorrectCode = registrationCaptchaManager.checkCode(player, captchaCode);
if (isCorrectCode) {
commonService.send(player, MessageKey.REGISTER_CAPTCHA_SUCCESS);
commonService.send(player, MessageKey.REGISTER_MESSAGE);
} else {
String newCode = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
}
limboService.resetMessageTask(player, LimboMessageType.REGISTER);
}
}
@@ -1,74 +0,0 @@
package fr.xephi.authme.command.executable.changepassword;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import java.util.Locale;
/**
* The command for a player to change his password with.
*/
public class ChangePasswordCommand extends PlayerCommand {
@Inject
private CommonService commonService;
@Inject
private PlayerCache playerCache;
@Inject
private ValidationService validationService;
@Inject
private Management management;
@Inject
private VerificationCodeManager codeManager;
@Override
public void runCommand(Player player, List<String> arguments) {
String name = player.getName().toLowerCase(Locale.ROOT);
if (!playerCache.isAuthenticated(name)) {
commonService.send(player, MessageKey.NOT_LOGGED_IN);
return;
}
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(name);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
String oldPassword = arguments.get(0);
String newPassword = arguments.get(1);
// Make sure the password is allowed
ValidationResult passwordValidation = validationService.validatePassword(newPassword, name);
if (passwordValidation.hasError()) {
commonService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs());
return;
}
management.performPasswordChange(player, oldPassword, newPassword);
}
@Override
protected String getAlternativeCommand() {
return "/authme password <playername> <password>";
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_CHANGE_PASSWORD;
}
}
@@ -1,40 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for setting an email to an account.
*/
public class AddEmailCommand extends PlayerCommand {
@Inject
private Management management;
@Inject
private CommonService commonService;
@Override
public void runCommand(Player player, List<String> arguments) {
String email = arguments.get(0);
String emailConfirmation = arguments.get(1);
if (email.equals(emailConfirmation)) {
// Closer inspection of the mail address handled by the async task
management.performAddEmail(player, email);
} else {
commonService.send(player, MessageKey.CONFIRM_EMAIL_MESSAGE);
}
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_ADD_EMAIL;
}
}
@@ -1,46 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Change email command.
*/
public class ChangeEmailCommand extends PlayerCommand {
@Inject
private Management management;
@Inject
private CommonService commonService;
@Inject
private VerificationCodeManager codeManager;
@Override
public void runCommand(Player player, List<String> arguments) {
final String playerName = player.getName();
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(playerName);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
String playerMailOld = arguments.get(0);
String playerMailNew = arguments.get(1);
management.performChangeEmail(player, playerMailOld, playerMailNew);
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_CHANGE_EMAIL;
}
}
@@ -1,29 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.command.FoundCommandResult;
import fr.xephi.authme.command.help.HelpProvider;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Collections;
import java.util.List;
/**
* Base command for /email, showing information about the child commands.
*/
public class EmailBaseCommand implements ExecutableCommand {
@Inject
private CommandMapper commandMapper;
@Inject
private HelpProvider helpProvider;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("email"));
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
}
}
@@ -1,61 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.PasswordRecoveryService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for changing password following successful recovery.
*/
public class EmailSetPasswordCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(EmailSetPasswordCommand.class);
@Inject
private DataSource dataSource;
@Inject
private CommonService commonService;
@Inject
private PasswordRecoveryService recoveryService;
@Inject
private PasswordSecurity passwordSecurity;
@Inject
private ValidationService validationService;
@Override
protected void runCommand(Player player, List<String> arguments) {
if (recoveryService.canChangePassword(player)) {
String name = player.getName();
String password = arguments.get(0);
ValidationResult result = validationService.validatePassword(password, name);
if (!result.hasError()) {
HashedPassword hashedPassword = passwordSecurity.computeHash(password, name);
dataSource.updatePassword(name, hashedPassword);
recoveryService.removeFromSuccessfulRecovery(player);
logger.info("Player '" + name + "' has changed their password from recovery");
commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS);
} else {
commonService.send(player, result.getMessageKey(), result.getArgs());
}
} else {
commonService.send(player, MessageKey.CHANGE_PASSWORD_EXPIRED);
}
}
}
@@ -1,46 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.PasswordRecoveryService;
import fr.xephi.authme.service.RecoveryCodeService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for submitting email recovery code.
*/
public class ProcessCodeCommand extends PlayerCommand {
@Inject
private CommonService commonService;
@Inject
private RecoveryCodeService codeService;
@Inject
private PasswordRecoveryService recoveryService;
@Override
protected void runCommand(Player player, List<String> arguments) {
String name = player.getName();
String code = arguments.get(0);
if (codeService.hasTriesLeft(name)) {
if (codeService.isCodeValid(name, code)) {
commonService.send(player, MessageKey.RECOVERY_CODE_CORRECT);
recoveryService.addSuccessfulRecovery(player);
codeService.removeCode(name);
} else {
commonService.send(player, MessageKey.INCORRECT_RECOVERY_CODE,
Integer.toString(codeService.getTriesLeft(name)));
}
} else {
codeService.removeCode(name);
commonService.send(player, MessageKey.RECOVERY_TRIES_EXCEEDED);
}
}
}
@@ -1,91 +0,0 @@
package fr.xephi.authme.command.executable.email;
import ch.jalu.datasourcecolumns.data.DataSourceValue;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.PasswordRecoveryService;
import fr.xephi.authme.service.RecoveryCodeService;
import fr.xephi.authme.util.Utils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for password recovery by email.
*/
public class RecoverEmailCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RecoverEmailCommand.class);
@Inject
private CommonService commonService;
@Inject
private DataSource dataSource;
@Inject
private PlayerCache playerCache;
@Inject
private EmailService emailService;
@Inject
private PasswordRecoveryService recoveryService;
@Inject
private RecoveryCodeService recoveryCodeService;
@Inject
private BukkitService bukkitService;
@Override
protected void runCommand(Player player, List<String> arguments) {
final String playerMail = arguments.get(0);
final String playerName = player.getName();
if (!emailService.hasAllInformation()) {
logger.warning("Mail API is not set");
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return;
}
if (playerCache.isAuthenticated(playerName)) {
commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return;
}
DataSourceValue<String> emailResult = dataSource.getEmail(playerName);
if (!emailResult.rowExists()) {
commonService.send(player, MessageKey.USAGE_REGISTER);
return;
}
final String email = emailResult.getValue();
if (Utils.isEmailEmpty(email) || !email.equalsIgnoreCase(playerMail)) {
commonService.send(player, MessageKey.INVALID_EMAIL);
return;
}
bukkitService.runTaskAsynchronously(() -> {
if (recoveryCodeService.isRecoveryCodeNeeded()) {
// Recovery code is needed; generate and send one
recoveryService.createAndSendRecoveryCode(player, email);
} else {
// Code not needed, just send them a new password
recoveryService.generateAndSendNewPassword(player, email);
}
});
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_RECOVER_EMAIL;
}
}
@@ -1,48 +0,0 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.Utils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Show email command.
*/
public class ShowEmailCommand extends PlayerCommand {
@Inject
private CommonService commonService;
@Inject
private PlayerCache playerCache;
@Override
public void runCommand(Player player, List<String> arguments) {
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth != null && !Utils.isEmailEmpty(auth.getEmail())) {
if (commonService.getProperty(SecuritySettings.USE_EMAIL_MASKING)){
commonService.send(player, MessageKey.EMAIL_SHOW, emailMask(auth.getEmail()));
} else {
commonService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail());
}
} else {
commonService.send(player, MessageKey.SHOW_NO_EMAIL);
}
}
private String emailMask(String email){
String[] frag = email.split("@"); //Split id and domain
int sid = frag[0].length() / 3 + 1; //Define the id view (required length >= 1)
int sdomain = frag[1].length() / 3; //Define the domain view (required length >= 0)
String id = frag[0].substring(0, sid) + "***"; //Build the id
String domain = "***" + frag[1].substring(sdomain); //Build the domain
return id + "@" + domain;
}
}
@@ -1,34 +0,0 @@
package fr.xephi.authme.command.executable.login;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Login command.
*/
public class LoginCommand extends PlayerCommand {
@Inject
private Management management;
@Override
public void runCommand(Player player, List<String> arguments) {
String password = arguments.get(0);
management.performLogin(player, password);
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_LOGIN;
}
@Override
protected String getAlternativeCommand() {
return "/authme forcelogin <player>";
}
}
@@ -1,22 +0,0 @@
package fr.xephi.authme.command.executable.logout;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.process.Management;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Logout command.
*/
public class LogoutCommand extends PlayerCommand {
@Inject
private Management management;
@Override
public void runCommand(Player player, List<String> arguments) {
management.performLogout(player);
}
}
@@ -1,225 +0,0 @@
package fr.xephi.authme.command.executable.register;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.captcha.RegistrationCaptchaManager;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
import fr.xephi.authme.process.register.RegistrationType;
import fr.xephi.authme.process.register.executors.EmailRegisterParams;
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationMethod;
import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.CONFIRMATION;
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_MANDATORY;
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.EMAIL_OPTIONAL;
import static fr.xephi.authme.process.register.RegisterSecondaryArgument.NONE;
import static fr.xephi.authme.settings.properties.RegistrationSettings.REGISTER_SECOND_ARGUMENT;
/**
* Command for /register.
*/
public class RegisterCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RegisterCommand.class);
@Inject
private Management management;
@Inject
private CommonService commonService;
@Inject
private DataSource dataSource;
@Inject
private EmailService emailService;
@Inject
private ValidationService validationService;
@Inject
private RegistrationCaptchaManager registrationCaptchaManager;
@Override
public void runCommand(Player player, List<String> arguments) {
if (!isCaptchaFulfilled(player)) {
return; // isCaptchaFulfilled handles informing the player on failure
}
if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
//for two factor auth we don't need to check the usage
management.performRegister(RegistrationMethod.TWO_FACTOR_REGISTRATION,
TwoFactorRegisterParams.of(player));
return;
} else if (arguments.size() < 1) {
commonService.send(player, MessageKey.USAGE_REGISTER);
return;
}
RegistrationType registrationType = commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE);
if (registrationType == RegistrationType.PASSWORD) {
handlePasswordRegistration(player, arguments);
} else if (registrationType == RegistrationType.EMAIL) {
handleEmailRegistration(player, arguments);
} else {
throw new IllegalStateException("Unknown registration type '" + registrationType + "'");
}
}
@Override
protected String getAlternativeCommand() {
return "/authme register <playername> <password>";
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_REGISTER;
}
private boolean isCaptchaFulfilled(Player player) {
if (registrationCaptchaManager.isCaptchaRequired(player.getName())) {
String code = registrationCaptchaManager.getCaptchaCodeOrGenerateNew(player.getName());
commonService.send(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, code);
return false;
}
return true;
}
private void handlePasswordRegistration(Player player, List<String> arguments) {
if (isSecondArgValidForPasswordRegistration(player, arguments)) {
final String password = arguments.get(0);
final String email = getEmailIfAvailable(arguments);
management.performRegister(RegistrationMethod.PASSWORD_REGISTRATION,
PasswordRegisterParams.of(player, password, email));
}
}
private String getEmailIfAvailable(List<String> arguments) {
if (arguments.size() >= 2) {
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
return arguments.get(1);
}
}
return null;
}
/**
* Verifies that the second argument is valid (based on the configuration)
* to perform a password registration. The player is informed if the check
* is unsuccessful.
*
* @param player the player to register
* @param arguments the provided arguments
* @return true if valid, false otherwise
*/
private boolean isSecondArgValidForPasswordRegistration(Player player, List<String> arguments) {
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
// cases where args.size < 2
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
return true;
} else if (arguments.size() < 2) {
commonService.send(player, MessageKey.USAGE_REGISTER);
return false;
}
if (secondArgType == CONFIRMATION) {
if (arguments.get(0).equals(arguments.get(1))) {
return true;
} else {
commonService.send(player, MessageKey.PASSWORD_MATCH_ERROR);
return false;
}
} else if (secondArgType == EMAIL_MANDATORY || secondArgType == EMAIL_OPTIONAL) {
if (validationService.validateEmail(arguments.get(1))) {
return true;
} else {
commonService.send(player, MessageKey.INVALID_EMAIL);
return false;
}
} else {
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
}
}
private void handleEmailRegistration(Player player, List<String> arguments) {
if (!emailService.hasAllInformation()) {
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
logger.warning("Cannot register player '" + player.getName() + "': no email or password is set "
+ "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
return;
}
final String email = arguments.get(0);
if (!validationService.validateEmail(email)) {
commonService.send(player, MessageKey.INVALID_EMAIL);
} else if (isSecondArgValidForEmailRegistration(player, arguments)) {
management.performRegister(RegistrationMethod.EMAIL_REGISTRATION,
EmailRegisterParams.of(player, email));
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (dataSource.getAuth(player.getName()) != null) {
if (dataSource.getAuth(player.getName()).getLastLogin() == null) {
management.performUnregisterByAdmin(null, player.getName(), player);
timer.cancel();
}
} else {
timer.cancel();
}
}
}, 600000);
}
}
/**
* Verifies that the second argument is valid (based on the configuration)
* to perform an email registration. The player is informed if the check
* is unsuccessful.
*
* @param player the player to register
* @param arguments the provided arguments
* @return true if valid, false otherwise
*/
private boolean isSecondArgValidForEmailRegistration(Player player, List<String> arguments) {
RegisterSecondaryArgument secondArgType = commonService.getProperty(REGISTER_SECOND_ARGUMENT);
// cases where args.size < 2
if (secondArgType == NONE || secondArgType == EMAIL_OPTIONAL && arguments.size() < 2) {
return true;
} else if (arguments.size() < 2) {
commonService.send(player, MessageKey.USAGE_REGISTER);
return false;
}
if (secondArgType == EMAIL_OPTIONAL || secondArgType == EMAIL_MANDATORY || secondArgType == CONFIRMATION) {
if (arguments.get(0).equals(arguments.get(1))) {
return true;
} else {
commonService.send(player, MessageKey.USAGE_REGISTER);
return false;
}
} else {
throw new IllegalStateException("Unknown secondary argument type '" + secondArgType + "'");
}
}
}
@@ -1,43 +0,0 @@
package fr.xephi.authme.command.executable.totp;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.security.totp.GenerateTotpService;
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for a player to enable TOTP.
*/
public class AddTotpCommand extends PlayerCommand {
@Inject
private GenerateTotpService generateTotpService;
@Inject
private PlayerCache playerCache;
@Inject
private Messages messages;
@Override
protected void runCommand(Player player, List<String> arguments) {
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth == null) {
messages.send(player, MessageKey.NOT_LOGGED_IN);
} else if (auth.getTotpKey() == null) {
TotpGenerationResult createdTotpInfo = generateTotpService.generateTotpKey(player);
messages.send(player, MessageKey.TWO_FACTOR_CREATE,
createdTotpInfo.getTotpKey(), createdTotpInfo.getAuthenticatorQrCodeUrl());
messages.send(player, MessageKey.TWO_FACTOR_CREATE_CONFIRMATION_REQUIRED);
} else {
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
}
}
}
@@ -1,74 +0,0 @@
package fr.xephi.authme.command.executable.totp;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.security.totp.GenerateTotpService;
import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command to enable TOTP by supplying the proper code as confirmation.
*/
public class ConfirmTotpCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(ConfirmTotpCommand.class);
@Inject
private GenerateTotpService generateTotpService;
@Inject
private PlayerCache playerCache;
@Inject
private DataSource dataSource;
@Inject
private Messages messages;
@Override
protected void runCommand(Player player, List<String> arguments) {
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth == null) {
messages.send(player, MessageKey.NOT_LOGGED_IN);
} else if (auth.getTotpKey() != null) {
messages.send(player, MessageKey.TWO_FACTOR_ALREADY_ENABLED);
} else {
verifyTotpCodeConfirmation(player, auth, arguments.get(0));
}
}
private void verifyTotpCodeConfirmation(Player player, PlayerAuth auth, String inputTotpCode) {
final TotpGenerationResult totpDetails = generateTotpService.getGeneratedTotpKey(player);
if (totpDetails == null) {
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_NO_CODE);
} else {
boolean isCodeValid = generateTotpService.isTotpCodeCorrectForGeneratedTotpKey(player, inputTotpCode);
if (isCodeValid) {
generateTotpService.removeGenerateTotpKey(player);
insertTotpKeyIntoDatabase(player, auth, totpDetails);
} else {
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_ERROR_WRONG_CODE);
}
}
}
private void insertTotpKeyIntoDatabase(Player player, PlayerAuth auth, TotpGenerationResult totpDetails) {
if (dataSource.setTotpKey(player.getName(), totpDetails.getTotpKey())) {
messages.send(player, MessageKey.TWO_FACTOR_ENABLE_SUCCESS);
auth.setTotpKey(totpDetails.getTotpKey());
playerCache.updatePlayer(auth);
logger.info("Player '" + player.getName() + "' has successfully added a TOTP key to their account");
} else {
messages.send(player, MessageKey.ERROR);
}
}
}
@@ -1,62 +0,0 @@
package fr.xephi.authme.command.executable.totp;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.security.totp.TotpAuthenticator;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for a player to remove 2FA authentication.
*/
public class RemoveTotpCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(RemoveTotpCommand.class);
@Inject
private DataSource dataSource;
@Inject
private PlayerCache playerCache;
@Inject
private TotpAuthenticator totpAuthenticator;
@Inject
private Messages messages;
@Override
protected void runCommand(Player player, List<String> arguments) {
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth == null) {
messages.send(player, MessageKey.NOT_LOGGED_IN);
} else if (auth.getTotpKey() == null) {
messages.send(player, MessageKey.TWO_FACTOR_NOT_ENABLED_ERROR);
} else {
if (totpAuthenticator.checkCode(auth, arguments.get(0))) {
removeTotpKeyFromDatabase(player, auth);
} else {
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
}
}
}
private void removeTotpKeyFromDatabase(Player player, PlayerAuth auth) {
if (dataSource.removeTotpKey(auth.getNickname())) {
auth.setTotpKey(null);
playerCache.updatePlayer(auth);
messages.send(player, MessageKey.TWO_FACTOR_REMOVED_SUCCESS);
logger.info("Player '" + player.getName() + "' removed their TOTP key");
} else {
messages.send(player, MessageKey.ERROR);
}
}
}
@@ -1,29 +0,0 @@
package fr.xephi.authme.command.executable.totp;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.command.FoundCommandResult;
import fr.xephi.authme.command.help.HelpProvider;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.Collections;
import java.util.List;
/**
* Base command for /totp.
*/
public class TotpBaseCommand implements ExecutableCommand {
@Inject
private CommandMapper commandMapper;
@Inject
private HelpProvider helpProvider;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
FoundCommandResult result = commandMapper.mapPartsToCommand(sender, Collections.singletonList("totp"));
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
}
}
@@ -1,79 +0,0 @@
package fr.xephi.authme.command.executable.totp;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.LimboPlayerState;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.process.login.AsynchronousLogin;
import fr.xephi.authme.security.totp.TotpAuthenticator;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* TOTP code command for processing the 2FA code during the login process.
*/
public class TotpCodeCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(TotpCodeCommand.class);
@Inject
private LimboService limboService;
@Inject
private PlayerCache playerCache;
@Inject
private Messages messages;
@Inject
private TotpAuthenticator totpAuthenticator;
@Inject
private DataSource dataSource;
@Inject
private AsynchronousLogin asynchronousLogin;
@Override
protected void runCommand(Player player, List<String> arguments) {
if (playerCache.isAuthenticated(player.getName())) {
messages.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return;
}
PlayerAuth auth = dataSource.getAuth(player.getName());
if (auth == null) {
messages.send(player, MessageKey.REGISTER_MESSAGE);
return;
}
LimboPlayer limbo = limboService.getLimboPlayer(player.getName());
if (limbo != null && limbo.getState() == LimboPlayerState.TOTP_REQUIRED) {
processCode(player, auth, arguments.get(0));
} else {
logger.debug(() -> "Aborting TOTP check for player '" + player.getName()
+ "'. Invalid limbo state: " + (limbo == null ? "no limbo" : limbo.getState()));
messages.send(player, MessageKey.LOGIN_MESSAGE);
}
}
private void processCode(Player player, PlayerAuth auth, String inputCode) {
boolean isCodeValid = totpAuthenticator.checkCode(auth, inputCode);
if (isCodeValid) {
logger.debug("Successfully checked TOTP code for `{0}`", player.getName());
asynchronousLogin.performLogin(player, auth);
} else {
logger.debug("Input TOTP code was invalid for player `{0}`", player.getName());
messages.send(player, MessageKey.TWO_FACTOR_INVALID_CODE);
}
}
}
@@ -1,62 +0,0 @@
package fr.xephi.authme.command.executable.unregister;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Command for a player to unregister himself.
*/
public class UnregisterCommand extends PlayerCommand {
@Inject
private Management management;
@Inject
private CommonService commonService;
@Inject
private PlayerCache playerCache;
@Inject
private VerificationCodeManager codeManager;
@Override
public void runCommand(Player player, List<String> arguments) {
String playerPass = arguments.get(0);
String playerName = player.getName();
// Make sure the player is authenticated
if (!playerCache.isAuthenticated(playerName)) {
commonService.send(player, MessageKey.NOT_LOGGED_IN);
return;
}
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(playerName);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
// Unregister the player
management.performUnregister(player, playerPass);
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_UNREGISTER;
}
@Override
protected String getAlternativeCommand() {
return "/authme unregister <player>";
}
}
@@ -1,61 +0,0 @@
package fr.xephi.authme.command.executable.verification;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Used to complete the email verification process.
*/
public class VerificationCommand extends PlayerCommand {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(VerificationCommand.class);
@Inject
private CommonService commonService;
@Inject
private VerificationCodeManager codeManager;
@Override
public void runCommand(Player player, List<String> arguments) {
final String playerName = player.getName();
if (!codeManager.canSendMail()) {
logger.warning("Mail API is not set");
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return;
}
if (codeManager.isVerificationRequired(player)) {
if (codeManager.isCodeRequired(playerName)) {
if (codeManager.checkCode(playerName, arguments.get(0))) {
commonService.send(player, MessageKey.VERIFICATION_CODE_VERIFIED);
} else {
commonService.send(player, MessageKey.INCORRECT_VERIFICATION_CODE);
}
} else {
commonService.send(player, MessageKey.VERIFICATION_CODE_EXPIRED);
}
} else {
if (codeManager.hasEmail(playerName)) {
commonService.send(player, MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED);
} else {
commonService.send(player, MessageKey.VERIFICATION_CODE_EMAIL_NEEDED);
commonService.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_VERIFICATION_CODE;
}
}