Merge branch 'master' of https://github.com/AuthMe/AuthMeReloaded into 5.4-Dev

This commit is contained in:
ljacqu 2017-07-23 12:12:16 +02:00
commit 870810a230
51 changed files with 1200 additions and 411 deletions

View File

@ -150,7 +150,7 @@
<property name="scope" value="package"/> <property name="scope" value="package"/>
<property name="allowMissingThrowsTags" value="true"/> <property name="allowMissingThrowsTags" value="true"/>
<property name="minLineCount" value="4"/> <property name="minLineCount" value="4"/>
<property name="allowedAnnotations" value="Override, Test, SectionComments, EventHandler"/> <property name="allowedAnnotations" value="Override, Test, SectionComments, EventHandler, Before, BeforeClass"/>
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- exclude CTOR_DEF --> <property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- exclude CTOR_DEF -->
</module> </module>
<module name="JavadocMethod"> <module name="JavadocMethod">

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Thu Jul 06 18:45:51 CEST 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Sat Jul 15 19:32:28 CEST 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -58,6 +58,9 @@ DataSource:
mySQLlastlocPitch: 'pitch' mySQLlastlocPitch: 'pitch'
# Overrides the size of the DB Connection Pool, -1 = Auto # Overrides the size of the DB Connection Pool, -1 = Auto
poolSize: -1 poolSize: -1
# The maximum lifetime of a connection in the pool, default = 1800 seconds
# You should set this at least 30 seconds less than mysql server wait_timeout
maxLifetime: 1800
ExternalBoardOptions: ExternalBoardOptions:
# Column for storing players passwords salts # Column for storing players passwords salts
mySQLColumnSalt: '' mySQLColumnSalt: ''
@ -194,8 +197,10 @@ settings:
maxJoinPerIp: 0 maxJoinPerIp: 0
# AuthMe will NEVER teleport players if set to true! # AuthMe will NEVER teleport players if set to true!
noTeleport: false noTeleport: false
# Regex syntax for allowed chars in passwords # Regex syntax for allowed chars in passwords. The default [!-~] allows all visible ASCII
allowedPasswordCharacters: '[\x21-\x7E]*' # characters, which is what we recommend. See also http://asciitable.com
# You can test your regex with https://regex101.com
allowedPasswordCharacters: '[!-~]*'
# Threshold of the other accounts command, a value less than 2 means disabled. # Threshold of the other accounts command, a value less than 2 means disabled.
otherAccountsCmdThreshold: 0 otherAccountsCmdThreshold: 0
# Command to run when a user has more accounts than the configured threshold. # Command to run when a user has more accounts than the configured threshold.
@ -476,8 +481,9 @@ limbo:
# Note: if you change this setting all data will be migrated. If you have a lot of data, # Note: if you change this setting all data will be migrated. If you have a lot of data,
# change this setting only on server restart, not with /authme reload. # change this setting only on server restart, not with /authme reload.
distributionSize: 'SIXTEEN' distributionSize: 'SIXTEEN'
# Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE. # Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE, NOTHING.
# RESTORE sets back the old property from the player. # RESTORE sets back the old property from the player. NOTHING will prevent AuthMe
# from modifying the 'allow flight' property on the player.
restoreAllowFlight: 'RESTORE' restoreAllowFlight: 'RESTORE'
# Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO. # Restore fly speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
# RESTORE: restore the speed the player had; # RESTORE: restore the speed the player had;
@ -487,7 +493,7 @@ limbo:
restoreFlySpeed: 'RESTORE_NO_ZERO' restoreFlySpeed: 'RESTORE_NO_ZERO'
# Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO. # Restore walk speed: RESTORE, DEFAULT, MAX_RESTORE, RESTORE_NO_ZERO.
# See above for a description of the values. # See above for a description of the values.
restoreWalkSpeed: 'MAX_RESTORE' restoreWalkSpeed: 'RESTORE_NO_ZERO'
BackupSystem: BackupSystem:
# General configuration for backups: if false, no backups are possible # General configuration for backups: if false, no backups are possible
ActivateBackup: false ActivateBackup: false
@ -528,4 +534,4 @@ To change settings on a running server, save your changes to config.yml and use
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Thu Jul 06 18:45:51 CEST 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Jul 15 19:32:28 CEST 2017

View File

