Merge pull request #120 from AuthMe-Team/603-improve-settings

603 improve settings
This commit is contained in:
ljacqu 2016-03-16 21:16:28 +01:00
commit f448cb5f9c
35 changed files with 777 additions and 816 deletions

View File

@ -44,6 +44,7 @@ import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.EmailSettings;
@ -52,6 +53,8 @@ import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.PurgeSettings; import fr.xephi.authme.settings.properties.PurgeSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
@ -464,8 +467,10 @@ public class AuthMe extends JavaPlugin {
private NewSetting createNewSetting() { private NewSetting createNewSetting() {
File configFile = new File(getDataFolder(), "config.yml"); File configFile = new File(getDataFolder(), "config.yml");
PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields();
SettingsMigrationService migrationService = new SettingsMigrationService();
return FileUtils.copyFileFromResource(configFile, "config.yml") return FileUtils.copyFileFromResource(configFile, "config.yml")
? new NewSetting(configFile, getDataFolder()) ? new NewSetting(configFile, getDataFolder(), properties, migrationService)
: null; : null;
} }
@ -674,7 +679,10 @@ public class AuthMe extends JavaPlugin {
} }
String name = player.getName().toLowerCase(); String name = player.getName().toLowerCase();
if (PlayerCache.getInstance().isAuthenticated(name) && !player.isDead() && Settings.isSaveQuitLocationEnabled) { if (PlayerCache.getInstance().isAuthenticated(name) && !player.isDead() && Settings.isSaveQuitLocationEnabled) {
final PlayerAuth auth = new PlayerAuth(player.getName().toLowerCase(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), player.getWorld().getName(), player.getName()); final PlayerAuth auth = PlayerAuth.builder()
.name(player.getName().toLowerCase())
.realName(player.getName())
.location(player.getLocation()).build();
database.updateQuitLoc(auth); database.updateQuitLoc(auth);
} }
if (LimboCache.getInstance().hasLimboPlayer(name)) { if (LimboCache.getInstance().hasLimboPlayer(name)) {

View File

@ -8,20 +8,24 @@ import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* AuthMe player data.
*/ */
public class PlayerAuth { public class PlayerAuth {
/** The player's name in lowercase, e.g. "xephi". */
private String nickname; private String nickname;
/** The player's name in the correct casing, e.g. "Xephi". */
private String realName;
private HashedPassword password; private HashedPassword password;
private String email;
private String ip; private String ip;
private int groupId;
private long lastLogin; private long lastLogin;
// Fields storing the player's quit location
private double x; private double x;
private double y; private double y;
private double z; private double z;
private String world; private String world;
private int groupId;
private String email;
private String realName;
/** /**
* @param serialized String * @param serialized String
@ -31,80 +35,19 @@ public class PlayerAuth {
} }
/** /**
* Constructor for PlayerAuth. * Constructor. Instantiate objects with the {@link #builder() builder}.
* *
* @param nickname String * @param nickname all lowercase name of the player
* @param x double * @param password password
* @param y double * @param groupId the group id
* @param z double * @param ip the associated ip address
* @param world String * @param lastLogin player's last login (timestamp)
* @param realName String * @param x quit location: x coordinate
*/ * @param y quit location: y coordinate
public PlayerAuth(String nickname, double x, double y, double z, String world, String realName) { * @param z quit location: z coordinate
this(nickname, new HashedPassword(""), -1, "127.0.0.1", System.currentTimeMillis(), x, y, z, world, * @param world quit location: world name
"your@email.com", realName); * @param email the associated email
} * @param realName the player's name with proper casing
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param ip String
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String realName) {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param ip String
* @param lastLogin long
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String email, String realName) {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param ip String
* @param lastLogin long
* @param x double
* @param y double
* @param z double
* @param world String
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z,
String world, String email, String realName) {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param password String
* @param groupId int
* @param ip String
* @param lastLogin long
* @param x double
* @param y double
* @param z double
* @param world String
* @param email String
* @param realName String
*/ */
private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) { double x, double y, double z, String world, String email, String realName) {
@ -121,24 +64,6 @@ public class PlayerAuth {
this.realName = realName; this.realName = realName;
} }
/**
* Method set.
*
* @param auth PlayerAuth
*/
public void set(PlayerAuth auth) {
this.setEmail(auth.getEmail());
this.setPassword(auth.getPassword());
this.setIp(auth.getIp());
this.setLastLogin(auth.getLastLogin());
this.setNickname(auth.getNickname());
this.setQuitLocX(auth.getQuitLocX());
this.setQuitLocY(auth.getQuitLocY());
this.setQuitLocZ(auth.getQuitLocZ());
this.setWorld(auth.getWorld());
this.setRealName(auth.getRealName());
}
public void setNickname(String nickname) { public void setNickname(String nickname) {
this.nickname = nickname.toLowerCase(); this.nickname = nickname.toLowerCase();

View File

@ -31,27 +31,13 @@ public class CrazyLoginConverter implements Converter {
this.sender = sender; this.sender = sender;
} }
/**
* Method getInstance.
*
* @return CrazyLoginConverter
*/
public CrazyLoginConverter getInstance() {
return this;
}
/**
* Method run.
*
* @see java.lang.Runnable#run()
*/
@Override @Override
public void run() { public void run() {
String fileName = Settings.crazyloginFileName; String fileName = Settings.crazyloginFileName;
try { try {
File source = new File(AuthMe.getInstance().getDataFolder() + File.separator + fileName); File source = new File(AuthMe.getInstance().getDataFolder() + File.separator + fileName);
if (!source.exists()) { if (!source.exists()) {
sender.sendMessage("Error while trying to import datas, please put " + fileName + " in AuthMe folder!"); sender.sendMessage("Error while trying to import data, please put " + fileName + " in AuthMe folder!");
return; return;
} }
String line; String line;
@ -59,14 +45,17 @@ public class CrazyLoginConverter implements Converter {
while ((line = users.readLine()) != null) { while ((line = users.readLine()) != null) {
if (line.contains("|")) { if (line.contains("|")) {
String[] args = line.split("\\|"); String[] args = line.split("\\|");
if (args.length < 2) if (args.length < 2 || "name".equalsIgnoreCase(args[0])) {
continue; continue;
if (args[0].equalsIgnoreCase("name")) }
continue; String playerName = args[0];
String playerName = args[0].toLowerCase();
String psw = args[1]; String psw = args[1];
if (psw != null) { if (psw != null) {
PlayerAuth auth = new PlayerAuth(playerName, psw, "127.0.0.1", System.currentTimeMillis(), playerName); PlayerAuth auth = PlayerAuth.builder()
.name(playerName.toLowerCase())
.realName(playerName)
.password(psw, null)
.build();
database.saveAuth(auth); database.saveAuth(auth);
} }
} }

View File

@ -5,35 +5,46 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File; import java.io.File;
import static fr.xephi.authme.util.StringUtils.makePath;
public class RoyalAuthConverter implements Converter { public class RoyalAuthConverter implements Converter {
private static final String LAST_LOGIN_PATH = "timestamps.quit";
private static final String PASSWORD_PATH = "login.password";
private final AuthMe plugin; private final AuthMe plugin;
private final DataSource data; private final DataSource dataSource;
public RoyalAuthConverter(AuthMe plugin) { public RoyalAuthConverter(AuthMe plugin) {
this.plugin = plugin; this.plugin = plugin;
this.data = plugin.getDataSource(); this.dataSource = plugin.getDataSource();
} }
@Override @Override
public void run() { public void run() {
for (OfflinePlayer o : plugin.getServer().getOfflinePlayers()) { for (OfflinePlayer player : plugin.getServer().getOfflinePlayers()) {
try { try {
String name = o.getName().toLowerCase(); String name = player.getName().toLowerCase();
String sp = File.separator; File file = new File(makePath(".", "plugins", "RoyalAuth", "userdata", name + ".yml"));
File file = new File("." + sp + "plugins" + sp + "RoyalAuth" + sp + "userdata" + sp + name + ".yml");
if (data.isAuthAvailable(name)) if (dataSource.isAuthAvailable(name) || !file.exists()) {
continue; continue;
if (!file.exists()) }
continue; FileConfiguration configuration = YamlConfiguration.loadConfiguration(file);
RoyalAuthYamlReader ra = new RoyalAuthYamlReader(file); PlayerAuth auth = PlayerAuth.builder()
PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); .name(name)
data.saveAuth(auth); .password(configuration.getString(PASSWORD_PATH), null)
.lastLogin(configuration.getLong(LAST_LOGIN_PATH))
.realName(player.getName())
.build();
dataSource.saveAuth(auth);
} catch (Exception e) { } catch (Exception e) {
ConsoleLogger.logException("Error while trying to import " + o.getName() + " RoyalAuth data", e); ConsoleLogger.logException("Error while trying to import " + player.getName() + " RoyalAuth data", e);
} }
} }
} }

View File

@ -1,22 +0,0 @@
package fr.xephi.authme.converter;
import fr.xephi.authme.settings.CustomConfiguration;
import java.io.File;
class RoyalAuthYamlReader extends CustomConfiguration {
public RoyalAuthYamlReader(File file) {
super(file);
load();
save();
}
public long getLastLogin() {
return getLong("timestamps.quit");
}
public String getHash() {
return getString("login.password");
}
}

View File

@ -12,6 +12,8 @@ import java.io.IOException;
import java.util.Scanner; import java.util.Scanner;
import java.util.UUID; import java.util.UUID;
import static fr.xephi.authme.util.StringUtils.makePath;
class vAuthFileReader { class vAuthFileReader {
private final AuthMe plugin; private final AuthMe plugin;
@ -28,7 +30,7 @@ class vAuthFileReader {
} }
public void convert() { public void convert() {
final File file = new File(plugin.getDataFolder().getParent() + File.separator + "vAuth" + File.separator + "passwords.yml"); final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml"));
Scanner scanner; Scanner scanner;
try { try {
scanner = new Scanner(file); scanner = new Scanner(file);
@ -46,9 +48,15 @@ class vAuthFileReader {
} }
if (pname == null) if (pname == null)
continue; continue;
auth = new PlayerAuth(pname.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", pname); auth = PlayerAuth.builder()
.name(pname.toLowerCase())
.realName(pname)
.password(password, null).build();
} else { } else {
auth = new PlayerAuth(name.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", name); auth = PlayerAuth.builder()
.name(name.toLowerCase())
.realName(name)
.password(password, null).build();
} }
database.saveAuth(auth); database.saveAuth(auth);
} }

View File

@ -50,7 +50,10 @@ class xAuthToFlat {
String pl = getIdPlayer(id); String pl = getIdPlayer(id);
String psw = getPassword(id); String psw = getPassword(id);
if (psw != null && !psw.isEmpty() && pl != null) { if (psw != null && !psw.isEmpty() && pl != null) {
PlayerAuth auth = new PlayerAuth(pl, psw, "192.168.0.1", 0, "your@email.com", pl); PlayerAuth auth = PlayerAuth.builder()
.name(pl.toLowerCase())
.realName(pl)
.password(psw, null).build();
database.saveAuth(auth); database.saveAuth(auth);
} }
} }
@ -69,7 +72,8 @@ class xAuthToFlat {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql); ps = conn.prepareStatement(sql);
ps.setInt(1, id); ps.setInt(1, id);
rs = ps.executeQuery(); rs = ps.executeQuery();
@ -91,7 +95,8 @@ class xAuthToFlat {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = String.format("SELECT * FROM `%s`", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); String sql = String.format("SELECT * FROM `%s`",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql); ps = conn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
@ -112,7 +117,8 @@ class xAuthToFlat {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql); ps = conn.prepareStatement(sql);
ps.setInt(1, accountId); ps.setInt(1, accountId);
rs = ps.executeQuery(); rs = ps.executeQuery();

View File

@ -10,6 +10,7 @@ import fr.xephi.authme.settings.Settings;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
@ -19,6 +20,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Deprecated flat file datasource. The only method guaranteed to work is {@link FlatFile#getAllAuths()}
* as to migrate the entries to {@link SQLite} when AuthMe starts.
*/ */
@Deprecated @Deprecated
public class FlatFile implements DataSource { public class FlatFile implements DataSource {
@ -76,19 +79,11 @@ public class FlatFile implements DataSource {
return true; return true;
} }
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
return false; return false;
} }
@ -110,17 +105,12 @@ public class FlatFile implements DataSource {
BufferedWriter bw = null; BufferedWriter bw = null;
try { try {
bw = new BufferedWriter(new FileWriter(source, true)); bw = new BufferedWriter(new FileWriter(source, true));
bw.write(auth.getNickname() + ":" + auth.getPassword() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n");
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (bw != null) { silentClose(bw);
try {
bw.close();
} catch (IOException ignored) {
}
}
} }
return true; return true;
} }
@ -131,6 +121,7 @@ public class FlatFile implements DataSource {
} }
@Override @Override
// Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file.
public boolean updatePassword(String user, HashedPassword password) { public boolean updatePassword(String user, HashedPassword password) {
user = user.toLowerCase(); user = user.toLowerCase();
if (!isAuthAvailable(user)) { if (!isAuthAvailable(user)) {
@ -144,45 +135,18 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args[0].equals(user)) { if (args[0].equals(user)) {
// Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file. newAuth = buildAuthFromArray(args);
switch (args.length) { if (newAuth != null) {
case 4: { newAuth.setPassword(password);
newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
case 7: {
newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
break;
}
case 8: {
newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
break;
}
case 9: {
newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
break;
}
default: {
newAuth = new PlayerAuth(args[0], password.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
} }
break; break;
} }
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
if (newAuth != null) { if (newAuth != null) {
removeAuth(user); removeAuth(user);
@ -204,44 +168,19 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args[0].equalsIgnoreCase(auth.getNickname())) { if (args[0].equalsIgnoreCase(auth.getNickname())) {
switch (args.length) { newAuth = buildAuthFromArray(args);
case 4: { if (newAuth != null) {
newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]); newAuth.setLastLogin(auth.getLastLogin());
break; newAuth.setIp(auth.getIp());
}
case 7: {
newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
break;
}
case 8: {
newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
break;
}
case 9: {
newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
break;
}
default: {
newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
} }
break; break;
} }
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
if (newAuth != null) { if (newAuth != null) {
removeAuth(auth.getNickname()); removeAuth(auth.getNickname());
@ -263,23 +202,22 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args[0].equalsIgnoreCase(auth.getNickname())) { if (args[0].equalsIgnoreCase(auth.getNickname())) {
newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld(), auth.getEmail(), args[0]); newAuth = buildAuthFromArray(args);
if (newAuth != null) {
newAuth.setQuitLocX(auth.getQuitLocX());
newAuth.setQuitLocY(auth.getQuitLocY());
newAuth.setQuitLocZ(auth.getQuitLocZ());
newAuth.setWorld(auth.getWorld());
newAuth.setEmail(auth.getEmail());
}
break; break;
} }
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
if (newAuth != null) { if (newAuth != null) {
removeAuth(auth.getNickname()); removeAuth(auth.getNickname());
@ -311,25 +249,12 @@ public class FlatFile implements DataSource {
for (String l : lines) { for (String l : lines) {
bw.write(l + "\n"); bw.write(l + "\n");
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return cleared;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return cleared; return cleared;
} finally { } finally {
if (br != null) { silentClose(br);
try { silentClose(bw);
br.close();
} catch (IOException ignored) {
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException ignored) {
}
}
} }
return cleared; return cleared;
} }
@ -355,25 +280,12 @@ public class FlatFile implements DataSource {
for (String l : lines) { for (String l : lines) {
bw.write(l + "\n"); bw.write(l + "\n");
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return false;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return false; return false;
} finally { } finally {
if (br != null) { silentClose(br);
try { silentClose(bw);
br.close();
} catch (IOException ignored) {
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException ignored) {
}
}
} }
return true; return true;
} }
@ -387,35 +299,14 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args[0].equalsIgnoreCase(user)) { if (args[0].equalsIgnoreCase(user)) {
switch (args.length) { return buildAuthFromArray(args);
case 2:
return new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]);
case 3:
return new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]);
case 4:
return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]);
case 7:
return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0]);
case 8:
return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
case 9:
return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
}
} }
} }
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return null;
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return null; return null;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
return null; return null;
} }
@ -437,7 +328,10 @@ public class FlatFile implements DataSource {
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args[0].equals(auth.getNickname())) { if (args[0].equals(auth.getNickname())) {
newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], auth.getEmail(), args[0]); newAuth = buildAuthFromArray(args);
if (newAuth != null) {
newAuth.setEmail(auth.getEmail());
}
break; break;
} }
} }
@ -545,18 +439,8 @@ public class FlatFile implements DataSource {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
} finally { } finally {
if (br != null) { silentClose(br);
try { silentClose(bw);
br.close();
} catch (IOException ignored) {
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException ignored) {
}
}
} }
} }
@ -595,12 +479,7 @@ public class FlatFile implements DataSource {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return result; return result;
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
return result; return result;
} }
@ -624,32 +503,15 @@ public class FlatFile implements DataSource {
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
// We expect to encounter 2, 3, 4, 7, 8 or 9 fields. Ignore the line otherwise PlayerAuth auth = buildAuthFromArray(args);
if (args.length >= 2 && args.length != 5 && args.length != 6 && args.length <= 9) { if (auth != null) {
PlayerAuth.Builder builder = PlayerAuth.builder() auths.add(auth);
.name(args[0]).realName(args[0])
.password(args[1], null);
if (args.length >= 3) builder.ip(args[2]);
if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3]));
if (args.length >= 7) {
builder.locX(Double.parseDouble(args[4]))
.locY(Double.parseDouble(args[5]))
.locZ(Double.parseDouble(args[6]));
}
if (args.length >= 8) builder.locWorld(args[7]);
if (args.length >= 9) builder.email(args[8]);
auths.add(builder.build());
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.logException("Error while getting auths from flatfile:", ex); ConsoleLogger.logException("Error while getting auths from flatfile:", ex);
} finally { } finally {
if (br != null) { silentClose(br);
try {
br.close();
} catch (IOException ignored) {
}
}
} }
return auths; return auths;
} }
@ -663,4 +525,34 @@ public class FlatFile implements DataSource {
public boolean isEmailStored(String email) { public boolean isEmailStored(String email) {
throw new UnsupportedOperationException("Flat file no longer supported"); throw new UnsupportedOperationException("Flat file no longer supported");
} }
private static PlayerAuth buildAuthFromArray(String[] args) {
// Format allows 2, 3, 4, 7, 8, 9 fields. Anything else is unknown
if (args.length >= 2 && args.length <= 9 && args.length != 5 && args.length != 6) {
PlayerAuth.Builder builder = PlayerAuth.builder()
.name(args[0]).realName(args[0]).password(args[1], null);
if (args.length >= 3) builder.ip(args[2]);
if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3]));
if (args.length >= 7) {
builder.locX(Double.parseDouble(args[4]))
.locY(Double.parseDouble(args[5]))
.locZ(Double.parseDouble(args[6]));
}
if (args.length >= 8) builder.locWorld(args[7]);
if (args.length >= 9) builder.email(args[8]);
return builder.build();
}
return null;
}
private static void silentClose(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ignored) {
// silent close
}
}
}
} }

