Merge branches 'master' and 'passwd_recovery_process' of https://github.com/AuthMe/AuthMeReloaded into passwd_recovery_process

# Conflicts:
#	docs/config.md
#	src/main/resources/messages/messages_bg.yml
#	src/main/resources/messages/messages_es.yml
#	src/main/resources/messages/messages_pt.yml
#	src/main/resources/messages/messages_zhcn.yml
This commit is contained in:
Gnat008
2017-03-21 17:26:29 -04:00
214 changed files with 5553 additions and 1921 deletions
@@ -19,6 +19,7 @@ import static java.util.Arrays.asList;
* {@code /authme} has a child whose label is {@code "register"}, then {@code /authme register} is the command that
* the child defines.
*/
@SuppressWarnings("checkstyle:FinalClass") // Justification: class is mocked in multiple tests
public class CommandDescription {
/**
@@ -10,8 +10,8 @@ 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.datasource.converter.VAuthConverter;
import fr.xephi.authme.datasource.converter.XAuthConverter;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService;
@@ -78,11 +78,11 @@ public class ConverterCommand implements ExecutableCommand {
*/
private static Map<String, Class<? extends Converter>> getConverters() {
return ImmutableMap.<String, Class<? extends Converter>>builder()
.put("xauth", xAuthConverter.class)
.put("xauth", XAuthConverter.class)
.put("crazylogin", CrazyLoginConverter.class)
.put("rakamak", RakamakConverter.class)
.put("royalauth", RoyalAuthConverter.class)
.put("vauth", vAuthConverter.class)
.put("vauth", VAuthConverter.class)
.put("sqlitetosql", SqliteToSql.class)
.put("mysqltosqlite", MySqlToSqlite.class)
.build();
@@ -1,9 +1,8 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.limbo.LimboCache;
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.security.PasswordSecurity;
@@ -38,9 +37,6 @@ public class RegisterAdminCommand implements ExecutableCommand {
@Inject
private ValidationService validationService;
@Inject
private LimboCache limboCache;
@Override
public void executeCommand(final CommandSender sender, List<String> arguments) {
// Get the player name and password
@@ -83,7 +79,6 @@ public class RegisterAdminCommand implements ExecutableCommand {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(new Runnable() {
@Override
public void run() {
limboCache.restoreData(player);
player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER));
}
});
@@ -11,10 +11,10 @@ import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.settings.Settings;
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.Collection;
import java.util.List;
/**
@@ -44,8 +44,7 @@ public class ReloadCommand implements ExecutableCommand {
ConsoleLogger.setLoggingOptions(settings);
// 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())) {
ConsoleLogger.info("Note: cannot change database type during /authme reload");
sender.sendMessage("Note: cannot change database type during /authme reload");
Utils.logAndSendMessage(sender, "Note: cannot change database type during /authme reload");
}
performReloadOnServices();
commonService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
@@ -57,14 +56,10 @@ public class ReloadCommand implements ExecutableCommand {
}
private void performReloadOnServices() {
Collection<Reloadable> reloadables = injector.retrieveAllOfType(Reloadable.class);
for (Reloadable reloadable : reloadables) {
reloadable.reload();
}
injector.retrieveAllOfType(Reloadable.class)
.forEach(r -> r.reload());
Collection<SettingsDependent> settingsDependents = injector.retrieveAllOfType(SettingsDependent.class);
for (SettingsDependent dependent : settingsDependents) {
dependent.reload(settings);
}
injector.retrieveAllOfType(SettingsDependent.class)
.forEach(s -> s.reload(settings));
}
}
@@ -0,0 +1,77 @@
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.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) {
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);
}
}
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");
}
private void outputInfoForPlayer(CommandSender sender, String name) {
PlayerAuth auth = dataSource.getAuth(name);
if (auth == null) {
sender.sendMessage("No player with name '" + name + "'");
} else {
sender.sendMessage("Player '" + name + "' has IP address " + auth.getIp());
outputInfoForIpAddr(sender, auth.getIp());
}
}
}
@@ -0,0 +1,72 @@
package fr.xephi.authme.command.executable.authme.debug;
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.initialization.factory.SingletonStore;
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("LimboPlayers in memory: " + applyToLimboPlayersMap(limboService, Map::size));
sender.sendMessage("PlayerCache size: " + playerCache.getLogged() + " (= logged in players)");
outputDatabaseStats(sender);
outputInjectorStats(sender);
}
private void outputDatabaseStats(CommandSender sender) {
sender.sendMessage("Total players in DB: " + dataSource.getAccountsRegistered());
sender.sendMessage("Total marked as logged in in DB: " + dataSource.getLoggedPlayers().size());
if (dataSource instanceof CacheDataSource) {
CacheDataSource cacheDataSource = (CacheDataSource) this.dataSource;
sender.sendMessage("Cached PlayerAuth objects: " + cacheDataSource.getCachedAuths().size());
}
}
private void outputInjectorStats(CommandSender sender) {
sender.sendMessage(
String.format("Singleton Java classes: %d (Reloadable: %d / SettingsDependent: %d / HasCleanup: %d)",
singletonStore.retrieveAllOfType().size(),
singletonStore.retrieveAllOfType(Reloadable.class).size(),
singletonStore.retrieveAllOfType(SettingsDependent.class).size(),
singletonStore.retrieveAllOfType(HasCleanup.class).size()));
}
}
@@ -6,10 +6,10 @@ import fr.xephi.authme.initialization.factory.Factory;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Debug command main.
@@ -19,8 +19,9 @@ public class DebugCommand implements ExecutableCommand {
@Inject
private Factory<DebugSection> debugSectionFactory;
private Set<Class<? extends DebugSection>> sectionClasses =
ImmutableSet.of(PermissionGroups.class, TestEmailSender.class);
private Set<Class<? extends DebugSection>> sectionClasses = ImmutableSet.of(PermissionGroups.class,
DataStatistics.class, CountryLookup.class, PlayerAuthViewer.class, LimboPlayerViewer.class, CountryLookup.class,
HasPermissionChecker.class, TestEmailSender.class);
private Map<String, DebugSection> sections;
@@ -46,7 +47,7 @@ public class DebugCommand implements ExecutableCommand {
// Lazy getter
private Map<String, DebugSection> getSections() {
if (sections == null) {
Map<String, DebugSection> sections = new HashMap<>();
Map<String, DebugSection> sections = new TreeMap<>();
for (Class<? extends DebugSection> sectionClass : sectionClasses) {
DebugSection section = debugSectionFactory.newInstance(sectionClass);
sections.put(section.getName(), section);
@@ -0,0 +1,98 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.LimboService;
import org.bukkit.Location;
import java.lang.reflect.Field;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.function.Function;
/**
* Utilities used within the DebugSection implementations.
*/
final class DebugSectionUtils {
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.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) {
ConsoleLogger.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 player names for which there is a LimboPlayer (or error message upon failure)
*/
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) {
ConsoleLogger.logException("Could not retrieve LimboService values:", e);
}
}
return null;
}
}
@@ -0,0 +1,130 @@
package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.permission.AdminPermission;
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 =
Arrays.asList(AdminPermission.class, PlayerPermission.class, PlayerStatePermission.class);
@Inject
private PermissionsManager permissionsManager;
@Inject
private BukkitService bukkitService;
@Override
public String getName() {
return "perm";
}
@Override
public String getDescription() {
return "Checks if player has given permission: /authme debug perm bobby my.perm";
}
@Override
public void execute(CommandSender sender, List<String> arguments) {
if (arguments.size() < 2) {
sender.sendMessage("Check if a player has permission:");
sender.sendMessage("Example: /authme debug perm bobby my.perm.node");
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);
}
}
/**
* 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;
}
};
}
}
@@ -0,0 +1,129 @@
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.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.formatLocation;
import static fr.xephi.authme.command.executable.authme.debug.DebugSectionUtils.applyToLimboPlayersMap;
/**
* 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;
@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("/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("No limbo info and no player online with name '" + arguments.get(0) + "'");
return;
}
sender.sendMessage(ChatColor.GOLD + "Showing disk limbo / limbo / player info for '" + arguments.get(0) + "'");
new InfoDisplayer(sender, diskLimbo, memoryLimbo, player)
.sendEntry("Is op", LimboPlayer::isOperator, Player::isOp)
.sendEntry("Walk speed", LimboPlayer::getWalkSpeed, Player::getWalkSpeed)
.sendEntry("Can fly", LimboPlayer::isCanFly, Player::getAllowFlight)
.sendEntry("Fly speed", LimboPlayer::getFlySpeed, Player::getFlySpeed)
.sendEntry("Location", l -> formatLocation(l.getLocation()), p -> formatLocation(p.getLocation()))
.sendEntry("Group", LimboPlayer::getGroup, p -> "");
sender.sendMessage("Note: group is not shown for Player. Use /authme debug groups");
}
/**
* 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<LimboPlayer> diskLimbo;
private final Optional<LimboPlayer> memoryLimbo;
private final Optional<Player> player;
/**
* Constructor.
*
* @param sender command sender to send the information to
* @param memoryLimbo the limbo player to get data from
* @param player the player to get data from
*/
InfoDisplayer(CommandSender sender, LimboPlayer diskLimbo, LimboPlayer memoryLimbo, Player player) {
this.sender = sender;
this.diskLimbo = Optional.ofNullable(diskLimbo);
this.memoryLimbo = Optional.ofNullable(memoryLimbo);
this.player = Optional.ofNullable(player);
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 limboGetter getter for data retrieval on the LimboPlayer
* @param playerGetter getter for data retrieval on Player
* @param <T> the data type
* @return this instance (for chaining)
*/
<T> InfoDisplayer sendEntry(String title,
Function<LimboPlayer, T> limboGetter,
Function<Player, T> playerGetter) {
sender.sendMessage(
title + ": "
+ getData(diskLimbo, limboGetter)
+ " / "
+ getData(memoryLimbo, limboGetter)
+ " / "
+ getData(player, playerGetter));
return this;
}
static <E, T> String getData(Optional<E> entity, Function<E, T> getter) {
return entity.map(getter).map(String::valueOf).orElse(" -- ");
}
}
}
@@ -0,0 +1,104 @@
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.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("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("No record exists for '" + arguments.get(0) + "'");
} else {
displayAuthToSender(auth, sender);
}
}
/**
* 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.GOLD + "[AuthMe] Player " + auth.getNickname() + " / " + auth.getRealName());
sender.sendMessage("Email: " + auth.getEmail() + ". IP: " + auth.getIp() + ". Group: " + auth.getGroupId());
sender.sendMessage("Quit location: "
+ formatLocation(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld()));
sender.sendMessage("Last login: " + formatLastLogin(auth));
HashedPassword hashedPass = auth.getPassword();
sender.sendMessage("Hash / salt (partial): '" + safeSubstring(hashedPass.getHash(), 6)
+ "' / '" + safeSubstring(hashedPass.getSalt(), 4) + "'");
}
/**
* 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.isEmpty(str)) {
return "";
} else if (str.length() < length) {
return str.substring(0, str.length() / 2) + "...";
} else {
return str.substring(0, length) + "...";
}
}
/**
* Formats the last login date from the given PlayerAuth.
*
* @param auth the auth object
* @return the last login as human readable date
*/
private static String formatLastLogin(PlayerAuth auth) {
long lastLogin = auth.getLastLogin();
if (lastLogin == 0) {
return "Never (0)";
} else {
LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastLogin), ZoneId.systemDefault());
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(date);
}
}
}
@@ -41,8 +41,8 @@ class TestEmailSender implements DebugSection {
@Override
public void execute(CommandSender sender, List<String> arguments) {
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");
sender.sendMessage(ChatColor.RED + "You haven't set all required configurations in config.yml "
+ "for sending emails. Please check your config.yml");
return;
}
@@ -69,7 +69,8 @@ class TestEmailSender implements DebugSection {
}
String email = auth.getEmail();
if (email == null || "your@email.com".equals(email)) {
sender.sendMessage(ChatColor.RED + "No email set for your account! Please use /authme debug mail <email>");
sender.sendMessage(ChatColor.RED + "No email set for your account!"
+ " Please use /authme debug mail <email>");
return null;
}
return email;
@@ -3,8 +3,7 @@ package fr.xephi.authme.command.executable.captcha;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.CaptchaManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.limbo.LimboCache;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
@@ -24,7 +23,7 @@ public class CaptchaCommand extends PlayerCommand {
private CommonService commonService;
@Inject
private LimboCache limboCache;
private LimboService limboService;
@Override
public void runCommand(Player player, List<String> arguments) {
@@ -44,7 +43,7 @@ public class CaptchaCommand extends PlayerCommand {
if (isCorrectCode) {
commonService.send(player, MessageKey.CAPTCHA_SUCCESS);
commonService.send(player, MessageKey.LOGIN_MESSAGE);
limboCache.getPlayerData(player.getName()).getMessageTask().setMuted(false);
limboService.unmuteMessageTask(player);
} else {
String newCode = captchaManager.generateCode(player.getName());
commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode);
@@ -24,7 +24,7 @@ public class ShowEmailCommand extends PlayerCommand {
@Override
public void runCommand(Player player, List<String> arguments) {
PlayerAuth auth = playerCache.getAuth(player.getName());
if (auth.getEmail() != null && !"your@email.com".equalsIgnoreCase(auth.getEmail())) {
if (auth != null && auth.getEmail() != null && !"your@email.com".equalsIgnoreCase(auth.getEmail())) {
commonService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail());
} else {
commonService.send(player, MessageKey.SHOW_NO_EMAIL);
@@ -7,7 +7,10 @@ import fr.xephi.authme.message.MessageKey;
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.RegistrationExecutorProvider;
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;
@@ -42,15 +45,12 @@ public class RegisterCommand extends PlayerCommand {
@Inject
private ValidationService validationService;
@Inject
private RegistrationExecutorProvider registrationExecutorProvider;
@Override
public void runCommand(Player player, List<String> arguments) {
if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
//for two factor auth we don't need to check the usage
management.performRegister(player,
registrationExecutorProvider.getTwoFactorRegisterExecutor(player));
management.performRegister(RegistrationMethod.TWO_FACTOR_REGISTRATION,
TwoFactorRegisterParams.of(player));
return;
} else if (arguments.size() < 1) {
commonService.send(player, MessageKey.USAGE_REGISTER);
@@ -82,8 +82,8 @@ public class RegisterCommand extends PlayerCommand {
final String password = arguments.get(0);
final String email = getEmailIfAvailable(arguments);
management.performRegister(
player, registrationExecutorProvider.getPasswordRegisterExecutor(player, password, email));
management.performRegister(RegistrationMethod.PASSWORD_REGISTRATION,
PasswordRegisterParams.of(player, password, email));
}
}
@@ -138,7 +138,8 @@ public class RegisterCommand extends PlayerCommand {
if (!validationService.validateEmail(email)) {
commonService.send(player, MessageKey.INVALID_EMAIL);
} else if (isSecondArgValidForEmailRegistration(player, arguments)) {
management.performRegister(player, registrationExecutorProvider.getEmailRegisterExecutor(player, email));
management.performRegister(RegistrationMethod.EMAIL_REGISTRATION,
EmailRegisterParams.of(player, email));
}
}