@ -69,6 +69,12 @@ public class CommandHandler {
return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus()); return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus());
} }
/**
* Processes the given {@link FoundCommandResult} for the provided command sender.
*
* @param sender the command sender who executed the command
* @param result the command mapping result
*/
private void handleCommandResult(CommandSender sender, FoundCommandResult result) { private void handleCommandResult(CommandSender sender, FoundCommandResult result) {
switch (result.getResultStatus()) { switch (result.getResultStatus()) {
case SUCCESS: case SUCCESS:

View File

@ -71,279 +71,13 @@ public class CommandInitializer {
/** /**
* Builds the command description objects for all available AuthMe commands. * Builds the command description objects for all available AuthMe commands.
*/ */
@SuppressWarnings({"checkstyle:LocalVariableName", "checkstyle:AbbreviationAsWordInName"})
private void buildCommands() { private void buildCommands() {
// Register the base AuthMe Reloaded command // Register /authme and /email commands
final CommandDescription AUTHME_BASE = CommandDescription.builder() CommandDescription authMeBase = buildAuthMeBaseCommand();
.labels("authme") CommandDescription emailBase = buildEmailBaseCommand();
.description("AuthMe op commands")
.detailedDescription("The main AuthMeReloaded command. The root for all admin commands.")
.executableCommand(AuthMeCommand.class)
.register();
// Register the register command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("register", "reg", "r")
.description("Register a player")
.detailedDescription("Register the specified player with the specified password.")
.withArgument("player", "Player name", false)
.withArgument("password", "Password", false)
.permission(AdminPermission.REGISTER)
.executableCommand(RegisterAdminCommand.class)
.register();
// Register the unregister command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("unregister", "unreg", "unr")
.description("Unregister a player")
.detailedDescription("Unregister the specified player.")
.withArgument("player", "Player name", false)
.permission(AdminPermission.UNREGISTER)
.executableCommand(UnregisterAdminCommand.class)
.register();
// Register the forcelogin command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("forcelogin", "login")
.description("Enforce login player")
.detailedDescription("Enforce the specified player to login.")
.withArgument("player", "Online player name", true)
.permission(AdminPermission.FORCE_LOGIN)
.executableCommand(ForceLoginCommand.class)
.register();
// Register the changepassword command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("password", "changepassword", "changepass", "cp")
.description("Change a player's password")
.detailedDescription("Change the password of a player.")
.withArgument("player", "Player name", false)
.withArgument("pwd", "New password", false)
.permission(AdminPermission.CHANGE_PASSWORD)
.executableCommand(ChangePasswordAdminCommand.class)
.register();
// Register the last login command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("lastlogin", "ll")
.description("Player's last login")
.detailedDescription("View the date of the specified players last login.")
.withArgument("player", "Player name", true)
.permission(AdminPermission.LAST_LOGIN)
.executableCommand(LastLoginCommand.class)
.register();
// Register the accounts command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("accounts", "account")
.description("Display player accounts")
.detailedDescription("Display all accounts of a player by his player name or IP.")
.withArgument("player", "Player name or IP", true)
.permission(AdminPermission.ACCOUNTS)
.executableCommand(AccountsCommand.class)
.register();
// Register the getemail command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("email", "mail", "getemail", "getmail")
.description("Display player's email")
.detailedDescription("Display the email address of the specified player if set.")
.withArgument("player", "Player name", true)
.permission(AdminPermission.GET_EMAIL)
.executableCommand(GetEmailCommand.class)
.register();
// Register the setemail command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("setemail", "setmail", "chgemail", "chgmail")
.description("Change player's email")
.detailedDescription("Change the email address of the specified player.")
.withArgument("player", "Player name", false)
.withArgument("email", "Player email", false)
.permission(AdminPermission.CHANGE_EMAIL)
.executableCommand(SetEmailCommand.class)
.register();
// Register the getip command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("getip", "ip")
.description("Get player's IP")
.detailedDescription("Get the IP address of the specified online player.")
.withArgument("player", "Player name", false)
.permission(AdminPermission.GET_IP)
.executableCommand(GetIpCommand.class)
.register();
// Register the spawn command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("spawn", "home")
.description("Teleport to spawn")
.detailedDescription("Teleport to the spawn.")
.permission(AdminPermission.SPAWN)
.executableCommand(SpawnCommand.class)
.register();
// Register the setspawn command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("setspawn", "chgspawn")
.description("Change the spawn")
.detailedDescription("Change the player's spawn to your current position.")
.permission(AdminPermission.SET_SPAWN)
.executableCommand(SetSpawnCommand.class)
.register();
// Register the firstspawn command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("firstspawn", "firsthome")
.description("Teleport to first spawn")
.detailedDescription("Teleport to the first spawn.")
.permission(AdminPermission.FIRST_SPAWN)
.executableCommand(FirstSpawnCommand.class)
.register();
// Register the setfirstspawn command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("setfirstspawn", "chgfirstspawn")
.description("Change the first spawn")
.detailedDescription("Change the first player's spawn to your current position.")
.permission(AdminPermission.SET_FIRST_SPAWN)
.executableCommand(SetFirstSpawnCommand.class)
.register();
// Register the purge command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("purge", "delete")
.description("Purge old data")
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
.withArgument("days", "Number of days", false)
.withArgument("all", "Add 'all' at the end to also purge players with lastlogin = 0", true)
.permission(AdminPermission.PURGE)
.executableCommand(PurgeCommand.class)
.register();
// Purge player command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("purgeplayer")
.description("Purges the data of one player")
.detailedDescription("Purges data of the given player.")
.withArgument("player", "The player to purge", false)
.withArgument("options", "'force' to run without checking if player is registered", true)
.permission(AdminPermission.PURGE_PLAYER)
.executableCommand(PurgePlayerCommand.class)
.register();
// Backup command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("backup")
.description("Perform a backup")
.detailedDescription("Creates a backup of the registered users.")
.permission(AdminPermission.BACKUP)
.executableCommand(BackupCommand.class)
.register();
// Register the purgelastposition command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("resetpos", "purgelastposition", "purgelastpos", "resetposition",
"resetlastposition", "resetlastpos")
.description("Purge player's last position")
.detailedDescription("Purge the last know position of the specified player or all of them.")
.withArgument("player/*", "Player name or * for all players", false)
.permission(AdminPermission.PURGE_LAST_POSITION)
.executableCommand(PurgeLastPositionCommand.class)
.register();
// Register the purgebannedplayers command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("purgebannedplayers", "purgebannedplayer", "deletebannedplayers", "deletebannedplayer")
.description("Purge banned players data")
.detailedDescription("Purge all AuthMeReloaded data for banned players.")
.permission(AdminPermission.PURGE_BANNED_PLAYERS)
.executableCommand(PurgeBannedPlayersCommand.class)
.register();
// Register the switchantibot command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("switchantibot", "toggleantibot", "antibot")
.description("Switch AntiBot mode")
.detailedDescription("Switch or toggle the AntiBot mode to the specified state.")
.withArgument("mode", "ON / OFF", true)
.permission(AdminPermission.SWITCH_ANTIBOT)
.executableCommand(SwitchAntiBotCommand.class)
.register();
// Register the reload command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("reload", "rld")
.description("Reload plugin")
.detailedDescription("Reload the AuthMeReloaded plugin.")
.permission(AdminPermission.RELOAD)
.executableCommand(ReloadCommand.class)
.register();
// Register the version command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("version", "ver", "v", "about", "info")
.description("Version info")
.detailedDescription("Show detailed information about the installed AuthMeReloaded version, the "
+ "developers, contributors, and license.")
.executableCommand(VersionCommand.class)
.register();
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("converter", "convert", "conv")
.description("Converter command")
.detailedDescription("Converter command for AuthMeReloaded.")
.withArgument("job", "Conversion job: xauth / crazylogin / rakamak / "
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", true)
.permission(AdminPermission.CONVERTER)
.executableCommand(ConverterCommand.class)
.register();
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("messages", "msg")
.description("Add missing messages")
.detailedDescription("Adds missing messages to the current messages file.")
.permission(AdminPermission.UPDATE_MESSAGES)
.executableCommand(MessagesCommand.class)
.register();
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("debug", "dbg")
.description("Debug features")
.detailedDescription("Allows various operations for debugging.")
.withArgument("child", "The child to execute", true)
.withArgument("arg", "argument (depends on debug section)", true)
.withArgument("arg", "argument (depends on debug section)", true)
.permission(DebugSectionPermissions.DEBUG_COMMAND)
.executableCommand(DebugCommand.class)
.register();
// Register the base login command // Register the base login command
final CommandDescription LOGIN_BASE = CommandDescription.builder() CommandDescription loginBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("login", "l", "log") .labels("login", "l", "log")
.description("Login command") .description("Login command")
@ -354,7 +88,7 @@ public class CommandInitializer {
.register(); .register();
// Register the base logout command // Register the base logout command
CommandDescription LOGOUT_BASE = CommandDescription.builder() CommandDescription logoutBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("logout") .labels("logout")
.description("Logout command") .description("Logout command")
@ -364,7 +98,7 @@ public class CommandInitializer {
.register(); .register();
// Register the base register command // Register the base register command
final CommandDescription REGISTER_BASE = CommandDescription.builder() CommandDescription registerBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("register", "reg") .labels("register", "reg")
.description("Register an account") .description("Register an account")
@ -376,7 +110,7 @@ public class CommandInitializer {
.register(); .register();
// Register the base unregister command // Register the base unregister command
CommandDescription UNREGISTER_BASE = CommandDescription.builder() CommandDescription unregisterBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("unregister", "unreg") .labels("unregister", "unreg")
.description("Unregister an account") .description("Unregister an account")
@ -387,7 +121,7 @@ public class CommandInitializer {
.register(); .register();
// Register the base changepassword command // Register the base changepassword command
final CommandDescription CHANGE_PASSWORD_BASE = CommandDescription.builder() CommandDescription changePasswordBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("changepassword", "changepass", "cp") .labels("changepassword", "changepass", "cp")
.description("Change password of an account") .description("Change password of an account")
@ -398,8 +132,317 @@ public class CommandInitializer {
.executableCommand(ChangePasswordCommand.class) .executableCommand(ChangePasswordCommand.class)
.register(); .register();
// Register the base captcha command
CommandDescription captchaBase = CommandDescription.builder()
.parent(null)
.labels("captcha")
.description("Captcha Command")
.detailedDescription("Captcha command for AuthMeReloaded.")
.withArgument("captcha", "The Captcha", false)
.permission(PlayerPermission.CAPTCHA)
.executableCommand(CaptchaCommand.class)
.register();
List<CommandDescription> baseCommands = ImmutableList.of(
authMeBase,
emailBase,
loginBase,
logoutBase,
registerBase,
unregisterBase,
changePasswordBase,
captchaBase);
setHelpOnAllBases(baseCommands);
commands = baseCommands;
}
/**
* Creates a command description object for {@code /authme} including its children.
*
* @return the authme base command description
*/
private CommandDescription buildAuthMeBaseCommand() {
// Register the base AuthMe Reloaded command
CommandDescription authmeBase = CommandDescription.builder()
.labels("authme")
.description("AuthMe op commands")
.detailedDescription("The main AuthMeReloaded command. The root for all admin commands.")
.executableCommand(AuthMeCommand.class)
.register();
// Register the register command
CommandDescription.builder()
.parent(authmeBase)
.labels("register", "reg", "r")
.description("Register a player")
.detailedDescription("Register the specified player with the specified password.")
.withArgument("player", "Player name", false)
.withArgument("password", "Password", false)
.permission(AdminPermission.REGISTER)
.executableCommand(RegisterAdminCommand.class)
.register();
// Register the unregister command
CommandDescription.builder()
.parent(authmeBase)
.labels("unregister", "unreg", "unr")
.description("Unregister a player")
.detailedDescription("Unregister the specified player.")
.withArgument("player", "Player name", false)
.permission(AdminPermission.UNREGISTER)
.executableCommand(UnregisterAdminCommand.class)
.register();
// Register the forcelogin command
CommandDescription.builder()
.parent(authmeBase)
.labels("forcelogin", "login")
.description("Enforce login player")
.detailedDescription("Enforce the specified player to login.")
.withArgument("player", "Online player name", true)
.permission(AdminPermission.FORCE_LOGIN)
.executableCommand(ForceLoginCommand.class)
.register();
// Register the changepassword command
CommandDescription.builder()
.parent(authmeBase)
.labels("password", "changepassword", "changepass", "cp")
.description("Change a player's password")
.detailedDescription("Change the password of a player.")
.withArgument("player", "Player name", false)
.withArgument("pwd", "New password", false)
.permission(AdminPermission.CHANGE_PASSWORD)
.executableCommand(ChangePasswordAdminCommand.class)
.register();
// Register the last login command
CommandDescription.builder()
.parent(authmeBase)
.labels("lastlogin", "ll")
.description("Player's last login")
.detailedDescription("View the date of the specified players last login.")
.withArgument("player", "Player name", true)
.permission(AdminPermission.LAST_LOGIN)
.executableCommand(LastLoginCommand.class)
.register();
// Register the accounts command
CommandDescription.builder()
.parent(authmeBase)
.labels("accounts", "account")
.description("Display player accounts")
.detailedDescription("Display all accounts of a player by his player name or IP.")
.withArgument("player", "Player name or IP", true)
.permission(AdminPermission.ACCOUNTS)
.executableCommand(AccountsCommand.class)
.register();
// Register the getemail command
CommandDescription.builder()
.parent(authmeBase)
.labels("email", "mail", "getemail", "getmail")
.description("Display player's email")
.detailedDescription("Display the email address of the specified player if set.")
.withArgument("player", "Player name", true)
.permission(AdminPermission.GET_EMAIL)
.executableCommand(GetEmailCommand.class)
.register();
// Register the setemail command
CommandDescription.builder()
.parent(authmeBase)
.labels("setemail", "setmail", "chgemail", "chgmail")
.description("Change player's email")
.detailedDescription("Change the email address of the specified player.")
.withArgument("player", "Player name", false)
.withArgument("email", "Player email", false)
.permission(AdminPermission.CHANGE_EMAIL)
.executableCommand(SetEmailCommand.class)
.register();
// Register the getip command
CommandDescription.builder()
.parent(authmeBase)
.labels("getip", "ip")
.description("Get player's IP")
.detailedDescription("Get the IP address of the specified online player.")
.withArgument("player", "Player name", false)
.permission(AdminPermission.GET_IP)
.executableCommand(GetIpCommand.class)
.register();
// Register the spawn command
CommandDescription.builder()
.parent(authmeBase)
.labels("spawn", "home")
.description("Teleport to spawn")
.detailedDescription("Teleport to the spawn.")
.permission(AdminPermission.SPAWN)
.executableCommand(SpawnCommand.class)
.register();
// Register the setspawn command
CommandDescription.builder()
.parent(authmeBase)
.labels("setspawn", "chgspawn")
.description("Change the spawn")
.detailedDescription("Change the player's spawn to your current position.")
.permission(AdminPermission.SET_SPAWN)
.executableCommand(SetSpawnCommand.class)
.register();
// Register the firstspawn command
CommandDescription.builder()
.parent(authmeBase)
.labels("firstspawn", "firsthome")
.description("Teleport to first spawn")
.detailedDescription("Teleport to the first spawn.")
.permission(AdminPermission.FIRST_SPAWN)
.executableCommand(FirstSpawnCommand.class)
.register();
// Register the setfirstspawn command
CommandDescription.builder()
.parent(authmeBase)
.labels("setfirstspawn", "chgfirstspawn")
.description("Change the first spawn")
.detailedDescription("Change the first player's spawn to your current position.")
.permission(AdminPermission.SET_FIRST_SPAWN)
.executableCommand(SetFirstSpawnCommand.class)
.register();
// Register the purge command
CommandDescription.builder()
.parent(authmeBase)
.labels("purge", "delete")
.description("Purge old data")
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
.withArgument("days", "Number of days", false)
.withArgument("all", "Add 'all' at the end to also purge players with lastlogin = 0", true)
.permission(AdminPermission.PURGE)
.executableCommand(PurgeCommand.class)
.register();
// Purge player command
CommandDescription.builder()
.parent(authmeBase)
.labels("purgeplayer")
.description("Purges the data of one player")
.detailedDescription("Purges data of the given player.")
.withArgument("player", "The player to purge", false)
.withArgument("options", "'force' to run without checking if player is registered", true)
.permission(AdminPermission.PURGE_PLAYER)
.executableCommand(PurgePlayerCommand.class)
.register();
// Backup command
CommandDescription.builder()
.parent(authmeBase)
.labels("backup")
.description("Perform a backup")
.detailedDescription("Creates a backup of the registered users.")
.permission(AdminPermission.BACKUP)
.executableCommand(BackupCommand.class)
.register();
// Register the purgelastposition command
CommandDescription.builder()
.parent(authmeBase)
.labels("resetpos", "purgelastposition", "purgelastpos", "resetposition",
"resetlastposition", "resetlastpos")
.description("Purge player's last position")
.detailedDescription("Purge the last know position of the specified player or all of them.")
.withArgument("player/*", "Player name or * for all players", false)
.permission(AdminPermission.PURGE_LAST_POSITION)
.executableCommand(PurgeLastPositionCommand.class)
.register();
// Register the purgebannedplayers command
CommandDescription.builder()
.parent(authmeBase)
.labels("purgebannedplayers", "purgebannedplayer", "deletebannedplayers", "deletebannedplayer")
.description("Purge banned players data")
.detailedDescription("Purge all AuthMeReloaded data for banned players.")
.permission(AdminPermission.PURGE_BANNED_PLAYERS)
.executableCommand(PurgeBannedPlayersCommand.class)
.register();
// Register the switchantibot command
CommandDescription.builder()
.parent(authmeBase)
.labels("switchantibot", "toggleantibot", "antibot")
.description("Switch AntiBot mode")
.detailedDescription("Switch or toggle the AntiBot mode to the specified state.")
.withArgument("mode", "ON / OFF", true)
.permission(AdminPermission.SWITCH_ANTIBOT)
.executableCommand(SwitchAntiBotCommand.class)
.register();
// Register the reload command
CommandDescription.builder()
.parent(authmeBase)
.labels("reload", "rld")
.description("Reload plugin")
.detailedDescription("Reload the AuthMeReloaded plugin.")
.permission(AdminPermission.RELOAD)
.executableCommand(ReloadCommand.class)
.register();
// Register the version command
CommandDescription.builder()
.parent(authmeBase)
.labels("version", "ver", "v", "about", "info")
.description("Version info")
.detailedDescription("Show detailed information about the installed AuthMeReloaded version, the "
+ "developers, contributors, and license.")
.executableCommand(VersionCommand.class)
.register();
CommandDescription.builder()
.parent(authmeBase)
.labels("converter", "convert", "conv")
.description("Converter command")
.detailedDescription("Converter command for AuthMeReloaded.")
.withArgument("job", "Conversion job: xauth / crazylogin / rakamak / "
+ "royalauth / vauth / sqliteToSql / mysqlToSqlite / loginsecurity", true)
.permission(AdminPermission.CONVERTER)
.executableCommand(ConverterCommand.class)
.register();
CommandDescription.builder()
.parent(authmeBase)
.labels("messages", "msg")
.description("Add missing messages")
.detailedDescription("Adds missing messages to the current messages file.")
.permission(AdminPermission.UPDATE_MESSAGES)
.executableCommand(MessagesCommand.class)
.register();
CommandDescription.builder()
.parent(authmeBase)
.labels("debug", "dbg")
.description("Debug features")
.detailedDescription("Allows various operations for debugging.")
.withArgument("child", "The child to execute", true)
.withArgument("arg", "argument (depends on debug section)", true)
.withArgument("arg", "argument (depends on debug section)", true)
.permission(DebugSectionPermissions.DEBUG_COMMAND)
.executableCommand(DebugCommand.class)
.register();
return authmeBase;
}
/**
* Creates a command description for {@code /email} including its children.
*
* @return the email base command description
*/
private CommandDescription buildEmailBaseCommand() {
// Register the base Email command // Register the base Email command
CommandDescription EMAIL_BASE = CommandDescription.builder() CommandDescription emailBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("email") .labels("email")
.description("Add email or recover password") .description("Add email or recover password")
@ -409,7 +452,7 @@ public class CommandInitializer {
// Register the show command // Register the show command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("show", "myemail") .labels("show", "myemail")
.description("Show Email") .description("Show Email")
.detailedDescription("Show your current email address.") .detailedDescription("Show your current email address.")
@ -418,7 +461,7 @@ public class CommandInitializer {
// Register the add command // Register the add command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("add", "addemail", "addmail") .labels("add", "addemail", "addmail")
.description("Add Email") .description("Add Email")
.detailedDescription("Add a new email address to your account.") .detailedDescription("Add a new email address to your account.")
@ -430,7 +473,7 @@ public class CommandInitializer {
// Register the change command // Register the change command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("change", "changeemail", "changemail") .labels("change", "changeemail", "changemail")
.description("Change Email") .description("Change Email")
.detailedDescription("Change an email address of your account.") .detailedDescription("Change an email address of your account.")
@ -442,7 +485,7 @@ public class CommandInitializer {
// Register the recover command // Register the recover command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("recover", "recovery", "recoveremail", "recovermail") .labels("recover", "recovery", "recoveremail", "recovermail")
.description("Recover password using email") .description("Recover password using email")
.detailedDescription("Recover your account using an Email address by sending a mail containing " .detailedDescription("Recover your account using an Email address by sending a mail containing "
@ -454,7 +497,7 @@ public class CommandInitializer {
// Register the process recovery code command // Register the process recovery code command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("code") .labels("code")
.description("Submit code to recover password") .description("Submit code to recover password")
.detailedDescription("Recover your account by submitting a code delivered to your email.") .detailedDescription("Recover your account by submitting a code delivered to your email.")
@ -465,7 +508,7 @@ public class CommandInitializer {
// Register the change password after recovery command // Register the change password after recovery command
CommandDescription.builder() CommandDescription.builder()
.parent(EMAIL_BASE) .parent(emailBase)
.labels("setpassword") .labels("setpassword")
.description("Set new password after recovery") .description("Set new password after recovery")
.detailedDescription("Set a new password after successfully recovering your account.") .detailedDescription("Set a new password after successfully recovering your account.")
@ -474,29 +517,7 @@ public class CommandInitializer {
.executableCommand(SetPasswordCommand.class) .executableCommand(SetPasswordCommand.class)
.register(); .register();
// Register the base captcha command return emailBase;
CommandDescription CAPTCHA_BASE = CommandDescription.builder()
.parent(null)
.labels("captcha")
.description("Captcha Command")
.detailedDescription("Captcha command for AuthMeReloaded.")
.withArgument("captcha", "The Captcha", false)
.permission(PlayerPermission.CAPTCHA)
.executableCommand(CaptchaCommand.class)
.register();
List<CommandDescription> baseCommands = ImmutableList.of(
AUTHME_BASE,
LOGIN_BASE,
LOGOUT_BASE,
REGISTER_BASE,
UNREGISTER_BASE,
CHANGE_PASSWORD_BASE,
EMAIL_BASE,
CAPTCHA_BASE);
setHelpOnAllBases(baseCommands);
commands = baseCommands;
} }
/** /**

View File

@ -5,7 +5,6 @@ import org.bukkit.ChatColor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* Utility functions for {@link CommandDescription} objects. * Utility functions for {@link CommandDescription} objects.
@ -76,19 +75,14 @@ public final class CommandUtils {
} }
/** /**
* Returns a textual representation of the command, including its arguments. * Constructs a command path with color formatting, based on the supplied labels. This includes
* For example: {@code /authme purge <days> [includeZero]}. * the command's arguments, as defined in the provided command description. The list of labels
* must contain all labels to be used.
* *
* @param command the command to create a usage string for * @param command the command to read arguments from
* @return the command's path and arguments * @param correctLabels the labels to use (must be complete)
* @return formatted command syntax incl. arguments
*/ */
public static String buildSyntax(CommandDescription command) {
String arguments = command.getArguments().stream()
.map(arg -> formatArgument(arg))
.collect(Collectors.joining(" "));
return (constructCommandPath(command) + " " + arguments).trim();
}
public static String buildSyntax(CommandDescription command, List<String> correctLabels) { public static String buildSyntax(CommandDescription command, List<String> correctLabels) {
String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW; String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW;
for (int i = 1; i < correctLabels.size(); ++i) { for (int i = 1; i < correctLabels.size(); ++i) {
@ -106,7 +100,7 @@ public final class CommandUtils {
* @param argument the argument to format * @param argument the argument to format
* @return the formatted argument * @return the formatted argument
*/ */
private static String formatArgument(CommandArgumentDescription argument) { public static String formatArgument(CommandArgumentDescription argument) {
if (argument.isOptional()) { if (argument.isOptional()) {
return "[" + argument.getName() + "]"; return "[" + argument.getName() + "]";
} }

View File

@ -119,7 +119,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup {
this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS); this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN); this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN);
this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH); this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH);
this.resetThreshold = settings.getProperty(TEMPBAN_MINUTES_BEFORE_RESET) * MILLIS_PER_MINUTE; this.resetThreshold = settings.getProperty(TEMPBAN_MINUTES_BEFORE_RESET);
} }
@Override @Override

View File

@ -2,8 +2,6 @@ package fr.xephi.authme.data.limbo;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.function.Function;
/** /**
* Possible types to restore the "allow flight" property * Possible types to restore the "allow flight" property
* from LimboPlayer to Bukkit Player. * from LimboPlayer to Bukkit Player.
@ -11,24 +9,41 @@ import java.util.function.Function;
public enum AllowFlightRestoreType { public enum AllowFlightRestoreType {
/** Set value from LimboPlayer to Player. */ /** Set value from LimboPlayer to Player. */
RESTORE(LimboPlayer::isCanFly), RESTORE {
@Override
public void restoreAllowFlight(Player player, LimboPlayer limbo) {
player.setAllowFlight(limbo.isCanFly());
}
},
/** Always set flight enabled to true. */ /** Always set flight enabled to true. */
ENABLE(l -> true), ENABLE {
@Override
public void restoreAllowFlight(Player player, LimboPlayer limbo) {
player.setAllowFlight(true);
}
},
/** Always set flight enabled to false. */ /** Always set flight enabled to false. */
DISABLE(l -> false); DISABLE {
@Override
public void restoreAllowFlight(Player player, LimboPlayer limbo) {
player.setAllowFlight(false);
}
},
private final Function<LimboPlayer, Boolean> valueGetter; /** Always set flight enabled to false. */
NOTHING {
@Override
public void restoreAllowFlight(Player player, LimboPlayer limbo) {
// noop
}
/** @Override
* Constructor. public void processPlayer(Player player) {
* // noop
* @param valueGetter function with which the value to set on the player can be retrieved }
*/ };
AllowFlightRestoreType(Function<LimboPlayer, Boolean> valueGetter) {
this.valueGetter = valueGetter;
}
/** /**
* Restores the "allow flight" property from the LimboPlayer to the Player. * Restores the "allow flight" property from the LimboPlayer to the Player.
@ -37,7 +52,15 @@ public enum AllowFlightRestoreType {
* @param player the player to modify * @param player the player to modify
* @param limbo the limbo player to read from * @param limbo the limbo player to read from
*/ */
public void restoreAllowFlight(Player player, LimboPlayer limbo) { public abstract void restoreAllowFlight(Player player, LimboPlayer limbo);
player.setAllowFlight(valueGetter.apply(limbo));
/**
* Processes the player when a LimboPlayer instance is created based on him. Typically this
* method revokes the {@code allowFlight} property to be restored again later.
*
* @param player the player to process
*/
public void processPlayer(Player player) {
player.setAllowFlight(false);
} }
} }

View File

@ -4,6 +4,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.LimboSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -59,7 +60,8 @@ class LimboServiceHelper {
*/ */
void revokeLimboStates(Player player) { void revokeLimboStates(Player player) {
player.setOp(false); player.setOp(false);
player.setAllowFlight(false); settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)
.processPlayer(player);
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) { if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
player.setFlySpeed(0.0f); player.setFlySpeed(0.0f);

View File

@ -114,7 +114,7 @@ class DistributedFilesPersistenceHandler implements LimboPersistenceHandler {
} }
try { try {
return gson.fromJson(Files.toString(file, StandardCharsets.UTF_8), LIMBO_MAP_TYPE); return gson.fromJson(Files.asCharSource(file, StandardCharsets.UTF_8).read(), LIMBO_MAP_TYPE);
} catch (Exception e) { } catch (Exception e) {
ConsoleLogger.logException("Failed reading '" + file + "':", e); ConsoleLogger.logException("Failed reading '" + file + "':", e);
} }

View File

@ -46,7 +46,7 @@ class IndividualFilesPersistenceHandler implements LimboPersistenceHandler {
} }
try { try {
String str = Files.toString(file, StandardCharsets.UTF_8); String str = Files.asCharSource(file, StandardCharsets.UTF_8).read();
return gson.fromJson(str, LimboPlayer.class); return gson.fromJson(str, LimboPlayer.class);
} catch (IOException e) { } catch (IOException e) {
ConsoleLogger.logException("Could not read player data on disk for '" + player.getName() + "'", e); ConsoleLogger.logException("Could not read player data on disk for '" + player.getName() + "'", e);

View File

@ -6,7 +6,6 @@ import fr.xephi.authme.security.crypts.HashedPassword;
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.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
@ -80,7 +79,9 @@ public class FlatFile implements DataSource {
return false; return false;
} }
try (BufferedWriter bw = new BufferedWriter(new FileWriter(source, true))) { try (BufferedWriter bw = new BufferedWriter(new FileWriter(source, true))) {
bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + 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.warning(ex.getMessage()); ConsoleLogger.warning(ex.getMessage());
return false; return false;
@ -203,7 +204,7 @@ public class FlatFile implements DataSource {
return false; return false;
} }
ArrayList<String> lines = new ArrayList<>(); ArrayList<String> lines = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(source));) { try (BufferedReader br = new BufferedReader(new FileReader(source))) {
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
@ -379,6 +380,13 @@ public class FlatFile implements DataSource {
throw new UnsupportedOperationException("Flat file no longer supported"); throw new UnsupportedOperationException("Flat file no longer supported");
} }
/**
* Creates a PlayerAuth object from the read data.
*
* @param args the data read from the line
* @return the player auth object with the data
*/
@SuppressWarnings("checkstyle:NeedBraces")
private static PlayerAuth buildAuthFromArray(String[] args) { private static PlayerAuth buildAuthFromArray(String[] args) {
// Format allows 2, 3, 4, 7, 8, 9 fields. Anything else is unknown // 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) { if (args.length >= 2 && args.length <= 9 && args.length != 5 && args.length != 6) {

View File

@ -41,6 +41,7 @@ public class MySQL implements DataSource {
private String database; private String database;
private String tableName; private String tableName;
private int poolSize; private int poolSize;
private int maxLifetime;
private List<String> columnOthers; private List<String> columnOthers;
private Columns col; private Columns col;
private HashAlgorithm hashAlgorithm; private HashAlgorithm hashAlgorithm;
@ -116,6 +117,7 @@ public class MySQL implements DataSource {
if (poolSize == -1) { if (poolSize == -1) {
poolSize = Utils.getCoreCount() * 3; poolSize = Utils.getCoreCount() * 3;
} }
this.maxLifetime = settings.getProperty(DatabaseSettings.MYSQL_CONNECTION_MAX_LIFETIME);
this.useSsl = settings.getProperty(DatabaseSettings.MYSQL_USE_SSL); this.useSsl = settings.getProperty(DatabaseSettings.MYSQL_USE_SSL);
} }
@ -126,8 +128,10 @@ public class MySQL implements DataSource {
ds = new HikariDataSource(); ds = new HikariDataSource();
ds.setPoolName("AuthMeMYSQLPool"); ds.setPoolName("AuthMeMYSQLPool");
// Pool size // Pool Settings
ds.setMaximumPoolSize(poolSize); ds.setMaximumPoolSize(poolSize);
ds.setMaxLifetime(maxLifetime * 1000);
// Database URL // Database URL
ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database);

View File

@ -73,6 +73,11 @@ public class AsynchronousJoin implements AsynchronousProcess {
AsynchronousJoin() { AsynchronousJoin() {
} }
/**
* Processes the given player that has just joined.
*
* @param player the player to process
*/
public void processJoin(final Player player) { public void processJoin(final Player player) {
final String name = player.getName().toLowerCase(); final String name = player.getName().toLowerCase();
final String ip = PlayerUtils.getPlayerIp(player); final String ip = PlayerUtils.getPlayerIp(player);
@ -91,15 +96,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
} }
if (!validationService.fulfillsNameRestrictions(player)) { if (!validationService.fulfillsNameRestrictions(player)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(new Runnable() { handlePlayerWithUnmetNameRestriction(player, ip);
@Override
public void run() {
player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR));
if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) {
server.banIP(ip);
}
}
});
return; return;
} }
@ -124,7 +121,8 @@ public class AsynchronousJoin implements AsynchronousProcess {
if (canResumeSession(player)) { if (canResumeSession(player)) {
service.send(player, MessageKey.SESSION_RECONNECTION); service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands // Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> commandManager.runCommandsOnSessionLogin(player)); bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> commandManager.runCommandsOnSessionLogin(player));
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player)); bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
return; return;
} }
@ -133,6 +131,26 @@ public class AsynchronousJoin implements AsynchronousProcess {
return; return;
} }
processJoinSync(player, isAuthAvailable);
}
private void handlePlayerWithUnmetNameRestriction(Player player, String ip) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR));
if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) {
server.banIP(ip);
}
});
}
/**
* Performs various operations in sync mode for an unauthenticated player (such as blindness effect and
* limbo player creation).
*
* @param player the player to process
* @param isAuthAvailable true if the player is registered, false otherwise
*/
private void processJoinSync(Player player, boolean isAuthAvailable) {
final int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; final int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {

View File

@ -37,7 +37,7 @@ class TwoFactorRegisterExecutor extends AbstractPasswordRegisterExecutor<TwoFact
// to two-factor authentication. Therefore, the hashed password is the result of the TwoFactor EncryptionMethod // to two-factor authentication. Therefore, the hashed password is the result of the TwoFactor EncryptionMethod
// implementation (contains the TOTP secret). // implementation (contains the TOTP secret).
String hash = params.getHashedPassword().getHash(); String hash = params.getHashedPassword().getHash();
String qrCodeUrl = TwoFactor.getQRBarcodeURL(params.getPlayerName(), Bukkit.getIp(), hash); String qrCodeUrl = TwoFactor.getQrBarcodeUrl(params.getPlayerName(), Bukkit.getIp(), hash);
commonService.send(params.getPlayer(), MessageKey.TWO_FACTOR_CREATE, hash, qrCodeUrl); commonService.send(params.getPlayer(), MessageKey.TWO_FACTOR_CREATE, hash, qrCodeUrl);
} }
} }

View File

@ -19,6 +19,11 @@ import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/**
* Two factor authentication.
*
* @see <a href="http://thegreyblog.blogspot.com/2011/12/google-authenticator-using-it-in-your.html">Original source</a>
*/
@Recommendation(Usage.DOES_NOT_WORK) @Recommendation(Usage.DOES_NOT_WORK)
@HasSalt(SaltType.NONE) @HasSalt(SaltType.NONE)
public class TwoFactor extends UnsaltedMethod { public class TwoFactor extends UnsaltedMethod {
@ -30,7 +35,15 @@ public class TwoFactor extends UnsaltedMethod {
private static final int TIME_PRECISION = 3; private static final int TIME_PRECISION = 3;
private static final String CRYPTO_ALGO = "HmacSHA1"; private static final String CRYPTO_ALGO = "HmacSHA1";
public static String getQRBarcodeURL(String user, String host, String secret) { /**
* Creates a link to a QR barcode with the provided secret.
*
* @param user the player's name
* @param host the server host
* @param secret the TOTP secret
* @return URL leading to a QR code
*/
public static String getQrBarcodeUrl(String user, String host, String secret) {
String format = "https://www.google.com/chart?chs=130x130&chld=M%%7C0&cht=qr&chl=" String format = "https://www.google.com/chart?chs=130x130&chld=M%%7C0&cht=qr&chl="
+ "otpauth://totp/" + "otpauth://totp/"
+ "%s@%s%%3Fsecret%%3D%s"; + "%s@%s%%3Fsecret%%3D%s";
@ -72,18 +85,17 @@ public class TwoFactor extends UnsaltedMethod {
} }
long currentTime = Calendar.getInstance().getTimeInMillis() / TimeUnit.SECONDS.toMillis(30); long currentTime = Calendar.getInstance().getTimeInMillis() / TimeUnit.SECONDS.toMillis(30);
return check_code(secretKey, code, currentTime); return checkCode(secretKey, code, currentTime);
} }
private boolean check_code(String secret, long code, long t) private boolean checkCode(String secret, long code, long t) throws NoSuchAlgorithmException, InvalidKeyException {
throws NoSuchAlgorithmException, InvalidKeyException {
byte[] decodedKey = BaseEncoding.base32().decode(secret); byte[] decodedKey = BaseEncoding.base32().decode(secret);
// Window is used to check codes generated in the near past. // Window is used to check codes generated in the near past.
// You can use this value to tune how far you're willing to go. // You can use this value to tune how far you're willing to go.
int window = TIME_PRECISION; int window = TIME_PRECISION;
for (int i = -window; i <= window; ++i) { for (int i = -window; i <= window; ++i) {
long hash = verify_code(decodedKey, t + i); long hash = verifyCode(decodedKey, t + i);
if (hash == code) { if (hash == code) {
return true; return true;
@ -94,7 +106,7 @@ public class TwoFactor extends UnsaltedMethod {
return false; return false;
} }
private int verify_code(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException { private int verifyCode(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] data = new byte[8]; byte[] data = new byte[8];
long value = t; long value = t;
for (int i = 8; i-- > 0; value >>>= 8) { for (int i = 8; i-- > 0; value >>>= 8) {

View File

@ -42,6 +42,12 @@ public class XfBCrypt implements EncryptionMethod {
return false; return false;
} }
/**
* Extracts the password hash from the given BLOB.
*
* @param blob the blob to process
* @return the extracted hash
*/
public static String getHashFromBlob(byte[] blob) { public static String getHashFromBlob(byte[] blob) {
String line = new String(blob); String line = new String(blob);
Matcher m = HASH_PATTERN.matcher(line); Matcher m = HASH_PATTERN.matcher(line);

View File

@ -76,7 +76,7 @@ public class Settings extends SettingsManager {
final File file = new File(pluginFolder, filename); final File file = new File(pluginFolder, filename);
if (copyFileFromResource(file, filename)) { if (copyFileFromResource(file, filename)) {
try { try {
return Files.toString(file, StandardCharsets.UTF_8); return Files.asCharSource(file, StandardCharsets.UTF_8).read();
} catch (IOException e) { } catch (IOException e) {
ConsoleLogger.logException("Failed to read file '" + filename + "':", e); ConsoleLogger.logException("Failed to read file '" + filename + "':", e);
} }

View File

@ -10,7 +10,7 @@ import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class DatabaseSettings implements SettingsHolder { public final class DatabaseSettings implements SettingsHolder {
@Comment({"What type of database do you want to use?", @Comment({"What type of database do you want to use?",
"Valid values: SQLITE, MYSQL"}) "Valid values: SQLITE, MYSQL"})
public static final Property<DataSourceType> BACKEND = public static final Property<DataSourceType> BACKEND =
newProperty(DataSourceType.class, "DataSource.backend", DataSourceType.SQLITE); newProperty(DataSourceType.class, "DataSource.backend", DataSourceType.SQLITE);
@ -114,6 +114,11 @@ public final class DatabaseSettings implements SettingsHolder {
public static final Property<Integer> MYSQL_POOL_SIZE = public static final Property<Integer> MYSQL_POOL_SIZE =
newProperty("DataSource.poolSize", -1); newProperty("DataSource.poolSize", -1);
@Comment({"The maximum lifetime of a connection in the pool, default = 1800 seconds",
"You should set this at least 30 seconds less than mysql server wait_timeout"})
public static final Property<Integer> MYSQL_CONNECTION_MAX_LIFETIME =
newProperty("DataSource.maxLifetime", 1800);
private DatabaseSettings() { private DatabaseSettings() {
} }

View File

@ -45,8 +45,9 @@ public final class LimboSettings implements SettingsHolder {
newProperty(SegmentSize.class, "limbo.persistence.distributionSize", SegmentSize.SIXTEEN); newProperty(SegmentSize.class, "limbo.persistence.distributionSize", SegmentSize.SIXTEEN);
@Comment({ @Comment({
"Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE.", "Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE, NOTHING.",
"RESTORE sets back the old property from the player." "RESTORE sets back the old property from the player. NOTHING will prevent AuthMe",
"from modifying the 'allow flight' property on the player."
}) })
public static final Property<AllowFlightRestoreType> RESTORE_ALLOW_FLIGHT = public static final Property<AllowFlightRestoreType> RESTORE_ALLOW_FLIGHT =
newProperty(AllowFlightRestoreType.class, "limbo.restoreAllowFlight", AllowFlightRestoreType.RESTORE); newProperty(AllowFlightRestoreType.class, "limbo.restoreAllowFlight", AllowFlightRestoreType.RESTORE);
@ -66,7 +67,7 @@ public final class LimboSettings implements SettingsHolder {
"See above for a description of the values." "See above for a description of the values."
}) })
public static final Property<WalkFlySpeedRestoreType> RESTORE_WALK_SPEED = public static final Property<WalkFlySpeedRestoreType> RESTORE_WALK_SPEED =
newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreWalkSpeed", WalkFlySpeedRestoreType.MAX_RESTORE); newProperty(WalkFlySpeedRestoreType.class, "limbo.restoreWalkSpeed", WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
private LimboSettings() { private LimboSettings() {
} }

View File

@ -156,9 +156,13 @@ public final class RestrictionSettings implements SettingsHolder {
public static final Property<Boolean> NO_TELEPORT = public static final Property<Boolean> NO_TELEPORT =
newProperty("settings.restrictions.noTeleport", false); newProperty("settings.restrictions.noTeleport", false);
@Comment("Regex syntax for allowed chars in passwords") @Comment({
"Regex syntax for allowed chars in passwords. The default [!-~] allows all visible ASCII",
"characters, which is what we recommend. See also http://asciitable.com",
"You can test your regex with https://regex101.com"
})
public static final Property<String> ALLOWED_PASSWORD_REGEX = public static final Property<String> ALLOWED_PASSWORD_REGEX =
newProperty("settings.restrictions.allowedPasswordCharacters", "[\\x21-\\x7E]*"); newProperty("settings.restrictions.allowedPasswordCharacters", "[!-~]*");
@Comment("Force survival gamemode when player joins?") @Comment("Force survival gamemode when player joins?")
public static final Property<Boolean> FORCE_SURVIVAL_MODE = public static final Property<Boolean> FORCE_SURVIVAL_MODE =

View File

@ -169,7 +169,6 @@ public class PurgeExecutor {
return; return;
} }
int i = 0;
File essentialsDataFolder = pluginHookService.getEssentialsDataFolder(); File essentialsDataFolder = pluginHookService.getEssentialsDataFolder();
if (essentialsDataFolder == null) { if (essentialsDataFolder == null) {
ConsoleLogger.info("Cannot purge Essentials: plugin is not loaded"); ConsoleLogger.info("Cannot purge Essentials: plugin is not loaded");
@ -181,14 +180,15 @@ public class PurgeExecutor {
return; return;
} }
int deletedFiles = 0;
for (OfflinePlayer offlinePlayer : cleared) { for (OfflinePlayer offlinePlayer : cleared) {
File playerFile = new File(userDataFolder, PlayerUtils.getUuidOrName(offlinePlayer) + ".yml"); File playerFile = new File(userDataFolder, PlayerUtils.getUuidOrName(offlinePlayer) + ".yml");
if (playerFile.exists() && playerFile.delete()) { if (playerFile.exists() && playerFile.delete()) {
i++; deletedFiles++;
} }
} }
ConsoleLogger.info("AutoPurge: Removed " + i + " EssentialsFiles"); ConsoleLogger.info("AutoPurge: Removed " + deletedFiles + " EssentialsFiles");
} }
// TODO #676: What is this method for? Is it correct? // TODO #676: What is this method for? Is it correct?

View File

@ -61,7 +61,7 @@ public final class FileUtils {
ConsoleLogger.warning("Could not create directory '" + dir + "'"); ConsoleLogger.warning("Could not create directory '" + dir + "'");
return false; return false;
} }
return true; return dir.isDirectory();
} }
/** /**

View File

@ -5,11 +5,11 @@ import java.util.regex.Pattern;
/** /**
* Utility class about the InternetProtocol * Utility class about the InternetProtocol
*/ */
public class InternetProtocolUtils { public final class InternetProtocolUtils {
private final static Pattern LOCAL_ADDRESS_PATTERN = private static final Pattern LOCAL_ADDRESS_PATTERN =
Pattern.compile("(^127\\.)|(^(0)?10\\.)|(^172\\.(0)?1[6-9]\\.)|(^172\\.(0)?2[0-9]\\.)" + Pattern.compile("(^127\\.)|(^(0)?10\\.)|(^172\\.(0)?1[6-9]\\.)|(^172\\.(0)?2[0-9]\\.)"
"|(^172\\.(0)?3[0-1]\\.)|(^169\\.254\\.)|(^192\\.168\\.)"); + "|(^172\\.(0)?3[0-1]\\.)|(^169\\.254\\.)|(^192\\.168\\.)");
// Utility class // Utility class
private InternetProtocolUtils() { private InternetProtocolUtils() {

View File

@ -0,0 +1,213 @@
# AuthmeReloaded帮助文件汉化
# Translated By CH1
# -------------------------------------------------------
common:
header: '==========[ AuthMeReloaded ]=========='
optional: '可选'
hasPermission: '您拥有权限去使用这个指令'
noPermission: '您没有权限使用这个指令'
default: '默认'
result: '您的权限'
defaultPermissions:
notAllowed: '任何人不能使用'
opOnly: 'OP拥有此权限'
allowed: '所有人都可以使用'
section:
command: '指令'
description: '功能'
detailedDescription: '功能详情'
arguments: '参数'
permissions: '权限'
alternatives: '别名'
children: '子命令'
commands:
authme.register:
description: '注册一个玩家'
detailedDescription: '注册一个玩家'
arg1:
label: '玩家'
description: '玩家名称'
arg2:
label: '密码'
description: '密码'
authme.unregister:
description: '注销一个玩家'
detailedDescription: '注销一个玩家'
arg1:
label: '玩家'
description: '玩家'
authme.forcelogin:
description: '强制玩家重新登录'
detailedDescription: '强制使指定玩家重新登录'
arg1:
label: '玩家'
description: '玩家'
authme.password:
description: '改变某个玩家的密码'
detailedDescription: '改变某个玩家的密码'
arg1:
label: '玩家'
description: '玩家'
arg2:
label: '新密码'
description: '新密码'
authme.lastlogin:
description: '查看玩家最后登录时间'
detailedDescription: '查看玩家最后登录时间'
arg1:
label: '玩家'
description: '玩家'
authme.accounts:
description: '查看玩家IP下的账户'
detailedDescription: '查看玩家IP下的账户'
arg1:
label: '玩家或IP'
description: '玩家或IP'
authme.email:
description: '查看玩家的邮箱'
detailedDescription: '查看玩家的邮箱'
arg1:
label: '玩家'
description: '玩家'
authme.setemail:
description: '改变玩家的邮箱'
detailedDescription: '改变玩家的邮箱'
arg1:
label: '玩家'
description: '玩家'
arg2:
label: '邮箱'
description: '邮箱'
authme.getip:
description: '获取玩家IP'
detailedDescription: '获取玩家IP'
arg1:
label: '玩家'
description: '玩家'
authme.spawn:
description: '传送到AuthMe出生点'
detailedDescription: '传送到AuthMe出生点'
authme.setspawn:
description: '改变AuthMe出生点'
detailedDescription: '改变AuthMe出生点'
authme.firstspawn:
description: '传送到第一次进入游戏出生点'
detailedDescription: '传送到第一次进入游戏出生点'
authme.setfirstspawn:
description: '设置第一次进入游戏的出生点'
detailedDescription: '设置第一次进入游戏的出生点'
authme.purge:
description: '删除指定天数之前没登录的玩家登陆数据'
detailedDescription: '删除指定天数之前没登录的玩家登陆数据'
arg1:
label: '天数'
description: '天数'
arg2:
label: 'all'
description: '添加all到最后来清理最后登录为0的玩家'
authme.resetpos:
description: '重置玩家登出位置'
detailedDescription: '重置玩家登出位置'
arg1:
label: '玩家/*'
description: '玩家名称或所有玩家'
authme.purgebannedplayers:
description: '删除已经被封禁的玩家数据'
detailedDescription: '删除已经被封禁的玩家数据'
authme.switchantibot:
description: '改变AntiBot的状态'
detailedDescription: '改变AntiBot的状态'
arg1:
label: '开关'
description: '选项: ON/OFF'
authme.reload:
description: '重载插件'
detailedDescription: '重载插件'
authme.version:
description: '查看版本信息'
detailedDescription: '查看AuthmeReload版本,开发者,贡献者和许可'
authme.converter:
description: '转换数据命令'
detailedDescription: '转换数据命令'
arg1:
label: '类型'
description: '转换类型:xauth/crazylogin/rakamak/royalauth/vauth/sqliteToSql/mysqlToSqlite'
authme.messages:
description: '添加信息'
detailedDescription: '在语言文件夹中添加缺少的信息'
authme.help:
description: '查看帮助'
detailedDescription: '查看帮助'
arg1:
label: '子命令'
description: '查看的指令'
unregister:
description: '注销您的账户'
detailedDescription: '注销您的账户'
arg1:
label: '密码'
description: '密码'
changepassword:
description: '更改您的密码'
detailedDescription: '更改您的密码'
arg1:
label: '旧的密码'
description: '旧的密码'
arg2:
label: '新的密码'
description: '新的密码'
email:
description: '绑定邮箱或更改密码'
detailedDescription: '绑定邮箱或更改密码'
email.show:
description: '查看邮箱'
detailedDescription: '查看您的邮箱地址'
email.add:
description: '绑定邮箱'
detailedDescription: '为您的账户绑定邮箱'
arg1:
label: '邮箱'
description: '邮箱地址'
arg2:
label: '邮箱'
description: '重新输入邮箱地址'
email.change:
description: '改变邮箱地址'
detailedDescription: '更改您账户的邮箱地址'
arg1:
label: '旧邮箱'
description: '旧的邮箱地址'
arg2:
label: '新邮箱'
description: '新的邮箱地址'
email.recover:
description: '通过邮箱改变密码'
detailedDescription: '通过邮箱改变密码'
arg1:
label: '邮箱'
description: '邮箱地址'
arg2:
label: '验证码'
description: '验证码'
email.help:
description: '查看帮助'
detailedDescription: '查看邮箱帮助'
arg1:
label: '子命令'
description: '指令'
captcha:
description: '验证码'
detailedDescription: '验证码'
arg1:
label: '验证码'
description: '验证码'
captcha.help:
description: '查看验证码帮助'
detailedDescription: '查看验证码帮助'
arg1:
label: '子命令'
description: '指令'

View File

@ -16,7 +16,7 @@ password_error: '&c비밀번호가 일치하지 않습니다, 다시 확인해
password_error_nick: '&c자신의 닉네임을 비밀번호로 사용할 수 없습니다, 다른 비밀번호를 사용하세요...' password_error_nick: '&c자신의 닉네임을 비밀번호로 사용할 수 없습니다, 다른 비밀번호를 사용하세요...'
password_error_unsafe: '&c이 비밀번호는 안전하지 않습니다, 다른 비밀번호를 사용하세요...' password_error_unsafe: '&c이 비밀번호는 안전하지 않습니다, 다른 비밀번호를 사용하세요...'
password_error_chars: '&4비밀번호에 잘못된 문자가 있습니다. 허용된 문자: REG_EX' password_error_chars: '&4비밀번호에 잘못된 문자가 있습니다. 허용된 문자: REG_EX'
pass_len: '&c너의 비번은 좆같이 길거나 좆같이 작아! 제발 다른걸 써줘!' pass_len: '&c비밀번호가 너무 짧거나 작습니다!'
# 로그인 # 로그인
usage_log: '&c사용법: /login <비밀번호>' usage_log: '&c사용법: /login <비밀번호>'
@ -76,7 +76,7 @@ usage_email_add: '&c사용법: /email add <이메일 주소> <이메일 주소
usage_email_change: '&c사용법: /email change <예전 이메일 주소> <새 이메일 주소>' usage_email_change: '&c사용법: /email change <예전 이메일 주소> <새 이메일 주소>'
usage_email_recovery: '&c사용법: /email recovery <이메일 주소>' usage_email_recovery: '&c사용법: /email recovery <이메일 주소>'
new_email_invalid: '&c새 이메일 주소가 잘못되었습니다, 다시 시도해보세요!' new_email_invalid: '&c새 이메일 주소가 잘못되었습니다, 다시 시도해보세요!'
old_email_invalid: '&c전 이메일 주소가 잘못되었습니다, 다시 시도해보세요!' old_email_invalid: '&c전 이메일 주소가 잘못되었습니다, 다시 시도해보세요!'
email_invalid: '&c이메일 주소가 잘못되었습니다, 다시 시도해보세요!' email_invalid: '&c이메일 주소가 잘못되었습니다, 다시 시도해보세요!'
email_added: '&2계정에 이메일 주소를 추가했습니다!' email_added: '&2계정에 이메일 주소를 추가했습니다!'
email_confirm: '&c이메일 주소를 확인해주세요!' email_confirm: '&c이메일 주소를 확인해주세요!'

View File

@ -1,11 +1,11 @@
# Registration # Registration
reg_msg: '&2Proszę się zarejestrować przy użyciu &6/register <hasło> <powtórz_hasło>' reg_msg: '&2Proszę się zarejestrować przy użyciu &6/register <hasło> <powtórz_hasło>'
usage_reg: '&4Użycie: /register <hasło> <powtórz_hasło>' usage_reg: '&4Użycie: /register <hasło> <powtórz_hasło>'
reg_only: '&fTylko zarejestrowani użytkownicy maja do tego dostęp!' reg_only: '&fTylko zarejestrowani użytkownicy mają do tego dostęp!'
kicked_admin_registered: 'Administrator zarejestrował Ciebie, możesz się zalogować.' kicked_admin_registered: 'Administrator zarejestrował Ciebie, możesz się zalogować.'
registered: '&aPomyślnie zarejestrowany!' registered: '&aPomyślnie zarejestrowany!'
reg_disabled: '&4Rejestracja jest wyłączona' reg_disabled: '&4Rejestracja jest wyłączona.'
user_regged: '&4Gracz już jest zarejestrowany' user_regged: '&4Gracz już jest zarejestrowany.'
# Password errors on registration # Password errors on registration
password_error: '&fHasło niepoprawne!' password_error: '&fHasło niepoprawne!'
@ -18,29 +18,29 @@ pass_len: '&fTwoje hasło jest za krótkie lub za długie! Spróbuj ponownie...'
usage_log: '&cUżycie: /login hasło' usage_log: '&cUżycie: /login hasło'
wrong_pwd: '&cNiepoprawne hasło.' wrong_pwd: '&cNiepoprawne hasło.'
login: '&aHasło zaakceptowane!' login: '&aHasło zaakceptowane!'
login_msg: '&2Prosze się zalogować przy użyciu &6/login <hasło>' login_msg: '&2Proszę się zalogować przy użyciu &6/login <hasło>'
timeout: '&cUpłynął limit czasu zalogowania' timeout: '&cUpłynął limit czasu zalogowania'
# Errors # Errors
unknown_user: '&fGracz nie jest zarejestrowany' unknown_user: '&fGracz nie jest zarejestrowany.'
denied_command: '&cAby użyć tej komendy musisz się zalogować!' denied_command: '&cAby użyć tej komendy musisz się zalogować!'
denied_chat: '&cAby pisać na chacie musisz się zalogować!' denied_chat: '&cAby pisać na chacie musisz się zalogować!'
not_logged_in: '&4Nie jesteś zalogowany!' not_logged_in: '&4Nie jesteś zalogowany!'
tempban_max_logins: '&cZostałeś tymczasowo zbanowany za dużą liczbę nieudanych logowań!' tempban_max_logins: '&cZostałeś tymczasowo zbanowany za dużą liczbę nieudanych logowań!'
max_reg: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.' max_reg: '&cPrzekroczyłeś limit zarejestrowanych kont na serwerze &8(&e%reg_count/%max_acc %reg_names&8) &cdla twojego połączenia.'
no_perm: '&4Nie posiadasz wymaganych uprawnień.' no_perm: '&4Nie posiadasz wymaganych uprawnień.'
error: '&fWystąpił błąd, prosimy napisać do administracji' error: '&fWystąpił błąd, prosimy skontaktować się z administracją serwera.'
kick_forvip: '&cGracz VIP dołączył do gry!' kick_forvip: '&cGracz VIP dołączył do gry!'
# AntiBot # AntiBot
kick_antibot: 'AntyBot został włączony, musisz poczekać minute przed dołączeniem do serwera' kick_antibot: '&cAntyBot został włączony, musisz poczekać minutę przed dołączeniem do serwera.'
antibot_auto_enabled: '&4[AntiBot] &aAntyBot włączony z powodu dużej liczby połączeń!' antibot_auto_enabled: '&4[AntiBot] &aAntyBot włączony z powodu dużej liczby połączeń!'
antibot_auto_disabled: '&2[AntiBot] &aAntyBot zostanie wyłączony za &7%m &aminut!' antibot_auto_disabled: '&2[AntiBot] &aAntyBot zostanie wyłączony za &7%m &aminut!'
# Other messages # Other messages
unregistered: '&4Pomyslnie wyrejestrowany!' unregistered: '&4Pomyślnie wyrejestrowany!'
accounts_owned_self: 'Posiadasz %count kont:' accounts_owned_self: '&7Posiadasz %count kont:'
accounts_owned_other: 'Gracz %name posiada %count kont:' accounts_owned_other: '&7Gracz %name posiada %count kont:'
two_factor_create: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url' two_factor_create: '&2Twój sekretny kod to %code. Możesz zeskanować go tutaj: %url'
recovery_code_sent: 'Kod odzyskiwania hasła został wysłany na adres email przypisany do konta.' recovery_code_sent: 'Kod odzyskiwania hasła został wysłany na adres email przypisany do konta.'
recovery_code_incorrect: '&cKod odzyskiwania hasła jest błędny! &4Pozostałe próby: %count.' recovery_code_incorrect: '&cKod odzyskiwania hasła jest błędny! &4Pozostałe próby: %count.'
@ -66,8 +66,8 @@ country_banned: '&4Ten kraj jest zbanowany na tym serwerze'
not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!' not_owner_error: '&cNie jesteś właścicielem tego konta, wybierz inny nick!'
kick_fullserver: '&cSerwer jest teraz zapełniony, przepraszamy!' kick_fullserver: '&cSerwer jest teraz zapełniony, przepraszamy!'
same_nick: '&fTen nick już gra na serwerze!' same_nick: '&fTen nick już gra na serwerze!'
invalid_name_case: 'Powinieneś dołączyć do serwera z nicku %valid, a nie %invalid.' invalid_name_case: '&cPowinieneś dołączyć do serwera z nicku %valid, a nie %invalid.'
same_ip_online: 'Gracz z takim samym adresem ip jest aktualnie w grze!' same_ip_online: '&cGracz z takim samym adresem ip jest aktualnie w grze!'
# Email # Email
usage_email_add: '&fWpisz: /email add <email> <powtórz_email> ' usage_email_add: '&fWpisz: /email add <email> <powtórz_email> '

View File

@ -55,7 +55,7 @@ public class ClassesConsistencyTest {
/** Classes excluded from the field visibility test. */ /** Classes excluded from the field visibility test. */
private static final Set<Class<?>> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of( private static final Set<Class<?>> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of(
Whirlpool.class, // not our implementation, so we don't touch it Whirlpool.class, // not our implementation, so we don't touch it
Columns.class // uses non-final String constants, which is safe Columns.class // uses non-static String constants, which is safe
); );
/** /**

View File

@ -22,7 +22,7 @@ public class CommandUtilsTest {
@BeforeClass @BeforeClass
public static void setUpTestCommands() { public static void setUpTestCommands() {
commands = TestCommandsUtil.generateCommands(); commands = Collections.unmodifiableCollection(TestCommandsUtil.generateCommands());
} }
@Test @Test
@ -49,10 +49,6 @@ public class CommandUtilsTest {
assertThat(commandPath, equalTo("/authme help")); assertThat(commandPath, equalTo("/authme help"));
} }
// ------
// min / max arguments
// ------
@Test @Test
public void shouldComputeMinAndMaxOnEmptyCommand() { public void shouldComputeMinAndMaxOnEmptyCommand() {
// given // given

View File

@ -0,0 +1,39 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.service.BackupService;
import org.bukkit.command.CommandSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link BackupCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class BackupCommandTest {
@InjectMocks
private BackupCommand command;
@Mock
private BackupService backupService;
@Test
public void shouldStartBackup() {
// given
CommandSender sender = mock(CommandSender.class);
// when
command.executeCommand(sender, Collections.emptyList());
// then
verify(backupService).doBackup(BackupService.BackupCause.COMMAND, sender);
}
}

View File

@ -0,0 +1,116 @@
package fr.xephi.authme.command.executable.authme.debug;
import com.google.common.cache.LoadingCache;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore;
import org.bukkit.command.CommandSender;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link DataStatistics}.
*/
@RunWith(MockitoJUnitRunner.class)
public class DataStatisticsTest {
@InjectMocks
private DataStatistics dataStatistics;
@Mock
private DataSource dataSource;
@Mock
private PlayerCache playerCache;
@Mock
private LimboService limboService;
@Mock
private SingletonStore<Object> singletonStore;
@Before
public void setUpLimboCacheMap() {
Map<String, LimboPlayer> limboMap = new HashMap<>();
limboMap.put("test", mock(LimboPlayer.class));
ReflectionTestUtils.setField(LimboService.class, limboService, "entries", limboMap);
}
@Test
public void shouldOutputStatistics() {
// given
CommandSender sender = mock(CommandSender.class);
given(singletonStore.retrieveAllOfType()).willReturn(mockListOfSize(Object.class, 7));
given(singletonStore.retrieveAllOfType(Reloadable.class)).willReturn(mockListOfSize(Reloadable.class, 4));
given(singletonStore.retrieveAllOfType(SettingsDependent.class)).willReturn(mockListOfSize(SettingsDependent.class, 3));
given(singletonStore.retrieveAllOfType(HasCleanup.class)).willReturn(mockListOfSize(HasCleanup.class, 2));
given(dataSource.getAccountsRegistered()).willReturn(219);
given(playerCache.getLogged()).willReturn(12);
// when
dataStatistics.execute(sender, Collections.emptyList());
// then
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
verify(sender, atLeastOnce()).sendMessage(stringCaptor.capture());
assertThat(stringCaptor.getAllValues(), containsInAnyOrder(
"Singleton Java classes: 7 (Reloadable: 4 / SettingsDependent: 3 / HasCleanup: 2)",
"LimboPlayers in memory: 1",
"Total players in DB: 219",
"PlayerCache size: 12 (= logged in players)"));
}
@Test
public void shouldOutputCachedDataSourceStatistics() {
// given
CacheDataSource cacheDataSource = mock(CacheDataSource.class);
LoadingCache<String, Optional<PlayerAuth>> cache = mock(LoadingCache.class);
given(cache.size()).willReturn(11L);
given(cacheDataSource.getCachedAuths()).willReturn(cache);
ReflectionTestUtils.setField(DataStatistics.class, dataStatistics, "dataSource", cacheDataSource);
CommandSender sender = mock(CommandSender.class);
// when
dataStatistics.execute(sender, Collections.emptyList());
// then
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
verify(sender, atLeastOnce()).sendMessage(stringCaptor.capture());
assertThat(stringCaptor.getAllValues(), hasItem("Cached PlayerAuth objects: 11"));
}
private static <T> List<T> mockListOfSize(Class<T> mockClass, int size) {
T mock = mock(mockClass);
List<T> mocks = new ArrayList<>(size);
for (int i = 0; i < size; ++i) {
mocks.add(mock);
}
return mocks;
}
}

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.data.limbo.LimboService;
import org.bukkit.Location; import org.bukkit.Location;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.HashMap; import java.util.HashMap;
@ -12,6 +13,7 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -21,6 +23,11 @@ import static org.mockito.Mockito.mock;
*/ */
public class DebugSectionUtilsTest { public class DebugSectionUtilsTest {
@Before
public void initMockLogger() {
TestHelper.setupLogger();
}
@Test @Test
public void shouldFormatLocation() { public void shouldFormatLocation() {
// given / when // given / when
@ -66,4 +73,20 @@ public class DebugSectionUtilsTest {
// then // then
assertThat(map, sameInstance(limboMap)); assertThat(map, sameInstance(limboMap));
} }
@Test
public void shouldHandleErrorGracefully() {
// given
LimboService limboService = mock(LimboService.class);
Map<String, LimboPlayer> limboMap = new HashMap<>();
ReflectionTestUtils.setField(LimboService.class, limboService, "entries", limboMap);
// when
Object result = DebugSectionUtils.applyToLimboPlayersMap(limboService, map -> {
throw new IllegalStateException();
});
// then
assertThat(result, nullValue());
}
} }

View File

@ -19,6 +19,8 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
@ -106,11 +108,16 @@ public class ChangePasswordCommandTest {
verify(management).performPasswordChange(player, oldPass, newPass); verify(management).performPasswordChange(player, oldPass, newPass);
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_CHANGE_PASSWORD));
}
private Player initPlayerWithName(String name, boolean loggedIn) { private Player initPlayerWithName(String name, boolean loggedIn) {
Player player = mock(Player.class); Player player = mock(Player.class);
when(player.getName()).thenReturn(name); when(player.getName()).thenReturn(name);
when(playerCache.isAuthenticated(name)).thenReturn(loggedIn); when(playerCache.isAuthenticated(name)).thenReturn(loggedIn);
return player; return player;
} }
} }

View File

@ -15,6 +15,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
@ -73,4 +75,9 @@ public class AddEmailCommandTest {
verify(commandService).send(sender, MessageKey.CONFIRM_EMAIL_MESSAGE); verify(commandService).send(sender, MessageKey.CONFIRM_EMAIL_MESSAGE);
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_ADD_EMAIL));
}
} }

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.email; package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import org.bukkit.command.BlockCommandSender; import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -10,9 +11,11 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
@ -36,7 +39,7 @@ public class ChangeEmailCommandTest {
CommandSender sender = mock(BlockCommandSender.class); CommandSender sender = mock(BlockCommandSender.class);
// when // when
command.executeCommand(sender, new ArrayList<String>()); command.executeCommand(sender, Collections.emptyList());
// then // then
verifyZeroInteractions(management); verifyZeroInteractions(management);
@ -54,4 +57,9 @@ public class ChangeEmailCommandTest {
verify(management).performChangeEmail(sender, "new.mail@example.org", "old_mail@example.org"); verify(management).performChangeEmail(sender, "new.mail@example.org", "old_mail@example.org");
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_CHANGE_EMAIL));
}
} }

View File

@ -0,0 +1,49 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.FoundCommandResult;
import fr.xephi.authme.command.help.HelpProvider;
import org.bukkit.command.CommandSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link EmailBaseCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class EmailBaseCommandTest {
@InjectMocks
private EmailBaseCommand command;
@Mock
private HelpProvider helpProvider;
@Mock
private CommandMapper commandMapper;
@Test
public void shouldDisplayHelp() {
// given
CommandSender sender = mock(CommandSender.class);
FoundCommandResult result = mock(FoundCommandResult.class);
given(commandMapper.mapPartsToCommand(eq(sender), anyList())).willReturn(result);
// when
command.executeCommand(sender, Collections.emptyList());
// then
verify(commandMapper).mapPartsToCommand(sender, Collections.singletonList("email"));
verify(helpProvider).outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
}
}

View File

@ -22,6 +22,8 @@ import org.mockito.Mock;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -208,4 +210,10 @@ public class RecoverEmailCommandTest {
verify(dataSource).getEmail(name); verify(dataSource).getEmail(name);
verify(recoveryService).generateAndSendNewPassword(sender, email); verify(recoveryService).generateAndSendNewPassword(sender, email);
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_RECOVER_EMAIL));
}
} }

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.login; package fr.xephi.authme.command.executable.login;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import org.bukkit.command.BlockCommandSender; import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -13,6 +14,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -57,4 +60,9 @@ public class LoginCommandTest {
verify(management).performLogin(eq(sender), eq("password")); verify(management).performLogin(eq(sender), eq("password"));
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_LOGIN));
}
} }

View File

@ -15,6 +15,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections; import java.util.Collections;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.hamcrest.MockitoHamcrest.argThat; import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -87,4 +89,9 @@ public class UnregisterCommandTest {
verify(sender).sendMessage(argThat(containsString("/authme unregister <player>"))); verify(sender).sendMessage(argThat(containsString("/authme unregister <player>")));
} }
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_UNREGISTER));
}
} }

View File

@ -10,6 +10,7 @@ import fr.xephi.authme.message.MessageFileHandlerProvider;
import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.DefaultPermission;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import java.util.Collection; import java.util.Collection;
@ -19,10 +20,13 @@ import static fr.xephi.authme.TestHelper.getJarFile;
import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel; import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
/** /**
* Test for {@link HelpMessagesService}. * Test for {@link HelpMessagesService}.
@ -46,6 +50,17 @@ public class HelpMessagesServiceTest {
given(messageFileHandlerProvider.initializeHandler(any(Function.class))).willReturn(handler); given(messageFileHandlerProvider.initializeHandler(any(Function.class))).willReturn(handler);
} }
@Test
@SuppressWarnings("unchecked")
public void shouldUseExistingFileAsTextFile() {
// given / when / then
ArgumentCaptor<Function<String, String>> functionCaptor = ArgumentCaptor.forClass(Function.class);
verify(messageFileHandlerProvider).initializeHandler(functionCaptor.capture());
Function<String, String> helpFilePathBuilder = functionCaptor.getValue();
String defaultFilePath = helpFilePathBuilder.apply("en");
assertThat(getClass().getClassLoader().getResource(defaultFilePath), not(nullValue()));
}
@Test @Test
public void shouldReturnLocalizedCommand() { public void shouldReturnLocalizedCommand() {
// given // given

View File

@ -6,6 +6,7 @@ import org.junit.Test;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* Test for {@link AllowFlightRestoreType}. * Test for {@link AllowFlightRestoreType}.
@ -64,6 +65,42 @@ public class AllowFlightRestoreTypeTest {
verify(player2).setAllowFlight(false); verify(player2).setAllowFlight(false);
} }
@Test
public void shouldNotInteractWithPlayer() {
// given
LimboPlayer limboWithFly = newLimboWithAllowFlight(true);
LimboPlayer limboWithoutFly = newLimboWithAllowFlight(false);
Player player1 = mock(Player.class);
Player player2 = mock(Player.class);
// when
AllowFlightRestoreType.NOTHING.restoreAllowFlight(player1, limboWithFly);
AllowFlightRestoreType.NOTHING.restoreAllowFlight(player2, limboWithoutFly);
// then
verifyZeroInteractions(player1, player2);
}
@Test
public void shouldRemoveFlightExceptForNothingType() {
// given
AllowFlightRestoreType noInteractionType = AllowFlightRestoreType.NOTHING;
for (AllowFlightRestoreType type : AllowFlightRestoreType.values()) {
Player player = mock(Player.class);
// when
type.processPlayer(player);
// then
if (type == noInteractionType) {
verifyZeroInteractions(player);
} else {
verify(player).setAllowFlight(false);
}
}
}
private static LimboPlayer newLimboWithAllowFlight(boolean allowFlight) { private static LimboPlayer newLimboWithAllowFlight(boolean allowFlight) {
LimboPlayer limbo = mock(LimboPlayer.class); LimboPlayer limbo = mock(LimboPlayer.class);
given(limbo.isCanFly()).willReturn(allowFlight); given(limbo.isCanFly()).willReturn(allowFlight);

View File

@ -84,6 +84,7 @@ public class LimboServiceTest {
given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc); given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc);
given(permissionsManager.hasGroupSupport()).willReturn(true); given(permissionsManager.hasGroupSupport()).willReturn(true);
given(permissionsManager.getGroups(player)).willReturn(Collections.singletonList("permgrwp")); given(permissionsManager.getGroups(player)).willReturn(Collections.singletonList("permgrwp"));
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE);
// when // when
limboService.createLimboPlayer(player, true); limboService.createLimboPlayer(player, true);
@ -114,6 +115,7 @@ public class LimboServiceTest {
Location playerLoc = mock(Location.class); Location playerLoc = mock(Location.class);
given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc); given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(playerLoc);
given(permissionsManager.hasGroupSupport()).willReturn(false); given(permissionsManager.hasGroupSupport()).willReturn(false);
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.RESTORE);
// when // when
limboService.createLimboPlayer(player, false); limboService.createLimboPlayer(player, false);
@ -143,6 +145,7 @@ public class LimboServiceTest {
LimboPlayer existingLimbo = mock(LimboPlayer.class); LimboPlayer existingLimbo = mock(LimboPlayer.class);
getLimboMap().put("carlos", existingLimbo); getLimboMap().put("carlos", existingLimbo);
Player player = newPlayer("Carlos"); Player player = newPlayer("Carlos");
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE);
// when // when
limboService.createLimboPlayer(player, false); limboService.createLimboPlayer(player, false);

View File

@ -25,7 +25,7 @@ public class TwoFactorTest {
String secret = "3AK6Y4KWGRLJMEQW"; String secret = "3AK6Y4KWGRLJMEQW";
// when // when
String url = TwoFactor.getQRBarcodeURL(user, host, secret); String url = TwoFactor.getQrBarcodeUrl(user, host, secret);
// then // then
String expected = "https://www.google.com/chart?chs=130x130&chld=M%7C0&cht=qr" String expected = "https://www.google.com/chart?chs=130x130&chld=M%7C0&cht=qr"

View File

@ -28,7 +28,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat; import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static fr.xephi.authme.AuthMeMatchers.equalToHash;
/** /**
* Test for {@link MigrationService}. * Test for {@link MigrationService}.

View File

@ -178,6 +178,19 @@ public class CommandManagerTest {
verifyZeroInteractions(bukkitService, geoIpService); verifyZeroInteractions(bukkitService, geoIpService);
} }
@Test
public void shouldExecuteCommandOnUnregister() {
// given
copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml");
initManager();
// when
manager.runCommandsOnUnregister(player);
// then
verify(bukkitService).dispatchConsoleCommand("msg Bobby sad to see you go!");
}
@Test @Test
public void shouldHaveHiddenConstructorInSettingsHolderClass() { public void shouldHaveHiddenConstructorInSettingsHolderClass() {
// given / when / then // given / when / then

View File

@ -75,6 +75,20 @@ public class FileUtilsTest {
assertThat(file.exists(), equalTo(false)); assertThat(file.exists(), equalTo(false));
} }
@Test
public void shouldReturnFalseForParentInvalidParentFolders() throws IOException {
// given
File folder = temporaryFolder.newFolder();
new File(folder, "hello").createNewFile();
File fileToCreate = new File(folder, "hello/test");
// when
boolean result = FileUtils.copyFileFromResource(fileToCreate, "welcome.txt");
// then
assertThat(result, equalTo(false));
}
@Test @Test
public void shouldPurgeDirectory() throws IOException { public void shouldPurgeDirectory() throws IOException {
// given // given
@ -137,6 +151,36 @@ public class FileUtilsTest {
assertThat(result, equalTo("path" + File.separator + "to" + File.separator + "test-file.txt")); assertThat(result, equalTo("path" + File.separator + "to" + File.separator + "test-file.txt"));
} }
@Test
public void shouldCreateDirectory() throws IOException {
// given
File root = temporaryFolder.newFolder();
File dir = new File(root, "folder/folder2/myFolder");
// when
boolean result = FileUtils.createDirectory(dir);
// then
assertThat(result, equalTo(true));
assertThat(dir.exists(), equalTo(true));
assertThat(dir.isDirectory(), equalTo(true));
}
@Test
public void shouldReturnFalseOnDirectoryCreateFail() throws IOException {
// given
File root = temporaryFolder.newFolder();
File dirAsFile = new File(root, "file");
dirAsFile.createNewFile();
// when
boolean result = FileUtils.createDirectory(dirAsFile);
// then
assertThat(result, equalTo(false));
assertThat(dirAsFile.isFile(), equalTo(true));
}
@Test @Test
public void shouldHaveHiddenConstructor() { public void shouldHaveHiddenConstructor() {
TestHelper.validateHasOnlyPrivateEmptyConstructor(FileUtils.class); TestHelper.validateHasOnlyPrivateEmptyConstructor(FileUtils.class);

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.util; package fr.xephi.authme.util;
import fr.xephi.authme.TestHelper;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -19,4 +20,10 @@ public class InternetProtocolUtilsTest {
assertThat(InternetProtocolUtils.isLocalAddress("192.168.0.1"), equalTo(true)); assertThat(InternetProtocolUtils.isLocalAddress("192.168.0.1"), equalTo(true));
assertThat(InternetProtocolUtils.isLocalAddress("94.32.34.5"), equalTo(false)); assertThat(InternetProtocolUtils.isLocalAddress("94.32.34.5"), equalTo(false));
} }
@Test
public void shouldHavePrivateConstructor() {
// given / when / then
TestHelper.validateHasOnlyPrivateEmptyConstructor(InternetProtocolUtils.class);
}
} }

View File

@ -53,7 +53,7 @@ public class RandomStringUtilsTest {
// when / then // when / then
for (int length : lengths) { for (int length : lengths) {
String result = RandomStringUtils.generateHex(length); String result = RandomStringUtils.generateLowerUpper(length);
assertThat("Result '" + result + "' should have length " + length, assertThat("Result '" + result + "' should have length " + length,
result.length(), equalTo(length)); result.length(), equalTo(length));
assertThat("Result '" + result + "' should only have characters a-z, A-Z, 0-9", assertThat("Result '" + result + "' should only have characters a-z, A-Z, 0-9",

View File

@ -1,16 +1,22 @@
package fr.xephi.authme.util; package fr.xephi.authme.util;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -99,6 +105,68 @@ public class UtilsTest {
verifyZeroInteractions(sender); verifyZeroInteractions(sender);
} }
@Test
public void shouldCheckIfCollectionIsEmpty() {
// given
List<String> emptyList = Collections.emptyList();
Collection<Integer> nonEmptyColl = Arrays.asList(3, 4, 5);
// when / then
assertThat(Utils.isCollectionEmpty(emptyList), equalTo(true));
assertThat(Utils.isCollectionEmpty(nonEmptyColl), equalTo(false));
assertThat(Utils.isCollectionEmpty(null), equalTo(true));
}
@Test
public void shouldReturnCoreCount() {
// given / when / then
assertThat(Utils.getCoreCount(), greaterThan(0));
}
@Test
public void shouldLogAndSendWarning() {
// given
Logger logger = TestHelper.setupLogger();
String message = "Error while performing action";
CommandSender sender = mock(CommandSender.class);
// when
Utils.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
verify(sender).sendMessage(ChatColor.RED + message);
}
@Test
public void shouldLogWarningAndNotSendToConsoleSender() {
// given
Logger logger = TestHelper.setupLogger();
String message = "Error while performing action";
CommandSender sender = mock(ConsoleCommandSender.class);
// when
Utils.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
verifyZeroInteractions(sender);
}
@Test
public void shouldLogWarningAndHandleNullCommandSender() {
// given
Logger logger = TestHelper.setupLogger();
String message = "Error while performing action";
CommandSender sender = null;
// when
Utils.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
}
@Test @Test
public void shouldCheckIfClassIsLoaded() { public void shouldCheckIfClassIsLoaded() {
// given / when / then // given / when / then

View File

@ -30,7 +30,7 @@ import static org.mockito.Mockito.when;
*/ */
public class EncryptionMethodInfoGatherer { public class EncryptionMethodInfoGatherer {
private final static Set<Class<? extends Annotation>> RELEVANT_ANNOTATIONS = private static final Set<Class<? extends Annotation>> RELEVANT_ANNOTATIONS =
ImmutableSet.of(HasSalt.class, Recommendation.class, AsciiRestricted.class); ImmutableSet.of(HasSalt.class, Recommendation.class, AsciiRestricted.class);
private static Injector injector = createInitializer(); private static Injector injector = createInitializer();
@ -104,6 +104,9 @@ public class EncryptionMethodInfoGatherer {
/** /**
* Returns the super class of the given encryption method if it is also of EncryptionMethod type. * Returns the super class of the given encryption method if it is also of EncryptionMethod type.
* (Anything beyond EncryptionMethod is not of interest.) * (Anything beyond EncryptionMethod is not of interest.)
*
* @param methodClass the class to process
* @return the super class of the given class if it is also an EncryptionMethod type, otherwise null
*/ */
private static Class<?> getSuperClass(Class<?> methodClass) { private static Class<?> getSuperClass(Class<?> methodClass) {
Class<?> zuper = methodClass.getSuperclass(); Class<?> zuper = methodClass.getSuperclass();

View File

@ -140,10 +140,15 @@ public class GeneratePluginYml implements AutoToolTask {
} }
private static String buildUsage(CommandDescription command) { private static String buildUsage(CommandDescription command) {
if (!command.getArguments().isEmpty()) {
return CommandUtils.buildSyntax(command);
}
final String commandStart = "/" + command.getLabels().get(0); final String commandStart = "/" + command.getLabels().get(0);
if (!command.getArguments().isEmpty()) {
// Command has arguments, so generate something like /authme register <password> <confirmPass>
final String arguments = command.getArguments().stream()
.map(CommandUtils::formatArgument)
.collect(Collectors.joining(" "));
return commandStart + " " + arguments;
}
// Argument-less command, list all children: /authme register|login|firstspawn|spawn|...
String usage = commandStart + " " + command.getChildren() String usage = commandStart + " " + command.getChildren()
.stream() .stream()
.filter(cmd -> !cmd.getLabels().contains("help")) .filter(cmd -> !cmd.getLabels().contains("help"))

View File

@ -18,3 +18,7 @@ doesNotExist:
wrongEntry: wrongEntry:
command: 'should be ignored' command: 'should be ignored'
executor: PLAYER executor: PLAYER
onUnregister:
farewell:
command: 'msg %p sad to see you go!'
executor: CONSOLE