View File

@ -1,94 +0,0 @@
package fr.xephi.authme.settings;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
/**
*/
public abstract class CustomConfiguration extends YamlConfiguration {
private final File configFile;
/**
* Constructor for CustomConfiguration.
*
* @param file the config file
*/
public CustomConfiguration(File file) {
this.configFile = file;
load();
}
public void load() {
try {
super.load(configFile);
} catch (FileNotFoundException e) {
ConsoleLogger.showError("Could not find " + configFile.getName() + ", creating new one...");
reLoad();
} catch (IOException e) {
ConsoleLogger.showError("Could not load " + configFile.getName());
} catch (InvalidConfigurationException e) {
ConsoleLogger.showError(configFile.getName() + " is no valid configuration file");
}
}
public boolean reLoad() {
boolean out = true;
if (!configFile.exists()) {
out = loadResource(configFile);
}
if (out)
load();
return out;
}
public void save() {
try {
super.save(configFile);
} catch (IOException ex) {
ConsoleLogger.showError("Could not save config to " + configFile.getName());
}
}
public File getConfigFile() {
return configFile;
}
private boolean loadResource(File file) {
if (!file.exists()) {
try {
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
return false;
}
int i = file.getPath().indexOf("AuthMe");
if (i > -1) {
String path = file.getPath().substring(i + 6).replace('\\', '/');
InputStream is = AuthMe.class.getResourceAsStream(path);
Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return true;
}
} catch (Exception e) {
ConsoleLogger.logException("Failed to load config from JAR", e);
}
}
return false;
}
public boolean containsAll(String... paths) {
for (String path : paths) {
if (!contains(path)) {
return false;
}
}
return true;
}
}

View File

@ -6,7 +6,6 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.SettingsFieldRetriever;
import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
@ -33,6 +32,8 @@ public class NewSetting {
private final File pluginFolder; private final File pluginFolder;
private final File configFile; private final File configFile;
private final PropertyMap propertyMap;
private final SettingsMigrationService migrationService;
private FileConfiguration configuration; private FileConfiguration configuration;
/** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */ /** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */
private File messagesFile; private File messagesFile;
@ -44,11 +45,16 @@ public class NewSetting {
* *
* @param configFile The configuration file * @param configFile The configuration file
* @param pluginFolder The AuthMe plugin folder * @param pluginFolder The AuthMe plugin folder
* @param propertyMap Collection of all available settings
* @param migrationService Migration service to check the settings file with
*/ */
public NewSetting(File configFile, File pluginFolder) { public NewSetting(File configFile, File pluginFolder, PropertyMap propertyMap,
SettingsMigrationService migrationService) {
this.configuration = YamlConfiguration.loadConfiguration(configFile); this.configuration = YamlConfiguration.loadConfiguration(configFile);
this.configFile = configFile; this.configFile = configFile;
this.pluginFolder = pluginFolder; this.pluginFolder = pluginFolder;
this.propertyMap = propertyMap;
this.migrationService = migrationService;
validateAndLoadOptions(); validateAndLoadOptions();
} }
@ -57,16 +63,21 @@ public class NewSetting {
* *
* @param configuration The FileConfiguration object to use * @param configuration The FileConfiguration object to use
* @param configFile The file to write to * @param configFile The file to write to
* @param pluginFolder The plugin folder
* @param propertyMap The property map whose properties should be verified for presence, or null to skip this * @param propertyMap The property map whose properties should be verified for presence, or null to skip this
* @param migrationService Migration service, or null to skip migration checks
*/ */
@VisibleForTesting @VisibleForTesting
NewSetting(FileConfiguration configuration, File configFile, PropertyMap propertyMap) { NewSetting(FileConfiguration configuration, File configFile, File pluginFolder, PropertyMap propertyMap,
SettingsMigrationService migrationService) {
this.configuration = configuration; this.configuration = configuration;
this.configFile = configFile; this.configFile = configFile;
this.pluginFolder = new File(""); this.pluginFolder = pluginFolder;
this.propertyMap = propertyMap;
this.migrationService = migrationService;
if (propertyMap != null && SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { if (propertyMap != null && migrationService != null) {
save(propertyMap); validateAndLoadOptions();
} }
} }
@ -92,13 +103,6 @@ public class NewSetting {
configuration.set(property.getPath(), value); configuration.set(property.getPath(), value);
} }
/**
* Save the config file. Use after migrating one or more settings.
*/
public void save() {
save(SettingsFieldRetriever.getAllPropertyFields());
}
/** /**
* Return the messages file based on the messages language config. * Return the messages file based on the messages language config.
* *
@ -133,7 +137,10 @@ public class NewSetting {
validateAndLoadOptions(); validateAndLoadOptions();
} }
private void save(PropertyMap propertyMap) { /**
* Save the config file. Use after migrating one or more settings.
*/
public void save() {
try (FileWriter writer = new FileWriter(configFile)) { try (FileWriter writer = new FileWriter(configFile)) {
Yaml simpleYaml = newYaml(false); Yaml simpleYaml = newYaml(false);
Yaml singleQuoteYaml = newYaml(true); Yaml singleQuoteYaml = newYaml(true);
@ -186,11 +193,10 @@ public class NewSetting {
} }
private void validateAndLoadOptions() { private void validateAndLoadOptions() {
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); if (migrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) {
if (SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) {
ConsoleLogger.info("Merged new config options"); ConsoleLogger.info("Merged new config options");
ConsoleLogger.info("Please check your config.yml file for new settings!"); ConsoleLogger.info("Please check your config.yml file for new settings!");
save(propertyMap); save();
} }
messagesFile = buildMessagesFile(); messagesFile = buildMessagesFile();
@ -205,18 +211,20 @@ public class NewSetting {
private File buildMessagesFile() { private File buildMessagesFile() {
String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE); String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE);
File messagesFile = buildMessagesFileFromCode(languageCode);
if (messagesFile.exists()) { String filePath = buildMessagesFilePathFromCode(languageCode);
File messagesFile = new File(pluginFolder, filePath);
if (copyFileFromResource(messagesFile, filePath)) {
return messagesFile; return messagesFile;
} }
return copyFileFromResource(messagesFile, buildMessagesFilePathFromCode(languageCode)) // File doesn't exist or couldn't be copied - try again with default, "en"
? messagesFile String defaultFilePath = buildMessagesFilePathFromCode("en");
: buildMessagesFileFromCode("en"); File defaultFile = new File(pluginFolder, defaultFilePath);
} copyFileFromResource(defaultFile, defaultFilePath);
private File buildMessagesFileFromCode(String language) { // No matter the result, need to return a file
return new File(pluginFolder, buildMessagesFilePathFromCode(language)); return defaultFile;
} }
private static String buildMessagesFilePathFromCode(String language) { private static String buildMessagesFilePathFromCode(String language) {
@ -243,7 +251,7 @@ public class NewSetting {
final Charset charset = Charset.forName("UTF-8"); final Charset charset = Charset.forName("UTF-8");
if (copyFileFromResource(emailFile, "email.html")) { if (copyFileFromResource(emailFile, "email.html")) {
try { try {
return StringUtils.join("", Files.readLines(emailFile, charset)); return StringUtils.join("\n", Files.readLines(emailFile, charset));
} catch (IOException e) { } catch (IOException e) {
ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e); ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e);
} }

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.settings.propertymap.PropertyMap;
@ -18,25 +17,23 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NI
/** /**
* Service for verifying that the configuration is up-to-date. * Service for verifying that the configuration is up-to-date.
*/ */
public final class SettingsMigrationService { public class SettingsMigrationService {
private SettingsMigrationService() {
}
/** /**
* Checks the config file and does any necessary migrations. * Checks the config file and performs any necessary migrations.
* *
* @param configuration The file configuration to check and migrate * @param configuration The file configuration to check and migrate
* @param propertyMap The property map of all existing properties * @param propertyMap The property map of all existing properties
* @param pluginFolder The plugin folder * @param pluginFolder The plugin folder
* @return True if there is a change and the config must be saved, false if the config is up-to-date * @return True if there is a change and the config must be saved, false if the config is up-to-date
*/ */
public static boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) {
return performMigrations(configuration, pluginFolder) || hasDeprecatedProperties(configuration) return performMigrations(configuration, pluginFolder)
|| hasDeprecatedProperties(configuration)
|| !containsAllSettings(configuration, propertyMap); || !containsAllSettings(configuration, propertyMap);
} }
private static boolean performMigrations(FileConfiguration configuration, File pluginFolder) { private boolean performMigrations(FileConfiguration configuration, File pluginFolder) {
boolean changes = false; boolean changes = false;
if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) { if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) {
configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*"); configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*");
@ -50,8 +47,7 @@ public final class SettingsMigrationService {
| migrateJoinLeaveMessages(configuration); | migrateJoinLeaveMessages(configuration);
} }
@VisibleForTesting public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) {
static boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) {
for (Property<?> property : propertyMap.keySet()) { for (Property<?> property : propertyMap.keySet()) {
if (!property.isPresent(configuration)) { if (!property.isPresent(configuration)) {
return false; return false;
@ -80,16 +76,16 @@ public final class SettingsMigrationService {
* Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet. * Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet.
* *
* @param configuration The file configuration to verify * @param configuration The file configuration to verify
* @param dataFolder The plugin data folder * @param pluginFolder The plugin data folder
* @return True if a migration has been completed, false otherwise * @return True if a migration has been completed, false otherwise
*/ */
private static boolean performMailTextToFileMigration(FileConfiguration configuration, File dataFolder) { private static boolean performMailTextToFileMigration(FileConfiguration configuration, File pluginFolder) {
final String oldSettingPath = "Email.mailText"; final String oldSettingPath = "Email.mailText";
if (!configuration.contains(oldSettingPath)) { if (!configuration.contains(oldSettingPath)) {
return false; return false;
} }
final File emailFile = new File(dataFolder, "email.html"); final File emailFile = new File(pluginFolder, "email.html");
final String mailText = configuration.getString(oldSettingPath) final String mailText = configuration.getString(oldSettingPath)
.replace("<playername>", "<playername />") .replace("<playername>", "<playername />")
.replace("<servername>", "<servername />") .replace("<servername>", "<servername />")

View File

@ -12,6 +12,6 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
public @interface Comment { public @interface Comment {
String[] value(); String[] value();
} }

View File

@ -0,0 +1,49 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.FileConfiguration;
import org.yaml.snakeyaml.Yaml;
/**
* Enum property.
*
* @param <E> The enum class
*/
class EnumProperty<E extends Enum<E>> extends Property<E> {
private Class<E> clazz;
public EnumProperty(Class<E> clazz, String path, E defaultValue) {
super(path, defaultValue);
this.clazz = clazz;
}
@Override
public E getFromFile(FileConfiguration configuration) {
String textValue = configuration.getString(getPath());
if (textValue == null) {
return getDefaultValue();
}
E mappedValue = mapToEnum(textValue);
return mappedValue != null ? mappedValue : getDefaultValue();
}
@Override
public boolean isPresent(FileConfiguration configuration) {
return super.isPresent(configuration) && mapToEnum(configuration.getString(getPath())) != null;
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
E value = getFromFile(configuration);
return singleQuoteYaml.dump(value.name());
}
private E mapToEnum(String value) {
for (E entry : clazz.getEnumConstants()) {
if (entry.name().equalsIgnoreCase(value)) {
return entry;
}
}
return null;
}
}

View File

@ -1,48 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.FileConfiguration;
import org.yaml.snakeyaml.Yaml;
/**
* Enum property type.
*
* @param <E> The enum class
*/
class EnumPropertyType<E extends Enum<E>> extends PropertyType<E> {
private Class<E> clazz;
public EnumPropertyType(Class<E> clazz) {
this.clazz = clazz;
}
@Override
public E getFromFile(Property<E> property, FileConfiguration configuration) {
String textValue = configuration.getString(property.getPath());
if (textValue == null) {
return property.getDefaultValue();
}
E mappedValue = mapToEnum(textValue);
return mappedValue != null ? mappedValue : property.getDefaultValue();
}
@Override
public boolean contains(Property<E> property, FileConfiguration configuration) {
return super.contains(property, configuration)
&& mapToEnum(configuration.getString(property.getPath())) != null;
}
@Override
public String toYaml(E value, Yaml simpleYaml, Yaml singleQuoteYaml) {
return singleQuoteYaml.dump(value.name());
}
private E mapToEnum(String value) {
for (E entry : clazz.getEnumConstants()) {
if (entry.name().equalsIgnoreCase(value)) {
return entry;
}
}
return null;
}
}

View File

@ -10,45 +10,27 @@ import java.util.Objects;
/** /**
* Property class, representing a <i>setting</i> that is read from the config.yml file. * Property class, representing a <i>setting</i> that is read from the config.yml file.
*/ */
public class Property<T> { public abstract class Property<T> {
private final PropertyType<T> type;
private final String path; private final String path;
private final T defaultValue; private final T defaultValue;
private Property(PropertyType<T> type, String path, T defaultValue) { protected Property(String path, T defaultValue) {
Objects.requireNonNull(defaultValue); Objects.requireNonNull(defaultValue);
this.type = type;
this.path = path; this.path = path;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
/** /**
* Create a new property. See also {@link #newProperty(PropertyType, String, Object[])} for lists and * Create a new string list property.
* {@link #newProperty(Class, String, Enum)}.
* *
* @param type The property type
* @param path The property's path * @param path The property's path
* @param defaultValue The default value * @param defaultValues The items in the default list
* @param <T> The type of the property
* @return The created property
*/
public static <T> Property<T> newProperty(PropertyType<T> type, String path, T defaultValue) {
return new Property<>(type, path, defaultValue);
}
/**
* Create a new list property.
*
* @param type The list type of the property
* @param path The property's path
* @param defaultValues The default value's items
* @param <U> The list type
* @return The created list property * @return The created list property
*/ */
@SafeVarargs public static Property<List<String>> newListProperty(String path, String... defaultValues) {
public static <U> Property<List<U>> newProperty(PropertyType<List<U>> type, String path, U... defaultValues) { // does not have the same name as not to clash with #newProperty(String, String)
return new Property<>(type, path, Arrays.asList(defaultValues)); return new StringListProperty(path, defaultValues);
} }
/** /**
@ -61,36 +43,28 @@ public class Property<T> {
* @return The created enum property * @return The created enum property
*/ */
public static <E extends Enum<E>> Property<E> newProperty(Class<E> clazz, String path, E defaultValue) { public static <E extends Enum<E>> Property<E> newProperty(Class<E> clazz, String path, E defaultValue) {
return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue); return new EnumProperty<>(clazz, path, defaultValue);
} }
// -----
// Overloaded convenience methods for specific types
// -----
public static Property<Boolean> newProperty(String path, boolean defaultValue) { public static Property<Boolean> newProperty(String path, boolean defaultValue) {
return new Property<>(PropertyType.BOOLEAN, path, defaultValue); return new BooleanProperty(path, defaultValue);
} }
public static Property<Integer> newProperty(String path, int defaultValue) { public static Property<Integer> newProperty(String path, int defaultValue) {
return new Property<>(PropertyType.INTEGER, path, defaultValue); return new IntegerProperty(path, defaultValue);
} }
public static Property<String> newProperty(String path, String defaultValue) { public static Property<String> newProperty(String path, String defaultValue) {
return new Property<>(PropertyType.STRING, path, defaultValue); return new StringProperty(path, defaultValue);
} }
// -----
// Hooks to the PropertyType methods
// -----
/** /**
* Get the property value from the given configuration &ndash; guaranteed to never return null. * Get the property value from the given configuration &ndash; guaranteed to never return null.
* *
* @param configuration The configuration to read the value from * @param configuration The configuration to read the value from
* @return The value, or default if not present * @return The value, or default if not present
*/ */
public T getFromFile(FileConfiguration configuration) { public abstract T getFromFile(FileConfiguration configuration);
return type.getFromFile(this, configuration);
}
/** /**
* Return whether or not the given configuration file contains the property. * Return whether or not the given configuration file contains the property.
@ -99,7 +73,7 @@ public class Property<T> {
* @return True if the property is present, false otherwise * @return True if the property is present, false otherwise
*/ */
public boolean isPresent(FileConfiguration configuration) { public boolean isPresent(FileConfiguration configuration) {
return type.contains(this, configuration); return configuration.contains(path);
} }
/** /**
@ -111,12 +85,9 @@ public class Property<T> {
* @return The generated YAML * @return The generated YAML
*/ */
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
return type.toYaml(getFromFile(configuration), simpleYaml, singleQuoteYaml); return simpleYaml.dump(getFromFile(configuration));
} }
// -----
// Trivial getters
// -----
/** /**
* Return the default value of the property. * Return the default value of the property.
* *
@ -140,4 +111,89 @@ public class Property<T> {
return "Property '" + path + "'"; return "Property '" + path + "'";
} }
/**
* Boolean property.
*/
private static final class BooleanProperty extends Property<Boolean> {
public BooleanProperty(String path, Boolean defaultValue) {
super(path, defaultValue);
}
@Override
public Boolean getFromFile(FileConfiguration configuration) {
return configuration.getBoolean(getPath(), getDefaultValue());
}
}
/**
* Integer property.
*/
private static final class IntegerProperty extends Property<Integer> {
public IntegerProperty(String path, Integer defaultValue) {
super(path, defaultValue);
}
@Override
public Integer getFromFile(FileConfiguration configuration) {
return configuration.getInt(getPath(), getDefaultValue());
}
}
/**
* String property.
*/
private static final class StringProperty extends Property<String> {
public StringProperty(String path, String defaultValue) {
super(path, defaultValue);
}
@Override
public String getFromFile(FileConfiguration configuration) {
return configuration.getString(getPath(), getDefaultValue());
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
return singleQuoteYaml.dump(getFromFile(configuration));
}
}
/**
* String list property.
*/
private static final class StringListProperty extends Property<List<String>> {
public StringListProperty(String path, String[] defaultValues) {
super(path, Arrays.asList(defaultValues));
}
@Override
public List<String> getFromFile(FileConfiguration configuration) {
if (!configuration.isList(getPath())) {
return getDefaultValue();
}
return configuration.getStringList(getPath());
}
@Override
public boolean isPresent(FileConfiguration configuration) {
return configuration.isList(getPath());
}
@Override
public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) {
List<String> value = getFromFile(configuration);
String yaml = singleQuoteYaml.dump(value);
// If the property is a non-empty list we need to append a new line because it will be
// something like the following, which requires a new line:
// - 'item 1'
// - 'second item in list'
return value.isEmpty() ? yaml : "\n" + yaml;
}
}
} }

View File

@ -1,116 +0,0 @@
package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.FileConfiguration;
import org.yaml.snakeyaml.Yaml;
import java.util.List;
/**
* Handles a certain property type and provides type-specific functionality.
*
* @param <T> The value of the property
* @see Property
*/
public abstract class PropertyType<T> {
public static final PropertyType<Boolean> BOOLEAN = new BooleanProperty();
public static final PropertyType<Integer> INTEGER = new IntegerProperty();
public static final PropertyType<String> STRING = new StringProperty();
public static final PropertyType<List<String>> STRING_LIST = new StringListProperty();
/**
* Get the property's value from the given YAML configuration.
*
* @param property The property to retrieve
* @param configuration The YAML configuration to read from
* @return The read value, or the default value if absent
*/
public abstract T getFromFile(Property<T> property, FileConfiguration configuration);
/**
* Return whether the property is present in the given configuration.
*
* @param property The property to search for
* @param configuration The configuration to verify
* @return True if the property is present, false otherwise
*/
public boolean contains(Property<T> property, FileConfiguration configuration) {
return configuration.contains(property.getPath());
}
/**
* Format the value as YAML.
*
* @param value The value to export
* @param simpleYaml YAML object (default)
* @param singleQuoteYaml YAML object set to use single quotes
* @return The generated YAML
*/
public String toYaml(T value, Yaml simpleYaml, Yaml singleQuoteYaml) {
return simpleYaml.dump(value);
}
/**
* Boolean property.
*/
private static final class BooleanProperty extends PropertyType<Boolean> {
@Override
public Boolean getFromFile(Property<Boolean> property, FileConfiguration configuration) {
return configuration.getBoolean(property.getPath(), property.getDefaultValue());
}
}
/**
* Integer property.
*/
private static final class IntegerProperty extends PropertyType<Integer> {
@Override
public Integer getFromFile(Property<Integer> property, FileConfiguration configuration) {
return configuration.getInt(property.getPath(), property.getDefaultValue());
}
}
/**
* String property.
*/
private static final class StringProperty extends PropertyType<String> {
@Override
public String getFromFile(Property<String> property, FileConfiguration configuration) {
return configuration.getString(property.getPath(), property.getDefaultValue());
}
@Override
public String toYaml(String value, Yaml simpleYaml, Yaml singleQuoteYaml) {
return singleQuoteYaml.dump(value);
}
}
/**
* String list property.
*/
private static final class StringListProperty extends PropertyType<List<String>> {
@Override
public List<String> getFromFile(Property<List<String>> property, FileConfiguration configuration) {
if (!configuration.isList(property.getPath())) {
return property.getDefaultValue();
}
return configuration.getStringList(property.getPath());
}
@Override
public boolean contains(Property<List<String>> property, FileConfiguration configuration) {
return configuration.contains(property.getPath()) && configuration.isList(property.getPath());
}
@Override
public String toYaml(List<String> value, Yaml simpleYaml, Yaml singleQuoteYaml) {
String yaml = singleQuoteYaml.dump(value);
// If the property is a non-empty list we need to append a new line because it will be
// something like the following, which requires a new line:
// - 'item 1'
// - 'second item in list'
return value.isEmpty() ? yaml : "\n" + yaml;
}
}
}

View File

@ -5,26 +5,24 @@ import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.SettingsClass; import fr.xephi.authme.settings.domain.SettingsClass;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
import static fr.xephi.authme.settings.domain.PropertyType.STRING;
public class ConverterSettings implements SettingsClass { public class ConverterSettings implements SettingsClass {
@Comment("Rakamak file name") @Comment("Rakamak file name")
public static final Property<String> RAKAMAK_FILE_NAME = public static final Property<String> RAKAMAK_FILE_NAME =
newProperty(STRING, "Converter.Rakamak.fileName", "users.rak"); newProperty("Converter.Rakamak.fileName", "users.rak");
@Comment("Rakamak use IP?") @Comment("Rakamak use IP?")
public static final Property<Boolean> RAKAMAK_USE_IP = public static final Property<Boolean> RAKAMAK_USE_IP =
newProperty(BOOLEAN, "Converter.Rakamak.useIP", false); newProperty("Converter.Rakamak.useIP", false);
@Comment("Rakamak IP file name") @Comment("Rakamak IP file name")
public static final Property<String> RAKAMAK_IP_FILE_NAME = public static final Property<String> RAKAMAK_IP_FILE_NAME =
newProperty(STRING, "Converter.Rakamak.ipFileName", "UsersIp.rak"); newProperty("Converter.Rakamak.ipFileName", "UsersIp.rak");
@Comment("CrazyLogin database file name") @Comment("CrazyLogin database file name")
public static final Property<String> CRAZYLOGIN_FILE_NAME = public static final Property<String> CRAZYLOGIN_FILE_NAME =
newProperty(STRING, "Converter.CrazyLogin.fileName", "accounts.db"); newProperty("Converter.CrazyLogin.fileName", "accounts.db");
private ConverterSettings() { private ConverterSettings() {
} }

View File

@ -6,29 +6,26 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
import static fr.xephi.authme.settings.domain.PropertyType.INTEGER;
import static fr.xephi.authme.settings.domain.PropertyType.STRING;
import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class EmailSettings implements SettingsClass { public class EmailSettings implements SettingsClass {
@Comment("Email SMTP server host") @Comment("Email SMTP server host")
public static final Property<String> SMTP_HOST = public static final Property<String> SMTP_HOST =
newProperty(STRING, "Email.mailSMTP", "smtp.gmail.com"); newProperty("Email.mailSMTP", "smtp.gmail.com");
@Comment("Email SMTP server port") @Comment("Email SMTP server port")
public static final Property<Integer> SMTP_PORT = public static final Property<Integer> SMTP_PORT =
newProperty(INTEGER, "Email.mailPort", 465); newProperty("Email.mailPort", 465);
@Comment("Email account which sends the mails") @Comment("Email account which sends the mails")
public static final Property<String> MAIL_ACCOUNT = public static final Property<String> MAIL_ACCOUNT =
newProperty(STRING, "Email.mailAccount", ""); newProperty("Email.mailAccount", "");
@Comment("Email account password") @Comment("Email account password")
public static final Property<String> MAIL_PASSWORD = public static final Property<String> MAIL_PASSWORD =
newProperty(STRING, "Email.mailPassword", ""); newProperty("Email.mailPassword", "");
@Comment("Custom sender name, replacing the mailAccount name in the email") @Comment("Custom sender name, replacing the mailAccount name in the email")
public static final Property<String> MAIL_SENDER_NAME = public static final Property<String> MAIL_SENDER_NAME =
@ -36,39 +33,39 @@ public class EmailSettings implements SettingsClass {
@Comment("Recovery password length") @Comment("Recovery password length")
public static final Property<Integer> RECOVERY_PASSWORD_LENGTH = public static final Property<Integer> RECOVERY_PASSWORD_LENGTH =
newProperty(INTEGER, "Email.RecoveryPasswordLength", 8); newProperty("Email.RecoveryPasswordLength", 8);
@Comment("Mail Subject") @Comment("Mail Subject")
public static final Property<String> RECOVERY_MAIL_SUBJECT = public static final Property<String> RECOVERY_MAIL_SUBJECT =
newProperty(STRING, "Email.mailSubject", "Your new AuthMe password"); newProperty("Email.mailSubject", "Your new AuthMe password");
@Comment("Like maxRegPerIP but with email") @Comment("Like maxRegPerIP but with email")
public static final Property<Integer> MAX_REG_PER_EMAIL = public static final Property<Integer> MAX_REG_PER_EMAIL =
newProperty(INTEGER, "Email.maxRegPerEmail", 1); newProperty("Email.maxRegPerEmail", 1);
@Comment("Recall players to add an email?") @Comment("Recall players to add an email?")
public static final Property<Boolean> RECALL_PLAYERS = public static final Property<Boolean> RECALL_PLAYERS =
newProperty(BOOLEAN, "Email.recallPlayers", false); newProperty("Email.recallPlayers", false);
@Comment("Delay in minute for the recall scheduler") @Comment("Delay in minute for the recall scheduler")
public static final Property<Integer> DELAY_RECALL = public static final Property<Integer> DELAY_RECALL =
newProperty(INTEGER, "Email.delayRecall", 5); newProperty("Email.delayRecall", 5);
@Comment("Blacklist these domains for emails") @Comment("Blacklist these domains for emails")
public static final Property<List<String>> DOMAIN_BLACKLIST = public static final Property<List<String>> DOMAIN_BLACKLIST =
newProperty(STRING_LIST, "Email.emailBlacklisted", "10minutemail.com"); newListProperty("Email.emailBlacklisted", "10minutemail.com");
@Comment("Whitelist ONLY these domains for emails") @Comment("Whitelist ONLY these domains for emails")
public static final Property<List<String>> DOMAIN_WHITELIST = public static final Property<List<String>> DOMAIN_WHITELIST =
newProperty(STRING_LIST, "Email.emailWhitelisted"); newListProperty("Email.emailWhitelisted");
@Comment("Send the new password drawn in an image?") @Comment("Send the new password drawn in an image?")
public static final Property<Boolean> PASSWORD_AS_IMAGE = public static final Property<Boolean> PASSWORD_AS_IMAGE =
newProperty(BOOLEAN, "Email.generateImage", false); newProperty("Email.generateImage", false);
@Comment("The OAuth2 token") @Comment("The OAuth2 token")
public static final Property<String> OAUTH2_TOKEN = public static final Property<String> OAUTH2_TOKEN =
newProperty(STRING, "Email.emailOauth2Token", ""); newProperty("Email.emailOauth2Token", "");
private EmailSettings() { private EmailSettings() {
} }

View File

@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass; import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
public class HooksSettings implements SettingsClass { public class HooksSettings implements SettingsClass {
@ -48,7 +48,7 @@ public class HooksSettings implements SettingsClass {
@Comment("Other MySQL columns where we need to put the username (case-sensitive)") @Comment("Other MySQL columns where we need to put the username (case-sensitive)")
public static final Property<List<String>> MYSQL_OTHER_USERNAME_COLS = public static final Property<List<String>> MYSQL_OTHER_USERNAME_COLS =
newProperty(PropertyType.STRING_LIST, "ExternalBoardOptions.mySQLOtherUsernameColumns"); newListProperty("ExternalBoardOptions.mySQLOtherUsernameColumns");
@Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)") @Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)")
public static final Property<Integer> BCRYPT_LOG2_ROUND = public static final Property<Integer> BCRYPT_LOG2_ROUND =

View File

@ -6,39 +6,37 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN;
import static fr.xephi.authme.settings.domain.PropertyType.INTEGER;
import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class ProtectionSettings implements SettingsClass { public class ProtectionSettings implements SettingsClass {
@Comment("Enable some servers protection (country based login, antibot)") @Comment("Enable some servers protection (country based login, antibot)")
public static final Property<Boolean> ENABLE_PROTECTION = public static final Property<Boolean> ENABLE_PROTECTION =
newProperty(BOOLEAN, "Protection.enableProtection", false); newProperty("Protection.enableProtection", false);
@Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes", @Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes",
"PLEASE USE QUOTES!"}) "PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_WHITELIST = public static final Property<List<String>> COUNTRIES_WHITELIST =
newProperty(STRING_LIST, "Protection.countries", "US", "GB", "A1"); newListProperty("Protection.countries", "US", "GB", "A1");
@Comment({"Countries not allowed to join the server and register", @Comment({"Countries not allowed to join the server and register",
"PLEASE USE QUOTES!"}) "PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_BLACKLIST = public static final Property<List<String>> COUNTRIES_BLACKLIST =
newProperty(STRING_LIST, "Protection.countriesBlacklist"); newListProperty("Protection.countriesBlacklist");
@Comment("Do we need to enable automatic antibot system?") @Comment("Do we need to enable automatic antibot system?")
public static final Property<Boolean> ENABLE_ANTIBOT = public static final Property<Boolean> ENABLE_ANTIBOT =
newProperty(BOOLEAN, "Protection.enableAntiBot", false); newProperty("Protection.enableAntiBot", false);
@Comment("Max number of player allowed to login in 5 secs before enable AntiBot system automatically") @Comment("Max number of player allowed to login in 5 secs before enable AntiBot system automatically")
public static final Property<Integer> ANTIBOT_SENSIBILITY = public static final Property<Integer> ANTIBOT_SENSIBILITY =
newProperty(INTEGER, "Protection.antiBotSensibility", 5); newProperty("Protection.antiBotSensibility", 5);
@Comment("Duration in minutes of the antibot automatic system") @Comment("Duration in minutes of the antibot automatic system")
public static final Property<Integer> ANTIBOT_DURATION = public static final Property<Integer> ANTIBOT_DURATION =
newProperty(INTEGER, "Protection.antiBotDuration", 10); newProperty("Protection.antiBotDuration", 10);
private ProtectionSettings() { private ProtectionSettings() {
} }

View File

@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass; import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
public class RegistrationSettings implements SettingsClass { public class RegistrationSettings implements SettingsClass {
@ -50,21 +50,21 @@ public class RegistrationSettings implements SettingsClass {
@Comment("Force these commands after /login, without any '/', use %p to replace with player name") @Comment("Force these commands after /login, without any '/', use %p to replace with player name")
public static final Property<List<String>> FORCE_COMMANDS = public static final Property<List<String>> FORCE_COMMANDS =
newProperty(PropertyType.STRING_LIST, "settings.forceCommands"); newListProperty("settings.forceCommands");
@Comment("Force these commands after /login as service console, without any '/'. " @Comment("Force these commands after /login as service console, without any '/'. "
+ "Use %p to replace with player name") + "Use %p to replace with player name")
public static final Property<List<String>> FORCE_COMMANDS_AS_CONSOLE = public static final Property<List<String>> FORCE_COMMANDS_AS_CONSOLE =
newProperty(PropertyType.STRING_LIST, "settings.forceCommandsAsConsole"); newListProperty("settings.forceCommandsAsConsole");
@Comment("Force these commands after /register, without any '/', use %p to replace with player name") @Comment("Force these commands after /register, without any '/', use %p to replace with player name")
public static final Property<List<String>> FORCE_REGISTER_COMMANDS = public static final Property<List<String>> FORCE_REGISTER_COMMANDS =
newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommands"); newListProperty("settings.forceRegisterCommands");
@Comment("Force these commands after /register as a server console, without any '/'. " @Comment("Force these commands after /register as a server console, without any '/'. "
+ "Use %p to replace with player name") + "Use %p to replace with player name")
public static final Property<List<String>> FORCE_REGISTER_COMMANDS_AS_CONSOLE = public static final Property<List<String>> FORCE_REGISTER_COMMANDS_AS_CONSOLE =
newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommandsAsConsole"); newListProperty("settings.forceRegisterCommandsAsConsole");
@Comment({ @Comment({
"Enable to display the welcome message (welcome.txt) after a registration or a login", "Enable to display the welcome message (welcome.txt) after a registration or a login",

View File

@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties;
import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Comment;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass; import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
public class RestrictionSettings implements SettingsClass { public class RestrictionSettings implements SettingsClass {
@ -26,7 +26,7 @@ public class RestrictionSettings implements SettingsClass {
@Comment("Allowed commands for unauthenticated players") @Comment("Allowed commands for unauthenticated players")
public static final Property<List<String>> ALLOW_COMMANDS = public static final Property<List<String>> ALLOW_COMMANDS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands", newListProperty("settings.restrictions.allowCommands",
"login", "register", "l", "reg", "email", "captcha"); "login", "register", "l", "reg", "email", "captcha");
@Comment("Max number of allowed registrations per IP") @Comment("Max number of allowed registrations per IP")
@ -75,7 +75,7 @@ public class RestrictionSettings implements SettingsClass {
" AllowedRestrictedUser:", " AllowedRestrictedUser:",
" - playername;127.0.0.1"}) " - playername;127.0.0.1"})
public static final Property<List<String>> ALLOWED_RESTRICTED_USERS = public static final Property<List<String>> ALLOWED_RESTRICTED_USERS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.AllowedRestrictedUser"); newListProperty("settings.restrictions.AllowedRestrictedUser");
@Comment("Should unregistered players be kicked immediately?") @Comment("Should unregistered players be kicked immediately?")
public static final Property<Boolean> KICK_NON_REGISTERED = public static final Property<Boolean> KICK_NON_REGISTERED =
@ -148,7 +148,7 @@ public class RestrictionSettings implements SettingsClass {
"WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled", "WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled",
"Case-sensitive!"}) "Case-sensitive!"})
public static final Property<List<String>> FORCE_SPAWN_ON_WORLDS = public static final Property<List<String>> FORCE_SPAWN_ON_WORLDS =
newProperty(PropertyType.STRING_LIST, "settings.restrictions.ForceSpawnOnTheseWorlds", newListProperty("settings.restrictions.ForceSpawnOnTheseWorlds",
"world", "world_nether", "world_the_end"); "world", "world_nether", "world_the_end");
@Comment("Ban ip when the ip is not the ip registered in database") @Comment("Ban ip when the ip is not the ip registered in database")
@ -189,7 +189,7 @@ public class RestrictionSettings implements SettingsClass {
"It is case-sensitive!" "It is case-sensitive!"
}) })
public static final Property<List<String>> UNRESTRICTED_NAMES = public static final Property<List<String>> UNRESTRICTED_NAMES =
newProperty(PropertyType.STRING_LIST, "settings.unrestrictions.UnrestrictedName"); newListProperty("settings.unrestrictions.UnrestrictedName");
private RestrictionSettings() { private RestrictionSettings() {

View File

@ -7,8 +7,8 @@ import fr.xephi.authme.settings.domain.SettingsClass;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST;
public class SecuritySettings implements SettingsClass { public class SecuritySettings implements SettingsClass {
@ -98,8 +98,7 @@ public class SecuritySettings implements SettingsClass {
"- '123456'", "- '123456'",
"- 'password'"}) "- 'password'"})
public static final Property<List<String>> UNSAFE_PASSWORDS = public static final Property<List<String>> UNSAFE_PASSWORDS =
newProperty(STRING_LIST, "settings.security.unsafePasswords", newListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321");
"123456", "password", "qwerty", "12345", "54321");
private SecuritySettings() { private SecuritySettings() {
} }

View File

@ -1,5 +1,7 @@
package fr.xephi.authme.settings.propertymap; package fr.xephi.authme.settings.propertymap;
import fr.xephi.authme.ConsoleLogger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -37,14 +39,13 @@ final class Node {
} }
/** /**
* Add a node to the root, creating any intermediary children that don't exist. * Add a child node, creating any intermediary children that don't exist.
* *
* @param root The root to add the path to
* @param fullPath The entire path of the node to add, separate by periods * @param fullPath The entire path of the node to add, separate by periods
*/ */
public static void addNode(Node root, String fullPath) { public void addNode(String fullPath) {
String[] pathParts = fullPath.split("\\."); String[] pathParts = fullPath.split("\\.");
Node parent = root; Node parent = this;
for (String part : pathParts) { for (String part : pathParts) {
Node child = parent.getChild(part); Node child = parent.getChild(part);
if (child == null) { if (child == null) {
@ -59,17 +60,16 @@ final class Node {
* Compare two nodes by this class' sorting behavior (insertion order). * Compare two nodes by this class' sorting behavior (insertion order).
* Note that this method assumes that both supplied paths exist in the tree. * Note that this method assumes that both supplied paths exist in the tree.
* *
* @param root The root of the tree
* @param fullPath1 The full path to the first node * @param fullPath1 The full path to the first node
* @param fullPath2 The full path to the second node * @param fullPath2 The full path to the second node
* @return The comparison result, in the same format as {@link Comparable#compareTo} * @return The comparison result, in the same format as {@link Comparable#compareTo}
*/ */
public static int compare(Node root, String fullPath1, String fullPath2) { public int compare(String fullPath1, String fullPath2) {
String[] path1 = fullPath1.split("\\."); String[] path1 = fullPath1.split("\\.");
String[] path2 = fullPath2.split("\\."); String[] path2 = fullPath2.split("\\.");
int commonCount = 0; int commonCount = 0;
Node commonNode = root; Node commonNode = this;
while (commonCount < path1.length && commonCount < path2.length while (commonCount < path1.length && commonCount < path2.length
&& path1[commonCount].equals(path2[commonCount]) && commonNode != null) { && path1[commonCount].equals(path2[commonCount]) && commonNode != null) {
commonNode = commonNode.getChild(path1[commonCount]); commonNode = commonNode.getChild(path1[commonCount]);
@ -77,7 +77,7 @@ final class Node {
} }
if (commonNode == null) { if (commonNode == null) {
System.err.println("Could not find common node for '" + fullPath1 + "' at index " + commonCount); ConsoleLogger.showError("Could not find common node for '" + fullPath1 + "' at index " + commonCount);
return fullPath1.compareTo(fullPath2); // fallback return fullPath1.compareTo(fullPath2); // fallback
} else if (commonCount >= path1.length || commonCount >= path2.length) { } else if (commonCount >= path1.length || commonCount >= path2.length) {
return Integer.compare(path1.length, path2.length); return Integer.compare(path1.length, path2.length);

View File

@ -23,12 +23,12 @@ final class PropertyMapComparator implements Comparator<Property> {
* @param property The property that is being added * @param property The property that is being added
*/ */
public void add(Property property) { public void add(Property property) {
Node.addNode(parent, property.getPath()); parent.addNode(property.getPath());
} }
@Override @Override
public int compare(Property p1, Property p2) { public int compare(Property p1, Property p2) {
return Node.compare(parent, p1.getPath(), p2.getPath()); return parent.compare(p1.getPath(), p2.getPath());
} }
} }

View File

@ -0,0 +1,75 @@
package fr.xephi.authme.output;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static org.junit.Assert.fail;
/**
* Tests that all YML message files can be loaded.
*/
public class MessagesFileYamlCheckerTest {
/** Path in the resources folder where the message files are located. */
private static final String MESSAGES_FOLDER = "/messages/";
/** Pattern of the message file names. */
private static final Pattern MESSAGE_FILE_PATTERN = Pattern.compile("messages_\\w+\\.yml");
/** Message key that is present in all files. Used to make sure that text is returned. */
private static final MessageKey MESSAGE_KEY = MessageKey.LOGIN_MESSAGE;
@Test
public void shouldAllBeValidYaml() {
// given
List<File> messageFiles = getMessageFiles();
// when
List<String> errors = new ArrayList<>();
for (File file : messageFiles) {
String error = null;
try {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
if (StringUtils.isEmpty(configuration.getString(MESSAGE_KEY.getKey()))) {
error = "Message for '" + MESSAGE_KEY + "' is empty";
}
} catch (Exception e) {
error = "Could not load file: " + StringUtils.formatException(e);
}
if (!StringUtils.isEmpty(error)) {
errors.add(file.getName() + ": " + error);
}
}
// then
if (!errors.isEmpty()) {
fail("Errors during verification of message files:\n-" + StringUtils.join("\n-", errors));
}
}
private List<File> getMessageFiles() {
File folder = TestHelper.getJarFile(MESSAGES_FOLDER);
File[] files = folder.listFiles();
if (files == null) {
throw new IllegalStateException("Could not read folder '" + folder.getName() + "'");
}
List<File> messageFiles = new ArrayList<>();
for (File file : files) {
if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) {
messageFiles.add(file);
}
}
if (messageFiles.isEmpty()) {
throw new IllegalStateException("Error getting message files: list of files is empty");
}
return messageFiles;
}
}

View File

@ -35,10 +35,10 @@ public class ConfigFileConsistencyTest {
// given // given
File configFile = TestHelper.getJarFile(CONFIG_FILE); File configFile = TestHelper.getJarFile(CONFIG_FILE);
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
SettingsMigrationService migration = new SettingsMigrationService();
// when // when
boolean result = SettingsMigrationService.containsAllSettings( boolean result = migration.containsAllSettings(configuration, SettingsFieldRetriever.getAllPropertyFields());
configuration, SettingsFieldRetriever.getAllPropertyFields());
// then // then
if (!result) { if (!result) {

View File

@ -1,27 +1,31 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.ReflectionTestUtils; import com.google.common.io.Files;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestConfiguration;
import fr.xephi.authme.settings.properties.TestEnum; import fr.xephi.authme.settings.properties.TestEnum;
import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.settings.propertymap.PropertyMap;
import fr.xephi.authme.util.WrapperMock;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static fr.xephi.authme.TestHelper.getJarFile; import static fr.xephi.authme.settings.TestSettingsMigrationServices.checkAllPropertiesPresent;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
/** /**
* Integration test for {@link NewSetting}. * Integration test for {@link NewSetting}.
@ -35,16 +39,33 @@ public class NewSettingIntegrationTest {
/** File name for testing difficult values. */ /** File name for testing difficult values. */
private static final String DIFFICULT_FILE = "/config-difficult-values.yml"; private static final String DIFFICULT_FILE = "/config-difficult-values.yml";
private static PropertyMap propertyMap = generatePropertyMap(); private static PropertyMap propertyMap = TestConfiguration.generatePropertyMap();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File testPluginFolder;
@BeforeClass
public static void setUpLogger() {
ConsoleLoggerTestInitializer.setupLogger();
}
@Before
public void setUpTestPluginFolder() throws IOException {
testPluginFolder = temporaryFolder.newFolder();
}
@Test @Test
public void shouldLoadAndReadAllProperties() { public void shouldLoadAndReadAllProperties() throws IOException {
// given // given
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(getJarFile(COMPLETE_FILE)); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(copyFileFromResources(COMPLETE_FILE));
File file = new File("unused"); // Pass another, non-existent file to check if the settings had to be rewritten
File newFile = temporaryFolder.newFile();
// when / then // when / then
NewSetting settings = new NewSetting(configuration, file, propertyMap); NewSetting settings = new NewSetting(configuration, newFile, testPluginFolder, propertyMap,
checkAllPropertiesPresent());
Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder() Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder()
.put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.DURATION_IN_SECONDS, 22)
.put(TestConfiguration.SYSTEM_NAME, "Custom sys name") .put(TestConfiguration.SYSTEM_NAME, "Custom sys name")
@ -61,23 +82,23 @@ public class NewSettingIntegrationTest {
assertThat("Property '" + entry.getKey().getPath() + "' has expected value", assertThat("Property '" + entry.getKey().getPath() + "' has expected value",
settings.getProperty(entry.getKey()), equalTo(entry.getValue())); settings.getProperty(entry.getKey()), equalTo(entry.getValue()));
} }
assertThat(file.exists(), equalTo(false)); assertThat(newFile.length(), equalTo(0L));
} }
@Test @Test
public void shouldWriteMissingProperties() { public void shouldWriteMissingProperties() {
// given/when // given/when
File file = getJarFile(INCOMPLETE_FILE); File file = copyFileFromResources(INCOMPLETE_FILE);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
assumeThat(configuration.contains(TestConfiguration.BORING_COLORS.getPath()), equalTo(false));
// Expectation: File is rewritten to since it does not have all configurations // Expectation: File is rewritten to since it does not have all configurations
new NewSetting(configuration, file, propertyMap); new NewSetting(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent());
// Load the settings again -> checks that what we wrote can be loaded again // Load the settings again -> checks that what we wrote can be loaded again
configuration = YamlConfiguration.loadConfiguration(file); configuration = YamlConfiguration.loadConfiguration(file);
// then // then
NewSetting settings = new NewSetting(configuration, file, propertyMap); NewSetting settings = new NewSetting(configuration, file, testPluginFolder, propertyMap,
checkAllPropertiesPresent());
Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder() Map<Property<?>, Object> expectedValues = ImmutableMap.<Property<?>, Object>builder()
.put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.DURATION_IN_SECONDS, 22)
.put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]") .put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]")
@ -100,29 +121,28 @@ public class NewSettingIntegrationTest {
@Test @Test
public void shouldProperlyExportAnyValues() { public void shouldProperlyExportAnyValues() {
// given // given
File file = getJarFile(DIFFICULT_FILE); File file = copyFileFromResources(DIFFICULT_FILE);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
assumeThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(false));
// Additional string properties // Additional string properties
List<Property<String>> additionalProperties = Arrays.asList( List<Property<String>> additionalProperties = Arrays.asList(
newProperty("more.string1", "it's a text with some \\'apostrophes'"), newProperty("more.string1", "it's a text with some \\'apostrophes'"),
newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test") newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test")
); );
PropertyMap propertyMap = generatePropertyMap();
for (Property<?> property : additionalProperties) { for (Property<?> property : additionalProperties) {
propertyMap.put(property, new String[0]); propertyMap.put(property, new String[0]);
} }
// when // when
new NewSetting(configuration, file, propertyMap); new NewSetting(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent());
// reload the file as settings should hav been rewritten // reload the file as settings should have been rewritten
configuration = YamlConfiguration.loadConfiguration(file); configuration = YamlConfiguration.loadConfiguration(file);
// then // then
// assert that we won't rewrite the settings again! One rewrite should produce a valid, complete configuration // assert that we won't rewrite the settings again! One rewrite should produce a valid, complete configuration
File unusedFile = new File("config-difficult-values.unused.yml"); File unusedFile = new File("config-difficult-values.unused.yml");
NewSetting settings = new NewSetting(configuration, unusedFile, propertyMap); NewSetting settings = new NewSetting(configuration, unusedFile, testPluginFolder, propertyMap,
checkAllPropertiesPresent());
assertThat(unusedFile.exists(), equalTo(false)); assertThat(unusedFile.exists(), equalTo(false));
assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true)); assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true));
@ -147,23 +167,32 @@ public class NewSettingIntegrationTest {
} }
} }
/** @Test
* Generate a property map with all properties in {@link TestConfiguration}. public void shouldReloadSettings() throws IOException {
* // given
* @return The generated property map YamlConfiguration configuration = YamlConfiguration.loadConfiguration(temporaryFolder.newFile());
*/ File fullConfigFile = copyFileFromResources(COMPLETE_FILE);
private static PropertyMap generatePropertyMap() { NewSetting settings = new NewSetting(configuration, fullConfigFile, testPluginFolder, propertyMap,
WrapperMock.createInstance(); TestSettingsMigrationServices.alwaysFulfilled());
PropertyMap propertyMap = new PropertyMap();
for (Field field : TestConfiguration.class.getDeclaredFields()) { // when
Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER),
if (fieldValue instanceof Property<?>) { equalTo(TestConfiguration.RATIO_ORDER.getDefaultValue()));
Property<?> property = (Property<?>) fieldValue; settings.reload();
String[] comments = new String[]{"Comment for '" + property.getPath() + "'"};
propertyMap.put(property, comments); // then
} assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.FIRST));
}
private File copyFileFromResources(String path) {
try {
File source = TestHelper.getJarFile(path);
File destination = temporaryFolder.newFile();
Files.copy(source, destination);
return destination;
} catch (IOException e) {
throw new IllegalStateException("Could not copy test file", e);
} }
return propertyMap;
} }
} }

View File

@ -1,17 +1,30 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestConfiguration;
import fr.xephi.authme.settings.properties.TestEnum; import fr.xephi.authme.settings.properties.TestEnum;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import static fr.xephi.authme.settings.properties.PluginSettings.MESSAGES_LANGUAGE;
import static fr.xephi.authme.util.StringUtils.makePath;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
@ -21,13 +34,28 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* Test for {@link NewSetting}. * Unit tests for {@link NewSetting}.
*/ */
public class NewSettingTest { public class NewSettingTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File testPluginFolder;
@BeforeClass
public static void setUpLogger() {
ConsoleLoggerTestInitializer.setupLogger();
}
@Before
public void setUpTestPluginFolder() throws IOException {
testPluginFolder = temporaryFolder.newFolder();
}
@Test @Test
public void shouldLoadAllConfigs() { public void shouldLoadAllConfigs() {
// given // given
@ -43,7 +71,7 @@ public class NewSettingTest {
setReturnValue(configuration, TestConfiguration.SYSTEM_NAME, "myTestSys"); setReturnValue(configuration, TestConfiguration.SYSTEM_NAME, "myTestSys");
// when / then // when / then
NewSetting settings = new NewSetting(configuration, null, null); NewSetting settings = new NewSetting(configuration, null, null, null, null);
assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20)); assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20));
assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true)); assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true));
@ -59,7 +87,7 @@ public class NewSettingTest {
public void shouldReturnDefaultFile() throws IOException { public void shouldReturnDefaultFile() throws IOException {
// given // given
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
NewSetting settings = new NewSetting(configuration, null, null); NewSetting settings = new NewSetting(configuration, null, null, null, null);
// when // when
String defaultFile = settings.getDefaultMessagesFile(); String defaultFile = settings.getDefaultMessagesFile();
@ -71,6 +99,99 @@ public class NewSettingTest {
assertThat(stream.read(), not(equalTo(0))); assertThat(stream.read(), not(equalTo(0)));
} }
@Test
public void shouldSetProperty() {
// given
YamlConfiguration configuration = mock(YamlConfiguration.class);
NewSetting settings = new NewSetting(configuration, null, null, null, null);
// when
settings.setProperty(TestConfiguration.DUST_LEVEL, -4);
// then
verify(configuration).set(TestConfiguration.DUST_LEVEL.getPath(), -4);
}
@Test
public void shouldReturnMessagesFile() {
// given
// Use some code that is for sure not present in our JAR
String languageCode = "notinjar";
File file = new File(testPluginFolder, makePath("messages", "messages_" + languageCode + ".yml"));
createFile(file);
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(anyString())).willReturn(true);
setReturnValue(configuration, MESSAGES_LANGUAGE, languageCode);
NewSetting settings = new NewSetting(configuration, null, testPluginFolder,
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled());
// when
File messagesFile = settings.getMessagesFile();
// then
assertThat(messagesFile.getPath(), endsWith("messages_" + languageCode + ".yml"));
assertThat(messagesFile.exists(), equalTo(true));
}
@Test
public void shouldCopyDefaultForUnknownLanguageCode() {
// given
YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(anyString())).willReturn(true);
setReturnValue(configuration, MESSAGES_LANGUAGE, "doesntexist");
NewSetting settings = new NewSetting(configuration, null, testPluginFolder,
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled());
// when
File messagesFile = settings.getMessagesFile();
// then
assertThat(messagesFile.getPath(), endsWith("messages_en.yml"));
assertThat(messagesFile.exists(), equalTo(true));
}
@Test
public void shouldLoadWelcomeMessage() throws IOException {
// given
String welcomeMessage = "This is my welcome message for testing\nBye!";
File welcomeFile = new File(testPluginFolder, "welcome.txt");
createFile(welcomeFile);
Files.write(welcomeFile.toPath(), welcomeMessage.getBytes());
YamlConfiguration configuration = mock(YamlConfiguration.class);
setReturnValue(configuration, RegistrationSettings.USE_WELCOME_MESSAGE, true);
NewSetting settings = new NewSetting(configuration, null, testPluginFolder,
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled());
// when
List<String> result = settings.getWelcomeMessage();
// then
assertThat(result, hasSize(2));
assertThat(result.get(0), equalTo(welcomeMessage.split("\\n")[0]));
assertThat(result.get(1), equalTo(welcomeMessage.split("\\n")[1]));
}
@Test
public void shouldLoadEmailMessage() throws IOException {
// given
String emailMessage = "Sample email message\nThat's all!";
File emailFile = new File(testPluginFolder, "email.html");
createFile(emailFile);
Files.write(emailFile.toPath(), emailMessage.getBytes());
YamlConfiguration configuration = mock(YamlConfiguration.class);
NewSetting settings = new NewSetting(configuration, null, testPluginFolder,
TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled());
// when
String result = settings.getEmailMessage();
// then
assertThat(result, equalTo(emailMessage));
}
private static <T> void setReturnValue(YamlConfiguration config, Property<T> property, T value) { private static <T> void setReturnValue(YamlConfiguration config, Property<T> property, T value) {
if (value instanceof String) { if (value instanceof String) {
when(config.getString(eq(property.getPath()), anyString())).thenReturn((String) value); when(config.getString(eq(property.getPath()), anyString())).thenReturn((String) value);
@ -91,4 +212,13 @@ public class NewSettingTest {
setting.getProperty(property).equals(property.getDefaultValue()), equalTo(true)); setting.getProperty(property).equals(property.getDefaultValue()), equalTo(true));
} }
private static void createFile(File file) {
try {
file.getParentFile().mkdirs();
file.createNewFile();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
} }

View File

@ -42,9 +42,10 @@ public class SettingsMigrationServiceTest {
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configTestFile); FileConfiguration configuration = YamlConfiguration.loadConfiguration(configTestFile);
PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields();
assumeThat(testFolder.listFiles(), arrayWithSize(1)); assumeThat(testFolder.listFiles(), arrayWithSize(1));
SettingsMigrationService migrationService = new SettingsMigrationService();
// when // when
boolean result = SettingsMigrationService.checkAndMigrate(configuration, propertyMap, testFolder); boolean result = migrationService.checkAndMigrate(configuration, propertyMap, testFolder);
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));

View File

@ -0,0 +1,49 @@
package fr.xephi.authme.settings;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import org.bukkit.configuration.file.FileConfiguration;
import java.io.File;
/**
* Provides {@link SettingsMigrationService} implementations for testing.
*/
public final class TestSettingsMigrationServices {
private TestSettingsMigrationServices() {
}
/**
* Returns a settings migration service which always answers that all data is up-to-date.
*
* @return test settings migration service
*/
public static SettingsMigrationService alwaysFulfilled() {
return new SettingsMigrationService() {
@Override
public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) {
return false;
}
@Override
public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) {
return true;
}
};
}
/**
* Returns a simple settings migration service which is fulfilled if all properties are present.
*
* @return test settings migration service
*/
public static SettingsMigrationService checkAllPropertiesPresent() {
return new SettingsMigrationService() {
// See parent javadoc: true = some migration had to be done, false = config file is up-to-date
@Override
public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) {
return !super.containsAllSettings(configuration, propertyMap);
}
};
}
}

View File

@ -9,20 +9,19 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Test for {@link EnumPropertyType}. * Test for {@link EnumProperty}.
*/ */
public class EnumPropertyTypeTest { public class EnumPropertyTest {
@Test @Test
public void shouldReturnCorrectEnumValue() { public void shouldReturnCorrectEnumValue() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn("Entry_B"); given(configuration.getString(property.getPath())).willReturn("Entry_B");
// when // when
TestEnum result = propertyType.getFromFile(property, configuration); TestEnum result = property.getFromFile(configuration);
// then // then
assertThat(result, equalTo(TestEnum.ENTRY_B)); assertThat(result, equalTo(TestEnum.ENTRY_B));
@ -31,13 +30,12 @@ public class EnumPropertyTypeTest {
@Test @Test
public void shouldFallBackToDefaultForInvalidValue() { public void shouldFallBackToDefaultForInvalidValue() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn("Bogus"); given(configuration.getString(property.getPath())).willReturn("Bogus");
// when // when
TestEnum result = propertyType.getFromFile(property, configuration); TestEnum result = property.getFromFile(configuration);
// then // then
assertThat(result, equalTo(TestEnum.ENTRY_C)); assertThat(result, equalTo(TestEnum.ENTRY_C));
@ -46,13 +44,12 @@ public class EnumPropertyTypeTest {
@Test @Test
public void shouldFallBackToDefaultForNonExistentValue() { public void shouldFallBackToDefaultForNonExistentValue() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.getString(property.getPath())).willReturn(null); given(configuration.getString(property.getPath())).willReturn(null);
// when // when
TestEnum result = propertyType.getFromFile(property, configuration); TestEnum result = property.getFromFile(configuration);
// then // then
assertThat(result, equalTo(TestEnum.ENTRY_C)); assertThat(result, equalTo(TestEnum.ENTRY_C));
@ -61,14 +58,13 @@ public class EnumPropertyTypeTest {
@Test @Test
public void shouldReturnTrueForContainsCheck() { public void shouldReturnTrueForContainsCheck() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(true); given(configuration.contains(property.getPath())).willReturn(true);
given(configuration.getString(property.getPath())).willReturn("ENTRY_B"); given(configuration.getString(property.getPath())).willReturn("ENTRY_B");
// when // when
boolean result = propertyType.contains(property, configuration); boolean result = property.isPresent(configuration);
// then // then
assertThat(result, equalTo(true)); assertThat(result, equalTo(true));
@ -77,13 +73,12 @@ public class EnumPropertyTypeTest {
@Test @Test
public void shouldReturnFalseForFileWithoutConfig() { public void shouldReturnFalseForFileWithoutConfig() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(false); given(configuration.contains(property.getPath())).willReturn(false);
// when // when
boolean result = propertyType.contains(property, configuration); boolean result = property.isPresent(configuration);
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));
@ -92,14 +87,13 @@ public class EnumPropertyTypeTest {
@Test @Test
public void shouldReturnFalseForUnknownValue() { public void shouldReturnFalseForUnknownValue() {
// given // given
PropertyType<TestEnum> propertyType = new EnumPropertyType<>(TestEnum.class);
Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); Property<TestEnum> property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C);
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
given(configuration.contains(property.getPath())).willReturn(true); given(configuration.contains(property.getPath())).willReturn(true);
given(configuration.getString(property.getPath())).willReturn("wrong value"); given(configuration.getString(property.getPath())).willReturn("wrong value");
// when // when
boolean result = propertyType.contains(property, configuration); boolean result = property.isPresent(configuration);
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));

View File

@ -3,8 +3,7 @@ package fr.xephi.authme.settings.domain;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.invocation.InvocationOnMock; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import org.mockito.stubbing.Answer;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -20,9 +19,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* Test for {@link PropertyType} and the contained subtypes. * Test for {@link Property} and the contained subtypes.
*/ */
public class PropertyTypeTest { public class PropertyTest {
private static YamlConfiguration configuration; private static YamlConfiguration configuration;
@ -31,11 +30,11 @@ public class PropertyTypeTest {
configuration = mock(YamlConfiguration.class); configuration = mock(YamlConfiguration.class);
when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true); when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true);
when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(secondParameter()); when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.getInt(eq("int.path.test"), anyInt())).thenReturn(27); when(configuration.getInt(eq("int.path.test"), anyInt())).thenReturn(27);
when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(secondParameter()); when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.getString(eq("str.path.test"), anyString())).thenReturn("Test value"); when(configuration.getString(eq("str.path.test"), anyString())).thenReturn("Test value");
when(configuration.getString(eq("str.path.wrong"), anyString())).thenAnswer(secondParameter()); when(configuration.getString(eq("str.path.wrong"), anyString())).thenAnswer(new ReturnsArgumentAt(1));
when(configuration.isList("list.path.test")).thenReturn(true); when(configuration.isList("list.path.test")).thenReturn(true);
when(configuration.getStringList("list.path.test")).thenReturn(Arrays.asList("test1", "Test2", "3rd test")); when(configuration.getStringList("list.path.test")).thenReturn(Arrays.asList("test1", "Test2", "3rd test"));
when(configuration.isList("list.path.wrong")).thenReturn(false); when(configuration.isList("list.path.wrong")).thenReturn(false);
@ -120,7 +119,7 @@ public class PropertyTypeTest {
@Test @Test
public void shouldGetStringListValue() { public void shouldGetStringListValue() {
// given // given
Property<List<String>> property = Property.newProperty(PropertyType.STRING_LIST, "list.path.test", "1", "b"); Property<List<String>> property = Property.newListProperty("list.path.test", "1", "b");
// when // when
List<String> result = property.getFromFile(configuration); List<String> result = property.getFromFile(configuration);
@ -133,7 +132,7 @@ public class PropertyTypeTest {
public void shouldGetStringListDefault() { public void shouldGetStringListDefault() {
// given // given
Property<List<String>> property = Property<List<String>> property =
Property.newProperty(PropertyType.STRING_LIST, "list.path.wrong", "default", "list", "elements"); Property.newListProperty("list.path.wrong", "default", "list", "elements");
// when // when
List<String> result = property.getFromFile(configuration); List<String> result = property.getFromFile(configuration);
@ -142,13 +141,4 @@ public class PropertyTypeTest {
assertThat(result, contains("default", "list", "elements")); assertThat(result, contains("default", "list", "elements"));
} }
private static <T> Answer<T> secondParameter() {
return new Answer<T>() {
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
// Return the second parameter -> the default
return (T) invocation.getArguments()[1];
}
};
}
} }

View File

@ -9,6 +9,7 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@ -92,6 +93,17 @@ public class SettingsClassConsistencyTest {
constructors, arrayWithSize(1)); constructors, arrayWithSize(1));
assertThat("Constructor of " + clazz + " is private", assertThat("Constructor of " + clazz + " is private",
Modifier.isPrivate(constructors[0].getModifiers()), equalTo(true)); Modifier.isPrivate(constructors[0].getModifiers()), equalTo(true));
// Ugly hack to get coverage on the private constructors
// http://stackoverflow.com/questions/14077842/how-to-test-a-private-constructor-in-java-application
try {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
constructor.newInstance();
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} }
} }

View File

@ -1,11 +1,14 @@
package fr.xephi.authme.settings.properties; package fr.xephi.authme.settings.properties;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.domain.PropertyType;
import fr.xephi.authme.settings.domain.SettingsClass; import fr.xephi.authme.settings.domain.SettingsClass;
import fr.xephi.authme.settings.propertymap.PropertyMap;
import java.lang.reflect.Field;
import java.util.List; import java.util.List;
import static fr.xephi.authme.settings.domain.Property.newListProperty;
import static fr.xephi.authme.settings.domain.Property.newProperty; import static fr.xephi.authme.settings.domain.Property.newProperty;
/** /**
@ -23,7 +26,7 @@ public final class TestConfiguration implements SettingsClass {
newProperty(TestEnum.class, "sample.ratio.order", TestEnum.SECOND); newProperty(TestEnum.class, "sample.ratio.order", TestEnum.SECOND);
public static final Property<List<String>> RATIO_FIELDS = public static final Property<List<String>> RATIO_FIELDS =
newProperty(PropertyType.STRING_LIST, "sample.ratio.fields", "a", "b", "c"); newListProperty("sample.ratio.fields", "a", "b", "c");
public static final Property<Integer> VERSION_NUMBER = public static final Property<Integer> VERSION_NUMBER =
newProperty("version", 32046); newProperty("version", 32046);
@ -32,7 +35,7 @@ public final class TestConfiguration implements SettingsClass {
newProperty("features.boring.skip", false); newProperty("features.boring.skip", false);
public static final Property<List<String>> BORING_COLORS = public static final Property<List<String>> BORING_COLORS =
newProperty(PropertyType.STRING_LIST, "features.boring.colors"); newListProperty("features.boring.colors");
public static final Property<Integer> DUST_LEVEL = public static final Property<Integer> DUST_LEVEL =
newProperty("features.boring.dustLevel", -1); newProperty("features.boring.dustLevel", -1);
@ -41,10 +44,28 @@ public final class TestConfiguration implements SettingsClass {
newProperty("features.cool.enabled", false); newProperty("features.cool.enabled", false);
public static final Property<List<String>> COOL_OPTIONS = public static final Property<List<String>> COOL_OPTIONS =
newProperty(PropertyType.STRING_LIST, "features.cool.options", "Sparks", "Sprinkles"); newListProperty("features.cool.options", "Sparks", "Sprinkles");
private TestConfiguration() { private TestConfiguration() {
} }
/**
* Generate a property map with all properties in {@link TestConfiguration}.
*
* @return The generated property map
*/
public static PropertyMap generatePropertyMap() {
PropertyMap propertyMap = new PropertyMap();
for (Field field : TestConfiguration.class.getDeclaredFields()) {
Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName());
if (fieldValue instanceof Property<?>) {
Property<?> property = (Property<?>) fieldValue;
String[] comments = new String[]{"Comment for '" + property.getPath() + "'"};
propertyMap.put(property, comments);
}
}
return propertyMap;
}
} }