diff --git a/README.md b/README.md index a5fa9fb52..e160fc40a 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,28 @@

-

The most used authentication plugin for CraftBukkit/Spigot!

+

The most used authentication plugin for Spigot and CraftBukkit!


-#####Development tools: +#####Links and Contacts: -- MAIN REPO (**release sources, issue tracker!**): [Github Main Page](https://github.com/Xephi/AuthMeReloaded) +- GitHub pages: + - [Main](https://github.com/Xephi/AuthMeReloaded) (**release sources, issue tracker!**) + - [Development](https://github.com/AuthMe/AuthMeReloaded) (**latest sources, please send PRs here!**): -- DEVELOPMENT TEAM REPO (**latest sources, please send PRs here!**): [Github Development Page](https://github.com/AuthMe/AuthMeReloaded) +- Contacts: + - [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Xephi/AuthMeReloaded?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -- Developers ChatRoom: [![Join the chat at https://gitter.im/Xephi/AuthMeReloaded](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Xephi/AuthMeReloaded?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +- CI Services: + - [Official Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) (**DEVELOPMENT BUILDS**) + - Travis CI: [![Travis CI](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded) + - CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) -- Build Server (**DEVELOPMENT BUILDS**): [Xephi's Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) +- Project status: + - Dependencies: [![Dependencies status](https://www.versioneye.com/user/projects/57b182e8d6ffcd0032d7cf2d/badge.svg)](https://www.versioneye.com/user/projects/57b182e8d6ffcd0032d7cf2d) + - Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) -- Build status: [![Build Status](https://travis-ci.org/AuthMe/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/AuthMe/AuthMeReloaded) - -- Dependencies: [![Dependency Status](https://www.versioneye.com/user/projects/57b182e8d6ffcd0032d7cf2d/badge.svg)](https://www.versioneye.com/user/projects/57b182e8d6ffcd0032d7cf2d) - -- Build status (CircleCI): [![Circle CI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) -- Alternative Dev Build download link (via CircleCi): Download -- JitPack (just in case): [![](https://jitpack.io/v/AuthMe/AuthMeReloaded.svg)](https://jitpack.io/#AuthMe/AuthMeReloaded) - -- Code Coverage: [![Coverage Status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) - -- Issue Tracking : [![Stories in Ready](https://badge.waffle.io/Xephi/AuthMeReloaded.png?label=ready&title=Ready)](https://waffle.io/Xephi/AuthMeReloaded) [![Stories in Bugs](https://badge.waffle.io/Xephi/AuthMeReloaded.png?label=bugs&title=Bugs)](https://waffle.io/Xephi/AuthMeReloaded) [![Stories in In%20Progress](https://badge.waffle.io/Xephi/AuthMeReloaded.png?label=in%20progress&title=In%20Progress)](https://waffle.io/Xephi/AuthMeReloaded) - -- JavaDoc: AuthMe Javadoc - -- Maven Repo: AuthMe Repo +- Development resources: + - JavaDocs + - Maven Repository #####Statistics: @@ -38,25 +34,21 @@ McStats: http://mcstats.org/plugin/AuthMe -#####Development history: -Outdated! -[![Gource AuthMe History Video](http://img.youtube.com/vi/hJRzNfYyd9k/hqdefault.jpg)](https://www.youtube.com/watch?v=hJRzNfYyd9k) -
-#####Compiling Requirements: +#####Compiling requirements: >- JDK 1.8 >- Maven >- Git/Github (Optional) #####How to compile the project: >- Clone the project with Git/Github ->- Execute command "mvn clean install" +>- Execute command "mvn clean package" -#####Running Requirements: +#####Running requirements: >- Java 1.8 ->- PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X, 1.9.X, 1.10.X) ->- ProtocolLib (optional, required by the protectInventory feature) +>- TacoSpigot, PaperSpigot, Spigot or CraftBukkit (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X) +>- ProtocolLib (optional, required by some features)
###Plugin Description: @@ -102,19 +94,19 @@ You can also create your own translation file and, if you want, you can share it
  • Custom MySQL tables/columns names (useful with forums databases)
  • Cached database queries!
  • -
  • Fully compatible with Citizens2, CombatTag, CombatTagPlus and ChestShop!
  • +
  • Fully compatible with Citizens2, CombatTag, CombatTagPlus!
  • Compatible with Minecraft mods like BuildCraft or RedstoneCraft
  • -
  • Restricted users (associate a Username with an IP)
  • -
  • Protect player's inventory until a correct Authentication
  • +
  • Restricted users (associate a username with an IP)
  • +
  • Protect player's inventory until correct authentication (requires ProtocolLib)
  • Saves the quit location of the player
  • -
  • Automatic database Backup
  • -
  • Available languages: en, de, br, cz, pl, fr, uk, ru, hu, sk, es, fi, zhtw, zhhk, zhcn, lt, it, ko, pt, nl, gl, bg, eu, tr, vn (feel free to send new translations)
  • +
  • Automatic database backup
  • +
  • Available languages: translations
  • Built-in Deprecated FlatFile (auths.db) to SQL (authme.sql) converter!
  • Import your old database from other plugins like Rakamak, xAuth, CrazyLogin, RoyalAuth and vAuth!
  • ####Configuration -How to Configure Authme +How to configure Authme ####Email Recovery Dependency How to configure email recovery system? ####Commands @@ -125,30 +117,21 @@ You can also create your own translation file and, if you want, you can share it - [List of all permission nodes](http://github.com/AuthMe-Team/AuthMeReloaded/blob/master/docs/permission_nodes.md) ####How To -- [How to install and set up](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-install-and-initial-configuration/) - [How to import database from xAuth](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-xauth/) - [Website integration](http://dev.bukkit.org/server-mods/authme-reloaded/pages/web-site-integration/) -- [Click here for an example of the config file](https://raw.githubusercontent.com/Xephi/AuthMeReloaded/master/src/main/resources/config.yml) - [How to convert from Rakamak](http://dev.bukkit.org/server-mods/authme-reloaded/pages/how-to-import-database-from-rakamak/) -- Convert from FlatFile (auths.db but not the sqlite one) to MySQL: /authme converter +- Convert between database types (e.g. SQLite to MySQL): /authme converter
    -#####GeoIP -This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com - -
    - -#####Donate -

    Do you like our work? Do you want to buy us a coffee? :)
    -EUR: -USD:

    - #####Sponsor GameHosting.it is leader in Italy as Game Server Provider. With its own DataCenter offers Anti-DDoS solutions at affordable prices. Game Server of Minecraft based on Multicraft are equipped with the latest technology in hardware. [![GameHosting](http://www.gamehosting.it/images/bn3.png)](http://www.gamehosting.it) #####Credits -

    Team members: look at the team.txt file +

    Team members: look at the member list

    Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka

    Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex

    + +#####GeoIP License +This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com diff --git a/circle.yml b/circle.yml index 80201e5e8..3359eb2ad 100644 --- a/circle.yml +++ b/circle.yml @@ -11,6 +11,8 @@ test: - cp ./target/AuthMe-*-SNAPSHOT-spigot.jar $CIRCLE_ARTIFACTS - cp ./target/AuthMe-*-SNAPSHOT-legacy.jar $CIRCLE_ARTIFACTS - cp ./target/AuthMe-*-SNAPSHOT-spigot.jar $CIRCLE_ARTIFACTS/AuthMe.jar + - mkdir -p $CIRCLE_TEST_REPORTS/junit/ + - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; notify: webhooks: - url: https://webhooks.gitter.im/e/7b92ac1a1741748b26bf diff --git a/docs/commands.md b/docs/commands.md index 4dd070fd6..b9f68054e 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` @@ -63,7 +63,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). - **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
    Requires `authme.player.changepassword` - **/changepassword help** [query]: View detailed help for /changepassword commands. -- **/email**: The AuthMeReloaded Email command base. +- **/email**: The AuthMeReloaded email command base. +- **/email show**: Show your current email address. - **/email add** <email> <verifyEmail>: Add a new email address to your account.
    Requires `authme.player.email.add` - **/email change** <oldEmail> <newEmail>: Change an email address of your account. @@ -75,7 +76,6 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
    Requires `authme.player.captcha` - **/captcha help** [query]: View detailed help for /captcha commands. - --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 16 21:39:08 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 18:25:12 CEST 2016 diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 000000000..212d83a7d --- /dev/null +++ b/docs/config.md @@ -0,0 +1,467 @@ + + + +## AuthMe Configuration +The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, +with which you can configure various settings. This following is the initial contents of +the generated config.yml file. + +```yml + +DataSource: + # What type of database do you want to use? + # Valid values: sqlite, mysql + backend: 'SQLITE' + # Enable database caching, should improve database performance + caching: true + # Database host address + mySQLHost: '127.0.0.1' + # Database port + mySQLPort: '3306' + # Username about Database Connection Infos + mySQLUsername: 'authme' + # Password about Database Connection Infos + mySQLPassword: '12345' + # Database Name, use with converters or as SQLITE database name + mySQLDatabase: 'authme' + # Table of the database + mySQLTablename: 'authme' + # Column of IDs to sort data + mySQLColumnId: 'id' + # Column for storing or checking players nickname + mySQLColumnName: 'username' + # Column for storing or checking players RealName + mySQLRealName: 'realname' + # Column for storing players passwords + mySQLColumnPassword: 'password' + # Column for storing players emails + mySQLColumnEmail: 'email' + # Column for storing if a player is logged in or not + mySQLColumnLogged: 'isLogged' + # Column for storing players ips + mySQLColumnIp: 'ip' + # Column for storing players lastlogins + mySQLColumnLastLogin: 'lastlogin' + # Column for storing player LastLocation - X + mySQLlastlocX: 'x' + # Column for storing player LastLocation - Y + mySQLlastlocY: 'y' + # Column for storing player LastLocation - Z + mySQLlastlocZ: 'z' + # Column for storing player LastLocation - World Name + mySQLlastlocWorld: 'world' + # Overrides the size of the DB Connection Pool, -1 = Auto + poolSize: -1 +ExternalBoardOptions: + # Column for storing players passwords salts + mySQLColumnSalt: '' + # Column for storing players groups + mySQLColumnGroup: '' + # -1 means disabled. If you want that only activated players + # can log into your server, you can set here the group number + # of unactivated users, needed for some forum/CMS support + nonActivedUserGroup: -1 + # Other MySQL columns where we need to put the username (case-sensitive) + mySQLOtherUsernameColumns: [] + # How much log2 rounds needed in BCrypt (do not change if you do not know what it does) + bCryptLog2Round: 10 + # phpBB table prefix defined during the phpBB installation process + phpbbTablePrefix: 'phpbb_' + # phpBB activated group ID; 2 is the default registered group defined by phpBB + phpbbActivatedGroupId: 2 + # Wordpress prefix defined during WordPress installation + wordpressTablePrefix: 'wp_' +Converter: + Rakamak: + # Rakamak file name + fileName: 'users.rak' + # Rakamak use IP? + useIP: false + # Rakamak IP file name + ipFileName: 'UsersIp.rak' + CrazyLogin: + # CrazyLogin database file name + fileName: 'accounts.db' +settings: + sessions: + # Do you want to enable the session feature? + # If enabled, when a player authenticates successfully, + # his IP and his nickname is saved. + # The next time the player joins the server, if his IP + # is the same as last time and the timeout hasn't + # expired, he will not need to authenticate. + enabled: false + # After how many minutes should a session expire? + # Remember that sessions will end only after the timeout, and + # if the player's IP has changed but the timeout hasn't expired, + # the player will be kicked from the server due to invalid session + timeout: 10 + # Should the session expire if the player tries to log in with + # another IP address? + sessionExpireOnIpChange: true + # Message language, available languages: + # https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md + messagesLanguage: 'en' + restrictions: + # Keeps collisions disabled for logged players + # Works only with MC 1.9 + keepCollisionsDisabled: false + # Can not authenticated players chat? + # Keep in mind that this feature also blocks all commands not + # listed in the list below. + allowChat: false + # Hide the chat log from players who are not authenticated? + hideChat: false + # Allowed commands for unauthenticated players + allowCommands: + - '/login' + - '/register' + - '/l' + - '/reg' + - '/email' + - '/captcha' + # Max number of allowed registrations per IP + # The value 0 means an unlimited number of registrations! + maxRegPerIp: 1 + # Minimum allowed username length + minNicknameLength: 3 + # Maximum allowed username length + maxNicknameLength: 16 + # When this setting is enabled, online players can't be kicked out + # due to "Logged in from another Location" + # This setting will prevent potential security exploits. + ForceSingleSession: true + ForceSpawnLocOnJoin: + # If enabled, every player that spawn in one of the world listed in + # "ForceSpawnLocOnJoin.worlds" will be teleported to the spawnpoint after successful + # authentication. The quit location of the player will be overwritten. + # This is different from "teleportUnAuthedToSpawn" that teleport player + # to the spawnpoint on join. + enabled: false + # WorldNames where we need to force the spawn location + # Case-sensitive! + worlds: + - 'world' + - 'world_nether' + - 'world_the_end' + # This option will save the quit location of the players. + SaveQuitLocation: false + # To activate the restricted user feature you need + # to enable this option and configure the AllowedRestrictedUser field. + AllowRestrictedUser: false + # The restricted user feature will kick players listed below + # if they don't match the defined IP address. Names are case-insensitive. + # Example: + # AllowedRestrictedUser: + # - playername;127.0.0.1 + AllowedRestrictedUser: [] + # Should unregistered players be kicked immediately? + kickNonRegistered: false + # Should players be kicked on wrong password? + kickOnWrongPassword: false + # Should not logged in players be teleported to the spawn? + # After the authentication they will be teleported back to + # their normal position. + teleportUnAuthedToSpawn: false + # Can unregistered players walk around? + allowMovement: false + # Should not authenticated players have speed = 0? + # This will reset the fly/walk speed to default value after the login. + removeSpeed: true + # After how many seconds should players who fail to login or register + # be kicked? Set to 0 to disable. + timeout: 30 + # Regex syntax of allowed characters in the player name. + allowedNicknameCharacters: '[a-zA-Z0-9_]*' + # How far can unregistered players walk? + # Set to 0 for unlimited radius + allowedMovementRadius: 100 + # Enable double check of password when you register + # when it's true, registration requires that kind of command: + # /register + enablePasswordConfirmation: true + # Should we protect the player inventory before logging in? Requires ProtocolLib. + ProtectInventoryBeforeLogIn: true + # Should we deny the tabcomplete feature before logging in? Requires ProtocolLib. + DenyTabCompleteBeforeLogin: true + # Should we display all other accounts from a player when he joins? + # permission: /authme.admin.accounts + displayOtherAccounts: true + # Ban ip when the ip is not the ip registered in database + banUnsafedIP: false + # Spawn priority; values: authme, essentials, multiverse, default + spawnPriority: 'authme,essentials,multiverse,default' + # Maximum Login authorized by IP + maxLoginPerIp: 0 + # Maximum Join authorized by IP + maxJoinPerIp: 0 + # AuthMe will NEVER teleport players if set to true! + noTeleport: false + # Regex syntax for allowed chars in passwords + allowedPasswordCharacters: '[\x21-\x7E]*' + # Threshold of the other accounts command, a value less than 2 means disabled. + otherAccountsCmdThreshold: 0 + # Command to run when a user has more accounts than the configured threshold. + # Available variables: %playername%, %playerip% + otherAccountsCmd: 'say The player %playername% with ip %playerip% has multiple accounts!' + # Log level: INFO, FINE, DEBUG. Use INFO for general messages, + # FINE for some additional detailed ones (like password failed), + # and DEBUG for debugging + logLevel: 'FINE' + # By default we schedule async tasks when talking to the database. If you want + # typical communication with the database to happen synchronously, set this to false + useAsyncTasks: true + GameMode: + # Force survival gamemode when player joins? + ForceSurvivalMode: false + unrestrictions: + # Below you can list all account names that AuthMe will ignore + # for registration or login. Configure it at your own risk!! + # This option adds compatibility with BuildCraft and some other mods. + # It is case-insensitive! Example: + # UnrestrictedName: + # - 'npcPlayer' + # - 'npcPlayer2' + UnrestrictedName: [] + security: + # Minimum length of password + minPasswordLength: 5 + # Maximum length of password + passwordMaxLength: 30 + # This is a very important option: every time a player joins the server, + # if they are registered, AuthMe will switch him to unLoggedInGroup. + # This should prevent all major exploits. + # You can set up your permission plugin with this special group to have no permissions, + # or only permission to chat (or permission to send private messages etc.). + # The better way is to set up this group with few permissions, so if a player + # tries to exploit an account they can do only what you've defined for the group. + # After, a logged in player will be moved to his correct permissions group! + # Please note that the group name is case-sensitive, so 'admin' is different from 'Admin' + # Otherwise your group will be wiped and the player will join in the default group []! + # Example unLoggedinGroup: NotLogged + unLoggedinGroup: 'unLoggedinGroup' + # Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, + # MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, + # DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only) + passwordHash: 'SHA256' + # Salt length for the SALTED2MD5 MD5(MD5(password)+salt) + doubleMD5SaltLength: 8 + # If a password check fails, AuthMe will also try to check with the following hash methods. + # Use this setting when you change from one hash method to another. + # AuthMe will update the password to the new hash. Example: + # legacyHashes: + # - 'SHA1' + legacyHashes: [] + # Prevent unsafe passwords from being used; put them in lowercase! + # You should always set 'help' as unsafePassword due to possible conflicts. + # unsafePasswords: + # - '123456' + # - 'password' + # - 'help' + unsafePasswords: + - '123456' + - 'password' + - 'qwerty' + - '12345' + - '54321' + - '123456789' + - 'help' + registration: + # Enable registration on the server? + enabled: true + # Send every X seconds a message to a player to + # remind him that he has to login/register + messageInterval: 5 + # Only registered and logged in players can play. + # See restrictions for exceptions + force: true + # Do we replace password registration by an email registration method? + enableEmailRegistrationSystem: false + # Enable double check of email when you register + # when it's true, registration requires that kind of command: + # /register + doubleEmailCheck: false + # Do we force kick a player after a successful registration? + # Do not use with login feature below + forceKickAfterRegister: false + # Does AuthMe need to enforce a /login after a successful registration? + forceLoginAfterRegister: false + # Force these commands after /login, without any '/', use %p to replace with player name + forceCommands: [] + # Force these commands after /login as service console, without any '/'. + # Use %p to replace with player name + forceCommandsAsConsole: [] + # Force these commands after /register, without any '/', use %p to replace with player name + forceRegisterCommands: [] + # Force these commands after /register as a server console, without any '/'. + # Use %p to replace with player name + forceRegisterCommandsAsConsole: [] + # Enable to display the welcome message (welcome.txt) after a login + # You can use colors in this welcome.txt + some replaced strings: + # {PLAYER}: player name, {ONLINE}: display number of online players, + # {MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged, + # {WORLD}: player current world, {SERVER}: server name + # {VERSION}: get current bukkit version, {COUNTRY}: player country + useWelcomeMessage: true + # Broadcast the welcome message to the server or only to the player? + # set true for server or false for player + broadcastWelcomeMessage: false + # Should we delay the join message and display it once the player has logged in? + delayJoinMessage: false + # Should we remove the leave messages of unlogged users? + removeUnloggedLeaveMessage: false + # Should we remove join messages altogether? + removeJoinMessage: false + # Should we remove leave messages altogether? + removeLeaveMessage: false + # Do we need to add potion effect Blinding before login/reigster? + applyBlindEffect: false + # Do we need to prevent people to login with another case? + # If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI + preventOtherCase: false +permission: + # Take care with this option; if you want + # to use group switching of AuthMe + # for unloggedIn players, set this setting to true. + # Default is false. + EnablePermissionCheck: false +Email: + # Email SMTP server host + mailSMTP: 'smtp.gmail.com' + # Email SMTP server port + mailPort: 465 + # Email account which sends the mails + mailAccount: '' + # Email account password + mailPassword: '' + # Custom sender name, replacing the mailAccount name in the email + mailSenderName: '' + # Recovery password length + RecoveryPasswordLength: 8 + # Mail Subject + mailSubject: 'Your new AuthMe password' + # Like maxRegPerIP but with email + maxRegPerEmail: 1 + # Recall players to add an email? + recallPlayers: false + # Delay in minute for the recall scheduler + delayRecall: 5 + # Blacklist these domains for emails + emailBlacklisted: + - '10minutemail.com' + # Whitelist ONLY these domains for emails + emailWhitelisted: [] + # Send the new password drawn in an image? + generateImage: false + # The OAuth2 token + emailOauth2Token: '' +Hooks: + # Do we need to hook with multiverse for spawn checking? + multiverse: true + # Do we need to hook with BungeeCord? + bungeecord: false + # Send player to this BungeeCord server after register/login + sendPlayerTo: '' + # Do we need to disable Essentials SocialSpy on join? + disableSocialSpy: true + # Do we need to force /motd Essentials command on join? + useEssentialsMotd: false +GroupOptions: + # Unregistered permission group + UnregisteredPlayerGroup: '' + # Registered permission group + RegisteredPlayerGroup: '' +Protection: + # Enable some servers protection (country based login, antibot) + enableProtection: false + # Apply the protection also to registered usernames + enableProtectionRegistered: true + # Countries allowed to join the server and register. For country codes, see + # http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ + # PLEASE USE QUOTES! + countries: + - 'US' + - 'GB' + # Countries not allowed to join the server and register + # PLEASE USE QUOTES! + countriesBlacklist: + - 'A1' + # Do we need to enable automatic antibot system? + enableAntiBot: true + # The interval in seconds + antiBotInterval: 5 + # Max number of players allowed to login in the interval + # before the AntiBot system is enabled automatically + antiBotSensibility: 10 + # Duration in minutes of the antibot automatic system + antiBotDuration: 10 + # Delay in seconds before the antibot activation + antiBotDelay: 60 +Purge: + # If enabled, AuthMe automatically purges old, unused accounts + useAutoPurge: false + # Number of days after which an account should be purged + daysBeforeRemovePlayer: 60 + # Do we need to remove the player.dat file during purge process? + removePlayerDat: false + # Do we need to remove the Essentials/userdata/player.yml file during purge process? + removeEssentialsFile: false + # World where are players.dat stores + defaultWorld: 'world' + # Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge? + removeLimitedCreativesInventories: false + # Do we need to remove the AntiXRayData/PlayerData/player file during purge process? + removeAntiXRayFile: false + # Do we need to remove permissions? + removePermissions: false +Security: + SQLProblem: + # Stop the server if we can't contact the sql database + # Take care with this, if you set this to false, + # AuthMe will automatically disable and the server won't be protected! + stopServer: true + console: + # Remove passwords from console? + removePassword: true + # Copy AuthMe log output in a separate file as well? + logConsole: true + captcha: + # Enable captcha when a player uses wrong password too many times + useCaptcha: false + # Max allowed tries before a captcha is required + maxLoginTry: 5 + # Captcha length + captchaLength: 5 + tempban: + # Tempban a user's IP address if they enter the wrong password too many times + enableTempban: false + # How many times a user can attempt to login before their IP being tempbanned + maxLoginTries: 10 + # The length of time a IP address will be tempbanned in minutes + # Default: 480 minutes, or 8 hours + tempbanLength: 480 + # How many minutes before resetting the count for failed logins by IP and username + # Default: 480 minutes (8 hours) + minutesBeforeCounterReset: 480 + recoveryCode: + # Number of characters a recovery code should have (0 to disable) + length: 8 + # How many hours is a recovery code valid for? + validForHours: 4 +BackupSystem: + # Enable or disable automatic backup + ActivateBackup: false + # Set backup at every start of server + OnServerStart: false + # Set backup at every stop of server + OnServerStop: true + # Windows only mysql installation Path + MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\' +``` + +To change settings on a running server, save your changes to config.yml and use +`/authme reload`. + +--- + +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Nov 13 13:34:49 CET 2016 diff --git a/docs/hash_algorithms.md b/docs/hash_algorithms.md index e7fd0ae36..02bcafe6f 100644 --- a/docs/hash_algorithms.md +++ b/docs/hash_algorithms.md @@ -1,5 +1,5 @@ - + ## Hash Algorithms AuthMe supports the following hash algorithms for storing your passwords safely. @@ -13,11 +13,11 @@ CRAZYCRYPT1 | Do not use | 128 | | | Username | | DOUBLEMD5 | Do not use | 32 | | | None | | IPB3 | Acceptable | 32 | | | Text | 5 | Y IPB4 | Does not work | 60 | | | Text | 22 | Y -JOOMLA | Recommended | 65 | | | Text | 32 | +JOOMLA | Acceptable | 65 | | | Text | 32 | MD5 | Do not use | 32 | | | None | | MD5VB | Acceptable | 56 | | | Text | 16 | MYBB | Acceptable | 32 | | | Text | 8 | Y -PBKDF2 | Does not work | 332 | | | Text | 12 | +PBKDF2 | Recommended | 165 | | | Text | 16 | PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 | PHPBB | Acceptable | 34 | | | Text | 16 | PHPFUSION | Do not use | 64 | Y | | | | Y @@ -82,4 +82,4 @@ or bad. --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 01 23:42:20 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Nov 25 15:48:35 CET 2016 diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index 60d39a15c..fb6408206 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. @@ -38,15 +38,15 @@ The following are the permission nodes that are currently supported by the lates - **authme.player.email** – Grants all email permissions. - **authme.player.email.add** – Command permission to add an email address. - **authme.player.email.change** – Command permission to change the email address. -- **authme.player.email.recover** – Command permission to recover an account using it's email address. +- **authme.player.email.recover** – Command permission to recover an account using its email address. - **authme.player.login** – Command permission to login. - **authme.player.logout** – Command permission to logout. - **authme.player.register** – Command permission to register. - **authme.player.seeownaccounts** – Permission to use to see own other accounts. - **authme.player.unregister** – Command permission to unregister. -- **authme.vip** – Permission node to identify VIP users. +- **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked. --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 16 21:39:10 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 15:38:58 CEST 2016 diff --git a/docs/translations.md b/docs/translations.md index c261bb8f6..a96f2cf61 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -8,32 +8,34 @@ in your config.yml to use the language, or use another language code to start a Code | Language | Translated |   ---- | -------- | ---------: | ------ [en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | bar -[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 73% | bar +[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 71% | bar [br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | bar -[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 91% | bar -[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 97% | bar +[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 88% | bar +[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 95% | bar [es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | bar -[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 66% | bar -[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 70% | bar -[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 97% | bar -[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 74% | bar +[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 64% | bar +[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 68% | bar +[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 95% | bar +[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 72% | bar [hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 100% | bar -[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 74% | bar +[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 72% | bar [it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | bar -[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 76% | bar -[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 57% | bar -[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | bar -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 95% | bar -[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 91% | bar -[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 97% | bar -[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 50% | bar -[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 85% | bar -[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 97% | bar -[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 85% | bar -[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 85% | bar -[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 85% | bar -[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 85% | bar +[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 74% | bar +[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 55% | bar +[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 78% | bar +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | bar +[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 88% | bar +[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | bar +[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 100% | bar +[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 49% | bar +[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 83% | bar +[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 95% | bar +[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 83% | bar +[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 83% | bar +[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 83% | bar +[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 83% | bar + --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 09 09:42:48 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Dec 11 08:16:38 CET 2016 diff --git a/pom.xml b/pom.xml index 60fb7c40d..52c17b68b 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Gnat008 - 1.10.2-R0.1-SNAPSHOT + 1.11-R0.1-SNAPSHOT @@ -196,7 +196,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.5.1 + 3.6.0 ${project.jdkVersion} ${project.jdkVersion} @@ -268,6 +268,10 @@ net.ricecode.similarity fr.xephi.authme.libs.ricecode.similarity + + de.rtner + fr.xephi.authme.libs.de.rtner + javax.inject fr.xephi.authme.libs.javax.inject @@ -319,6 +323,10 @@ net.ricecode.similarity fr.xephi.authme.libs.ricecode.similarity + + de.rtner + fr.xephi.authme.libs.de.rtner + javax.inject fr.xephi.authme.libs.javax.inject @@ -524,6 +532,13 @@ true + + + de.rtner + PBKDF2 + 1.1.2 + + @@ -873,7 +888,7 @@ com.github.authme configme - 0.2 + 0.2.1 compile true @@ -904,7 +919,7 @@ org.mockito mockito-core test - 2.0.5-beta + 2.2.27 hamcrest-core @@ -917,13 +932,13 @@ org.xerial sqlite-jdbc - 3.8.11.2 + 3.15.1 test com.h2database h2 - 1.4.192 + 1.4.193 test diff --git a/samples/website_integration/AuthMeController.php b/samples/website_integration/AuthMeController.php index 66fe03b4a..f67cf0608 100644 --- a/samples/website_integration/AuthMeController.php +++ b/samples/website_integration/AuthMeController.php @@ -109,7 +109,6 @@ abstract class AuthMeController { * @return string|null the hash, or null if unavailable (e.g. username doesn't exist) */ private function getHashFromDatabase($username) { - // Add here your database host, username, password and database name $mysqli = $this->getAuthmeMySqli(); if ($mysqli !== null) { $stmt = $mysqli->prepare('SELECT password FROM ' . self::AUTHME_TABLE . ' WHERE username = ?'); diff --git a/samples/website_integration/Pbkdf2.php b/samples/website_integration/Pbkdf2.php new file mode 100644 index 000000000..456c004fc --- /dev/null +++ b/samples/website_integration/Pbkdf2.php @@ -0,0 +1,53 @@ +CHARS = self::initCharRange(); + } + + protected function isValidPassword($password, $hash) { + // hash := pbkdf2_sha256$iterations$salt$hash + $parts = explode('$', $hash); + return count($parts) === 4 && $hash === $this->computeHash($parts[1], $parts[2], $password); + } + + protected function hash($password) { + $salt = $this->generateSalt(); + return $this->computeHash(self::NUMBER_OF_ITERATIONS, $salt, $password); + } + + private function computeHash($iterations, $salt, $password) { + return 'pbkdf2_sha256$' . self::NUMBER_OF_ITERATIONS . '$' . $salt + . '$' . hash_pbkdf2('sha256', $password, $salt, self::NUMBER_OF_ITERATIONS, 64, false); + } + + /** + * @return string randomly generated salt + */ + private function generateSalt() { + $maxCharIndex = count($this->CHARS) - 1; + $salt = ''; + for ($i = 0; $i < self::SALT_LENGTH; ++$i) { + $salt .= $this->CHARS[mt_rand(0, $maxCharIndex)]; + } + return $salt; + } + + private static function initCharRange() { + return array_merge(range('0', '9'), range('a', 'f')); + } +} diff --git a/samples/website_integration/Sha256.php b/samples/website_integration/Sha256.php index 815abe59a..80c0588fe 100644 --- a/samples/website_integration/Sha256.php +++ b/samples/website_integration/Sha256.php @@ -15,7 +15,7 @@ class Sha256 extends AuthMeController { const SALT_LENGTH = 16; public function __construct() { - $this->CHARS = self::initRandomChars(); + $this->CHARS = self::initCharRange(); } protected function isValidPassword($password, $hash) { @@ -41,7 +41,7 @@ class Sha256 extends AuthMeController { return $salt; } - private static function initRandomChars() { + private static function initCharRange() { return array_merge(range('0', '9'), range('a', 'f')); } diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index b52caf32e..74a986543 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -5,44 +5,45 @@ import ch.jalu.injector.InjectorBuilder; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.api.API; import fr.xephi.authme.api.NewAPI; -import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.command.CommandHandler; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.initialization.Initializer; -import fr.xephi.authme.initialization.MetricsManager; +import fr.xephi.authme.initialization.DataSourceProvider; import fr.xephi.authme.initialization.OnShutdownPlayerSaver; +import fr.xephi.authme.initialization.OnStartupTasks; +import fr.xephi.authme.initialization.SettingsProvider; import fr.xephi.authme.initialization.TaskCloser; import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.PlayerListener; +import fr.xephi.authme.listener.PlayerListener111; import fr.xephi.authme.listener.PlayerListener16; import fr.xephi.authme.listener.PlayerListener18; import fr.xephi.authme.listener.PlayerListener19; import fr.xephi.authme.listener.ServerListener; -import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsSystemType; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.service.BackupService; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.GeoIpService; +import fr.xephi.authme.service.MigrationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.purge.PurgeService; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.MigrationService; import fr.xephi.authme.util.PlayerUtils; import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; import java.io.File; @@ -60,7 +61,7 @@ public class AuthMe extends JavaPlugin { private static final String LOG_FILENAME = "authme.log"; private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE; - // Default version and build number values; + // Default version and build number values private static String pluginVersion = "N/D"; private static String pluginBuildNumber = "Unknown"; @@ -68,7 +69,6 @@ public class AuthMe extends JavaPlugin { private CommandHandler commandHandler; private PermissionsManager permsMan; private Settings settings; - private Messages messages; private DataSource database; private BukkitService bukkitService; private Injector injector; @@ -86,9 +86,8 @@ public class AuthMe extends JavaPlugin { */ @VisibleForTesting @SuppressWarnings("deprecation") // the super constructor is deprecated to mark it for unit testing only - protected AuthMe(final PluginLoader loader, final Server server, final PluginDescriptionFile description, - final File dataFolder, final File file) { - super(loader, server, description, dataFolder, file); + protected AuthMe(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { + super(loader, description, dataFolder, file); } /** @@ -133,7 +132,7 @@ public class AuthMe extends JavaPlugin { @Override public void onEnable() { // Load the plugin version data from the plugin description file - loadPluginInfo(); + loadPluginInfo(getDescription().getVersion()); // Initialize the plugin try { @@ -156,7 +155,7 @@ public class AuthMe extends JavaPlugin { new BackupService(this, settings).doBackup(BackupService.BackupCause.START); // Set up Metrics - MetricsManager.sendMetrics(this, settings); + OnStartupTasks.sendMetrics(this, settings); // Sponsor messages ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt."); @@ -176,9 +175,10 @@ public class AuthMe extends JavaPlugin { /** * Load the version and build number of the plugin from the description file. + * + * @param versionRaw the version as given by the plugin description file */ - private void loadPluginInfo() { - String versionRaw = this.getDescription().getVersion(); + private static void loadPluginInfo(String versionRaw) { int index = versionRaw.lastIndexOf("-"); if (index != -1) { pluginVersion = versionRaw.substring(0, index); @@ -191,10 +191,8 @@ public class AuthMe extends JavaPlugin { /** * Initialize the plugin and all the services. - * - * @throws Exception if the initialization fails */ - private void initialize() throws Exception { + private void initialize() { // Set the Logger instance and log file path ConsoleLogger.setLogger(getLogger()); ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME)); @@ -202,40 +200,30 @@ public class AuthMe extends JavaPlugin { // Create plugin folder getDataFolder().mkdir(); - // Load settings and set up the console and console filter - settings = Initializer.createSettings(this); - bukkitService = new BukkitService(this, settings); - Initializer initializer = new Initializer(this, bukkitService); - - ConsoleLogger.setLoggingOptions(settings); - initializer.setupConsoleFilter(settings, getLogger()); - - // Connect to the database and set up tables - database = initializer.setupDatabase(settings); - - // Convert deprecated PLAINTEXT hash entries - MigrationService.changePlainTextToSha256(settings, database, new SHA256()); - - // Injector initialization + // Create injector, provide elements from the Bukkit environment and register providers injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); - - // Register elements of the Bukkit / JavaPlugin environment injector.register(AuthMe.class, this); injector.register(Server.class, getServer()); injector.register(PluginManager.class, getServer().getPluginManager()); injector.register(BukkitScheduler.class, getServer().getScheduler()); injector.provide(DataFolder.class, getDataFolder()); + injector.registerProvider(Settings.class, SettingsProvider.class); + injector.registerProvider(DataSource.class, DataSourceProvider.class); - // Register elements we instantiate manually - injector.register(Settings.class, settings); - injector.register(DataSource.class, database); - injector.register(BukkitService.class, bukkitService); + // Get settings and set up logger + settings = injector.getSingleton(Settings.class); + ConsoleLogger.setLoggingOptions(settings); + OnStartupTasks.setupConsoleFilter(settings, getLogger()); + // Set all service fields on the AuthMe class instantiateServices(injector); + // Convert deprecated PLAINTEXT hash entries + MigrationService.changePlainTextToSha256(settings, database, new SHA256()); + // TODO: does this still make sense? -sgdc3 // If the server is empty (fresh start) just set all the players as unlogged - if (bukkitService.getOnlinePlayers().size() == 0) { + if (bukkitService.getOnlinePlayers().isEmpty()) { database.purgeLogged(); } @@ -243,7 +231,8 @@ public class AuthMe extends JavaPlugin { registerEventListeners(injector); // Start Email recall task if needed - initializer.scheduleRecallEmailTask(settings, database, messages); + OnStartupTasks onStartupTasks = injector.newInstance(OnStartupTasks.class); + onStartupTasks.scheduleRecallEmailTask(); } /** @@ -256,7 +245,7 @@ public class AuthMe extends JavaPlugin { playerCache = PlayerCache.getInstance(); injector.register(PlayerCache.class, playerCache); - messages = injector.getSingleton(Messages.class); + database = injector.getSingleton(DataSource.class); permsMan = injector.getSingleton(PermissionsManager.class); bukkitService = injector.getSingleton(BukkitService.class); commandHandler = injector.getSingleton(CommandHandler.class); @@ -312,6 +301,11 @@ public class AuthMe extends JavaPlugin { if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) { pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this); } + + // Register listener for 1.11 events if available + if (isClassLoaded("org.bukkit.event.entity.EntityAirChangeEvent")) { + pluginManager.registerEvents(injector.getSingleton(PlayerListener111.class), this); + } } /** @@ -343,10 +337,7 @@ public class AuthMe extends JavaPlugin { } // Wait for tasks and close data source - new Thread( - new TaskCloser(this, database), - "AuthMe-DataSource#close" - ).start(); + new TaskCloser(this, database).run(); // Disabled correctly ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); diff --git a/src/main/java/fr/xephi/authme/api/API.java b/src/main/java/fr/xephi/authme/api/API.java index 7fa027882..d05fbe6b9 100644 --- a/src/main/java/fr/xephi/authme/api/API.java +++ b/src/main/java/fr/xephi/authme/api/API.java @@ -19,12 +19,13 @@ import javax.inject.Inject; /** * Deprecated API of AuthMe. Please use {@link NewAPI} instead. + * + * @deprecated Use {@link NewAPI} */ @Deprecated public class API { - public static final String newline = System.getProperty("line.separator"); - public static AuthMe instance; + private static AuthMe instance; private static DataSource dataSource; private static PasswordSecurity passwordSecurity; private static Management management; @@ -83,28 +84,17 @@ public class API { } public static Location getLastLocation(Player player) { - try { - PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase()); + PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName().toLowerCase()); - if (auth != null) { - Location loc = new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); - return loc; - } else { - return null; - } - - } catch (NullPointerException ex) { - return null; + if (auth != null) { + return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); } + return null; } - public static void setPlayerInventory(Player player, ItemStack[] content, - ItemStack[] armor) { - try { - player.getInventory().setContents(content); - player.getInventory().setArmorContents(armor); - } catch (NullPointerException ignored) { - } + public static void setPlayerInventory(Player player, ItemStack[] content, ItemStack[] armor) { + player.getInventory().setContents(content); + player.getInventory().setArmorContents(armor); } /** diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index 5805e4afd..82a2483bc 100644 --- a/src/main/java/fr/xephi/authme/api/NewAPI.java +++ b/src/main/java/fr/xephi/authme/api/NewAPI.java @@ -25,8 +25,8 @@ import java.util.List; */ public class NewAPI { - public static NewAPI singleton; - public final AuthMe plugin; + private static NewAPI singleton; + private final AuthMe plugin; private final PluginHookService pluginHookService; private final DataSource dataSource; private final PasswordSecurity passwordSecurity; diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java index e36664929..8e2b1bdc1 100644 --- a/src/main/java/fr/xephi/authme/command/CommandMapper.java +++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java @@ -87,10 +87,10 @@ public class CommandMapper { return classes; } - private FoundCommandResult getCommandWithSmallestDifference(CommandDescription base, List parts) { + private static FoundCommandResult getCommandWithSmallestDifference(CommandDescription base, List parts) { // Return the base command with incorrect arg count error if we only have one part if (parts.size() <= 1) { - return new FoundCommandResult(base, parts, new ArrayList(), 0.0, INCORRECT_ARGUMENTS); + return new FoundCommandResult(base, parts, new ArrayList<>(), 0.0, INCORRECT_ARGUMENTS); } final String childLabel = parts.get(1); @@ -115,7 +115,7 @@ public class CommandMapper { final int partsSize = parts.size(); List labels = parts.subList(0, Math.min(closestCommand.getLabelCount(), partsSize)); List arguments = (labels.size() == partsSize) - ? new ArrayList() + ? new ArrayList<>() : parts.subList(labels.size(), partsSize); return new FoundCommandResult(closestCommand, labels, arguments, minDifference, status); @@ -141,7 +141,7 @@ public class CommandMapper { * * @return A command if there was a complete match (including proper argument count), null otherwise */ - private CommandDescription getSuitableChild(CommandDescription baseCommand, List parts) { + private static CommandDescription getSuitableChild(CommandDescription baseCommand, List parts) { if (CollectionUtils.isEmpty(parts)) { return null; } diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java deleted file mode 100644 index bb8bbeca4..000000000 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ /dev/null @@ -1,94 +0,0 @@ -package fr.xephi.authme.command; - -import com.github.authme.configme.properties.Property; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.message.Messages; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.service.ValidationService; -import org.bukkit.command.CommandSender; - -import javax.inject.Inject; - -/** - * Service for implementations of {@link ExecutableCommand} to execute some common tasks. - * This service basically wraps calls, forwarding them to other classes. - */ -public class CommandService { - - @Inject - private Messages messages; - @Inject - private Settings settings; - @Inject - private ValidationService validationService; - - /** - * Send a message to a player. - * - * @param sender The command sender to send the message to - * @param messageKey The message key to send - */ - public void send(CommandSender sender, MessageKey messageKey) { - messages.send(sender, messageKey); - } - - /** - * Send a message to a player. - * - * @param sender The command sender to send the message to - * @param messageKey The message key to send - * @param replacements The replacement arguments for the message key's tags - */ - public void send(CommandSender sender, MessageKey messageKey, String... replacements) { - messages.send(sender, messageKey, replacements); - } - - /** - * Retrieve a message by its message key. - * - * @param key The message to retrieve - * @return The message - */ - public String[] retrieveMessage(MessageKey key) { - return messages.retrieve(key); - } - - /** - * Retrieve a message as a single String by its message key. - * - * @param key The message to retrieve - * @return The message - */ - public String retrieveSingle(MessageKey key) { - return messages.retrieveSingle(key); - } - - /** - * Retrieve the given property's value. - * - * @param property The property to retrieve - * @param The type of the property - * @return The property's value - */ - public T getProperty(Property property) { - return settings.getProperty(property); - } - - /** - * Return the settings manager. - * - * @return The settings manager - */ - public Settings getSettings() { - return settings; - } - - public boolean validateEmail(String email) { - return validationService.validateEmail(email); - } - - public boolean isEmailFreeForRegistration(String email, CommandSender sender) { - return validationService.isEmailFreeForRegistration(email, sender); - } - -} diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java index f3248b565..073655746 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java @@ -1,11 +1,11 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -23,7 +23,7 @@ public class AccountsCommand implements ExecutableCommand { private BukkitService bukkitService; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(final CommandSender sender, List arguments) { @@ -50,13 +50,13 @@ public class AccountsCommand implements ExecutableCommand { public void run() { PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase()); if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + commonService.send(sender, MessageKey.UNKNOWN_USER); return; } List accountList = dataSource.getAllAuthsByIp(auth.getIp()); if (accountList.isEmpty()) { - commandService.send(sender, MessageKey.USER_NOT_REGISTERED); + commonService.send(sender, MessageKey.USER_NOT_REGISTERED); } else if (accountList.size() == 1) { sender.sendMessage("[AuthMe] " + playerName + " is a single account player"); } else { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index 9842b9f89..d3d975b29 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -1,15 +1,15 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; @@ -38,7 +38,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { private ValidationService validationService; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(final CommandSender sender, List arguments) { @@ -49,38 +49,45 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { // Validate the password ValidationResult validationResult = validationService.validatePassword(playerPass, playerName); if (validationResult.hasError()) { - commandService.send(sender, validationResult.getMessageKey(), validationResult.getArgs()); + commonService.send(sender, validationResult.getMessageKey(), validationResult.getArgs()); return; } // Set the password - final String playerNameLowerCase = playerName.toLowerCase(); - bukkitService.runTaskOptionallyAsync(new Runnable() { + bukkitService.runTaskOptionallyAsync(() -> changePassword(playerName.toLowerCase(), playerPass, sender)); + } - @Override - public void run() { - PlayerAuth auth = null; - if (playerCache.isAuthenticated(playerNameLowerCase)) { - auth = playerCache.getAuth(playerNameLowerCase); - } else if (dataSource.isAuthAvailable(playerNameLowerCase)) { - auth = dataSource.getAuth(playerNameLowerCase); - } - if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); - return; - } + /** + * Changes the password of the given player to the given password. + * + * @param nameLowercase the name of the player + * @param password the password to set + * @param sender the sender initiating the password change + */ + private void changePassword(String nameLowercase, String password, CommandSender sender) { + PlayerAuth auth = getAuth(nameLowercase); + if (auth == null) { + commonService.send(sender, MessageKey.UNKNOWN_USER); + return; + } - HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase); - auth.setPassword(hashedPassword); + HashedPassword hashedPassword = passwordSecurity.computeHash(password, nameLowercase); + auth.setPassword(hashedPassword); - if (dataSource.updatePassword(auth)) { - commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); - ConsoleLogger.info(sender.getName() + " changed password of " + playerNameLowerCase); - } else { - commandService.send(sender, MessageKey.ERROR); - } - } + if (dataSource.updatePassword(auth)) { + commonService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + ConsoleLogger.info(sender.getName() + " changed password of " + nameLowercase); + } else { + commonService.send(sender, MessageKey.ERROR); + } + } - }); + private PlayerAuth getAuth(String nameLowercase) { + if (playerCache.isAuthenticated(nameLowercase)) { + return playerCache.getAuth(nameLowercase); + } else if (dataSource.isAuthAvailable(nameLowercase)) { + return dataSource.getAuth(nameLowercase); + } + return null; } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index 0cbadf2f0..efa27ff09 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -4,7 +4,6 @@ import ch.jalu.injector.Injector; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.datasource.converter.CrazyLoginConverter; @@ -16,6 +15,7 @@ import fr.xephi.authme.datasource.converter.vAuthConverter; import fr.xephi.authme.datasource.converter.xAuthConverter; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -31,7 +31,7 @@ public class ConverterCommand implements ExecutableCommand { static final Map> CONVERTERS = getConverters(); @Inject - private CommandService commandService; + private CommonService commonService; @Inject private BukkitService bukkitService; @@ -61,7 +61,7 @@ public class ConverterCommand implements ExecutableCommand { try { converter.execute(sender); } catch (Exception e) { - commandService.send(sender, MessageKey.ERROR); + commonService.send(sender, MessageKey.ERROR); ConsoleLogger.logException("Error during conversion:", e); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java index 57ff497ab..efe9251ea 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -19,7 +19,7 @@ public class GetEmailCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(CommandSender sender, List arguments) { @@ -27,7 +27,7 @@ public class GetEmailCommand implements ExecutableCommand { PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + commonService.send(sender, MessageKey.UNKNOWN_USER); } else { sender.sendMessage("[AuthMe] " + playerName + "'s email: " + auth.getEmail()); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java index 3c2b8cd5d..a7b622d3b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -20,16 +20,16 @@ public class LastLoginCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(CommandSender sender, List arguments) { // Get the player - String playerName = (arguments.size() >= 1) ? arguments.get(0) : sender.getName(); + String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { - commandService.send(sender, MessageKey.USER_NOT_REGISTERED); + commonService.send(sender, MessageKey.USER_NOT_REGISTERED); return; } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java index fa45367f9..3a32c8ee2 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -19,7 +19,7 @@ public class PurgeLastPositionCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(final CommandSender sender, List arguments) { @@ -35,7 +35,7 @@ public class PurgeLastPositionCommand implements ExecutableCommand { // Get the user auth and make sure the user exists PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + commonService.send(sender, MessageKey.UNKNOWN_USER); return; } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index 784e9f4fc..cba5edf0d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -3,13 +3,13 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.limbo.LimboCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; @@ -27,7 +27,7 @@ public class RegisterAdminCommand implements ExecutableCommand { private PasswordSecurity passwordSecurity; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private DataSource dataSource; @@ -51,7 +51,7 @@ public class RegisterAdminCommand implements ExecutableCommand { // Command logic ValidationResult passwordValidation = validationService.validatePassword(playerPass, playerName); if (passwordValidation.hasError()) { - commandService.send(sender, passwordValidation.getMessageKey(), passwordValidation.getArgs()); + commonService.send(sender, passwordValidation.getMessageKey(), passwordValidation.getArgs()); return; } @@ -60,7 +60,7 @@ public class RegisterAdminCommand implements ExecutableCommand { @Override public void run() { if (dataSource.isAuthAvailable(playerNameLowerCase)) { - commandService.send(sender, MessageKey.NAME_ALREADY_REGISTERED); + commonService.send(sender, MessageKey.NAME_ALREADY_REGISTERED); return; } HashedPassword hashedPassword = passwordSecurity.computeHash(playerPass, playerNameLowerCase); @@ -71,12 +71,12 @@ public class RegisterAdminCommand implements ExecutableCommand { .build(); if (!dataSource.saveAuth(auth)) { - commandService.send(sender, MessageKey.ERROR); + commonService.send(sender, MessageKey.ERROR); return; } dataSource.setUnlogged(playerNameLowerCase); - commandService.send(sender, MessageKey.REGISTER_SUCCESS); + commonService.send(sender, MessageKey.REGISTER_SUCCESS); ConsoleLogger.info(sender.getName() + " registered " + playerName); final Player player = bukkitService.getPlayerExact(playerName); if (player != null) { @@ -84,7 +84,7 @@ public class RegisterAdminCommand implements ExecutableCommand { @Override public void run() { limboCache.restoreData(player); - player.kickPlayer(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER)); + player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER)); } }); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java index dc1ba6389..037846aaf 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java @@ -3,12 +3,12 @@ package fr.xephi.authme.command.executable.authme; import ch.jalu.injector.Injector; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import org.bukkit.command.CommandSender; @@ -35,7 +35,7 @@ public class ReloadCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void executeCommand(CommandSender sender, List arguments) { @@ -48,7 +48,7 @@ public class ReloadCommand implements ExecutableCommand { sender.sendMessage("Note: cannot change database type during /authme reload"); } performReloadOnServices(); - commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); + commonService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); } catch (Exception e) { sender.sendMessage("Error occurred during reload of AuthMe: aborting"); ConsoleLogger.logException("Aborting! Encountered exception during reload of AuthMe:", e); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java index 18eb29f56..987338fa2 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java @@ -1,12 +1,13 @@ package fr.xephi.authme.command.executable.authme; +import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import org.bukkit.command.CommandSender; import javax.inject.Inject; @@ -21,7 +22,7 @@ public class SetEmailCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private PlayerCache playerCache; @@ -29,6 +30,9 @@ public class SetEmailCommand implements ExecutableCommand { @Inject private BukkitService bukkitService; + @Inject + private ValidationService validationService; + @Override public void executeCommand(final CommandSender sender, List arguments) { // Get the player name and email address @@ -36,8 +40,8 @@ public class SetEmailCommand implements ExecutableCommand { final String playerEmail = arguments.get(1); // Validate the email address - if (!commandService.validateEmail(playerEmail)) { - commandService.send(sender, MessageKey.INVALID_EMAIL); + if (!validationService.validateEmail(playerEmail)) { + commonService.send(sender, MessageKey.INVALID_EMAIL); return; } @@ -47,17 +51,17 @@ public class SetEmailCommand implements ExecutableCommand { // Validate the user PlayerAuth auth = dataSource.getAuth(playerName); if (auth == null) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + commonService.send(sender, MessageKey.UNKNOWN_USER); return; - } else if (!commandService.isEmailFreeForRegistration(playerEmail, sender)) { - commandService.send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR); + } else if (!validationService.isEmailFreeForRegistration(playerEmail, sender)) { + commonService.send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR); return; } // Set the email address auth.setEmail(playerEmail); if (!dataSource.updateEmail(auth)) { - commandService.send(sender, MessageKey.ERROR); + commonService.send(sender, MessageKey.ERROR); return; } @@ -67,7 +71,7 @@ public class SetEmailCommand implements ExecutableCommand { } // Show a status message - commandService.send(sender, MessageKey.EMAIL_CHANGED_SUCCESS); + commonService.send(sender, MessageKey.EMAIL_CHANGED_SUCCESS); } }); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java index 345351d99..d8901994e 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java @@ -1,11 +1,11 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -21,7 +21,7 @@ public class UnregisterAdminCommand implements ExecutableCommand { private DataSource dataSource; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private BukkitService bukkitService; @@ -38,7 +38,7 @@ public class UnregisterAdminCommand implements ExecutableCommand { // Make sure the user exists if (!dataSource.isAuthAvailable(playerName)) { - commandService.send(sender, MessageKey.UNKNOWN_USER); + commonService.send(sender, MessageKey.UNKNOWN_USER); return; } diff --git a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java index 44917f056..f39c7abb4 100644 --- a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java @@ -1,11 +1,13 @@ package fr.xephi.authme.command.executable.captcha; +import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.CaptchaManager; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.limbo.LimboCache; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -20,7 +22,7 @@ public class CaptchaCommand extends PlayerCommand { private CaptchaManager captchaManager; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private LimboCache limboCache; @@ -30,9 +32,9 @@ public class CaptchaCommand extends PlayerCommand { final String playerName = player.getName().toLowerCase(); if (playerCache.isAuthenticated(playerName)) { - commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); } else if (!captchaManager.isCaptchaRequired(playerName)) { - commandService.send(player, MessageKey.USAGE_LOGIN); + commonService.send(player, MessageKey.USAGE_LOGIN); } else { checkCaptcha(player, arguments.get(0)); } @@ -46,7 +48,7 @@ public class CaptchaCommand extends PlayerCommand { limboCache.getPlayerData(player.getName()).getMessageTask().setMuted(false); } else { String newCode = captchaManager.generateCode(player.getName()); - commandService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode); + commonService.send(player, MessageKey.CAPTCHA_WRONG_ERROR, newCode); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java index bfcdbe778..4403c982c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.changepassword; -import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.entity.Player; @@ -18,7 +18,7 @@ import java.util.List; public class ChangePasswordCommand extends PlayerCommand { @Inject - private CommandService commandService; + private CommonService commonService; @Inject private PlayerCache playerCache; @@ -36,14 +36,14 @@ public class ChangePasswordCommand extends PlayerCommand { String name = player.getName().toLowerCase(); if (!playerCache.isAuthenticated(name)) { - commandService.send(player, MessageKey.NOT_LOGGED_IN); + commonService.send(player, MessageKey.NOT_LOGGED_IN); return; } // Make sure the password is allowed ValidationResult passwordValidation = validationService.validatePassword(newPassword, name); if (passwordValidation.hasError()) { - commandService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs()); + commonService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs()); return; } diff --git a/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java index 85baa2d65..eae64fdb0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java @@ -1,9 +1,9 @@ package fr.xephi.authme.command.executable.email; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -18,7 +18,7 @@ public class AddEmailCommand extends PlayerCommand { private Management management; @Inject - private CommandService commandService; + private CommonService commonService; @Override public void runCommand(Player player, List arguments) { @@ -29,7 +29,7 @@ public class AddEmailCommand extends PlayerCommand { // Closer inspection of the mail address handled by the async task management.performAddEmail(player, email); } else { - commandService.send(player, MessageKey.CONFIRM_EMAIL_MESSAGE); + commonService.send(player, MessageKey.CONFIRM_EMAIL_MESSAGE); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java index 89138b413..c3acb475b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java @@ -1,17 +1,17 @@ package fr.xephi.authme.command.executable.email; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.util.RandomStringUtils; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.RecoveryCodeService; +import fr.xephi.authme.util.RandomStringUtils; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -28,7 +28,7 @@ public class RecoverEmailCommand extends PlayerCommand { private PasswordSecurity passwordSecurity; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private DataSource dataSource; @@ -49,23 +49,23 @@ public class RecoverEmailCommand extends PlayerCommand { if (!sendMailSsl.hasAllInformation()) { ConsoleLogger.warning("Mail API is not set"); - commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); + commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); return; } if (playerCache.isAuthenticated(playerName)) { - commandService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + commonService.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return; } PlayerAuth auth = dataSource.getAuth(playerName); // TODO: Create method to get email only if (auth == null) { - commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE); + commonService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE); return; } final String email = auth.getEmail(); if (email == null || !email.equalsIgnoreCase(playerMail) || "your@email.com".equalsIgnoreCase(email)) { - commandService.send(player, MessageKey.INVALID_EMAIL); + commonService.send(player, MessageKey.INVALID_EMAIL); return; } @@ -84,28 +84,35 @@ public class RecoverEmailCommand extends PlayerCommand { private void createAndSendRecoveryCode(Player player, String email) { String recoveryCode = recoveryCodeService.generateCode(player.getName()); - sendMailSsl.sendRecoveryCode(player.getName(), email, recoveryCode); - commandService.send(player, MessageKey.RECOVERY_CODE_SENT); + boolean couldSendMail = sendMailSsl.sendRecoveryCode(player.getName(), email, recoveryCode); + if (couldSendMail) { + commonService.send(player, MessageKey.RECOVERY_CODE_SENT); + } else { + commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); + } } private void processRecoveryCode(Player player, String code, String email) { final String name = player.getName(); - if (!recoveryCodeService.isCodeValid(name, code)) { - commandService.send(player, MessageKey.INCORRECT_RECOVERY_CODE); - return; + if (recoveryCodeService.isCodeValid(name, code)) { + generateAndSendNewPassword(player, email); + recoveryCodeService.removeCode(name); + } else { + commonService.send(player, MessageKey.INCORRECT_RECOVERY_CODE); } - - generateAndSendNewPassword(player, email); - recoveryCodeService.removeCode(name); } private void generateAndSendNewPassword(Player player, String email) { String name = player.getName(); - String thePass = RandomStringUtils.generate(commandService.getProperty(RECOVERY_PASSWORD_LENGTH)); + String thePass = RandomStringUtils.generate(commonService.getProperty(RECOVERY_PASSWORD_LENGTH)); HashedPassword hashNew = passwordSecurity.computeHash(thePass, name); dataSource.updatePassword(name, hashNew); - sendMailSsl.sendPasswordMail(name, email, thePass); - commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); + boolean couldSendMail = sendMailSsl.sendPasswordMail(name, email, thePass); + if (couldSendMail) { + commonService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); + } else { + commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); + } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java index ae55c628a..151236e1a 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.email; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -14,8 +14,9 @@ import java.util.List; * Show email command. */ public class ShowEmailCommand extends PlayerCommand { + @Inject - private CommandService commandService; + private CommonService commonService; @Inject private PlayerCache playerCache; @@ -23,10 +24,10 @@ public class ShowEmailCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments) { PlayerAuth auth = playerCache.getAuth(player.getName()); - if (auth.getEmail() != null && !auth.getEmail().equalsIgnoreCase("your@email.com")) { - commandService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail()); + if (auth.getEmail() != null && !"your@email.com".equalsIgnoreCase(auth.getEmail())) { + commonService.send(player, MessageKey.EMAIL_SHOW, auth.getEmail()); } else { - commandService.send(player, MessageKey.SHOW_NO_EMAIL); + commonService.send(player, MessageKey.SHOW_NO_EMAIL); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java index f49ec2db2..cc18adbf0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java @@ -1,15 +1,16 @@ package fr.xephi.authme.command.executable.register; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; -import fr.xephi.authme.util.RandomStringUtils; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.util.RandomStringUtils; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -20,20 +21,26 @@ import static fr.xephi.authme.settings.properties.RegistrationSettings.ENABLE_CO import static fr.xephi.authme.settings.properties.RegistrationSettings.USE_EMAIL_REGISTRATION; import static fr.xephi.authme.settings.properties.RestrictionSettings.ENABLE_PASSWORD_CONFIRMATION; +/** + * Command for /register. + */ public class RegisterCommand extends PlayerCommand { @Inject private Management management; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private SendMailSSL sendMailSsl; + @Inject + private ValidationService validationService; + @Override public void runCommand(Player player, List arguments) { - if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { + if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { //for two factor auth we don't need to check the usage management.performRegister(player, "", "", true); return; @@ -42,11 +49,11 @@ public class RegisterCommand extends PlayerCommand { // Ensure that there is 1 argument, or 2 if confirmation is required final boolean useConfirmation = isConfirmationRequired(); if (arguments.isEmpty() || useConfirmation && arguments.size() < 2) { - commandService.send(player, MessageKey.USAGE_REGISTER); + commonService.send(player, MessageKey.USAGE_REGISTER); return; } - if (commandService.getProperty(USE_EMAIL_REGISTRATION)) { + if (commonService.getProperty(USE_EMAIL_REGISTRATION)) { handleEmailRegistration(player, arguments); } else { handlePasswordRegistration(player, arguments); @@ -59,8 +66,8 @@ public class RegisterCommand extends PlayerCommand { } private void handlePasswordRegistration(Player player, List arguments) { - if (commandService.getProperty(ENABLE_PASSWORD_CONFIRMATION) && !arguments.get(0).equals(arguments.get(1))) { - commandService.send(player, MessageKey.PASSWORD_MATCH_ERROR); + if (commonService.getProperty(ENABLE_PASSWORD_CONFIRMATION) && !arguments.get(0).equals(arguments.get(1))) { + commonService.send(player, MessageKey.PASSWORD_MATCH_ERROR); } else { management.performRegister(player, arguments.get(0), "", true); } @@ -68,19 +75,19 @@ public class RegisterCommand extends PlayerCommand { private void handleEmailRegistration(Player player, List arguments) { if (!sendMailSsl.hasAllInformation()) { - commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); + commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); ConsoleLogger.warning("Cannot register player '" + player.getName() + "': no email or password is set " + "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath()); return; } final String email = arguments.get(0); - if (!commandService.validateEmail(email)) { - commandService.send(player, MessageKey.INVALID_EMAIL); - } else if (commandService.getProperty(ENABLE_CONFIRM_EMAIL) && !email.equals(arguments.get(1))) { - commandService.send(player, MessageKey.USAGE_REGISTER); + if (!validationService.validateEmail(email)) { + commonService.send(player, MessageKey.INVALID_EMAIL); + } else if (commonService.getProperty(ENABLE_CONFIRM_EMAIL) && !email.equals(arguments.get(1))) { + commonService.send(player, MessageKey.USAGE_REGISTER); } else { - String thePass = RandomStringUtils.generate(commandService.getProperty(RECOVERY_PASSWORD_LENGTH)); + String thePass = RandomStringUtils.generate(commonService.getProperty(RECOVERY_PASSWORD_LENGTH)); management.performRegister(player, thePass, email, true); } } @@ -91,8 +98,8 @@ public class RegisterCommand extends PlayerCommand { * @return True if the confirmation is needed, false otherwise */ private boolean isConfirmationRequired() { - return commandService.getProperty(USE_EMAIL_REGISTRATION) - ? commandService.getProperty(ENABLE_CONFIRM_EMAIL) - : commandService.getProperty(ENABLE_PASSWORD_CONFIRMATION); + return commonService.getProperty(USE_EMAIL_REGISTRATION) + ? commonService.getProperty(ENABLE_CONFIRM_EMAIL) + : commonService.getProperty(ENABLE_PASSWORD_CONFIRMATION); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java index c3aaf58a7..09b996a27 100644 --- a/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java @@ -1,10 +1,10 @@ package fr.xephi.authme.command.executable.unregister; -import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -19,7 +19,7 @@ public class UnregisterCommand extends PlayerCommand { private Management management; @Inject - private CommandService commandService; + private CommonService commonService; @Inject private PlayerCache playerCache; @@ -31,7 +31,7 @@ public class UnregisterCommand extends PlayerCommand { // Make sure the player is authenticated if (!playerCache.isAuthenticated(playerName)) { - commandService.send(player, MessageKey.NOT_LOGGED_IN); + commonService.send(player, MessageKey.NOT_LOGGED_IN); return; } diff --git a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java index e4fd9230f..f6ef65a7a 100644 --- a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java @@ -189,7 +189,7 @@ public class PlayerAuth { * @return String */ public String serialize() { - StringBuffer str = new StringBuffer(); + StringBuilder str = new StringBuilder(); char d = ';'; str.append(this.nickname).append(d); str.append(this.realName).append(d); diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboCache.java b/src/main/java/fr/xephi/authme/data/limbo/LimboCache.java index 82e42fba8..893aba229 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboCache.java +++ b/src/main/java/fr/xephi/authme/data/limbo/LimboCache.java @@ -84,10 +84,10 @@ public class LimboCache { float walkSpeed = data.getWalkSpeed(); float flySpeed = data.getFlySpeed(); // Reset the speed value if it was 0 - if(walkSpeed == 0f) { + if(walkSpeed < 0.01f) { walkSpeed = 0.2f; } - if(flySpeed == 0f) { + if(flySpeed < 0.01f) { flySpeed = 0.2f; } player.setWalkSpeed(walkSpeed); diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index ebd9299ef..fbbaa5076 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -1,6 +1,5 @@ package fr.xephi.authme.datasource; -import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -11,15 +10,13 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.crypts.HashedPassword; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -48,17 +45,12 @@ public class CacheDataSource implements DataSource { .build(new CacheLoader>() { @Override public Optional load(String key) { - return Optional.fromNullable(source.getAuth(key)); + return Optional.ofNullable(source.getAuth(key)); } @Override public ListenableFuture> reload(final String key, Optional oldValue) { - return executorService.submit(new Callable>() { - @Override - public Optional call() { - return load(key); - } - }); + return executorService.submit(() -> load(key)); } }); } @@ -90,7 +82,7 @@ public class CacheDataSource implements DataSource { @Override public PlayerAuth getAuth(String user) { user = user.toLowerCase(); - return cachedAuths.getUnchecked(user).orNull(); + return cachedAuths.getUnchecked(user).orElse(null); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index 49b5442d1..8c1ecf4b5 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -9,7 +9,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -295,19 +294,11 @@ public class FlatFile implements DataSource { break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.warning(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.warning(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(auth.getNickname()); @@ -330,19 +321,11 @@ public class FlatFile implements DataSource { } } return countIp; - } catch (FileNotFoundException ex) { - ConsoleLogger.warning(ex.getMessage()); - return new ArrayList<>(); } catch (IOException ex) { ConsoleLogger.warning(ex.getMessage()); return new ArrayList<>(); } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } } @@ -363,12 +346,7 @@ public class FlatFile implements DataSource { } catch (IOException ex) { ConsoleLogger.warning(ex.getMessage()); } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return 0; } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 9da225b94..20ffa6d53 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -5,9 +5,6 @@ import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.Columns; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.XFBCRYPT; @@ -15,8 +12,8 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.RuntimeUtils; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import java.sql.Blob; import java.sql.Connection; @@ -50,7 +47,7 @@ public class MySQL implements DataSource { private int phpBbGroup; private String wordpressPrefix; - public MySQL(Settings settings) throws ClassNotFoundException, SQLException, PoolInitializationException { + public MySQL(Settings settings) throws ClassNotFoundException, SQLException { setParameters(settings); // Set the connection arguments (and check if connection is ok) @@ -100,17 +97,17 @@ public class MySQL implements DataSource { this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID); this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX); this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE); - if(poolSize == -1) { - poolSize = RuntimeUtils.getCoreCount(); + if (poolSize == -1) { + poolSize = Utils.getCoreCount(); } } - private void setConnectionArguments() throws RuntimeException { + private void setConnectionArguments() { ds = new HikariDataSource(); ds.setPoolName("AuthMeMYSQLPool"); // Pool size - ds.setMaximumPoolSize(poolSize); + ds.setMaximumPoolSize(poolSize); // Database URL ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); @@ -137,7 +134,7 @@ public class MySQL implements DataSource { } @Override - public void reload() throws RuntimeException { + public void reload() { if (ds != null) { ds.close(); } diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index d19c4aa22..fdb9c9197 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -3,9 +3,6 @@ package fr.xephi.authme.datasource; import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.Columns; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -397,7 +394,7 @@ public class SQLite implements DataSource { } } - private void close(Statement st) { + private static void close(Statement st) { if (st != null) { try { st.close(); @@ -407,7 +404,7 @@ public class SQLite implements DataSource { } } - private void close(Connection con) { + private static void close(Connection con) { if (con != null) { try { con.close(); @@ -417,7 +414,7 @@ public class SQLite implements DataSource { } } - private void close(ResultSet rs) { + private static void close(ResultSet rs) { if (rs != null) { try { rs.close(); @@ -479,7 +476,7 @@ public class SQLite implements DataSource { pst.setString(1, user); rs = pst.executeQuery(); if (rs.next()) - return (rs.getInt(col.IS_LOGGED) == 1); + return rs.getInt(col.IS_LOGGED) == 1; } catch (SQLException ex) { logSqlException(ex); } finally { diff --git a/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java index 535c0ebab..13266a891 100644 --- a/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java +++ b/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java @@ -43,20 +43,7 @@ public class CrazyLoginConverter implements Converter { try (BufferedReader users = new BufferedReader(new FileReader(source))) { while ((line = users.readLine()) != null) { if (line.contains("|")) { - String[] args = line.split("\\|"); - if (args.length < 2 || "name".equalsIgnoreCase(args[0])) { - continue; - } - String playerName = args[0]; - String password = args[1]; - if (password != null) { - PlayerAuth auth = PlayerAuth.builder() - .name(playerName.toLowerCase()) - .realName(playerName) - .password(password, null) - .build(); - database.saveAuth(auth); - } + migrateAccount(line); } } ConsoleLogger.info("CrazyLogin database has been imported correctly"); @@ -66,4 +53,26 @@ public class CrazyLoginConverter implements Converter { } } + /** + * Moves an account from CrazyLogin to the AuthMe database. + * + * @param line line read from the CrazyLogin file (one account) + */ + private void migrateAccount(String line) { + String[] args = line.split("\\|"); + if (args.length < 2 || "name".equalsIgnoreCase(args[0])) { + return; + } + String playerName = args[0]; + String password = args[1]; + if (password != null) { + PlayerAuth auth = PlayerAuth.builder() + .name(playerName.toLowerCase()) + .realName(playerName) + .password(password, null) + .build(); + database.saveAuth(auth); + } + } + } diff --git a/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java b/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java new file mode 100644 index 000000000..686059539 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java @@ -0,0 +1,123 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.datasource.CacheDataSource; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceType; +import fr.xephi.authme.datasource.FlatFile; +import fr.xephi.authme.datasource.MySQL; +import fr.xephi.authme.datasource.SQLite; +import fr.xephi.authme.datasource.converter.ForceFlatToSqlite; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.DatabaseSettings; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; + +/** + * Creates the AuthMe data source. + */ +public class DataSourceProvider implements Provider { + + private static final String FLATFILE_FILENAME = "auths.db"; + private static final int SQLITE_MAX_SIZE = 4000; + + @Inject + @DataFolder + private File dataFolder; + @Inject + private Settings settings; + @Inject + private BukkitService bukkitService; + + DataSourceProvider() { + } + + @Override + public DataSource get() { + try { + return createDataSource(); + } catch (Exception e) { + ConsoleLogger.logException("Could not create data source:", e); + throw new IllegalStateException("Error during initialization of data source", e); + } + } + + /** + * Sets up the data source. + * + * @return the constructed datasource + * @throws ClassNotFoundException if no driver could be found for the datasource + * @throws SQLException when initialization of a SQL datasource failed + * @throws IOException if flat file cannot be read + */ + private DataSource createDataSource() throws ClassNotFoundException, SQLException, IOException { + DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); + DataSource dataSource; + switch (dataSourceType) { + case FILE: + File source = new File(dataFolder, FLATFILE_FILENAME); + dataSource = new FlatFile(source); + break; + case MYSQL: + dataSource = new MySQL(settings); + break; + case SQLITE: + dataSource = new SQLite(settings); + break; + default: + throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'"); + } + + dataSource = convertFlatfileToSqlite(dataSource); + + if (settings.getProperty(DatabaseSettings.USE_CACHING)) { + dataSource = new CacheDataSource(dataSource); + } + if (DataSourceType.SQLITE.equals(dataSourceType)) { + checkDataSourceSize(dataSource, bukkitService); + } + return dataSource; + } + + private void checkDataSourceSize(final DataSource dataSource, BukkitService bukkitService) { + bukkitService.runTaskAsynchronously(() -> { + int accounts = dataSource.getAccountsRegistered(); + if (accounts >= SQLITE_MAX_SIZE) { + ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH " + + accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!"); + } + }); + } + + /** + * Converts the data source from the deprecated FLATFILE type to SQLITE. + * + * @param dataSource the data source to convert if necessary + * @return the data source to use: the converted datasource (SQLite), + * or the same data source if no conversion was performed + */ + private DataSource convertFlatfileToSqlite(DataSource dataSource) { + if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) { + ConsoleLogger.warning("FlatFile backend has been detected and is now deprecated; it will be changed " + + "to SQLite... Connection will be impossible until conversion is done!"); + FlatFile flatFile = (FlatFile) dataSource; + try { + SQLite sqlite = new SQLite(settings); + ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, sqlite); + converter.execute(null); + settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE); + settings.save(); + return sqlite; + } catch (Exception e) { + ConsoleLogger.logException("Error during conversion from Flatfile to SQLite", e); + throw new IllegalStateException(e); + } + } + return dataSource; + } +} diff --git a/src/main/java/fr/xephi/authme/initialization/HasCleanup.java b/src/main/java/fr/xephi/authme/initialization/HasCleanup.java index db047d840..351df4f69 100644 --- a/src/main/java/fr/xephi/authme/initialization/HasCleanup.java +++ b/src/main/java/fr/xephi/authme/initialization/HasCleanup.java @@ -3,6 +3,8 @@ package fr.xephi.authme.initialization; /** * Common interface for types which have data that becomes outdated * and that can be cleaned up periodically. + * + * @see fr.xephi.authme.task.CleanupTask */ public interface HasCleanup { diff --git a/src/main/java/fr/xephi/authme/initialization/Initializer.java b/src/main/java/fr/xephi/authme/initialization/Initializer.java deleted file mode 100644 index 525334c27..000000000 --- a/src/main/java/fr/xephi/authme/initialization/Initializer.java +++ /dev/null @@ -1,176 +0,0 @@ -package fr.xephi.authme.initialization; - -import com.github.authme.configme.knownproperties.ConfigurationData; -import com.github.authme.configme.resource.PropertyResource; -import com.github.authme.configme.resource.YamlFileResource; -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.CacheDataSource; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceType; -import fr.xephi.authme.datasource.FlatFile; -import fr.xephi.authme.datasource.MySQL; -import fr.xephi.authme.datasource.SQLite; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.message.Messages; -import fr.xephi.authme.output.ConsoleFilter; -import fr.xephi.authme.output.Log4JFilter; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.MigrationService; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SettingsMigrationService; -import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; -import fr.xephi.authme.settings.properties.DatabaseSettings; -import fr.xephi.authme.settings.properties.EmailSettings; -import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.FileUtils; -import fr.xephi.authme.util.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.io.File; -import java.io.IOException; -import java.sql.SQLException; -import java.util.logging.Logger; - -import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; -import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; - -/** - * Initializes various services. - */ -public class Initializer { - - private static final String FLATFILE_FILENAME = "auths.db"; - private static final int SQLITE_MAX_SIZE = 4000; - - private AuthMe authMe; - private BukkitService bukkitService; - - public Initializer(AuthMe authMe, BukkitService bukkitService) { - this.authMe = authMe; - this.bukkitService = bukkitService; - } - - /** - * Loads the plugin's settings. - * - * @param authMe the plugin instance - * @return the settings instance, or null if it could not be constructed - */ - public static Settings createSettings(AuthMe authMe) throws Exception { - File configFile = new File(authMe.getDataFolder(), "config.yml"); - if(!configFile.exists()) { - configFile.createNewFile(); - } - PropertyResource resource = new YamlFileResource(configFile); - SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder()); - ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); - return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData); - } - - /** - * Sets up the data source. - * - * @param settings the settings - * @return the constructed datasource - * @throws ClassNotFoundException if no driver could be found for the datasource - * @throws SQLException when initialization of a SQL datasource failed - * @throws IOException if flat file cannot be read - */ - public DataSource setupDatabase(Settings settings) throws ClassNotFoundException, SQLException, IOException { - DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); - DataSource dataSource; - switch (dataSourceType) { - case FILE: - File source = new File(authMe.getDataFolder(), FLATFILE_FILENAME); - dataSource = new FlatFile(source); - break; - case MYSQL: - dataSource = new MySQL(settings); - break; - case SQLITE: - dataSource = new SQLite(settings); - break; - default: - throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'"); - } - - DataSource convertedSource = MigrationService.convertFlatfileToSqlite(settings, dataSource); - dataSource = convertedSource == null ? dataSource : convertedSource; - - if (settings.getProperty(DatabaseSettings.USE_CACHING)) { - dataSource = new CacheDataSource(dataSource); - } - if (DataSourceType.SQLITE.equals(dataSourceType)) { - checkDataSourceSize(dataSource); - } - return dataSource; - } - - private void checkDataSourceSize(final DataSource dataSource) { - bukkitService.runTaskAsynchronously(new Runnable() { - @Override - public void run() { - int accounts = dataSource.getAccountsRegistered(); - if (accounts >= SQLITE_MAX_SIZE) { - ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH " - + accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!"); - } - } - }); - } - - /** - * Sets up the console filter if enabled. - * - * @param settings the settings - * @param logger the plugin logger - */ - public void setupConsoleFilter(Settings settings, Logger logger) { - if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) { - return; - } - // Try to set the log4j filter - try { - Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter"); - setLog4JFilter(); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // log4j is not available - ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled"); - ConsoleFilter filter = new ConsoleFilter(); - logger.setFilter(filter); - Bukkit.getLogger().setFilter(filter); - Logger.getLogger("Minecraft").setFilter(filter); - } - } - - // Set the console filter to remove the passwords - private static void setLog4JFilter() { - org.apache.logging.log4j.core.Logger logger; - logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); - logger.addFilter(new Log4JFilter()); - } - - public void scheduleRecallEmailTask(Settings settings, final DataSource dataSource, final Messages messages) { - if (!settings.getProperty(RECALL_PLAYERS)) { - return; - } - bukkitService.runTaskTimerAsynchronously(new Runnable() { - @Override - public void run() { - for (PlayerAuth auth : dataSource.getLoggedPlayers()) { - String email = auth.getEmail(); - if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) { - Player player = bukkitService.getPlayerExact(auth.getRealName()); - if (player != null) { - messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); - } - } - } - } - }, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL)); - } -} diff --git a/src/main/java/fr/xephi/authme/initialization/MetricsManager.java b/src/main/java/fr/xephi/authme/initialization/MetricsManager.java deleted file mode 100644 index e22e742e4..000000000 --- a/src/main/java/fr/xephi/authme/initialization/MetricsManager.java +++ /dev/null @@ -1,47 +0,0 @@ -package fr.xephi.authme.initialization; - -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.DatabaseSettings; -import fr.xephi.authme.settings.properties.PluginSettings; -import org.mcstats.Metrics; -import org.mcstats.Metrics.Graph; - -import java.io.IOException; - -public class MetricsManager { - - private MetricsManager() { - } - - public static void sendMetrics(AuthMe plugin, Settings settings) { - try { - final Metrics metrics = new Metrics(plugin); - - final Graph languageGraph = metrics.createGraph("Messages Language"); - final String messagesLanguage = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); - languageGraph.addPlotter(new Metrics.Plotter(messagesLanguage) { - @Override - public int getValue() { - return 1; - } - }); - - final Graph databaseBackend = metrics.createGraph("Database Backend"); - final String dataSource = settings.getProperty(DatabaseSettings.BACKEND).toString(); - databaseBackend.addPlotter(new Metrics.Plotter(dataSource) { - @Override - public int getValue() { - return 1; - } - }); - - // Submit metrics - metrics.start(); - } catch (IOException e) { - // Failed to submit the metrics data - ConsoleLogger.logException("Can't send Metrics data! The plugin will work anyway...", e); - } - } -} diff --git a/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java b/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java index 6141e47ef..1e7862443 100644 --- a/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java +++ b/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java @@ -76,10 +76,8 @@ public class OnShutdownPlayerSaver { dataSource.updateQuitLoc(auth); } if (settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN) - && !settings.getProperty(RestrictionSettings.NO_TELEPORT)) { - if (!limboPlayerStorage.hasData(player)) { - limboPlayerStorage.saveData(player); - } + && !settings.getProperty(RestrictionSettings.NO_TELEPORT) && !limboPlayerStorage.hasData(player)) { + limboPlayerStorage.saveData(player); } } } diff --git a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java new file mode 100644 index 000000000..0dd421817 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java @@ -0,0 +1,127 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.output.ConsoleFilter; +import fr.xephi.authme.output.Log4JFilter; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.util.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.mcstats.Metrics; + +import javax.inject.Inject; +import java.io.IOException; +import java.util.logging.Logger; + +import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; +import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; + +/** + * Contains actions such as migrations that should be performed on startup. + */ +public class OnStartupTasks { + + @Inject + private DataSource dataSource; + @Inject + private Settings settings; + @Inject + private BukkitService bukkitService; + @Inject + private Messages messages; + + OnStartupTasks() { + } + + public static void sendMetrics(AuthMe plugin, Settings settings) { + try { + final Metrics metrics = new Metrics(plugin); + + final Metrics.Graph languageGraph = metrics.createGraph("Messages Language"); + final String messagesLanguage = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + languageGraph.addPlotter(new Metrics.Plotter(messagesLanguage) { + @Override + public int getValue() { + return 1; + } + }); + + final Metrics.Graph databaseBackend = metrics.createGraph("Database Backend"); + final String dataSource = settings.getProperty(DatabaseSettings.BACKEND).toString(); + databaseBackend.addPlotter(new Metrics.Plotter(dataSource) { + @Override + public int getValue() { + return 1; + } + }); + + // Submit metrics + metrics.start(); + } catch (IOException e) { + // Failed to submit the metrics data + ConsoleLogger.logException("Can't send Metrics data! The plugin will work anyway...", e); + } + } + + /** + * Sets up the console filter if enabled. + * + * @param settings the settings + * @param logger the plugin logger + */ + public static void setupConsoleFilter(Settings settings, Logger logger) { + if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) { + return; + } + // Try to set the log4j filter + try { + Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter"); + setLog4JFilter(); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // log4j is not available + ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled"); + ConsoleFilter filter = new ConsoleFilter(); + logger.setFilter(filter); + Bukkit.getLogger().setFilter(filter); + Logger.getLogger("Minecraft").setFilter(filter); + } + } + + // Set the console filter to remove the passwords + private static void setLog4JFilter() { + org.apache.logging.log4j.core.Logger logger; + logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); + logger.addFilter(new Log4JFilter()); + } + + public void scheduleRecallEmailTask() { + if (!settings.getProperty(RECALL_PLAYERS)) { + return; + } + bukkitService.runTaskTimerAsynchronously(new Runnable() { + @Override + public void run() { + for (PlayerAuth auth : dataSource.getLoggedPlayers()) { + String email = auth.getEmail(); + if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) { + Player player = bukkitService.getPlayerExact(auth.getRealName()); + if (player != null) { + messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); + } + } + } + } + }, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL)); + } +} diff --git a/src/main/java/fr/xephi/authme/initialization/Reloadable.java b/src/main/java/fr/xephi/authme/initialization/Reloadable.java index 6b28fc7dd..65c3a3379 100644 --- a/src/main/java/fr/xephi/authme/initialization/Reloadable.java +++ b/src/main/java/fr/xephi/authme/initialization/Reloadable.java @@ -2,6 +2,8 @@ package fr.xephi.authme.initialization; /** * Interface for reloadable entities. + * + * @see fr.xephi.authme.command.executable.authme.ReloadCommand */ public interface Reloadable { diff --git a/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java b/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java index 487f70d58..89d18318e 100644 --- a/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java +++ b/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java @@ -4,6 +4,8 @@ import fr.xephi.authme.settings.Settings; /** * Interface for classes that keep a local copy of certain settings. + * + * @see fr.xephi.authme.command.executable.authme.ReloadCommand */ public interface SettingsDependent { diff --git a/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java b/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java new file mode 100644 index 000000000..f9c8638af --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java @@ -0,0 +1,45 @@ +package fr.xephi.authme.initialization; + +import com.github.authme.configme.knownproperties.ConfigurationData; +import com.github.authme.configme.resource.PropertyResource; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SettingsMigrationService; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import fr.xephi.authme.util.FileUtils; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.io.File; + +/** + * Initializes the settings. + */ +public class SettingsProvider implements Provider { + + @Inject + @DataFolder + private File dataFolder; + @Inject + private SettingsMigrationService migrationService; + + SettingsProvider() { + } + + /** + * Loads the plugin's settings. + * + * @return the settings instance, or null if it could not be constructed + */ + @Override + public Settings get() { + File configFile = new File(dataFolder, "config.yml"); + if (!configFile.exists()) { + FileUtils.create(configFile); + } + PropertyResource resource = new YamlFileResource(configFile); + ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); + return new Settings(dataFolder, resource, migrationService, configurationData); + } + +} diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index 93783346d..e3ff4ec5b 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -9,14 +9,14 @@ import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.service.AntiBotService; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; -import fr.xephi.authme.service.ValidationService; import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerLoginEvent; @@ -68,10 +68,10 @@ class OnJoinVerifier implements Reloadable { * @param isAuthAvailable whether or not the player is registered */ public void checkAntibot(Player player, boolean isAuthAvailable) throws FailedVerificationException { - if (permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)) { + if (isAuthAvailable || permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)) { return; } - if (antiBotService.shouldKick(isAuthAvailable)) { + if (antiBotService.shouldKick()) { antiBotService.addPlayerKick(player.getName()); throw new FailedVerificationException(MessageKey.KICK_ANTIBOT); } @@ -170,10 +170,9 @@ class OnJoinVerifier implements Reloadable { public void checkPlayerCountry(boolean isAuthAvailable, String playerIp) throws FailedVerificationException { if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)) - && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)) { - if (!validationService.isCountryAdmitted(playerIp)) { + && settings.getProperty(ProtectionSettings.ENABLE_PROTECTION) + && !validationService.isCountryAdmitted(playerIp)) { throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR); - } } } diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java index b6484fb35..c380fbe18 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -6,14 +6,14 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.AntiBotService; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.TeleportationService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.TeleportationService; -import fr.xephi.authme.service.ValidationService; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -42,6 +42,7 @@ import org.bukkit.event.player.PlayerShearEntityEvent; import javax.inject.Inject; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -53,7 +54,7 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAU */ public class PlayerListener implements Listener { - public static final ConcurrentHashMap joinMessage = new ConcurrentHashMap<>(); + public static final Map joinMessage = new ConcurrentHashMap<>(); @Inject private Settings settings; @@ -81,7 +82,7 @@ public class PlayerListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { String cmd = event.getMessage().split(" ")[0].toLowerCase(); - if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && cmd.equals("/motd")) { + if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && "/motd".equals(cmd)) { return; } if (settings.getProperty(RestrictionSettings.ALLOW_COMMANDS).contains(cmd)) { @@ -113,7 +114,7 @@ public class PlayerListener implements Listener { iter.remove(); } } - if (recipients.size() == 0) { + if (recipients.isEmpty()) { event.setCancelled(true); } } @@ -156,9 +157,7 @@ public class PlayerListener implements Listener { if (spawn != null && spawn.getWorld() != null) { if (!player.getWorld().equals(spawn.getWorld())) { player.teleport(spawn); - return; - } - if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) { + } else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) { player.teleport(spawn); } } @@ -208,9 +207,11 @@ public class PlayerListener implements Listener { final String name = player.getName(); if (validationService.isUnrestricted(name)) { return; - } else if (onJoinVerifier.refusePlayerForFullServer(event)) { + } + if (onJoinVerifier.refusePlayerForFullServer(event)) { return; - } else if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { + } + if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { return; } @@ -222,9 +223,9 @@ public class PlayerListener implements Listener { // Get the auth later as this may cause the single session check to fail // Slow stuff final PlayerAuth auth = dataSource.getAuth(name); - final boolean isAuthAvailable = (auth != null); - onJoinVerifier.checkAntibot(player, isAuthAvailable); + final boolean isAuthAvailable = auth != null; onJoinVerifier.checkKickNonRegistered(isAuthAvailable); + onJoinVerifier.checkAntibot(player, isAuthAvailable); onJoinVerifier.checkNameCasing(player, auth); onJoinVerifier.checkPlayerCountry(isAuthAvailable, event.getAddress().getHostAddress()); } catch (FailedVerificationException e) { @@ -233,7 +234,6 @@ public class PlayerListener implements Listener { return; } - antiBotService.handlePlayerJoin(); teleportationService.teleportOnJoin(player); } @@ -306,27 +306,14 @@ public class PlayerListener implements Listener { * @note little hack cause InventoryOpenEvent cannot be cancelled for * real, cause no packet is send to server by client for the main inv */ - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - player.closeInventory(); - } - }, 1); + bukkitService.scheduleSyncDelayedTask(player::closeInventory, 1); } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerInventoryClick(InventoryClickEvent event) { - if (event.getWhoClicked() == null) { - return; + if (listenerService.shouldCancelEvent(event.getWhoClicked())) { + event.setCancelled(true); } - if (!(event.getWhoClicked() instanceof Player)) { - return; - } - Player player = (Player) event.getWhoClicked(); - if (!listenerService.shouldCancelEvent(player)) { - return; - } - event.setCancelled(true); } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener111.java b/src/main/java/fr/xephi/authme/listener/PlayerListener111.java new file mode 100644 index 000000000..43b4ca339 --- /dev/null +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener111.java @@ -0,0 +1,24 @@ +package fr.xephi.authme.listener; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityAirChangeEvent; + +import javax.inject.Inject; + +/** + * Listener of player events for events introduced in Minecraft 1.11. + */ +public class PlayerListener111 implements Listener { + + @Inject + private ListenerService listenerService; + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerAirChange(EntityAirChangeEvent event) { + if (listenerService.shouldCancelEvent(event)) { + event.setCancelled(true); + } + } +} diff --git a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java index e629402f1..c90d9422b 100644 --- a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java +++ b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java @@ -1,19 +1,22 @@ package fr.xephi.authme.mail; -import fr.xephi.authme.AuthMe; +import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.StringUtils; import org.apache.commons.mail.EmailConstants; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; +import org.bukkit.Server; +import javax.activation.CommandMap; import javax.activation.DataSource; import javax.activation.FileDataSource; +import javax.activation.MailcapCommandMap; import javax.imageio.ImageIO; import javax.inject.Inject; import javax.mail.Session; @@ -27,18 +30,19 @@ import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; /** - * @author Xephi59 + * Sends emails to players on behalf of the server. */ public class SendMailSSL { - @Inject - private AuthMe plugin; - @Inject - private Settings settings; - @Inject - private BukkitService bukkitService; + private final File dataFolder; + private final String serverName; + private final Settings settings; - SendMailSSL() { + @Inject + SendMailSSL(@DataFolder File dataFolder, Server server, Settings settings) { + this.dataFolder = dataFolder; + this.serverName = server.getServerName(); + this.settings = settings; } /** @@ -57,62 +61,57 @@ public class SendMailSSL { * @param name the name of the player * @param mailAddress the player's email * @param newPass the new password + * @return true if email could be sent, false otherwise */ - public void sendPasswordMail(String name, String mailAddress, String newPass) { + public boolean sendPasswordMail(String name, String mailAddress, String newPass) { if (!hasAllInformation()) { ConsoleLogger.warning("Cannot perform email registration: not all email settings are complete"); - return; + return false; } - final String mailText = replaceTagsForPasswordMail(settings.getPasswordEmailMessage(), name, newPass); - bukkitService.runTaskAsynchronously(new Runnable() { + HtmlEmail email; + try { + email = initializeMail(mailAddress); + } catch (EmailException e) { + ConsoleLogger.logException("Failed to create email with the given settings:", e); + return false; + } - @Override - public void run() { - HtmlEmail email; - try { - email = initializeMail(mailAddress); - } catch (EmailException e) { - ConsoleLogger.logException("Failed to create email with the given settings:", e); - return; - } - - String content = mailText; - // Generate an image? - File file = null; - if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { - try { - file = generateImage(name, plugin, newPass); - content = embedImageIntoEmailContent(file, email, content); - } catch (IOException | EmailException e) { - ConsoleLogger.logException( - "Unable to send new password as image for email " + mailAddress + ":", e); - } - } - - sendEmail(content, email); - FileUtils.delete(file); + String mailText = replaceTagsForPasswordMail(settings.getPasswordEmailMessage(), name, newPass); + // Generate an image? + File file = null; + if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { + try { + file = generateImage(name, newPass); + mailText = embedImageIntoEmailContent(file, email, mailText); + } catch (IOException | EmailException e) { + ConsoleLogger.logException( + "Unable to send new password as image for email " + mailAddress + ":", e); } - }); + } + + boolean couldSendEmail = sendEmail(mailText, email); + FileUtils.delete(file); + return couldSendEmail; } - public void sendRecoveryCode(String name, String email, String code) { - String message = replaceTagsForRecoveryCodeMail(settings.getRecoveryCodeEmailMessage(), - name, code, settings.getProperty(SecuritySettings.RECOVERY_CODE_HOURS_VALID)); - + public boolean sendRecoveryCode(String name, String email, String code) { HtmlEmail htmlEmail; try { htmlEmail = initializeMail(email); } catch (EmailException e) { ConsoleLogger.logException("Failed to create email for recovery code:", e); - return; + return false; } - sendEmail(message, htmlEmail); + + String message = replaceTagsForRecoveryCodeMail(settings.getRecoveryCodeEmailMessage(), + name, code, settings.getProperty(SecuritySettings.RECOVERY_CODE_HOURS_VALID)); + return sendEmail(message, htmlEmail); } - private static File generateImage(String name, AuthMe plugin, String newPass) throws IOException { + private File generateImage(String name, String newPass) throws IOException { ImageGenerator gen = new ImageGenerator(newPass); - File file = new File(plugin.getDataFolder(), name + "_new_pass.jpg"); + File file = new File(dataFolder, name + "_new_pass.jpg"); ImageIO.write(gen.generateImage(), "jpg", file); return file; } @@ -124,7 +123,8 @@ public class SendMailSSL { return content.replace("", ""); } - private HtmlEmail initializeMail(String emailAddress) throws EmailException { + @VisibleForTesting + HtmlEmail initializeMail(String emailAddress) throws EmailException { String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT); String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME)) ? senderMail @@ -145,8 +145,18 @@ public class SendMailSSL { return email; } - private static boolean sendEmail(String content, HtmlEmail email) { + @VisibleForTesting + boolean sendEmail(String content, HtmlEmail email) { Thread.currentThread().setContextClassLoader(SendMailSSL.class.getClassLoader()); + // Issue #999: Prevent UnsupportedDataTypeException: no object DCH for MIME type multipart/alternative + // cf. http://stackoverflow.com/questions/21856211/unsupporteddatatypeexception-no-object-dch-for-mime-type + MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); + mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html"); + mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"); + mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain"); + mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed"); + mc.addMailcap("message/rfc822;; x-java-content- handler=com.sun.mail.handlers.message_rfc822"); + try { email.setHtmlMsg(content); email.setTextMsg(content); @@ -166,14 +176,14 @@ public class SendMailSSL { private String replaceTagsForPasswordMail(String mailText, String name, String newPass) { return mailText .replace("", name) - .replace("", plugin.getServer().getServerName()) + .replace("", serverName) .replace("", newPass); } private String replaceTagsForRecoveryCodeMail(String mailText, String name, String code, int hoursValid) { return mailText .replace("", name) - .replace("", plugin.getServer().getServerName()) + .replace("", serverName) .replace("", code) .replace("", String.valueOf(hoursValid)); } diff --git a/src/main/java/fr/xephi/authme/message/MessageFileHandler.java b/src/main/java/fr/xephi/authme/message/MessageFileHandler.java index e870bdf36..a0d4e947c 100644 --- a/src/main/java/fr/xephi/authme/message/MessageFileHandler.java +++ b/src/main/java/fr/xephi/authme/message/MessageFileHandler.java @@ -53,7 +53,7 @@ public class MessageFileHandler { if (message == null) { ConsoleLogger.warning("Error getting message with key '" + key + "'. " - + "Please verify your config file at '" + filename + "'"); + + "Please update your config file '" + filename + "' (or run /authme messages)"); return getDefault(key); } return message; diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/src/main/java/fr/xephi/authme/message/MessageKey.java index ed3df4b37..c9fd5fec6 100644 --- a/src/main/java/fr/xephi/authme/message/MessageKey.java +++ b/src/main/java/fr/xephi/authme/message/MessageKey.java @@ -5,156 +5,235 @@ package fr.xephi.authme.message; */ public enum MessageKey { + /** In order to use this command you must be authenticated! */ DENIED_COMMAND("denied_command"), + /** A player with the same IP is already in game! */ SAME_IP_ONLINE("same_ip_online"), + /** In order to chat you must be authenticated! */ DENIED_CHAT("denied_chat"), + /** AntiBot protection mode is enabled! You have to wait some minutes before joining the server. */ KICK_ANTIBOT("kick_antibot"), + /** Can't find the requested user in the database! */ UNKNOWN_USER("unknown_user"), + /** Your quit location was unsafe, you have been teleported to the world's spawnpoint. */ UNSAFE_QUIT_LOCATION("unsafe_spawn"), + /** You're not logged in! */ NOT_LOGGED_IN("not_logged_in"), + /** You can register yourself to the server with the command "/register >password> >ConfirmPassword>" */ REGISTER_VOLUNTARILY("reg_voluntarily"), + /** Usage: /login >password> */ USAGE_LOGIN("usage_log"), + /** Wrong password! */ WRONG_PASSWORD("wrong_pwd"), + /** Successfully unregistered! */ UNREGISTERED_SUCCESS("unregistered"), + /** In-game registration is disabled! */ REGISTRATION_DISABLED("reg_disabled"), + /** Logged-in due to Session Reconnection. */ SESSION_RECONNECTION("valid_session"), + /** Successful login! */ LOGIN_SUCCESS("login"), + /** Your account isn't activated yet, please check your emails! */ ACCOUNT_NOT_ACTIVATED("vb_nonActiv"), + /** You already have registered this username! */ NAME_ALREADY_REGISTERED("user_regged"), + /** You don't have the permission to perform this action! */ NO_PERMISSION("no_perm"), + /** An unexpected error occurred, please contact an administrator! */ ERROR("error"), + /** Please, login with the command "/login >password>" */ LOGIN_MESSAGE("login_msg"), + /** Please, register to the server with the command "/register >password> >ConfirmPassword>" */ REGISTER_MESSAGE("reg_msg"), + /** Please, register to the server with the command "/register >email> >confirmEmail>" */ REGISTER_EMAIL_MESSAGE("reg_email_msg"), + /** You have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection! */ MAX_REGISTER_EXCEEDED("max_reg", "%max_acc", "%reg_count", "%reg_names"), + /** Usage: /register >password> >ConfirmPassword> */ USAGE_REGISTER("usage_reg"), + /** Usage: /unregister >password> */ USAGE_UNREGISTER("usage_unreg"), + /** Password changed successfully! */ PASSWORD_CHANGED_SUCCESS("pwd_changed"), + /** This user isn't registered! */ USER_NOT_REGISTERED("user_unknown"), + /** Passwords didn't match, check them again! */ PASSWORD_MATCH_ERROR("password_error"), + /** You can't use your name as password, please choose another one... */ PASSWORD_IS_USERNAME_ERROR("password_error_nick"), + /** The chosen password isn't safe, please choose another one... */ PASSWORD_UNSAFE_ERROR("password_error_unsafe"), + /** Your password contains illegal characters. Allowed chars: REG_EX */ PASSWORD_CHARACTERS_ERROR("password_error_chars", "REG_EX"), + /** Your IP has been changed and your session data has expired! */ SESSION_EXPIRED("invalid_session"), + /** Only registered users can join the server! Please visit http://example.com to register yourself! */ MUST_REGISTER_MESSAGE("reg_only"), + /** You're already logged in! */ ALREADY_LOGGED_IN_ERROR("logged_in"), + /** Logged-out successfully! */ LOGOUT_SUCCESS("logout"), + /** The same username is already playing on the server! */ USERNAME_ALREADY_ONLINE_ERROR("same_nick"), + /** Successfully registered! */ REGISTER_SUCCESS("registered"), + /** Your password is too short or too long! Please try with another one! */ INVALID_PASSWORD_LENGTH("pass_len"), + /** Configuration and database have been reloaded correctly! */ CONFIG_RELOAD_SUCCESS("reload"), + /** Login timeout exceeded, you have been kicked from the server, please try again! */ LOGIN_TIMEOUT_ERROR("timeout"), + /** Usage: /changepassword >oldPassword> >newPassword> */ USAGE_CHANGE_PASSWORD("usage_changepassword"), + /** Your username is either too short or too long! */ INVALID_NAME_LENGTH("name_len"), + /** Your username contains illegal characters. Allowed chars: REG_EX */ INVALID_NAME_CHARACTERS("regex", "REG_EX"), + /** Please add your email to your account with the command "/email add >yourEmail> >confirmEmail>" */ ADD_EMAIL_MESSAGE("add_email"), + /** Forgot your password? Please use the command "/email recovery >yourEmail>" */ FORGOT_PASSWORD_MESSAGE("recovery_email"), + /** To login you have to solve a captcha code, please use the command "/captcha >theCaptcha>" */ USAGE_CAPTCHA("usage_captcha", ""), + /** Wrong captcha, please type "/captcha THE_CAPTCHA" into the chat! */ CAPTCHA_WRONG_ERROR("wrong_captcha", "THE_CAPTCHA"), + /** Captcha code solved correctly! */ CAPTCHA_SUCCESS("valid_captcha"), + /** A VIP player has joined the server when it was full! */ KICK_FOR_VIP("kick_forvip"), + /** The server is full, try again later! */ KICK_FULL_SERVER("kick_fullserver"), + /** Usage: /email add >email> >confirmEmail> */ USAGE_ADD_EMAIL("usage_email_add"), + /** Usage: /email change >oldEmail> >newEmail> */ USAGE_CHANGE_EMAIL("usage_email_change"), + /** Usage: /email recovery >Email> */ USAGE_RECOVER_EMAIL("usage_email_recovery"), + /** Invalid new email, try again! */ INVALID_NEW_EMAIL("new_email_invalid"), + /** Invalid old email, try again! */ INVALID_OLD_EMAIL("old_email_invalid"), + /** Invalid email address, try again! */ INVALID_EMAIL("email_invalid"), + /** Email address successfully added to your account! */ EMAIL_ADDED_SUCCESS("email_added"), + /** Please confirm your email address! */ CONFIRM_EMAIL_MESSAGE("email_confirm"), + /** Email address changed correctly! */ EMAIL_CHANGED_SUCCESS("email_changed"), + /** Your current email address is: %email */ EMAIL_SHOW("email_show", "%email"), + /** You currently don't have email address associated with this account. */ SHOW_NO_EMAIL("show_no_email"), + /** Recovery email sent successfully! Please check your email inbox! */ RECOVERY_EMAIL_SENT_MESSAGE("email_send"), + /** A recovery email was already sent! You can discard it and send a new one using the command below: */ RECOVERY_EMAIL_ALREADY_SENT_MESSAGE("email_exists"), + /** Your country is banned from this server! */ COUNTRY_BANNED_ERROR("country_banned"), + /** [AntiBotService] AntiBot enabled due to the huge number of connections! */ ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"), + /** [AntiBotService] AntiBot disabled after %m minutes! */ ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m"), + /** The email address is already being used */ EMAIL_ALREADY_USED_ERROR("email_already_used"), + /** Your secret code is %code. You can scan it from here %url */ TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"), + /** You are not the owner of this account. Please choose another name! */ NOT_OWNER_ERROR("not_owner_error"), + /** You should join using username %valid, not %invalid. */ INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid"), + /** You have been temporarily banned for failing to log in too many times. */ TEMPBAN_MAX_LOGINS("tempban_max_logins"), + /** You own %count accounts: */ ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"), + /** The player %name has %count accounts: */ ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"), + /** An admin just registered you; please log in again */ KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"), + /** Error: not all required settings are set for sending emails. Please contact an admin. */ INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings"), + /** The email could not be sent. Please contact an administrator. */ + EMAIL_SEND_FAILURE("email_send_failure"), + + /** A recovery code to reset your password has been sent to your email. */ RECOVERY_CODE_SENT("recovery_code_sent"), + /** The recovery code is not correct! Use /email recovery [email] to generate a new one */ INCORRECT_RECOVERY_CODE("recovery_code_incorrect"); private String key; diff --git a/src/main/java/fr/xephi/authme/output/Log4JFilter.java b/src/main/java/fr/xephi/authme/output/Log4JFilter.java index 90b9f0fb6..1d044e184 100644 --- a/src/main/java/fr/xephi/authme/output/Log4JFilter.java +++ b/src/main/java/fr/xephi/authme/output/Log4JFilter.java @@ -12,14 +12,9 @@ import org.apache.logging.log4j.message.Message; * * @author Xephi59 */ -@SuppressWarnings("serial") public class Log4JFilter extends AbstractFilter { - /** - * Constructor. - */ - public Log4JFilter() { - } + private static final long serialVersionUID = -5594073755007974254L; /** * Validates a Message instance and returns the {@link Result} value diff --git a/src/main/java/fr/xephi/authme/permission/AuthGroupHandler.java b/src/main/java/fr/xephi/authme/permission/AuthGroupHandler.java index fb4bff486..537c3ebc5 100644 --- a/src/main/java/fr/xephi/authme/permission/AuthGroupHandler.java +++ b/src/main/java/fr/xephi/authme/permission/AuthGroupHandler.java @@ -10,6 +10,7 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; +import javax.annotation.PostConstruct; import javax.inject.Inject; import java.util.Arrays; @@ -31,7 +32,8 @@ public class AuthGroupHandler implements Reloadable { private String unregisteredGroup; private String registeredGroup; - AuthGroupHandler() { } + AuthGroupHandler() { + } /** * Set the group of a player, by its AuthMe group type. @@ -114,6 +116,7 @@ public class AuthGroupHandler implements Reloadable { } @Override + @PostConstruct public void reload() { unloggedInGroup = settings.getProperty(SecuritySettings.UNLOGGEDIN_GROUP); unregisteredGroup = settings.getProperty(HooksSettings.UNREGISTERED_GROUP); diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index c0c6981aa..29406be6c 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -78,12 +78,12 @@ public class PermissionsManager implements Reloadable { if (handler != null) { // Show a success message and return this.handler = handler; - ConsoleLogger.info("Hooked into " + type.getName() + "!"); + ConsoleLogger.info("Hooked into " + type.getDisplayName() + "!"); return; } } catch (Exception ex) { // An error occurred, show a warning message - ConsoleLogger.logException("Error while hooking into " + type.getName(), ex); + ConsoleLogger.logException("Error while hooking into " + type.getDisplayName(), ex); } } @@ -101,7 +101,7 @@ public class PermissionsManager implements Reloadable { // Make sure the plugin is enabled before hooking if (!plugin.isEnabled()) { - ConsoleLogger.info("Not hooking into " + type.getName() + " because it's disabled!"); + ConsoleLogger.info("Not hooking into " + type.getDisplayName() + " because it's disabled!"); return null; } @@ -414,7 +414,7 @@ public class PermissionsManager implements Reloadable { */ public boolean setGroups(Player player, List groupNames) { // If no permissions system is used or if there's no group supplied, return false - if (!isEnabled() || groupNames.size() <= 0) + if (!isEnabled() || groupNames.isEmpty()) return false; // Set the main group diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java b/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java index 1410bc668..72a0753cb 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java @@ -33,21 +33,21 @@ public enum PermissionsSystemType { /** * The display name of the permissions system. */ - public String name; + private String displayName; /** * The name of the permissions system plugin. */ - public String pluginName; + private String pluginName; /** * Constructor for PermissionsSystemType. * - * @param name Display name of the permissions system. + * @param displayName Display name of the permissions system. * @param pluginName Name of the plugin. */ - PermissionsSystemType(String name, String pluginName) { - this.name = name; + PermissionsSystemType(String displayName, String pluginName) { + this.displayName = displayName; this.pluginName = pluginName; } @@ -56,8 +56,8 @@ public enum PermissionsSystemType { * * @return Display name. */ - public String getName() { - return this.name; + public String getDisplayName() { + return this.displayName; } /** @@ -76,7 +76,7 @@ public enum PermissionsSystemType { */ @Override public String toString() { - return getName(); + return getDisplayName(); } /** diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java index 0820be011..aaeb0eea3 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java @@ -17,7 +17,7 @@ public enum PlayerStatePermission implements PermissionNode { BYPASS_FORCE_SURVIVAL("authme.bypassforcesurvival", DefaultPermission.OP_ONLY), /** - * Permission node to identify VIP users. + * When the server is full and someone with this permission joins the server, someone will be kicked. */ IS_VIP("authme.vip", DefaultPermission.OP_ONLY), diff --git a/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java index 983e42e27..9f52214b9 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/BPermissionsHandler.java @@ -55,7 +55,7 @@ public class BPermissionsHandler implements PermissionHandler { List groups = getGroups(player); // Make sure there is any group available, or return null - if (groups.size() == 0) + if (groups.isEmpty()) return null; // Return the first group diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionsBukkitHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/PermissionsBukkitHandler.java index 344e0aec5..e8e8a21e5 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/PermissionsBukkitHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/PermissionsBukkitHandler.java @@ -54,7 +54,7 @@ public class PermissionsBukkitHandler implements PermissionHandler { List groups = getGroups(player); // Make sure there is any group available, or return null - if (groups.size() == 0) + if (groups.isEmpty()) return null; // Return the first group diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java b/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java index e3e03ee32..b11d00003 100644 --- a/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java +++ b/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java @@ -77,7 +77,7 @@ public class PermissionsExHandler implements PermissionHandler { PermissionUser user = permissionManager.getUser(player); List groups = user.getParentIdentifiers(null); - if (groups.size() == 0) + if (groups.isEmpty()) return null; return groups.get(0); diff --git a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java index 12471b0ea..2a3fa1755 100644 --- a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java +++ b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java @@ -6,7 +6,7 @@ import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import org.bukkit.entity.Player; @@ -19,7 +19,7 @@ public class AsyncChangePassword implements AsynchronousProcess { private DataSource dataSource; @Inject - private ProcessService processService; + private CommonService commonService; @Inject private PasswordSecurity passwordSecurity; @@ -27,7 +27,8 @@ public class AsyncChangePassword implements AsynchronousProcess { @Inject private PlayerCache playerCache; - AsyncChangePassword() { } + AsyncChangePassword() { + } public void changePassword(final Player player, String oldPassword, String newPassword) { @@ -38,15 +39,15 @@ public class AsyncChangePassword implements AsynchronousProcess { auth.setPassword(hashedPassword); if (!dataSource.updatePassword(auth)) { - processService.send(player, MessageKey.ERROR); + commonService.send(player, MessageKey.ERROR); return; } playerCache.updatePlayer(auth); - processService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); + commonService.send(player, MessageKey.PASSWORD_CHANGED_SUCCESS); ConsoleLogger.info(player.getName() + " changed his password"); } else { - processService.send(player, MessageKey.WRONG_PASSWORD); + commonService.send(player, MessageKey.WRONG_PASSWORD); } } } diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java index 5a8a8e90c..fb5c6c9c3 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java @@ -6,7 +6,8 @@ import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.RegistrationSettings; import org.bukkit.entity.Player; @@ -18,7 +19,7 @@ import javax.inject.Inject; public class AsyncAddEmail implements AsynchronousProcess { @Inject - private ProcessService service; + private CommonService service; @Inject private DataSource dataSource; @@ -26,6 +27,9 @@ public class AsyncAddEmail implements AsynchronousProcess { @Inject private PlayerCache playerCache; + @Inject + private ValidationService validationService; + AsyncAddEmail() { } public void addEmail(Player player, String email) { @@ -37,9 +41,9 @@ public class AsyncAddEmail implements AsynchronousProcess { if (currentEmail != null && !"your@email.com".equals(currentEmail)) { service.send(player, MessageKey.USAGE_CHANGE_EMAIL); - } else if (!service.validateEmail(email)) { + } else if (!validationService.validateEmail(email)) { service.send(player, MessageKey.INVALID_EMAIL); - } else if (!service.isEmailFreeForRegistration(email, player)) { + } else if (!validationService.isEmailFreeForRegistration(email, player)) { service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { auth.setEmail(email); diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java index 6bc83106b..d876ab584 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java @@ -5,7 +5,8 @@ import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.RegistrationSettings; import org.bukkit.entity.Player; @@ -17,7 +18,7 @@ import javax.inject.Inject; public class AsyncChangeEmail implements AsynchronousProcess { @Inject - private ProcessService service; + private CommonService service; @Inject private PlayerCache playerCache; @@ -25,6 +26,9 @@ public class AsyncChangeEmail implements AsynchronousProcess { @Inject private DataSource dataSource; + @Inject + private ValidationService validationService; + AsyncChangeEmail() { } public void changeEmail(Player player, String oldEmail, String newEmail) { @@ -35,11 +39,11 @@ public class AsyncChangeEmail implements AsynchronousProcess { if (currentEmail == null) { service.send(player, MessageKey.USAGE_ADD_EMAIL); - } else if (newEmail == null || !service.validateEmail(newEmail)) { + } else if (newEmail == null || !validationService.validateEmail(newEmail)) { service.send(player, MessageKey.INVALID_NEW_EMAIL); } else if (!oldEmail.equals(currentEmail)) { service.send(player, MessageKey.INVALID_OLD_EMAIL); - } else if (!service.isEmailFreeForRegistration(newEmail, player)) { + } else if (!validationService.isEmailFreeForRegistration(newEmail, player)) { service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { saveNewEmail(auth, player, newEmail); diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index 89e7a671e..dab9cdc2c 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -13,8 +13,9 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupType; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.login.AsynchronousLogin; +import fr.xephi.authme.settings.commandconfig.CommandManager; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -22,9 +23,7 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.LimboPlayerTaskManager; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.util.PlayerUtils; -import org.apache.commons.lang.reflect.MethodUtils; import org.bukkit.GameMode; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -39,9 +38,6 @@ import static fr.xephi.authme.service.BukkitService.TICKS_PER_SECOND; */ public class AsynchronousJoin implements AsynchronousProcess { - private static final boolean DISABLE_COLLISIONS = MethodUtils - .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; - @Inject private AuthMe plugin; @@ -49,7 +45,7 @@ public class AsynchronousJoin implements AsynchronousProcess { private DataSource database; @Inject - private ProcessService service; + private CommonService service; @Inject private PlayerCache playerCache; @@ -72,23 +68,20 @@ public class AsynchronousJoin implements AsynchronousProcess { @Inject private AsynchronousLogin asynchronousLogin; + @Inject + private CommandManager commandManager; + AsynchronousJoin() { } - public void processJoin(final Player player) { final String name = player.getName().toLowerCase(); final String ip = PlayerUtils.getPlayerIp(player); - if (isPlayerUnrestricted(name)) { + if (service.getProperty(RestrictionSettings.UNRESTRICTED_NAMES).contains(name)) { return; } - // Prevent player collisions in 1.9 - if (DISABLE_COLLISIONS) { - player.setCollidable(false); - } - if (service.getProperty(RestrictionSettings.FORCE_SURVIVAL_MODE) && !service.hasPermission(player, PlayerStatePermission.BYPASS_FORCE_SURVIVAL)) { bukkitService.runTask(() -> player.setGameMode(GameMode.SURVIVAL)); @@ -115,7 +108,6 @@ public class AsynchronousJoin implements AsynchronousProcess { return; } - final boolean isAuthAvailable = database.isAuthAvailable(name); if (isAuthAvailable) { @@ -162,26 +154,23 @@ public class AsynchronousJoin implements AsynchronousProcess { final int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(new Runnable() { - @Override - public void run() { - player.setOp(false); - if (!service.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT) - && service.getProperty(RestrictionSettings.REMOVE_SPEED)) { - player.setFlySpeed(0.0f); - player.setWalkSpeed(0.0f); - } - player.setNoDamageTicks(registrationTimeout); - if (pluginHookService.isEssentialsAvailable() && service.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)) { - player.performCommand("motd"); - } - if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { - // Allow infinite blindness effect - int blindTimeOut = (registrationTimeout <= 0) ? 99999 : registrationTimeout; - player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blindTimeOut, 2)); - } + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { + player.setOp(false); + if (!service.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT) + && service.getProperty(RestrictionSettings.REMOVE_SPEED)) { + player.setFlySpeed(0.0f); + player.setWalkSpeed(0.0f); } - + player.setNoDamageTicks(registrationTimeout); + if (pluginHookService.isEssentialsAvailable() && service.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)) { + player.performCommand("motd"); + } + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { + // Allow infinite blindness effect + int blindTimeOut = (registrationTimeout <= 0) ? 99999 : registrationTimeout; + player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blindTimeOut, 2)); + } + commandManager.runCommandsOnJoin(player); }); // Timeout and message task @@ -189,10 +178,6 @@ public class AsynchronousJoin implements AsynchronousProcess { limboPlayerTaskManager.registerMessageTask(name, isAuthAvailable); } - private boolean isPlayerUnrestricted(String name) { - return service.getProperty(RestrictionSettings.UNRESTRICTED_NAMES).contains(name); - } - /** * Returns whether the name is restricted based on the restriction settings. * diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index f9843a169..80b12a200 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -16,7 +16,7 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -44,7 +44,7 @@ public class AsynchronousLogin implements AsynchronousProcess { private DataSource dataSource; @Inject - private ProcessService service; + private CommonService service; @Inject private PermissionsManager permissionsManager; @@ -224,7 +224,11 @@ public class AsynchronousLogin implements AsynchronousProcess { player.setNoDamageTicks(0); service.send(player, MessageKey.LOGIN_SUCCESS); - displayOtherAccounts(auth, player); + + // Other auths + List auths = dataSource.getAllAuthsByIp(auth.getIp()); + runCommandOtherAccounts(auths, player, auth.getIp()); + displayOtherAccounts(auths, player); final String email = auth.getEmail(); if (service.getProperty(EmailSettings.RECALL_PLAYERS) @@ -252,12 +256,29 @@ public class AsynchronousLogin implements AsynchronousProcess { } } - private void displayOtherAccounts(PlayerAuth auth, Player player) { - if (!service.getProperty(RestrictionSettings.DISPLAY_OTHER_ACCOUNTS) || auth == null) { + private void runCommandOtherAccounts(List auths, Player player, String ip) { + int threshold = service.getProperty(RestrictionSettings.OTHER_ACCOUNTS_CMD_THRESHOLD); + String command = service.getProperty(RestrictionSettings.OTHER_ACCOUNTS_CMD); + + if(threshold < 2 || command.isEmpty()) { + return; + } + + if (auths.size() < threshold) { + return; + } + + bukkitService.dispatchConsoleCommand(command + .replaceAll("%playername%", player.getName()) + .replaceAll("%playerip%", ip) + ); + } + + private void displayOtherAccounts(List auths, Player player) { + if (!service.getProperty(RestrictionSettings.DISPLAY_OTHER_ACCOUNTS)) { return; } - List auths = dataSource.getAllAuthsByIp(auth.getIp()); if (auths.size() <= 1) { return; } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index a31f4d28f..d64bda776 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -8,38 +8,31 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; import fr.xephi.authme.listener.PlayerListener; -import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.process.SynchronousProcess; -import fr.xephi.authme.service.BungeeService; -import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.BungeeService; import fr.xephi.authme.service.TeleportationService; -import org.apache.commons.lang.reflect.MethodUtils; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.commandconfig.CommandManager; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.util.StringUtils; import org.bukkit.Bukkit; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.potion.PotionEffectType; import javax.inject.Inject; -import static fr.xephi.authme.settings.properties.PluginSettings.KEEP_COLLISIONS_DISABLED; import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN; public class ProcessSyncPlayerLogin implements SynchronousProcess { - private static final boolean RESTORE_COLLISIONS = MethodUtils - .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; - @Inject private AuthMe plugin; @Inject private BungeeService bungeeService; - @Inject - private ProcessService service; - @Inject private LimboCache limboCache; @@ -55,6 +48,12 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { @Inject private DataSource dataSource; + @Inject + private CommandManager commandManager; + + @Inject + private Settings settings; + ProcessSyncPlayerLogin() { } @@ -66,16 +65,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { } } - private void forceCommands(Player player) { - for (String command : service.getProperty(RegistrationSettings.FORCE_COMMANDS)) { - player.performCommand(command.replace("%p", player.getName())); - } - for (String command : service.getProperty(RegistrationSettings.FORCE_COMMANDS_AS_CONSOLE)) { - Bukkit.getServer().dispatchCommand( - Bukkit.getServer().getConsoleSender(), command.replace("%p", player.getName())); - } - } - public void processPlayerLogin(Player player) { final String name = player.getName().toLowerCase(); @@ -88,11 +77,7 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { // because LimboCache#restoreData teleport player to last location. } - if (RESTORE_COLLISIONS && !service.getProperty(KEEP_COLLISIONS_DISABLED)) { - player.setCollidable(true); - } - - if (service.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) { + if (settings.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) { restoreInventory(player); } @@ -100,19 +85,16 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { teleportationService.teleportOnLogin(player, auth, limbo); // We can now display the join message (if delayed) - String jm = PlayerListener.joinMessage.get(name); - if (jm != null) { - if (!jm.isEmpty()) { - for (Player p : bukkitService.getOnlinePlayers()) { - if (p.isOnline()) { - p.sendMessage(jm); - } + String joinMessage = PlayerListener.joinMessage.remove(name); + if (!StringUtils.isEmpty(joinMessage)) { + for (Player p : bukkitService.getOnlinePlayers()) { + if (p.isOnline()) { + p.sendMessage(joinMessage); } } - PlayerListener.joinMessage.remove(name); } - if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { + if (settings.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { player.removePotionEffect(PotionEffectType.BLINDNESS); } @@ -121,20 +103,20 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { player.saveData(); // Login is done, display welcome message - if (service.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) { - if (service.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) { - for (String s : service.getSettings().getWelcomeMessage()) { + if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) { + if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) { + for (String s : settings.getWelcomeMessage()) { Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); } } else { - for (String s : service.getSettings().getWelcomeMessage()) { + for (String s : settings.getWelcomeMessage()) { player.sendMessage(plugin.replaceAllInfo(s, player)); } } } // Login is now finished; we can force all commands - forceCommands(player); + commandManager.runCommandsOnLogin(player); // Send Bungee stuff. The service will check if it is enabled or not. bungeeService.connectPlayer(player); diff --git a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java index 7374d7389..efd3066aa 100644 --- a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java @@ -6,7 +6,7 @@ import fr.xephi.authme.data.limbo.LimboCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.entity.Player; @@ -19,7 +19,7 @@ public class AsynchronousLogout implements AsynchronousProcess { private DataSource database; @Inject - private ProcessService service; + private CommonService service; @Inject private PlayerCache playerCache; diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java b/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java index 72ff935e0..17710df45 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java @@ -6,7 +6,7 @@ import fr.xephi.authme.events.LogoutEvent; import fr.xephi.authme.listener.protocollib.ProtocolLibService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupType; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -25,7 +25,7 @@ import static fr.xephi.authme.service.BukkitService.TICKS_PER_SECOND; public class ProcessSynchronousPlayerLogout implements SynchronousProcess { @Inject - private ProcessService service; + private CommonService service; @Inject private BukkitService bukkitService; diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java index c8f63bbd4..d34cbde07 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -7,7 +7,7 @@ import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -27,7 +27,7 @@ public class AsynchronousQuit implements AsynchronousProcess { private DataSource database; @Inject - private ProcessService service; + private CommonService service; @Inject private PlayerCache playerCache; diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java index 627a993b6..02ef0ce14 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -7,7 +7,7 @@ import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.process.login.AsynchronousLogin; import fr.xephi.authme.security.HashAlgorithm; @@ -51,7 +51,7 @@ public class AsyncRegister implements AsynchronousProcess { @Inject private PasswordSecurity passwordSecurity; @Inject - private ProcessService service; + private CommonService service; @Inject private SyncProcessManager syncProcessManager; @Inject @@ -148,8 +148,12 @@ public class AsyncRegister implements AsynchronousProcess { } database.updateEmail(auth); database.updateSession(auth); - sendMailSsl.sendPasswordMail(name, email, password); - syncProcessManager.processSyncEmailRegister(player); + boolean couldSendMail = sendMailSsl.sendPasswordMail(name, email, password); + if (couldSendMail) { + syncProcessManager.processSyncEmailRegister(player); + } else { + service.send(player, MessageKey.EMAIL_SEND_FAILURE); + } } private void passwordRegister(final Player player, String password, boolean autoLogin) { diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java index de77a17d0..aea1b4be8 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -3,7 +3,7 @@ package fr.xephi.authme.process.register; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupType; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.task.LimboPlayerTaskManager; @@ -16,13 +16,13 @@ import javax.inject.Inject; public class ProcessSyncEmailRegister implements SynchronousProcess { @Inject - private ProcessService service; + private CommonService service; @Inject private LimboPlayerTaskManager limboPlayerTaskManager; - ProcessSyncEmailRegister() { } - + ProcessSyncEmailRegister() { + } public void processEmailRegister(Player player) { final String name = player.getName().toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index cc190a3c5..fae428d00 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -4,15 +4,15 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.limbo.LimboCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupType; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.service.BungeeService; +import fr.xephi.authme.settings.commandconfig.CommandManager; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.task.LimboPlayerTaskManager; import fr.xephi.authme.util.PlayerUtils; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -25,7 +25,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { private BungeeService bungeeService; @Inject - private ProcessService service; + private CommonService service; @Inject private LimboCache limboCache; @@ -33,17 +33,10 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { @Inject private LimboPlayerTaskManager limboPlayerTaskManager; - ProcessSyncPasswordRegister() { - } + @Inject + private CommandManager commandManager; - private void forceCommands(Player player) { - for (String command : service.getProperty(RegistrationSettings.FORCE_REGISTER_COMMANDS)) { - player.performCommand(command.replace("%p", player.getName())); - } - for (String command : service.getProperty(RegistrationSettings.FORCE_REGISTER_COMMANDS_AS_CONSOLE)) { - Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), - command.replace("%p", player.getName())); - } + ProcessSyncPasswordRegister() { } /** @@ -83,7 +76,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { } // Register is now finished; we can force all commands - forceCommands(player); + commandManager.runCommandsOnRegister(player); // Request login after registration if (service.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER)) { diff --git a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java index 7d63d0555..764e56768 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -9,7 +9,7 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupType; import fr.xephi.authme.process.AsynchronousProcess; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -31,7 +31,7 @@ public class AsynchronousUnregister implements AsynchronousProcess { private DataSource dataSource; @Inject - private ProcessService service; + private CommonService service; @Inject private PasswordSecurity passwordSecurity; diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java index 7bb5681ff..732582e03 100644 --- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java +++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java @@ -3,8 +3,7 @@ package fr.xephi.authme.security; import fr.xephi.authme.security.crypts.EncryptionMethod; /** - * The list of hash algorithms supported by AuthMe. The linked {@link EncryptionMethod} implementation - * must be able to be instantiated with the default constructor. + * Hash algorithms supported by AuthMe. */ public enum HashAlgorithm { @@ -18,8 +17,8 @@ public enum HashAlgorithm { MD5(fr.xephi.authme.security.crypts.MD5.class), MD5VB(fr.xephi.authme.security.crypts.MD5VB.class), MYBB(fr.xephi.authme.security.crypts.MYBB.class), - PBKDF2(fr.xephi.authme.security.crypts.CryptPBKDF2.class), - PBKDF2DJANGO(fr.xephi.authme.security.crypts.CryptPBKDF2Django.class), + PBKDF2(fr.xephi.authme.security.crypts.Pbkdf2.class), + PBKDF2DJANGO(fr.xephi.authme.security.crypts.Pbkdf2Django.class), PHPBB(fr.xephi.authme.security.crypts.PHPBB.class), PHPFUSION(fr.xephi.authme.security.crypts.PHPFUSION.class), @Deprecated diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index 521decfae..246070586 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -6,12 +6,14 @@ import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.EnumSetProperty; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.plugin.PluginManager; import javax.annotation.PostConstruct; import javax.inject.Inject; +import java.util.Collection; /** * Manager class for password-related operations. @@ -31,7 +33,7 @@ public class PasswordSecurity implements Reloadable { private Injector injector; private HashAlgorithm algorithm; - private boolean supportOldAlgorithm; + private Collection legacyAlgorithms; /** * Load or reload the configuration. @@ -40,7 +42,8 @@ public class PasswordSecurity implements Reloadable { @Override public void reload() { this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); - this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH); + // TODO #1014: Need to cast to specific type because ConfigMe ignores fields of child Property types + this.legacyAlgorithms = ((EnumSetProperty) SecuritySettings.LEGACY_HASHES).asEnumSet(settings); } /** @@ -83,7 +86,7 @@ public class PasswordSecurity implements Reloadable { EncryptionMethod method = initializeEncryptionMethodWithEvent(algorithm, playerName); String playerLowerCase = playerName.toLowerCase(); return methodMatches(method, password, hashedPassword, playerLowerCase) - || supportOldAlgorithm && compareWithAllEncryptionMethods(password, hashedPassword, playerLowerCase); + || compareWithLegacyHashes(password, hashedPassword, playerLowerCase); } /** @@ -97,14 +100,12 @@ public class PasswordSecurity implements Reloadable { * * @return True if there was a password match with another encryption method, false otherwise */ - private boolean compareWithAllEncryptionMethods(String password, HashedPassword hashedPassword, String playerName) { - for (HashAlgorithm algorithm : HashAlgorithm.values()) { - if (!HashAlgorithm.CUSTOM.equals(algorithm)) { - EncryptionMethod method = initializeEncryptionMethod(algorithm); - if (methodMatches(method, password, hashedPassword, playerName)) { - hashPasswordForNewAlgorithm(password, playerName); - return true; - } + private boolean compareWithLegacyHashes(String password, HashedPassword hashedPassword, String playerName) { + for (HashAlgorithm algorithm : legacyAlgorithms) { + EncryptionMethod method = initializeEncryptionMethod(algorithm); + if (methodMatches(method, password, hashedPassword, playerName)) { + hashPasswordForNewAlgorithm(password, playerName); + return true; } } return false; diff --git a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java b/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java deleted file mode 100644 index 57c13ee6c..000000000 --- a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java +++ /dev/null @@ -1,40 +0,0 @@ -package fr.xephi.authme.security.crypts; - -import fr.xephi.authme.security.crypts.description.Recommendation; -import fr.xephi.authme.security.crypts.description.Usage; -import fr.xephi.authme.security.pbkdf2.PBKDF2Engine; -import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters; - -import java.util.Arrays; - -@Recommendation(Usage.DOES_NOT_WORK) -public class CryptPBKDF2 extends HexSaltedMethod { - - @Override - public String computeHash(String password, String salt, String name) { - String result = "pbkdf2_sha256$10000$" + salt + "$"; - PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 10000); - PBKDF2Engine engine = new PBKDF2Engine(params); - - return result + Arrays.toString(engine.deriveKey(password, 64)); - } - - @Override - public boolean comparePassword(String password, HashedPassword hashedPassword, String unusedName) { - String[] line = hashedPassword.getHash().split("\\$"); - if (line.length != 4) { - return false; - } - String salt = line[2]; - String derivedKey = line[3]; - PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 10000, derivedKey.getBytes()); - PBKDF2Engine engine = new PBKDF2Engine(params); - return engine.verifyKey(password); - } - - @Override - public int getSaltLength() { - return 12; - } - -} diff --git a/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java b/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java index ee7e49c79..ca72674b3 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java +++ b/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java @@ -4,7 +4,7 @@ import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; -@Recommendation(Usage.RECOMMENDED) +@Recommendation(Usage.ACCEPTABLE) public class JOOMLA extends HexSaltedMethod { @Override diff --git a/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java b/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java index dc2cb3b40..a294ca917 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java @@ -1,5 +1,10 @@ package fr.xephi.authme.security.crypts; +/** + * Plaintext password storage. + * + * @deprecated Using this is no longer supported. AuthMe will migrate to SHA256 on startup. + */ @Deprecated public class PLAINTEXT extends UnsaltedMethod { diff --git a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java new file mode 100644 index 000000000..5367a2a12 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java @@ -0,0 +1,60 @@ +package fr.xephi.authme.security.crypts; + +import de.rtner.misc.BinTools; +import de.rtner.security.auth.spi.PBKDF2Engine; +import de.rtner.security.auth.spi.PBKDF2Parameters; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.security.crypts.description.Recommendation; +import fr.xephi.authme.security.crypts.description.Usage; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; + +import javax.inject.Inject; + +@Recommendation(Usage.RECOMMENDED) +public class Pbkdf2 extends HexSaltedMethod { + + private static final int DEFAULT_ROUNDS = 10_000; + private int numberOfRounds; + + @Inject + Pbkdf2(Settings settings) { + int configuredRounds = settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS); + this.numberOfRounds = configuredRounds > 0 ? configuredRounds : DEFAULT_ROUNDS; + } + + @Override + public String computeHash(String password, String salt, String name) { + String result = "pbkdf2_sha256$" + numberOfRounds + "$" + salt + "$"; + PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", salt.getBytes(), numberOfRounds); + PBKDF2Engine engine = new PBKDF2Engine(params); + + return result + BinTools.bin2hex(engine.deriveKey(password, 64)); + } + + @Override + public boolean comparePassword(String password, HashedPassword hashedPassword, String unusedName) { + String[] line = hashedPassword.getHash().split("\\$"); + if (line.length != 4) { + return false; + } + int iterations; + try { + iterations = Integer.parseInt(line[1]); + } catch (NumberFormatException e) { + ConsoleLogger.logException("Cannot read number of rounds for Pbkdf2", e); + return false; + } + String salt = line[2]; + byte[] derivedKey = BinTools.hex2bin(line[3]); + PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", salt.getBytes(), iterations, derivedKey); + PBKDF2Engine engine = new PBKDF2Engine(params); + return engine.verifyKey(password); + } + + @Override + public int getSaltLength() { + return 16; + } + +} diff --git a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2Django.java b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java similarity index 75% rename from src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2Django.java rename to src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java index 79d06e0a8..93988e0d0 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2Django.java +++ b/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java @@ -1,15 +1,14 @@ package fr.xephi.authme.security.crypts; +import de.rtner.security.auth.spi.PBKDF2Engine; +import de.rtner.security.auth.spi.PBKDF2Parameters; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.security.crypts.description.AsciiRestricted; -import fr.xephi.authme.security.pbkdf2.PBKDF2Engine; -import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters; -import fr.xephi.authme.util.StringUtils; import javax.xml.bind.DatatypeConverter; @AsciiRestricted -public class CryptPBKDF2Django extends HexSaltedMethod { +public class Pbkdf2Django extends HexSaltedMethod { private static final int DEFAULT_ITERATIONS = 24000; @@ -19,7 +18,7 @@ public class CryptPBKDF2Django extends HexSaltedMethod { PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), DEFAULT_ITERATIONS); PBKDF2Engine engine = new PBKDF2Engine(params); - return result + String.valueOf(DatatypeConverter.printBase64Binary(engine.deriveKey(password, 32))); + return result + DatatypeConverter.printBase64Binary(engine.deriveKey(password, 32)); } @Override @@ -32,8 +31,7 @@ public class CryptPBKDF2Django extends HexSaltedMethod { try { iterations = Integer.parseInt(line[1]); } catch (NumberFormatException e) { - ConsoleLogger.warning("Could not read number of rounds for CryptPBKDF2Django:" - + StringUtils.formatException(e)); + ConsoleLogger.logException("Could not read number of rounds for Pbkdf2Django:", e); return false; } String salt = line[2]; diff --git a/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java b/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java index 186128744..f2ebf1976 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java @@ -18,14 +18,14 @@ public class XAUTH extends HexSaltedMethod { @Override public String computeHash(String password, String salt, String name) { String hash = getWhirlpool(salt + password).toLowerCase(); - int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length()); + int saltPos = password.length() >= hash.length() ? hash.length() - 1 : password.length(); return hash.substring(0, saltPos) + salt + hash.substring(saltPos); } @Override public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) { String hash = hashedPassword.getHash(); - int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length()); + int saltPos = password.length() >= hash.length() ? hash.length() - 1 : password.length(); if (saltPos + 12 > hash.length()) { return false; } diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java b/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java deleted file mode 100644 index 34bf8aac2..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java +++ /dev/null @@ -1,114 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/* - * Free auxiliary functions. Copyright 2007, 2014, Matthias Gärtner - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -/** - * Free auxiliary functions - * - * @author Matthias Gärtner - */ -public class BinTools { - public static final String hex = "0123456789ABCDEF"; - - /** - * Simple binary-to-hexadecimal conversion. - * - * @param b - * Input bytes. May be null. - * @return Hexadecimal representation of b. Uppercase A-F, two characters - * per byte. Empty string on null input. - */ - public static String bin2hex(final byte[] b) { - if (b == null) { - return ""; - } - StringBuffer sb = new StringBuffer(2 * b.length); - for (int i = 0; i < b.length; i++) { - int v = (256 + b[i]) % 256; - sb.append(hex.charAt((v / 16) & 15)); - sb.append(hex.charAt((v % 16) & 15)); - } - return sb.toString(); - } - - /** - * Convert hex string to array of bytes. - * - * @param s - * String containing hexadecimal digits. May be null - * . On odd length leading zero will be assumed. - * @return Array on bytes, non-null. - * @throws IllegalArgumentException - * when string contains non-hex character - */ - public static byte[] hex2bin(final String s) { - String m = s; - if (s == null) { - // Allow empty input string. - m = ""; - } else if (s.length() % 2 != 0) { - // Assume leading zero for odd string length - m = "0" + s; - } - byte r[] = new byte[m.length() / 2]; - for (int i = 0, n = 0; i < m.length(); n++) { - char h = m.charAt(i++); - char l = m.charAt(i++); - r[n] = (byte) (hex2bin(h) * 16 + hex2bin(l)); - } - return r; - } - - /** - * Convert hex digit to numerical value. - * - * @param c - * 0-9, a-f, A-F allowd. - * @return 0-15 - * @throws IllegalArgumentException - * on non-hex character - */ - public static int hex2bin(char c) { - if (c >= '0' && c <= '9') { - return (c - '0'); - } - if (c >= 'A' && c <= 'F') { - return (c - 'A' + 10); - } - if (c >= 'a' && c <= 'f') { - return (c - 'a' + 10); - } - throw new IllegalArgumentException("Input string may only contain hex digits, but found '" + c + "'"); - } - - public static void main(String[] args) { - byte b[] = new byte[256]; - byte bb = 0; - for (int i = 0; i < 256; i++) { - b[i] = bb++; - } - String s = bin2hex(b); - byte c[] = hex2bin(s); - String t = bin2hex(c); - if (!s.equals(t)) { - throw new AssertionError("Mismatch"); - } - } -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java b/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java deleted file mode 100644 index 88ff11bfc..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/MacBasedPRF.java +++ /dev/null @@ -1,96 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -/** - *

    - * Default PRF implementation based on standard javax.crypt.Mac mechanisms. - *

    - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public class MacBasedPRF implements PRF { - - protected Mac mac; - - protected int hLen; - - protected final String macAlgorithm; - - /** - * Create Mac-based Pseudo Random Function. - * - * @param macAlgorithm Mac algorithm to use, i.e. HMacSHA1 or HMacMD5. - */ - public MacBasedPRF(String macAlgorithm) { - this.macAlgorithm = macAlgorithm; - try { - mac = Mac.getInstance(macAlgorithm); - hLen = mac.getMacLength(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - public MacBasedPRF(String macAlgorithm, String provider) { - this.macAlgorithm = macAlgorithm; - try { - mac = Mac.getInstance(macAlgorithm, provider); - hLen = mac.getMacLength(); - } catch (NoSuchAlgorithmException | NoSuchProviderException e) { - throw new RuntimeException(e); - } - } - - @Override - public byte[] doFinal(byte[] M) { - byte[] r = mac.doFinal(M); - return r; - } - - @Override - public int getHLen() { - return hLen; - } - - @Override - public void init(byte[] P) { - try { - mac.init(new SecretKeySpec(P, macAlgorithm)); - } catch (InvalidKeyException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java deleted file mode 100644 index 59bc96df2..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2.java +++ /dev/null @@ -1,97 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/** - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public interface PBKDF2 { - - /** - * Convert String-based input to internal byte array, then invoke PBKDF2. - * Desired key length defaults to Pseudo Random Function block size. - * - * @param inputPassword Candidate password to compute the derived key for. - * - * @return internal byte array - */ - byte[] deriveKey(String inputPassword); - - /** - * Convert String-based input to internal byte array, then invoke PBKDF2. - * - * @param inputPassword Candidate password to compute the derived key for. - * @param dkLen Specify desired key length - * - * @return internal byte array - */ - byte[] deriveKey(String inputPassword, int dkLen); - - /** - * Convert String-based input to internal byte arrays, then invoke PBKDF2 - * and verify result against the reference data that is supplied in the - * PBKDF2Parameters. - * - * @param inputPassword Candidate password to compute the derived key for. - * - * @return true password match; false incorrect - * password - */ - boolean verifyKey(String inputPassword); - - /** - * Allow reading of configured parameters. - * - * @return Currently set parameters. - */ - PBKDF2Parameters getParameters(); - - /** - * Allow setting of configured parameters. - * - * @param parameters PBKDF2Parameters - */ - void setParameters(PBKDF2Parameters parameters); - - /** - * Get currently set Pseudo Random Function. - * - * @return Currently set Pseudo Random Function - */ - PRF getPseudoRandomFunction(); - - /** - * Set the Pseudo Random Function to use. Note that deriveKeys/getPRF does - * init this object using the supplied candidate password. If this is - * undesired, one has to override getPRF. - * - * @param prf Pseudo Random Function to set. - */ - void setPseudoRandomFunction(PRF prf); -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java deleted file mode 100644 index d32b8752d..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Engine.java +++ /dev/null @@ -1,346 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -/** - *

    - * Request for Comments: 2898 PKCS #5: Password-Based Cryptography Specification - *

    - * Version 2.0 - *

    - *

    - * PBKDF2 (P, S, c, dkLen) - *

    - * Options: - *
      - *
    • PRF underlying pseudorandom function (hLen denotes the length in octets - * of the pseudorandom function output). PRF is pluggable.
    • - *
    - * Input: - *
      - *
    • P password, an octet string
    • - *
    • S salt, an octet string
    • - *
    • c iteration count, a positive integer
    • - *
    • dkLen intended length in octets of the derived key, a positive integer, - * at most (2^32 - 1) * hLen
    • - *
    - * Output: - *
      - *
    • DK derived key, a dkLen-octet string
    • - *
    - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see - * http://www. - * gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - * @see RFC 2898 - */ -public class PBKDF2Engine implements PBKDF2 { - - protected PBKDF2Parameters parameters; - - protected PRF prf; - - /** - * Constructor for PBKDF2 implementation object. PBKDF2 parameters must be - * passed later. - */ - public PBKDF2Engine() { - this.parameters = null; - prf = null; - } - - /** - * Constructor for PBKDF2 implementation object. PBKDF2 parameters are - * passed so that this implementation knows iteration count, method to use - * and String encoding. - * - * @param parameters Data holder for iteration count, method to use et cetera. - */ - public PBKDF2Engine(PBKDF2Parameters parameters) { - this.parameters = parameters; - prf = null; - } - - /** - * Constructor for PBKDF2 implementation object. PBKDF2 parameters are - * passed so that this implementation knows iteration count, method to use - * and String encoding. - * - * @param parameters Data holder for iteration count, method to use et cetera. - * @param prf Supply customer Pseudo Random Function. - */ - public PBKDF2Engine(PBKDF2Parameters parameters, PRF prf) { - this.parameters = parameters; - this.prf = prf; - } - - /** - * Convenience client function. Convert supplied password with random 8-byte - * salt and 1000 iterations using HMacSHA1. Assume that password is in - * ISO-8559-1 encoding. Output result as - * "Salt:iteration-count:PBKDF2" with binary data in hexadecimal - * encoding. - *

    - * Example: Password "password" (without the quotes) leads to - * 48290A0B96C426C3:1000:973899B1D4AFEB3ED371060D0797E0EE0142BD04 - *

    - * @param args Supply the password as argument. - * - * @throws IOException an ioexception occured - * @throws NoSuchAlgorithmException a NoSuchAlgorithmException occured - */ - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException { - String password = "password"; - String candidate = null; - PBKDF2Formatter formatter = new PBKDF2HexFormatter(); - - if (args.length >= 1) { - password = args[0]; - } - if (args.length >= 2) { - candidate = args[1]; - } - if (candidate == null) { - // Creation mode - SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); - byte[] salt = new byte[8]; - sr.nextBytes(salt); - int iterations = 1000; - PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, iterations); - PBKDF2Engine e = new PBKDF2Engine(p); - p.setDerivedKey(e.deriveKey(password)); - candidate = formatter.toString(p); - } else { - // Verification mode - PBKDF2Parameters p = new PBKDF2Parameters(); - p.setHashAlgorithm("HmacSHA1"); - p.setHashCharset("ISO-8859-1"); - if (formatter.fromString(p, candidate)) { - throw new IllegalArgumentException("Candidate data does not have correct format (\"" + candidate + "\")"); - } - PBKDF2Engine e = new PBKDF2Engine(p); - boolean verifyOK = e.verifyKey(password); - System.exit(verifyOK ? 0 : 1); - } - } - - @Override - public byte[] deriveKey(String inputPassword) { - return deriveKey(inputPassword, 0); - } - - @Override - public byte[] deriveKey(String inputPassword, int dkLen) { - byte[] r = null; - byte[] P = null; - String charset = parameters.getHashCharset(); - if (inputPassword == null) { - inputPassword = ""; - } - try { - if (charset == null) { - P = inputPassword.getBytes(); - } else { - P = inputPassword.getBytes(charset); - } - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - assertPRF(P); - if (dkLen == 0) { - dkLen = prf.getHLen(); - } - r = PBKDF2(prf, parameters.getSalt(), parameters.getIterationCount(), dkLen); - return r; - } - - @Override - public boolean verifyKey(String inputPassword) { - byte[] referenceKey = getParameters().getDerivedKey(); - if (referenceKey == null || referenceKey.length == 0) { - return false; - } - byte[] inputKey = deriveKey(inputPassword, referenceKey.length); - - if (inputKey == null || inputKey.length != referenceKey.length) { - return false; - } - for (int i = 0; i < inputKey.length; i++) { - if (inputKey[i] != referenceKey[i]) { - return false; - } - } - return true; - } - - /** - * Factory method. Default implementation is (H)MAC-based. To be overridden - * in derived classes. - * - * @param P User-supplied candidate password as array of bytes. - */ - protected void assertPRF(byte[] P) { - if (prf == null) { - prf = new MacBasedPRF(parameters.getHashAlgorithm()); - } - prf.init(P); - } - - @Override - public PRF getPseudoRandomFunction() { - return prf; - } - - @Override - public void setPseudoRandomFunction(PRF prf) { - this.prf = prf; - } - - /** - * Core Password Based Key Derivation Function 2. - * - * @param prf Pseudo Random Function (i.e. HmacSHA1) - * @param S Salt as array of bytes. null means no salt. - * @param c Iteration count (see RFC 2898 4.2) - * @param dkLen desired length of derived key. - * - * @return internal byte array * @see RFC 2898 5.2 - */ - protected byte[] PBKDF2(PRF prf, byte[] S, int c, int dkLen) { - if (S == null) { - S = new byte[0]; - } - int hLen = prf.getHLen(); - int l = ceil(dkLen, hLen); - int r = dkLen - (l - 1) * hLen; - byte T[] = new byte[l * hLen]; - int ti_offset = 0; - for (int i = 1; i <= l; i++) { - _F(T, ti_offset, prf, S, c, i); - ti_offset += hLen; - } - if (r < hLen) { - // Incomplete last block - byte DK[] = new byte[dkLen]; - System.arraycopy(T, 0, DK, 0, dkLen); - return DK; - } - return T; - } - - /** - * Integer division with ceiling function. - * - * @param a Integer - * @param b Integer - * - * @return ceil(a/b) * @see RFC 2898 5.2 Step - * 2. - */ - protected int ceil(int a, int b) { - int m = 0; - if (a % b > 0) { - m = 1; - } - return a / b + m; - } - - /** - * Function F. - * - * @param dest Destination byte buffer - * @param offset Offset into destination byte buffer - * @param prf Pseudo Random Function - * @param S Salt as array of bytes - * @param c Iteration count - * @param blockIndex Integer - * - * @see RFC 2898 5.2 Step - * 3. - */ - protected void _F(byte[] dest, int offset, PRF prf, byte[] S, int c, - int blockIndex) { - int hLen = prf.getHLen(); - byte U_r[] = new byte[hLen]; - - // U0 = S || INT (i); - byte U_i[] = new byte[S.length + 4]; - System.arraycopy(S, 0, U_i, 0, S.length); - INT(U_i, S.length, blockIndex); - - for (int i = 0; i < c; i++) { - U_i = prf.doFinal(U_i); - xor(U_r, U_i); - } - System.arraycopy(U_r, 0, dest, offset, hLen); - } - - /** - * Block-Xor. Xor source bytes into destination byte buffer. Destination - * buffer must be same length or less than source buffer. - * - * @param dest byte array - * @param src byte array - */ - protected void xor(byte[] dest, byte[] src) { - for (int i = 0; i < dest.length; i++) { - dest[i] ^= src[i]; - } - } - - /** - * Four-octet encoding of the integer i, most significant octet first. - * - * @param dest byte array - * @param offset Integer - * @param i Integer - * - * @see RFC 2898 5.2 Step - * 3. - */ - protected void INT(byte[] dest, int offset, int i) { - dest[offset] = (byte) (i / (256 * 256 * 256)); - dest[offset + 1] = (byte) (i / (256 * 256)); - dest[offset + 2] = (byte) (i / (256)); - dest[offset + 3] = (byte) (i); - } - - @Override - public PBKDF2Parameters getParameters() { - return parameters; - } - - @Override - public void setParameters(PBKDF2Parameters parameters) { - this.parameters = parameters; - } -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Formatter.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Formatter.java deleted file mode 100644 index 3916d1c03..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Formatter.java +++ /dev/null @@ -1,56 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/** - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public interface PBKDF2Formatter { - - /** - * Convert parameters to String. - * - * @param p Parameters object to output. - * - * @return String representation - */ - String toString(PBKDF2Parameters p); - - /** - * Convert String to parameters. Depending on actual implementation, it may - * be required to set further fields externally. - * - * @param s String representation of parameters to decode. - * @param p PBKDF2Parameters - * - * @return false syntax OK, true some syntax - * issue. - */ - boolean fromString(PBKDF2Parameters p, String s); -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2HexFormatter.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2HexFormatter.java deleted file mode 100644 index 54544a4c2..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2HexFormatter.java +++ /dev/null @@ -1,62 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/** - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public class PBKDF2HexFormatter implements PBKDF2Formatter { - - @Override - public boolean fromString(PBKDF2Parameters p, String s) { - if (p == null || s == null) { - return true; - } - - String[] p123 = s.split(":"); - if (p123.length != 3) { - return true; - } - - byte salt[] = BinTools.hex2bin(p123[0]); - int iterationCount = Integer.parseInt(p123[1]); - byte bDK[] = BinTools.hex2bin(p123[2]); - - p.setSalt(salt); - p.setIterationCount(iterationCount); - p.setDerivedKey(bDK); - return false; - } - - @Override - public String toString(PBKDF2Parameters p) { - String s = BinTools.bin2hex(p.getSalt()) + ":" + String.valueOf(p.getIterationCount()) + ":" + BinTools.bin2hex(p.getDerivedKey()); - return s; - } -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java deleted file mode 100644 index 04abaa9f8..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PBKDF2Parameters.java +++ /dev/null @@ -1,139 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/** - *

    - * Parameter data holder for PBKDF2 configuration. - *

    - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public class PBKDF2Parameters { - - protected byte[] salt; - - protected int iterationCount; - - protected String hashAlgorithm; - - protected String hashCharset; - - /** - * The derived key is actually only a convenience to store a reference - * derived key. It is not used during computation. - */ - protected byte[] derivedKey; - - /** - * Constructor. Defaults to null for byte arrays, UTF-8 as - * character set and 1000 for iteration count. - */ - public PBKDF2Parameters() { - this.hashAlgorithm = null; - this.hashCharset = "UTF-8"; - this.salt = null; - this.iterationCount = 1000; - this.derivedKey = null; - } - - /** - * Constructor. - * - * @param hashAlgorithm for example HMacSHA1 or HMacMD5 - * @param hashCharset for example UTF-8 - * @param salt Salt as byte array, may be null (not recommended) - * @param iterationCount Number of iterations to execute. Recommended value 1000. - */ - public PBKDF2Parameters(String hashAlgorithm, String hashCharset, - byte[] salt, int iterationCount) { - this.hashAlgorithm = hashAlgorithm; - this.hashCharset = hashCharset; - this.salt = salt; - this.iterationCount = iterationCount; - this.derivedKey = null; - } - - /** - * Constructor. - * - * @param hashAlgorithm for example HMacSHA1 or HMacMD5 - * @param hashCharset for example UTF-8 - * @param salt Salt as byte array, may be null (not recommended) - * @param iterationCount Number of iterations to execute. Recommended value 1000. - * @param derivedKey Convenience data holder, not used during computation. - */ - public PBKDF2Parameters(String hashAlgorithm, String hashCharset, - byte[] salt, int iterationCount, byte[] derivedKey) { - this.hashAlgorithm = hashAlgorithm; - this.hashCharset = hashCharset; - this.salt = salt; - this.iterationCount = iterationCount; - this.derivedKey = derivedKey; - } - - public int getIterationCount() { - return iterationCount; - } - - public void setIterationCount(int iterationCount) { - this.iterationCount = iterationCount; - } - - public byte[] getSalt() { - return salt; - } - - public void setSalt(byte[] salt) { - this.salt = salt; - } - - public byte[] getDerivedKey() { - return derivedKey; - } - - public void setDerivedKey(byte[] derivedKey) { - this.derivedKey = derivedKey; - } - - public String getHashAlgorithm() { - return hashAlgorithm; - } - - public void setHashAlgorithm(String hashAlgorithm) { - this.hashAlgorithm = hashAlgorithm; - } - - public String getHashCharset() { - return hashCharset; - } - - public void setHashCharset(String hashCharset) { - this.hashCharset = hashCharset; - } -} diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/PRF.java b/src/main/java/fr/xephi/authme/security/pbkdf2/PRF.java deleted file mode 100644 index af40e171d..000000000 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/PRF.java +++ /dev/null @@ -1,60 +0,0 @@ -package fr.xephi.authme.security.pbkdf2; - -/** - *

    - * A free Java implementation of Password Based Key Derivation Function 2 as - * defined by RFC 2898. Copyright (c) 2007 Matthias Gärtner - *

    - *

    - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - *

    - *

    - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - *

    - *

    - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *

    - *

    - * For Details, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - *

    - * - * @author Matthias Gärtner - * @version 1.0 - */ -public interface PRF { - - /** - * Initialize this instance with the user-supplied password. - * - * @param P The password supplied as array of bytes. It is the caller's - * task to convert String passwords to bytes as appropriate. - */ - void init(byte[] P); - - /** - * Pseudo Random Function - * - * @param M Input data/message etc. Together with any data supplied during - * initilization. - * - * @return Random bytes of hLen length. - */ - byte[] doFinal(byte[] M); - - /** - * Query block size of underlying algorithm/mechanism. - * - * @return block size - */ - int getHLen(); -} diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/src/main/java/fr/xephi/authme/service/AntiBotService.java index 24118d40c..91f80041d 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -7,10 +7,11 @@ import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; -import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import javax.inject.Inject; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.concurrent.CopyOnWriteArrayList; import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; @@ -25,18 +26,18 @@ public class AntiBotService implements SettingsDependent { private final Messages messages; private final PermissionsManager permissionsManager; private final BukkitService bukkitService; - + private final CopyOnWriteArrayList antibotKicked = new CopyOnWriteArrayList<>(); // Settings private int duration; private int sensibility; private int delay; - + private int interval; // Service status private AntiBotStatus antiBotStatus; private boolean startup; private BukkitTask disableTask; - private int antibotPlayers; - private final CopyOnWriteArrayList antibotKicked = new CopyOnWriteArrayList<>(); + private Instant lastFlaggedJoin; + private int flagged = 0; @Inject AntiBotService(Settings settings, Messages messages, PermissionsManager permissionsManager, @@ -47,7 +48,7 @@ public class AntiBotService implements SettingsDependent { this.bukkitService = bukkitService; // Initial status disableTask = null; - antibotPlayers = 0; + flagged = 0; antiBotStatus = AntiBotStatus.DISABLED; startup = true; // Load settings and start if required @@ -60,6 +61,7 @@ public class AntiBotService implements SettingsDependent { duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION); sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY); delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY); + interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL); // Stop existing protection stopProtection(); @@ -71,15 +73,10 @@ public class AntiBotService implements SettingsDependent { } // Bot activation task - Runnable enableTask = new Runnable() { - @Override - public void run() { - antiBotStatus = AntiBotStatus.LISTENING; - } - }; + Runnable enableTask = () -> antiBotStatus = AntiBotStatus.LISTENING; // Delay the schedule on first start - if(startup) { + if (startup) { bukkitService.scheduleSyncDelayedTask(enableTask, delay * TICKS_PER_SECOND); startup = false; } else { @@ -94,19 +91,12 @@ public class AntiBotService implements SettingsDependent { antiBotStatus = AntiBotStatus.ACTIVE; // Inform admins - for (Player player : bukkitService.getOnlinePlayers()) { - if (permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) { - messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE); - } - } + bukkitService.getOnlinePlayers().stream() + .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) + .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)); // Schedule auto-disable - disableTask = bukkitService.runTaskLater(new Runnable() { - @Override - public void run() { - stopProtection(); - } - }, duration * TICKS_PER_MINUTE); + disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE); } private void stopProtection() { @@ -116,7 +106,7 @@ public class AntiBotService implements SettingsDependent { // Change status antiBotStatus = AntiBotStatus.LISTENING; - antibotPlayers = 0; + flagged = 0; antibotKicked.clear(); // Cancel auto-disable task @@ -124,11 +114,10 @@ public class AntiBotService implements SettingsDependent { disableTask = null; // Inform admins - for (Player player : bukkitService.getOnlinePlayers()) { - if (permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) { - messages.send(player, MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, Integer.toString(duration)); - } - } + String durationString = Integer.toString(duration); + bukkitService.getOnlinePlayers().stream() + .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) + .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, durationString)); } /** @@ -155,36 +144,33 @@ public class AntiBotService implements SettingsDependent { } } - /** - * Handles a player joining the server and checks if AntiBot needs to be activated. - */ - public void handlePlayerJoin() { - if (antiBotStatus != AntiBotStatus.LISTENING) { - return; - } - - antibotPlayers++; - if (antibotPlayers > sensibility) { - startProtection(); - return; - } - - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - antibotPlayers--; - } - }, 5 * TICKS_PER_SECOND); - } - /** * Returns if a player should be kicked due to antibot service. * - * @param isAuthAvailable if the player is registered * @return if the player should be kicked */ - public boolean shouldKick(boolean isAuthAvailable) { - return !isAuthAvailable && (antiBotStatus == AntiBotStatus.ACTIVE); + public boolean shouldKick() { + if (antiBotStatus == AntiBotStatus.DISABLED) { + return false; + } else if (antiBotStatus == AntiBotStatus.ACTIVE) { + return true; + } + + if (lastFlaggedJoin == null) { + lastFlaggedJoin = Instant.now(); + } + if (ChronoUnit.SECONDS.between(lastFlaggedJoin, Instant.now()) <= interval) { + flagged++; + } else { + // reset to 1 because this player is also count as not registered + flagged = 1; + lastFlaggedJoin = null; + } + if (flagged > sensibility) { + startProtection(); + return true; + } + return false; } /** diff --git a/src/main/java/fr/xephi/authme/service/BackupService.java b/src/main/java/fr/xephi/authme/service/BackupService.java index b5dfa06d3..846e34415 100644 --- a/src/main/java/fr/xephi/authme/service/BackupService.java +++ b/src/main/java/fr/xephi/authme/service/BackupService.java @@ -104,36 +104,22 @@ public class BackupService { dirBackup.mkdir(); } String backupWindowsPath = settings.getProperty(BackupSettings.MYSQL_WINDOWS_PATH); - if (checkWindows(backupWindowsPath)) { - String executeCmd = backupWindowsPath + "\\bin\\mysqldump.exe -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; - Process runtimeProcess; - try { - runtimeProcess = Runtime.getRuntime().exec(executeCmd); - int processComplete = runtimeProcess.waitFor(); - if (processComplete == 0) { - ConsoleLogger.info("Backup created successfully."); - return true; - } else { - ConsoleLogger.warning("Could not create the backup! (Windows)"); - } - } catch (IOException | InterruptedException e) { - ConsoleLogger.logException("Error during Windows backup:", e); - } - } else { - String executeCmd = "mysqldump -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; - Process runtimeProcess; - try { - runtimeProcess = Runtime.getRuntime().exec(executeCmd); - int processComplete = runtimeProcess.waitFor(); - if (processComplete == 0) { - ConsoleLogger.info("Backup created successfully."); - return true; - } else { - ConsoleLogger.warning("Could not create the backup!"); - } - } catch (IOException | InterruptedException e) { - ConsoleLogger.logException("Error during backup:", e); + boolean isUsingWindows = checkWindows(backupWindowsPath); + String backupCommand = isUsingWindows + ? backupWindowsPath + "\\bin\\mysqldump.exe" + buildMysqlDumpArguments() + : "mysqldump" + buildMysqlDumpArguments(); + + try { + Process runtimeProcess = Runtime.getRuntime().exec(backupCommand); + int processComplete = runtimeProcess.waitFor(); + if (processComplete == 0) { + ConsoleLogger.info("Backup created successfully. (Using Windows = " + isUsingWindows + ")"); + return true; + } else { + ConsoleLogger.warning("Could not create the backup! (Using Windows = " + isUsingWindows + ")"); } + } catch (IOException | InterruptedException e) { + ConsoleLogger.logException("Error during backup (using Windows = " + isUsingWindows + "):", e); } return false; } @@ -173,6 +159,16 @@ public class BackupService { return false; } + /** + * Builds the command line arguments to pass along when running the {@code mysqldump} command. + * + * @return the mysqldump command line arguments + */ + private String buildMysqlDumpArguments() { + return " -u " + dbUserName + " -p" + dbPassword + " " + dbName + + " --tables " + tblname + " -r " + path + ".sql"; + } + private static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst); diff --git a/src/main/java/fr/xephi/authme/service/BukkitService.java b/src/main/java/fr/xephi/authme/service/BukkitService.java index cd5595fff..603e39757 100644 --- a/src/main/java/fr/xephi/authme/service/BukkitService.java +++ b/src/main/java/fr/xephi/authme/service/BukkitService.java @@ -10,6 +10,7 @@ import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.World; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.plugin.Plugin; @@ -17,6 +18,7 @@ import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; +import javax.inject.Inject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -40,7 +42,8 @@ public class BukkitService implements SettingsDependent { private Method getOnlinePlayers; private boolean useAsyncTasks; - public BukkitService(AuthMe authMe, Settings settings) { + @Inject + BukkitService(AuthMe authMe, Settings settings) { this.authMe = authMe; getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField(); reload(settings); @@ -271,6 +274,27 @@ public class BukkitService implements SettingsDependent { return Bukkit.getWorld(name); } + /** + * Dispatches a command on this server, and executes it if found. + * + * @param sender the apparent sender of the command + * @param commandLine the command + arguments. Example: test abc 123 + * @return returns false if no target is found + */ + public boolean dispatchCommand(CommandSender sender, String commandLine) { + return Bukkit.dispatchCommand(sender, commandLine); + } + + /** + * Dispatches a command to be run as console user on this server, and executes it if found. + * + * @param commandLine the command + arguments. Example: test abc 123 + * @return returns false if no target is found + */ + public boolean dispatchConsoleCommand(String commandLine) { + return Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandLine); + } + @Override public void reload(Settings settings) { useAsyncTasks = settings.getProperty(PluginSettings.USE_ASYNC_TASKS); @@ -307,5 +331,4 @@ public class BukkitService implements SettingsDependent { public BanEntry banIp(String ip, String reason, Date expires, String source) { return Bukkit.getServer().getBanList(BanList.Type.IP).addBan(ip, reason, expires, source); } - } diff --git a/src/main/java/fr/xephi/authme/process/ProcessService.java b/src/main/java/fr/xephi/authme/service/CommonService.java similarity index 71% rename from src/main/java/fr/xephi/authme/process/ProcessService.java rename to src/main/java/fr/xephi/authme/service/CommonService.java index 575b80536..1b1d9593b 100644 --- a/src/main/java/fr/xephi/authme/process/ProcessService.java +++ b/src/main/java/fr/xephi/authme/service/CommonService.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.process; +package fr.xephi.authme.service; import com.github.authme.configme.properties.Property; import fr.xephi.authme.message.MessageKey; @@ -8,16 +8,15 @@ import fr.xephi.authme.permission.AuthGroupType; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.service.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import javax.inject.Inject; /** - * Service for asynchronous and synchronous processes. + * Service for the most common operations regarding settings, messages and permissions. */ -public class ProcessService { +public class CommonService { @Inject private Settings settings; @@ -25,17 +24,17 @@ public class ProcessService { @Inject private Messages messages; - @Inject - private ValidationService validationService; - @Inject private PermissionsManager permissionsManager; @Inject private AuthGroupHandler authGroupHandler; + CommonService() { + } + /** - * Retrieve a property's value. + * Retrieves a property's value. * * @param property the property to retrieve * @param the property type @@ -46,16 +45,7 @@ public class ProcessService { } /** - * Return the settings manager. - * - * @return settings manager - */ - public Settings getSettings() { - return settings; - } - - /** - * Send a message to the command sender. + * Sends a message to the command sender. * * @param sender the command sender * @param key the message key @@ -65,7 +55,7 @@ public class ProcessService { } /** - * Send a message to the command sender with the given replacements. + * Sends a message to the command sender with the given replacements. * * @param sender the command sender * @param key the message key @@ -76,7 +66,7 @@ public class ProcessService { } /** - * Retrieve a message. + * Retrieves a message. * * @param key the key of the message * @return the message, split by line @@ -86,7 +76,7 @@ public class ProcessService { } /** - * Retrieve a message as one piece. + * Retrieves a message in one piece. * * @param key the key of the message * @return the message @@ -95,18 +85,24 @@ public class ProcessService { return messages.retrieveSingle(key); } - public boolean validateEmail(String email) { - return validationService.validateEmail(email); - } - - public boolean isEmailFreeForRegistration(String email, CommandSender sender) { - return validationService.isEmailFreeForRegistration(email, sender); - } - + /** + * Checks whether the player has the given permission. + * + * @param player the player + * @param node the permission node to check + * @return true if player has permission, false otherwise + */ public boolean hasPermission(Player player, PermissionNode node) { return permissionsManager.hasPermission(player, node); } + /** + * Sets the permission group of the given player. + * + * @param player the player to process + * @param group the group to add the player to + * @return true on success, false otherwise + */ public boolean setGroup(Player player, AuthGroupType group) { return authGroupHandler.setGroup(player, group); } diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/src/main/java/fr/xephi/authme/service/GeoIpService.java index f1335c7df..22e9af2ca 100644 --- a/src/main/java/fr/xephi/authme/service/GeoIpService.java +++ b/src/main/java/fr/xephi/authme/service/GeoIpService.java @@ -17,6 +17,8 @@ import java.net.URLConnection; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; +import static com.maxmind.geoip.LookupService.GEOIP_MEMORY_CACHE; + public class GeoIpService { private static final String LICENSE = "[LICENSE] This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com"; @@ -57,7 +59,7 @@ public class GeoIpService { boolean dataIsOld = (System.currentTimeMillis() - dataFile.lastModified()) > TimeUnit.DAYS.toMillis(30); if (!dataIsOld) { try { - lookupService = new LookupService(dataFile); + lookupService = new LookupService(dataFile, GEOIP_MEMORY_CACHE); ConsoleLogger.info(LICENSE); return true; } catch (IOException e) { diff --git a/src/main/java/fr/xephi/authme/service/MessageUpdater.java b/src/main/java/fr/xephi/authme/service/MessageUpdater.java index 129a47b81..036ad9fda 100644 --- a/src/main/java/fr/xephi/authme/service/MessageUpdater.java +++ b/src/main/java/fr/xephi/authme/service/MessageUpdater.java @@ -57,7 +57,7 @@ public class MessageUpdater { properties = buildPropertyEntriesForMessageKeys(); settingsManager = new SettingsManager( - new YamlFileResource(userFile), (r, p) -> true, new ConfigurationData((List) properties)); + new YamlFileResource(userFile), null, new ConfigurationData(properties)); } /** @@ -85,7 +85,6 @@ public class MessageUpdater { } } - @SuppressWarnings("unchecked") private void copyMissingMessages() { for (Property property : properties) { String message = userConfiguration.getString(property.getPath()); diff --git a/src/main/java/fr/xephi/authme/service/ValidationService.java b/src/main/java/fr/xephi/authme/service/ValidationService.java index e3a69fc94..c6cdd99a1 100644 --- a/src/main/java/fr/xephi/authme/service/ValidationService.java +++ b/src/main/java/fr/xephi/authme/service/ValidationService.java @@ -40,7 +40,8 @@ public class ValidationService implements Reloadable { private Pattern passwordRegex; private Set unrestrictedNames; - ValidationService() { } + ValidationService() { + } @PostConstruct @Override diff --git a/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java b/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java new file mode 100644 index 000000000..8b1fa7893 --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java @@ -0,0 +1,46 @@ +package fr.xephi.authme.settings; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.properties.StringListProperty; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Property whose value is a set of entries of a given enum. + */ +// TODO #1014: This property type currently extends StringListProperty with a dedicated method to convert the values +// into a Set of the selected enum due to multiple issues on ConfigMe's side. +public class EnumSetProperty> extends StringListProperty { + + private final Class enumClass; + + public EnumSetProperty(Class enumClass, String path, String... values) { + super(path, values); + this.enumClass = enumClass; + } + + /** + * Returns the value as a set of enum entries. + * + * @param settings the settings manager to look up the raw value with + * @return the property's value as mapped enum entries + */ + public Set asEnumSet(SettingsManager settings) { + List entries = settings.getProperty(this); + return entries.stream() + .map(str -> toEnum(str)) + .filter(e -> e != null) + .collect(Collectors.toSet()); + } + + private E toEnum(String str) { + for (E e : enumClass.getEnumConstants()) { + if (str.equalsIgnoreCase(e.name())) { + return e; + } + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index fb4c06246..5bf9d0030 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -68,7 +68,7 @@ public class Settings extends SettingsManager { private void loadSettingsFromFiles() { passwordEmailMessage = readFile("email.html"); recoveryCodeEmailMessage = readFile("recovery_code_email.html"); - welcomeMessage = readFile("welcome.txt").split("\n"); + welcomeMessage = readFile("welcome.txt").split("\\n"); } @Override diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index 30163bf0f..529327704 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -2,15 +2,20 @@ package fr.xephi.authme.settings; import com.github.authme.configme.migration.PlainMigrationService; import com.github.authme.configme.properties.Property; +import com.github.authme.configme.properties.StringListProperty; import com.github.authme.configme.resource.PropertyResource; import com.google.common.base.Objects; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import javax.inject.Inject; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Collections; import java.util.List; import static com.github.authme.configme.properties.PropertyInitializer.newListProperty; @@ -29,7 +34,17 @@ public class SettingsMigrationService extends PlainMigrationService { private final File pluginFolder; - public SettingsMigrationService(File pluginFolder) { + // Stores old commands that need to be migrated to the new commands configuration + // We need to store it in here for retrieval when we build the CommandConfig. Retrieving it from the config.yml is + // not possible since this migration service may trigger config.yml to be resaved. As the old command settings + // don't exist in the code anymore, as soon as config.yml is resaved we lose this information. + private List onLoginCommands = Collections.emptyList(); + private List onLoginConsoleCommands = Collections.emptyList(); + private List onRegisterCommands = Collections.emptyList(); + private List onRegisterConsoleCommands = Collections.emptyList(); + + @Inject + SettingsMigrationService(@DataFolder File pluginFolder) { this.pluginFolder = pluginFolder; } @@ -41,6 +56,8 @@ public class SettingsMigrationService extends PlainMigrationService { changes = true; } + gatherOldCommandSettings(resource); + // Note ljacqu 20160211: Concatenating migration methods with | instead of the usual || // ensures that all migrations will be performed return changes @@ -49,6 +66,7 @@ public class SettingsMigrationService extends PlainMigrationService { | migrateForceSpawnSettings(resource) | changeBooleanSettingToLogLevelProperty(resource) | hasOldHelpHeaderProperty(resource) + | hasSupportOldPasswordProperty(resource) || hasDeprecatedProperties(resource); } @@ -57,7 +75,9 @@ public class SettingsMigrationService extends PlainMigrationService { "Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications", "Passpartu", "Performances", "settings.restrictions.enablePasswordVerifier", "Xenoforo.predefinedSalt", "VeryGames", "settings.restrictions.allowAllCommandsIfRegistrationIsOptional", "DataSource.mySQLWebsite", - "Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping"}; + "Hooks.customAttributes", "Security.stop.kickPlayersBeforeStopping", + "settings.restrictions.keepCollisionsDisabled", "settings.forceCommands", "settings.forceCommandsAsConsole", + "settings.forceRegisterCommands", "settings.forceRegisterCommandsAsConsole"}; for (String deprecatedPath : deprecatedProperties) { if (resource.contains(deprecatedPath)) { return true; @@ -66,6 +86,37 @@ public class SettingsMigrationService extends PlainMigrationService { return false; } + // ---------------- + // Forced commands relocation (from config.yml to commands.yml) + // ---------------- + private void gatherOldCommandSettings(PropertyResource resource) { + onLoginCommands = getStringList(resource, "settings.forceCommands"); + onLoginConsoleCommands = getStringList(resource, "settings.forceCommandsAsConsole"); + onRegisterCommands = getStringList(resource, "settings.forceRegisterCommands"); + onRegisterConsoleCommands = getStringList(resource, "settings.forceRegisterCommandsAsConsole"); + } + + private List getStringList(PropertyResource resource, String path) { + List entries = new StringListProperty(path).getFromResource(resource); + return entries == null ? Collections.emptyList() : entries; + } + + public List getOnLoginCommands() { + return onLoginCommands; + } + + public List getOnLoginConsoleCommands() { + return onLoginConsoleCommands; + } + + public List getOnRegisterCommands() { + return onRegisterCommands; + } + + public List getOnRegisterConsoleCommands() { + return onRegisterConsoleCommands; + } + // -------- // Specific migrations // -------- @@ -118,7 +169,7 @@ public class SettingsMigrationService extends PlainMigrationService { } /** - * Detect old "force spawn loc on join" and "force spawn on these worlds" settings and moves them + * Detects old "force spawn loc on join" and "force spawn on these worlds" settings and moves them * to the new paths. * * @param resource The property resource @@ -163,6 +214,16 @@ public class SettingsMigrationService extends PlainMigrationService { return false; } + private static boolean hasSupportOldPasswordProperty(PropertyResource resource) { + String path = "settings.security.supportOldPasswordHash"; + if (resource.contains(path)) { + ConsoleLogger.warning("Property '" + path + "' is no longer supported. " + + "Use '" + SecuritySettings.LEGACY_HASHES.getPath() + "' instead."); + return true; + } + return false; + } + /** * Checks for an old property path and moves it to a new path if present. * diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java index b3a06f5eb..ac1742fa6 100644 --- a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java +++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -48,10 +48,10 @@ public class SpawnLoader implements Reloadable { @Inject SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService, DataSource dataSource) { - File spawnFile = new File(pluginFolder, "spawn.yml"); // TODO ljacqu 20160312: Check if resource could be copied and handle the case if not + File spawnFile = new File(pluginFolder, "spawn.yml"); FileUtils.copyFileFromResource(spawnFile, "spawn.yml"); - this.authMeConfigurationFile = new File(pluginFolder, "spawn.yml"); + this.authMeConfigurationFile = spawnFile; this.settings = settings; this.pluginHookService = pluginHookService; reload(); diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java b/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java new file mode 100644 index 000000000..3828dc0ee --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java @@ -0,0 +1,50 @@ +package fr.xephi.authme.settings.commandconfig; + +/** + * Command to be run. + */ +public class Command { + + /** The command to execute. */ + private String command; + /** The executor of the command. */ + private Executor executor = Executor.PLAYER; + + /** + * Default constructor (for bean mapping). + */ + public Command() { + } + + /** + * Constructor. + * + * @param command the command + * @param executor the executor of the command + */ + public Command(String command, Executor executor) { + this.command = command; + this.executor = executor; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public Executor getExecutor() { + return executor; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + + @Override + public String toString() { + return command + " (" + executor + ")"; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java new file mode 100644 index 000000000..19c05505f --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java @@ -0,0 +1,40 @@ +package fr.xephi.authme.settings.commandconfig; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Command configuration. + * + * @see CommandManager + */ +public class CommandConfig { + + private Map onJoin = new LinkedHashMap<>(); + private Map onLogin = new LinkedHashMap<>(); + private Map onRegister = new LinkedHashMap<>(); + + public Map getOnJoin() { + return onJoin; + } + + public void setOnJoin(Map onJoin) { + this.onJoin = onJoin; + } + + public Map getOnLogin() { + return onLogin; + } + + public void setOnLogin(Map onLogin) { + this.onLogin = onLogin; + } + + public Map getOnRegister() { + return onRegister; + } + + public void setOnRegister(Map onRegister) { + this.onRegister = onRegister; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java new file mode 100644 index 000000000..f21f7e5cb --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java @@ -0,0 +1,84 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.util.FileUtils; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import java.io.File; +import java.util.Map; + +/** + * Manages configurable commands to be run when various events occur. + */ +public class CommandManager implements Reloadable { + + private final File dataFolder; + private final BukkitService bukkitService; + private final CommandMigrationService commandMigrationService; + + private CommandConfig commandConfig; + + @Inject + CommandManager(@DataFolder File dataFolder, BukkitService bukkitService, + CommandMigrationService commandMigrationService) { + this.dataFolder = dataFolder; + this.bukkitService = bukkitService; + this.commandMigrationService = commandMigrationService; + reload(); + } + + /** + * Runs the configured commands for when a player has joined. + * + * @param player the joining player + */ + public void runCommandsOnJoin(Player player) { + executeCommands(player, commandConfig.getOnJoin()); + } + + /** + * Runs the configured commands for when a player has successfully registered. + * + * @param player the player who has registered + */ + public void runCommandsOnRegister(Player player) { + executeCommands(player, commandConfig.getOnRegister()); + } + + /** + * Runs the configured commands for when a player has logged in successfully. + * + * @param player the player that logged in + */ + public void runCommandsOnLogin(Player player) { + executeCommands(player, commandConfig.getOnLogin()); + } + + private void executeCommands(Player player, Map commands) { + for (Command command : commands.values()) { + final String execution = command.getCommand().replace("%p", player.getName()); + if (Executor.CONSOLE.equals(command.getExecutor())) { + bukkitService.dispatchConsoleCommand(execution); + } else { + bukkitService.dispatchCommand(player, execution); + } + } + } + + @Override + public void reload() { + File file = new File(dataFolder, "commands.yml"); + FileUtils.copyFileFromResource(file, "commands.yml"); + + SettingsManager settingsManager = new SettingsManager( + new YamlFileResource(file), commandMigrationService, CommandSettingsHolder.class); + commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); + } + + +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java new file mode 100644 index 000000000..6b357615f --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java @@ -0,0 +1,122 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.github.authme.configme.migration.MigrationService; +import com.github.authme.configme.properties.Property; +import com.github.authme.configme.resource.PropertyResource; +import com.google.common.annotations.VisibleForTesting; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.settings.SettingsMigrationService; +import fr.xephi.authme.util.RandomStringUtils; + +import javax.inject.Inject; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Migrates the commands from their old location, in config.yml, to the dedicated commands configuration file. + */ +class CommandMigrationService implements MigrationService { + + @Inject + private SettingsMigrationService settingsMigrationService; + + CommandMigrationService() { + } + + @Override + public boolean checkAndMigrate(PropertyResource resource, List> properties) { + final CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getValue(resource); + final boolean didMoveCommands = transformOldCommands(commandConfig); + + if (didMoveCommands) { // TODO ConfigMe/#29: Check that loaded file isn't completely empty + resource.setValue("", commandConfig); + return true; + } + return false; + } + + /** + * Adds command settings from their old location (in config.yml) to the given command configuration object. + * + * @param commandConfig the command config object to move old commands to + * @return true if commands have been moved, false if no migration was necessary + */ + @VisibleForTesting + boolean transformOldCommands(CommandConfig commandConfig) { + boolean didMoveCommands = false; + for (MigratableCommandSection section : MigratableCommandSection.values()) { + didMoveCommands |= section.convertCommands(settingsMigrationService, commandConfig); + } + return didMoveCommands; + } + + /** + * Enum defining the forced command settings that should be moved from config.yml to the new commands.yml file. + */ + private enum MigratableCommandSection { + + ON_JOIN( + SettingsMigrationService::getOnLoginCommands, + Executor.PLAYER, + CommandConfig::getOnLogin), + + ON_JOIN_CONSOLE( + SettingsMigrationService::getOnLoginConsoleCommands, + Executor.CONSOLE, + CommandConfig::getOnLogin), + + ON_REGISTER( + SettingsMigrationService::getOnRegisterCommands, + Executor.PLAYER, + CommandConfig::getOnRegister), + + ON_REGISTER_CONSOLE( + SettingsMigrationService::getOnRegisterConsoleCommands, + Executor.CONSOLE, + CommandConfig::getOnRegister); + + private final Function> legacyCommandsGetter; + private final Executor executor; + private final Function> commandMapGetter; + + /** + * Constructor. + * + * @param legacyCommandsGetter getter on MigrationService to get the deprecated command entries + * @param executor the executor of the commands + * @param commandMapGetter the getter for the commands map in the new settings structure to add the old + * settings to after conversion + */ + MigratableCommandSection(Function> legacyCommandsGetter, + Executor executor, + Function> commandMapGetter) { + this.legacyCommandsGetter = legacyCommandsGetter; + this.executor = executor; + this.commandMapGetter = commandMapGetter; + } + + /** + * Adds the commands from the sections' settings migration service to the appropriate place in the new + * command config object. + * + * @param settingsMigrationService settings migration service to read old commands from + * @param commandConfig command config object to add converted commands to + * @return true if there were commands to migrate, false otherwise + */ + boolean convertCommands(SettingsMigrationService settingsMigrationService, CommandConfig commandConfig) { + List commands = legacyCommandsGetter.apply(settingsMigrationService).stream() + .map(cmd -> new Command(cmd, executor)).collect(Collectors.toList()); + + if (commands.isEmpty()) { + return false; + } + Map commandMap = commandMapGetter.apply(commandConfig); + commands.forEach(cmd -> commandMap.put(RandomStringUtils.generate(10), cmd)); + ConsoleLogger.info("Moving " + commands.size() + " commands of type " + this + + " from config.yml to commands.yml"); + return true; + } + } +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java new file mode 100644 index 000000000..99a1c6e8c --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java @@ -0,0 +1,54 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.github.authme.configme.SectionComments; +import com.github.authme.configme.SettingsHolder; +import com.github.authme.configme.beanmapper.BeanProperty; +import com.github.authme.configme.properties.Property; + +import java.util.HashMap; +import java.util.Map; + +/** + * Settings holder class for the commands.yml settings. + */ +public final class CommandSettingsHolder implements SettingsHolder { + + public static final Property COMMANDS = + new BeanProperty<>(CommandConfig.class, "", new CommandConfig()); + + + private CommandSettingsHolder() { + } + + @SectionComments + public static Map sectionComments() { + String[] comments = { + "This configuration file allows you to execute commands on various events.", + "%p in commands will be replaced with the player name.", + "For example, if you want to send a welcome message to a player who just registered:", + "onRegister:", + " welcome:", + " command: 'msg %p Welcome to the server!'", + " as: CONSOLE", + "", + "This will make the console execute the msg command to the player.", + "Each command under an event has a name you can choose freely (e.g. 'welcome' as above),", + "after which a mandatory 'command' field defines the command to run,", + "and 'as' defines who will run the command (either PLAYER or CONSOLE). Longer example:", + "onLogin:", + " welcome:", + " command: 'msg %p Welcome back!'", + " as: PLAYER", + " broadcast:", + " command: 'broadcast %p has joined, welcome back!'", + " as: CONSOLE", + "", + "Supported command events: onLogin, onJoin, onRegister" + }; + Map commentMap = new HashMap<>(); + // TODO ConfigMe/#25 cannot set comments on the root ("") + commentMap.put("onLogin", comments); + return commentMap; + } + +} diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java b/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java new file mode 100644 index 000000000..c7043de2d --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.settings.commandconfig; + +/** + * The executor of the command. + */ +public enum Executor { + + /** The player of the event. */ + PLAYER, + + /** The console user. */ + CONSOLE + +} diff --git a/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java b/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java index 80bd02cfd..88b896f6f 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java +++ b/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java @@ -6,8 +6,7 @@ import com.github.authme.configme.knownproperties.ConfigurationDataBuilder; import com.github.authme.configme.properties.Property; /** - * Utility class responsible for retrieving all {@link Property} fields - * from {@link SettingsHolder} implementations via reflection. + * Utility class responsible for retrieving all {@link Property} fields from {@link SettingsHolder} classes. */ public final class AuthMeSettingsRetriever { diff --git a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java index 77b1767ed..68d2ad73d 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java @@ -25,7 +25,7 @@ public class HooksSettings implements SettingsHolder { @Comment("Do we need to disable Essentials SocialSpy on join?") public static final Property DISABLE_SOCIAL_SPY = - newProperty("Hooks.disableSocialSpy", true); + newProperty("Hooks.disableSocialSpy", false); @Comment("Do we need to force /motd Essentials command on join?") public static final Property USE_ESSENTIALS_MOTD = diff --git a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java index bfb565337..91d71d7ff 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java @@ -36,7 +36,10 @@ public class PluginSettings implements SettingsHolder { public static final Property SESSIONS_EXPIRE_ON_IP_CHANGE = newProperty("settings.sessions.sessionExpireOnIpChange", true); - @Comment("Message language, available: en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt") + @Comment({ + "Message language, available languages:", + "https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md" + }) public static final Property MESSAGES_LANGUAGE = newProperty("settings.messagesLanguage", "en"); @@ -49,13 +52,6 @@ public class PluginSettings implements SettingsHolder { public static final Property ENABLE_PERMISSION_CHECK = newProperty("permission.EnablePermissionCheck", false); - @Comment({ - "Keeps collisions disabled for logged players", - "Works only with MC 1.9" - }) - public static final Property KEEP_COLLISIONS_DISABLED = - newProperty("settings.restrictions.keepCollisionsDisabled", false); - @Comment({ "Log level: INFO, FINE, DEBUG. Use INFO for general messages,", "FINE for some additional detailed ones (like password failed),", @@ -65,8 +61,8 @@ public class PluginSettings implements SettingsHolder { newProperty(LogLevel.class, "settings.logLevel", LogLevel.FINE); @Comment({ - "By default we schedule async tasks when talking to the database", - "If you want typical communication with the database to happen synchronously, set this to false" + "By default we schedule async tasks when talking to the database. If you want", + "typical communication with the database to happen synchronously, set this to false" }) public static final Property USE_ASYNC_TASKS = newProperty("settings.useAsyncTasks", true); diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index cdd760bd2..b725172f0 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -20,13 +20,16 @@ public class ProtectionSettings implements SettingsHolder { public static final Property ENABLE_PROTECTION_REGISTERED = newProperty("Protection.enableProtectionRegistered", true); - @Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes", - "PLEASE USE QUOTES!"}) + @Comment({ + "Countries allowed to join the server and register. For country codes, see", + "http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/", + "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_WHITELIST = newListProperty("Protection.countries", "US", "GB"); - @Comment({"Countries not allowed to join the server and register", - "PLEASE USE QUOTES!"}) + @Comment({ + "Countries not allowed to join the server and register", + "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_BLACKLIST = newListProperty("Protection.countriesBlacklist", "A1"); @@ -34,7 +37,13 @@ public class ProtectionSettings implements SettingsHolder { public static final Property ENABLE_ANTIBOT = newProperty("Protection.enableAntiBot", true); - @Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically") + @Comment("The interval in seconds") + public static final Property ANTIBOT_INTERVAL = + newProperty("Protection.antiBotInterval", 5); + + @Comment({ + "Max number of players allowed to login in the interval", + "before the AntiBot system is enabled automatically"}) public static final Property ANTIBOT_SENSIBILITY = newProperty("Protection.antiBotSensibility", 10); diff --git a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java index f23a3eb12..b3733b5ac 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java @@ -12,7 +12,7 @@ public class PurgeSettings implements SettingsHolder { public static final Property USE_AUTO_PURGE = newProperty("Purge.useAutoPurge", false); - @Comment("Number of Days an account become Unused") + @Comment("Number of days after which an account should be purged") public static final Property DAYS_BEFORE_REMOVE_PLAYER = newProperty("Purge.daysBeforeRemovePlayer", 60); @@ -28,7 +28,7 @@ public class PurgeSettings implements SettingsHolder { public static final Property DEFAULT_WORLD = newProperty("Purge.defaultWorld", "world"); - @Comment("Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?") + @Comment("Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?") public static final Property REMOVE_LIMITED_CREATIVE_INVENTORIES = newProperty("Purge.removeLimitedCreativesInventories", false); diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index b593869a3..a3de56b82 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -4,9 +4,6 @@ import com.github.authme.configme.Comment; import com.github.authme.configme.SettingsHolder; import com.github.authme.configme.properties.Property; -import java.util.List; - -import static com.github.authme.configme.properties.PropertyInitializer.newListProperty; import static com.github.authme.configme.properties.PropertyInitializer.newProperty; public class RegistrationSettings implements SettingsHolder { @@ -48,35 +45,19 @@ public class RegistrationSettings implements SettingsHolder { public static final Property FORCE_LOGIN_AFTER_REGISTER = newProperty("settings.registration.forceLoginAfterRegister", false); - @Comment("Force these commands after /login, without any '/', use %p to replace with player name") - public static final Property> FORCE_COMMANDS = - newListProperty("settings.forceCommands"); - - @Comment("Force these commands after /login as service console, without any '/'. " - + "Use %p to replace with player name") - public static final Property> FORCE_COMMANDS_AS_CONSOLE = - newListProperty("settings.forceCommandsAsConsole"); - - @Comment("Force these commands after /register, without any '/', use %p to replace with player name") - public static final Property> FORCE_REGISTER_COMMANDS = - newListProperty("settings.forceRegisterCommands"); - - @Comment("Force these commands after /register as a server console, without any '/'. " - + "Use %p to replace with player name") - public static final Property> FORCE_REGISTER_COMMANDS_AS_CONSOLE = - newListProperty("settings.forceRegisterCommandsAsConsole"); - @Comment({ "Enable to display the welcome message (welcome.txt) after a login", "You can use colors in this welcome.txt + some replaced strings:", - "{PLAYER}: player name, {ONLINE}: display number of online players, {MAXPLAYERS}: display server slots,", - "{IP}: player ip, {LOGINS}: number of players logged, {WORLD}: player current world, {SERVER}: server name", + "{PLAYER}: player name, {ONLINE}: display number of online players,", + "{MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged,", + "{WORLD}: player current world, {SERVER}: server name", "{VERSION}: get current bukkit version, {COUNTRY}: player country"}) public static final Property USE_WELCOME_MESSAGE = newProperty("settings.useWelcomeMessage", true); - @Comment("Do we need to broadcast the welcome message to all server or only to the player? set true for " - + "server or false for player") + @Comment({ + "Broadcast the welcome message to the server or only to the player?", + "set true for server or false for player"}) public static final Property BROADCAST_WELCOME_MESSAGE = newProperty("settings.broadcastWelcomeMessage", false); @@ -104,7 +85,7 @@ public class RegistrationSettings implements SettingsHolder { "Do we need to prevent people to login with another case?", "If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI"}) public static final Property PREVENT_OTHER_CASE = - newProperty("settings.preventOtherCase", false); + newProperty("settings.preventOtherCase", true); private RegistrationSettings() { diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 9e16714dd..c62b93e3b 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -36,7 +36,7 @@ public class RestrictionSettings implements SettingsHolder { @Comment("Minimum allowed username length") public static final Property MIN_NICKNAME_LENGTH = - newProperty("settings.restrictions.minNicknameLength", 4); + newProperty("settings.restrictions.minNicknameLength", 3); @Comment("Maximum allowed username length") public static final Property MAX_NICKNAME_LENGTH = @@ -50,9 +50,9 @@ public class RestrictionSettings implements SettingsHolder { newProperty("settings.restrictions.ForceSingleSession", true); @Comment({ - "If enabled, every player that spawn in one of the world listed in \"ForceSpawnLocOnJoin.worlds\"", - "will be teleported to the spawnpoint after successful authentication.", - "The quit location of the player will be overwritten.", + "If enabled, every player that spawn in one of the world listed in", + "\"ForceSpawnLocOnJoin.worlds\" will be teleported to the spawnpoint after successful", + "authentication. The quit location of the player will be overwritten.", "This is different from \"teleportUnAuthedToSpawn\" that teleport player", "to the spawnpoint on join."}) public static final Property FORCE_SPAWN_LOCATION_AFTER_LOGIN = @@ -77,7 +77,7 @@ public class RestrictionSettings implements SettingsHolder { @Comment({ "The restricted user feature will kick players listed below", - "if they don't match the defined IP address.", + "if they don't match the defined IP address. Names are case-insensitive.", "Example:", " AllowedRestrictedUser:", " - playername;127.0.0.1"}) @@ -90,7 +90,7 @@ public class RestrictionSettings implements SettingsHolder { @Comment("Should players be kicked on wrong password?") public static final Property KICK_ON_WRONG_PASSWORD = - newProperty("settings.restrictions.kickOnWrongPassword", false); + newProperty("settings.restrictions.kickOnWrongPassword", true); @Comment({ "Should not logged in players be teleported to the spawn?", @@ -139,7 +139,7 @@ public class RestrictionSettings implements SettingsHolder { @Comment("Should we deny the tabcomplete feature before logging in? Requires ProtocolLib.") public static final Property DENY_TABCOMPLETE_BEFORE_LOGIN = - newProperty("settings.restrictions.DenyTabCompleteBeforeLogin", true); + newProperty("settings.restrictions.DenyTabCompleteBeforeLogin", false); @Comment({ "Should we display all other accounts from a player when he joins?", @@ -176,17 +176,28 @@ public class RestrictionSettings implements SettingsHolder { newProperty("settings.GameMode.ForceSurvivalMode", false); @Comment({ - "Below you can list all account names that", - "AuthMe will ignore for registration or login, configure it", - "at your own risk!! Remember that if you are going to add", - "nickname with [], you have to delimit name with ' '.", - "this option add compatibility with BuildCraft and some", - "other mods.", - "It is case-sensitive!" + "Below you can list all account names that AuthMe will ignore", + "for registration or login. Configure it at your own risk!!", + "This option adds compatibility with BuildCraft and some other mods.", + "It is case-insensitive! Example:", + "UnrestrictedName:", + "- 'npcPlayer'", + "- 'npcPlayer2'" }) public static final Property> UNRESTRICTED_NAMES = newLowercaseListProperty("settings.unrestrictions.UnrestrictedName"); + @Comment("Threshold of the other accounts command, a value less than 2 means disabled.") + public static final Property OTHER_ACCOUNTS_CMD_THRESHOLD = + newProperty("settings.restrictions.otherAccountsCmdThreshold", 0); + + @Comment({ + "Command to run when a user has more accounts than the configured threshold.", + "Available variables: %playername%, %playerip%" + }) + public static final Property OTHER_ACCOUNTS_CMD = + newProperty("settings.restrictions.otherAccountsCmd", + "say The player %playername% with ip %playerip% has multiple accounts!"); private RestrictionSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 7d91f8183..5bad44b07 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -4,6 +4,7 @@ import com.github.authme.configme.Comment; import com.github.authme.configme.SettingsHolder; import com.github.authme.configme.properties.Property; import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.settings.EnumSetProperty; import java.util.List; @@ -18,10 +19,6 @@ public class SecuritySettings implements SettingsHolder { public static final Property STOP_SERVER_ON_PROBLEM = newProperty("Security.SQLProblem.stopServer", true); - @Comment("/reload support") - public static final Property USE_RELOAD_COMMAND_SUPPORT = - newProperty("Security.ReloadCommand.useReloadCommandSupport", true); - @Comment("Remove passwords from console?") public static final Property REMOVE_PASSWORD_FROM_CONSOLE = newProperty("Security.console.removePassword", true); @@ -67,9 +64,10 @@ public class SecuritySettings implements SettingsHolder { newProperty("settings.security.unLoggedinGroup", "unLoggedinGroup"); @Comment({ - "Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB,", - "MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,", - "DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only)" + "Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512, WHIRLPOOL,", + "MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,", + "PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only). See full list at", + "https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md" }) public static final Property PASSWORD_HASH = newProperty(HashAlgorithm.class, "settings.security.passwordHash", HashAlgorithm.SHA256); @@ -78,18 +76,28 @@ public class SecuritySettings implements SettingsHolder { public static final Property DOUBLE_MD5_SALT_LENGTH = newProperty("settings.security.doubleMD5SaltLength", 8); - @Comment({"If password checking return false, do we need to check with all", - "other password algorithm to check an old password?", - "AuthMe will update the password to the new password hash"}) - public static final Property SUPPORT_OLD_PASSWORD_HASH = - newProperty("settings.security.supportOldPasswordHash", false); + @Comment({ + "If a password check fails, AuthMe will also try to check with the following hash methods.", + "Use this setting when you change from one hash method to another.", + "AuthMe will update the password to the new hash. Example:", + "legacyHashes:", + "- 'SHA1'" + }) + public static final Property> LEGACY_HASHES = + new EnumSetProperty<>(HashAlgorithm.class, "settings.security.legacyHashes"); + + @Comment("Number of rounds to use if passwordHash is set to PBKDF2. Default is 10000") + public static final Property PBKDF2_NUMBER_OF_ROUNDS = + newProperty("settings.security.pbkdf2Rounds", 10000); @Comment({"Prevent unsafe passwords from being used; put them in lowercase!", + "You should always set 'help' as unsafePassword due to possible conflicts.", "unsafePasswords:", "- '123456'", - "- 'password'"}) + "- 'password'", + "- 'help'"}) public static final Property> UNSAFE_PASSWORDS = - newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789"); + newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789", "help"); @Comment("Tempban a user's IP address if they enter the wrong password too many times") public static final Property TEMPBAN_ON_MAX_LOGINS = diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java index a69c5fee5..92391af90 100644 --- a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java +++ b/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java @@ -72,7 +72,7 @@ class PurgeTask extends BukkitRunnable { } OfflinePlayer offlinePlayer = offlinePlayers[nextPosition]; - if (toPurge.remove(offlinePlayer.getName().toLowerCase())) { + if (offlinePlayer.getName() != null && toPurge.remove(offlinePlayer.getName().toLowerCase())) { if (!permissionsManager.hasPermissionOffline(offlinePlayer, PlayerStatePermission.BYPASS_PURGE)) { playerPortion.add(offlinePlayer); namePortion.add(offlinePlayer.getName()); @@ -81,7 +81,7 @@ class PurgeTask extends BukkitRunnable { } if (!toPurge.isEmpty() && playerPortion.isEmpty()) { - ConsoleLogger.info("Finished lookup up offlinePlayers. Begin looking purging player names only"); + ConsoleLogger.info("Finished lookup of offlinePlayers. Begin looking purging player names only"); //we went through all offlineplayers but there are still names remaining for (String name : toPurge) { diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index 86c8cae46..c9edcf735 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -98,6 +98,22 @@ public final class FileUtils { } } + /** + * Creates the given file or throws an exception. + * + * @param file the file to create + */ + public static void create(File file) { + try { + boolean result = file.createNewFile(); + if (!result) { + throw new IllegalStateException("Could not create file '" + file + "'"); + } + } catch (IOException e) { + throw new IllegalStateException("Error while creating file '" + file + "'", e); + } + } + /** * Construct a file path from the given elements, i.e. separate the given elements by the file separator. * diff --git a/src/main/java/fr/xephi/authme/util/RuntimeUtils.java b/src/main/java/fr/xephi/authme/util/RuntimeUtils.java deleted file mode 100644 index 6af0c4576..000000000 --- a/src/main/java/fr/xephi/authme/util/RuntimeUtils.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.xephi.authme.util; - -/** - * Runtime utilities. - */ -public class RuntimeUtils { - - // Utility class - private RuntimeUtils() { - } - - /** - * Return the available core count of the JVM. - * - * @return the core count - */ - public static int getCoreCount() { - return Runtime.getRuntime().availableProcessors(); - } -} diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 1136854d9..39419689e 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -48,4 +48,13 @@ public final class Utils { return false; } } + + /** + * Return the available core count of the JVM. + * + * @return the core count + */ + public static int getCoreCount() { + return Runtime.getRuntime().availableProcessors(); + } } diff --git a/src/main/resources/commands.yml b/src/main/resources/commands.yml new file mode 100644 index 000000000..c3741a293 --- /dev/null +++ b/src/main/resources/commands.yml @@ -0,0 +1,26 @@ + +# This configuration file allows you to execute commands on various events. +# %p in commands will be replaced with the player name. +# For example, if you want to send a welcome message to a player who just registered: +# onRegister: +# welcome: +# command: 'msg %p Welcome to the server!' +# as: CONSOLE +# +# This will make the console execute the msg command to the player. +# Each command under an event has a name you can choose freely (e.g. 'welcome' as above), +# after which a mandatory 'command' field defines the command to run, +# and 'as' defines who will run the command (either PLAYER or CONSOLE). Longer example: +# onLogin: +# welcome: +# command: 'msg %p Welcome back!' +# as: PLAYER +# broadcast: +# command: 'broadcast %p has joined, welcome back!' +# as: CONSOLE +# +# Supported command events: onLogin, onJoin, onRegister +onLogin: + welcome: + command: 'msg %p Welcome back!' + executor: 'PLAYER' diff --git a/src/main/resources/messages/help_br.yml b/src/main/resources/messages/help_br.yml new file mode 100644 index 000000000..90fc6cc04 --- /dev/null +++ b/src/main/resources/messages/help_br.yml @@ -0,0 +1,44 @@ +# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called + +# ------------------------------------------------------- +# List of texts used in the help section +common: + optional: 'Opcional' + hasPermission: 'Você tem permissão' + noPermission: 'Sem Permissão' + default: 'Default' + result: 'Resultado' + defaultPermissions: + notAllowed: 'Sem Permissão' + opOnly: 'OP''s only' + allowed: 'Todos podem' + +# ------------------------------------------------------- +# Titles of the individual help sections +# Set the translation text to empty text to disable the section, e.g. to hide alternatives: +# alternatives: '' +section: + command: 'Comando' + description: 'Pequena descrição' + detailedDescription: 'Descrição detalhada' + arguments: 'Argumentos' + permissions: 'Permissões' + alternatives: 'Alternativas' + children: 'Comandos' + +# ------------------------------------------------------- +# You can translate the data for all commands using the below pattern. +# For example to translate /authme reload, create a section "authme.reload", or "login" for /login +# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth +# Translations don't need to be complete; any missing section will be taken from the default silently +# Important: Put main commands like "authme" before their children (e.g. "authme.reload") +commands: + authme.register: + description: 'Registra um jogador' + detailedDescription: 'Registra um esprecifico jogador com uma senha especifica.' + arg1: + label: 'player' + description: 'Nome do player' + arg2: + label: 'password' + description: 'Senha' diff --git a/src/main/resources/messages/help_pl.yml b/src/main/resources/messages/help_pl.yml new file mode 100644 index 000000000..a5f59a2ea --- /dev/null +++ b/src/main/resources/messages/help_pl.yml @@ -0,0 +1,45 @@ +# Tlumaczenie configu dla AuthMe pomoc, kiedy wpiszesz /authme help lub /authme help register podana wiadomosc zostanie wyslana + +# ------------------------------------------------------- +# Lista tekstu uzyta w pomocy. +common: + header: '==========[ AuthMeReloaded - Pomoc ]==========' + optional: 'Opcjonalnie' + hasPermission: 'Nie posidasz permisji' + noPermission: 'Brak permisji' + default: 'Domyslnie' + result: 'Wynik' + defaultPermissions: + notAllowed: 'Nie posiadasz permisji' + opOnly: 'Tylko OP' + allowed: 'Dozwolone dla wszyskich' + +# ------------------------------------------------------- +# Tytuly z inwidualnych stref w pomoc. +# Zostaw tlumaczenie puste aby wylaczyc dana komende. Np.: +# alternatives: '' +section: + command: 'Komenda' + description: 'Krotki opis' + detailedDescription: 'Dlugi opis' + arguments: 'Argumenty' + permissions: 'Permisje' + alternatives: 'Alternatywy' + children: 'Komendy' + +# ------------------------------------------------------- +# Mozesz przetlumaczyc wszystkie komendy uzywajac tego wzoru. +# Na przyklad jesli chcesz przetlumaczyc /authme reload, utworz selekcje "authme.reload", lub "login" dla /login +# Jesli komenda posiada argumenty, mozesz uzyc arg1 aby przetlumaczyc pierwszy argument, i nastepne +# Tlumaczenia nie musza byc kompletne; kazde braki beda uzupelniane domyslnymi wiadomosciami z pluginu. +# Uwaga: Postaw glowna klase (np. "authme") przed ich dziecmi (np. "authme.reload") +commands: + authme.register: + description: 'Rejestracja gracza' + detailedDescription: 'Rerejestracja gracza z okreslonym haslem' + arg1: + label: 'player' + description: 'Nazwa gracza' + arg2: + label: 'password' + description: 'Haslo' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 8002a7a1a..8181658cd 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -61,6 +61,8 @@ email_confirm: '&cPor favor confirme seu endereço de email!' email_changed: '&2Troca de email com sucesso.!' email_send: '&2Recuperação de email enviada com sucesso! Por favor, verifique sua caixa de entrada de e-mail!' email_exists: '&cUm e-mail de recuperação já foi enviado! Você pode descartá-lo e enviar um novo usando o comando abaixo:' +email_show: '&2O seu endereço de e-mail atual é: &f%email' +show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' country_banned: '&4O seu país está banido neste servidor!' antibot_auto_enabled: '&4[AntiBotService] habilitado devido ao enorme número de conexões!' antibot_auto_disabled: '&2[AntiBotService] AntiBot desativada após %m minutos!' @@ -75,5 +77,3 @@ kicked_admin_registered: 'Um administrador registrou você; por favor faça logi incomplete_email_settings: 'Erro: Nem todas as configurações necessárias estão definidas para o envio de e-mails. Entre em contato com um administrador.' recovery_code_sent: 'Um código de recuperação para redefinir sua senha foi enviada para o seu e-mail.' recovery_code_incorrect: 'O código de recuperação esta incorreto! Use /email recovery [email] para gerar um novo!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 0463d17ca..d04e38cd1 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -72,5 +72,6 @@ accounts_owned_self: 'You own %count accounts:' accounts_owned_other: 'The player %name has %count accounts:' kicked_admin_registered: 'An admin just registered you; please log in again' incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' +email_send_failure: 'The email could not be sent. Please contact an administrator.' recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 8e0014789..d72c7d44b 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -71,8 +71,8 @@ kick_antibot: '¡El modo de protección AntiBot está habilitado! Tienes que esp email_exists: '&c¡El correo de recuperación ya ha sido enviado! Puedes descartarlo y enviar uno nuevo utilizando el siguiente comando:' password_error_nick: '&cNo puedes utilizar tu nombre como contraseña, por favor elige otra...' accounts_owned_other: 'El jugador %name tiene %count cuentas:' -incomplete_email_settings: 'Error: no todos los ajustes necesario se han configurado para enviar correos. Por favor, contacta con un administrador.' +incomplete_email_settings: 'Error: no todos los ajustes necesarios se han configurado para enviar correos. Por favor, contacta con un administrador.' recovery_code_sent: 'El código de recuperación para recuperar tu contraseña se ha enviado a tu correo.' invalid_name_case: 'Solo puedes unirte mediante el nombre de usuario %valid, no %invalid.' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file +email_show: '&2Tu dirección de E-Mail actual es: &f%email' +show_no_email: '&2No tienes ningun E-Mail asociado en esta cuenta.' diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 88142bbce..daabe5734 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -72,5 +72,5 @@ kicked_admin_registered: 'Adminisztrátor által regisztrálva lettél; kérlek accounts_owned_self: '%count db regisztrációd van:' recovery_code_incorrect: 'A visszaállító kód helytelen volt! Használd a következő parancsot: /email recovery [email címed] egy új generálásához' recovery_code_sent: 'A jelszavad visszaállításához szükséges kódot sikeresen kiküldtük az email címedre!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file +email_show: '&2A jelenlegi email-ed a következő: &f%email' +show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 777ced5ff..cd3c55ac0 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -69,9 +69,9 @@ antibot_auto_enabled: '&4[AntiBotService] AntyBot wlaczony z powodu duzej liczby accounts_owned_self: 'Posiadasz %count kont:' two_factor_create: '&2Twoj sekretny kod to %code. Mozesz zeskanowac go tutaj %url' not_owner_error: 'Nie jestes wlascicielem tego konta, wybierz inny nick!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -# TODO kicked_admin_registered: 'An admin just registered you; please log in again' -# TODO incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' \ No newline at end of file +email_show: '&2Twoj aktualny adres email to: &f%email' +show_no_email: '&2Nie posiadasz adresu email przypisanego do tego konta.' +kicked_admin_registered: 'Administrator zarejestrowal Ciebie, mozesz sie zalogowac.' +incomplete_email_settings: 'Error: Nie wszystkie opcje odpowiedzialne za wysylanie emaili zostaly ustawione. Skontaktuj sie z administracja.' +recovery_code_sent: 'Kod odzyskiwania hasla zostal wyslany na adres email przypisany do konta.' +recovery_code_incorrect: 'Kod odzyskiwania hasla jest bledny! Uzyj /email recovery [email] aby wygenerowac nowy.' diff --git a/src/main/resources/messages/messages_ro.yml b/src/main/resources/messages/messages_ro.yml new file mode 100644 index 000000000..c872fe287 --- /dev/null +++ b/src/main/resources/messages/messages_ro.yml @@ -0,0 +1,76 @@ +kick_antibot: 'Protectia AntiBot-ului este activata! Trebuie sa astepti cateva minute pentru a intra pe server.' +unknown_user: '&cNu putem gasi jucatorul in baza de date!' +unsafe_spawn: '&cLocatia in care esti acum nu este sigura, ai fost teleportat la spawn.' +not_logged_in: '&cNu te-ai logat!' +reg_voluntarily: 'Te poti inregista pe server folosind comanda "/register "' +usage_log: '&cFoloseste: /login pentru a te loga.' +wrong_pwd: '&cParola gresita!' +unregistered: '&cTe-ai unregistart cu succes!' +reg_disabled: '&cInegistrarea in joc nu este activata!' +valid_session: '&2Te-ai logat cu reconectarea sesiuni.' +login: '&2Te-ai logat cu succes!' +vb_nonActiv: '&cContul tau nu este activat, te rugam verifica-ti email-ul!' +user_regged: '&cCineva este inregistrat cu acest nume!' +no_perm: '&4Nu ai permisiunea!' +error: '&4A aparut o eroare, te rugam contacteaza un membru din staff!' +login_msg: '&cTe rugam sa te autentifici folosind comanda "/login "' +reg_msg: '&3Te rugam sa te inregistrezi folosind comanda "/register "' +reg_email_msg: '&3Te rugam sa te inregistrezi folosind comanda "/register "' +max_reg: '&cTe-ai inregistrat cu prea multe counturi (%reg_count/%max_acc %reg_names) pentru conexiunea ta!' +usage_reg: '&cFoloseste comanda: /register ' +usage_unreg: '&cFoloseste comanda: /unregister ' +pwd_changed: '&2Parola a fost inregistrata cu succes!' +user_unknown: '&cAcest jucator nu este inregistrat!' +password_error: '&cParolele nu corespund, verifica-le din nou!' +password_error_nick: '&cNu poti folosi numele ca si parola, te rugam incearca alta...' +password_error_unsafe: '&cParola aleasa nu este sigura, te rugam alege alta...' +invalid_session: '&cIP-ul tau a fost schimbat si sesiunea ta a expirat!' +reg_only: '&4Doar jucatori inregistrati pot intra pe server! Te rugam foloseste http://example.ro pentru a te inregistra!' +logged_in: '&cEsti deja autentificat!' +logout: '&2Te-ai dezautentificat cu succes!' +same_nick: '&4Acelasi nume se joaca deja pe server!' +registered: '&2Te-ai inregistrat cu succes!' +pass_len: '&cParola ta este prea scurta pentru a te inregistra! Te rugam incearca alta!' +reload: '&2Configuratiile si baza de date sau reincarcat corect!' +timeout: '&4A expirat timpul de autentificare si ai fost dat afara de server, te rugam incearca din nou!' +usage_changepassword: '&cFoloseste comanda: /changepassword ' +name_len: '&4Numele tau este prea scurt pentru a te inregistra!' +regex: '&4Numele tau contine caractere ilegale. Caractere permise: REG_EX' +add_email: '&3Te rugam adaugati email-ul la contul tau folosind comanda "/email add "' +recovery_email: '&3Ti-ai uitat parola? Te rugam foloseste comanda "/email recovery "' +usage_captcha: '&3Pentru a te autentifica trebuie sa folosesti codul de la captcha, te rugam foloseste comanda "/captcha "' +wrong_captcha: '&cCod-ul captcha este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!' +valid_captcha: '&2Cod-ul captcha a fost scris corect!' +kick_forvip: '&3Un V.I.P a intrat pe server cand era plin!' +kick_fullserver: '&4Server-ul este plin, te rugam incearca din nou mai tarziu!' +usage_email_add: '&cFoloseste comanda: /email add ' +usage_email_change: '&cFoloseste comanda: /email change ' +usage_email_recovery: '&cFoloseste comanda: /email recovery ' +new_email_invalid: '&cNoul email este invalid, incearca din nou!' +old_email_invalid: '&cEmail-ul vechi este invalid, incearca din nou!' +email_invalid: '&cEmail-ul este invalid, incearca din nou!' +email_added: '&2Email-ul a fost adaugat cu succes la contul tau!' +email_confirm: '&cTe rugam confirma adresa de email!' +email_changed: '&2Email-ul a fost schimbat corect!' +email_send: ' &2Recuperarea email-ul a fost trimis cu succes! Te rugam sa verificati email-ul in inbox!' +email_exists: '&cRecuperarea email-ului a fost deja trimisa! Puteti sa-l anulati si sa trimiteti unul nou folosind comanda de mai jos:' +country_banned: '&4Tara ta este interzisa pe acest server!' +antibot_auto_enabled: '&4[AntiBotService] AntiBot-ul a fost activat din cauza numărului mare de conexiuni!' +antibot_auto_disabled: '&2[AntiBotService] AntiBot-ul a fost dezactivat dupa %m de minute!' +email_already_used: '&4Email-ul a fost deja folosit' +two_factor_create: '&2Codul tau secret este %code. Il poti scana de aici %url' +not_owner_error: 'Tu nu esti detinatorul acestui cont. Te rugam alege alt nume!' +invalid_name_case: 'Ar trebui sa intri cu numele %valid nu %invalid' +denied_command: '&cPentru a folosi comanda trebuie sa te autentifici!' +same_ip_online: 'Un jucator cu acelasi IP joaca deja!' +denied_chat: 'Pentru a folosi chat-ul trebuie sa te autentifici!' +password_error_chars: '&4Parola ta contine caractere ilegale. Caractere permise: REG_EX' +email_show: '&2Adresa ta curenta de email este: &f%email' +show_no_email: '&2Nu ai nici-o adresa de email asociat cu acest cont.' +tempban_max_logins: '&cAi fost interzis temporar deoarece ai incercat sa te autentifici de prea multe ori.' +accounts_owned_self: 'Detii %count conturi:' +accounts_owned_other: 'Jucatorul %name are %count conturi:' +kicked_admin_registered: 'Un administrator tocmai te-a inregistrat, te rog autentifica-te din nou.' +incomplete_email_settings: 'Eroare: nu toate setarile necesare pentru trimiterea email-ului sunt facute! Te rugam contacteaza un administrator.' +recovery_code_sent: 'Un cod de recuperare a parolei a fost trimis catre email-ul tau.' +recovery_code_incorrect: 'Codul de recuperare nu este corect! Foloseste /email recovery [email] pentru a genera unul nou.' diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 01374ae89..f37f3a41e 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -72,7 +72,7 @@ kicked_admin_registered: 'Администратор зарегистриров tempban_max_logins: '&cВы были временно забанены, потому что вы пытались войти в систему слишком много раз.' accounts_owned_self: 'Вы являетесь владельцем %count аккаунтов:' incomplete_email_settings: 'Ошибка: не все необходимые параметры установлены для отправки электронной почты. Пожалуйста, обратитесь к администратору.' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' -# TODO recovery_code_sent: 'A recovery code to reset your password has been sent to your email.' -# TODO recovery_code_incorrect: 'The recovery code is not correct! Use /email recovery [email] to generate a new one' \ No newline at end of file +email_show: '&2Ваш текущий адрес электронной почты: &f%email' +show_no_email: '&2В данный момент к вашему аккаунте не привязана электронная почта.' +recovery_code_sent: 'Код восстановления для сброса пароля был отправлен на вашу электронную почту.' +recovery_code_incorrect: 'Код восстановления неверный! Введите /email recovery [email], чтобы отправить новый код' diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index ba7f76068..3b5432bf6 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -19,9 +19,8 @@ import fr.xephi.authme.task.purge.PurgeService; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.PluginManager; -import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.plugin.java.JavaPluginLoader; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -29,13 +28,12 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; import java.util.logging.Logger; -import static fr.xephi.authme.settings.TestSettingsMigrationServices.alwaysFulfilled; import static fr.xephi.authme.settings.properties.AuthMeSettingsRetriever.buildConfigurationData; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -50,9 +48,6 @@ import static org.mockito.Mockito.mock; @RunWith(MockitoJUnitRunner.class) public class AuthMeInitializationTest { - @Mock - private PluginLoader pluginLoader; - @Mock private Server server; @@ -61,7 +56,6 @@ public class AuthMeInitializationTest { private AuthMe authMe; private File dataFolder; - private File settingsFile; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -74,13 +68,13 @@ public class AuthMeInitializationTest { @Before public void initAuthMe() throws IOException { dataFolder = temporaryFolder.newFolder(); - settingsFile = new File(dataFolder, "config.yml"); + File settingsFile = new File(dataFolder, "config.yml"); + JavaPluginLoader pluginLoader = new JavaPluginLoader(server); Files.copy(TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "config.test.yml"), settingsFile); // Mock / wire various Bukkit components given(server.getLogger()).willReturn(mock(Logger.class)); ReflectionTestUtils.setField(Bukkit.class, null, "server", server); - given(server.getScheduler()).willReturn(mock(BukkitScheduler.class)); given(server.getPluginManager()).willReturn(pluginManager); // PluginDescriptionFile is final: need to create a sample one @@ -88,14 +82,14 @@ public class AuthMeInitializationTest { "AuthMe", "N/A", AuthMe.class.getCanonicalName()); // Initialize AuthMe - authMe = new AuthMe(pluginLoader, server, descriptionFile, dataFolder, null); + authMe = new AuthMe(pluginLoader, descriptionFile, dataFolder, null); } @Test public void shouldInitializeAllServices() { // given Settings settings = - new Settings(dataFolder, mock(PropertyResource.class), alwaysFulfilled(), buildConfigurationData()); + new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData()); Injector injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); injector.provide(DataFolder.class, dataFolder); diff --git a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java b/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java index 6ae87df63..8eae45ec2 100644 --- a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java +++ b/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java @@ -12,7 +12,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; @@ -26,8 +26,8 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/src/test/java/fr/xephi/authme/TestHelper.java b/src/test/java/fr/xephi/authme/TestHelper.java index d16bd31f7..5017c4769 100644 --- a/src/test/java/fr/xephi/authme/TestHelper.java +++ b/src/test/java/fr/xephi/authme/TestHelper.java @@ -18,8 +18,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.logging.Logger; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/src/test/java/fr/xephi/authme/api/NewAPITest.java b/src/test/java/fr/xephi/authme/api/NewAPITest.java index 02dfbec26..cc3dd1882 100644 --- a/src/test/java/fr/xephi/authme/api/NewAPITest.java +++ b/src/test/java/fr/xephi/authme/api/NewAPITest.java @@ -18,7 +18,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java b/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java index cfcb00f55..4e0c43af2 100644 --- a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Collections; @@ -29,18 +29,18 @@ import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link CommandHandler}. @@ -85,7 +85,7 @@ public class CommandHandlerTest { given(injector.newInstance(any(Class.class))).willAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { - Class clazz = (Class) invocation.getArguments()[0]; + Class clazz = invocation.getArgument(0); if (ExecutableCommand.class.isAssignableFrom(clazz)) { Class commandClass = (Class) clazz; ExecutableCommand mock = mock(commandClass); @@ -108,7 +108,7 @@ public class CommandHandlerTest { CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); doReturn(TestLoginCommand.class).when(command).getExecutableCommand(); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))) + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())) .willReturn(new FoundCommandResult(command, asList("Authme", "Login"), asList("myPass"), 0.0, SUCCESS)); // when @@ -129,7 +129,7 @@ public class CommandHandlerTest { String[] bukkitArgs = {"testPlayer"}; CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))) + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())) .willReturn(new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 0.0, NO_PERMISSION)); // when @@ -148,7 +148,7 @@ public class CommandHandlerTest { String[] bukkitArgs = {"testPlayer"}; CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))).willReturn( + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())).willReturn( new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 0.0, INCORRECT_ARGUMENTS)); given(permissionsManager.hasPermission(sender, command.getPermission())).willReturn(true); @@ -170,7 +170,7 @@ public class CommandHandlerTest { String[] bukkitArgs = {"testPlayer"}; CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))).willReturn( + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())).willReturn( new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 0.0, INCORRECT_ARGUMENTS)); given(permissionsManager.hasPermission(sender, command.getPermission())).willReturn(false); @@ -192,7 +192,7 @@ public class CommandHandlerTest { String[] bukkitArgs = {"testPlayer"}; CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))).willReturn( + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())).willReturn( new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 0.0, MISSING_BASE_COMMAND)); // when @@ -212,7 +212,7 @@ public class CommandHandlerTest { CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); given(command.getLabels()).willReturn(Collections.singletonList("test_cmd")); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))).willReturn( + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())).willReturn( new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 0.01, UNKNOWN_LABEL)); // when @@ -237,8 +237,7 @@ public class CommandHandlerTest { String[] bukkitArgs = {"testPlayer"}; CommandSender sender = mock(CommandSender.class); CommandDescription command = mock(CommandDescription.class); - given(command.getLabels()).willReturn(Collections.singletonList("test_cmd")); - given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyListOf(String.class))).willReturn( + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())).willReturn( new FoundCommandResult(command, asList("unreg"), asList("testPlayer"), 1.0, UNKNOWN_LABEL)); // when @@ -263,7 +262,7 @@ public class CommandHandlerTest { CommandDescription command = mock(CommandDescription.class); doReturn(TestRegisterCommand.class).when(command).getExecutableCommand(); - given(commandMapper.mapPartsToCommand(eq(sender), anyListOf(String.class))) + given(commandMapper.mapPartsToCommand(eq(sender), anyList())) .willReturn(new FoundCommandResult(command, asList("AuthMe", "REGISTER"), asList("testArg"), 0.0, SUCCESS)); // when diff --git a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java index 46efdaec4..dd7bad6e4 100644 --- a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java @@ -28,9 +28,10 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; /** @@ -257,7 +258,7 @@ public class CommandMapperTest { // given List parts = asList("email", "helptest", "arg1"); CommandSender sender = mock(CommandSender.class); - given(permissionsManager.hasPermission(eq(sender), any(PermissionNode.class))).willReturn(true); + given(permissionsManager.hasPermission(eq(sender), isNull())).willReturn(true); // when FoundCommandResult result = mapper.mapPartsToCommand(sender, parts); diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java deleted file mode 100644 index dabc8a09c..000000000 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package fr.xephi.authme.command; - -import com.github.authme.configme.properties.Property; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.message.Messages; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.service.ValidationService; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Test for {@link CommandService}. - */ -@RunWith(MockitoJUnitRunner.class) -public class CommandServiceTest { - - @InjectMocks - private CommandService commandService; - @Mock - private Messages messages; - @Mock - private Settings settings; - @Mock - private ValidationService validationService; - - @Test - public void shouldSendMessage() { - // given - CommandSender sender = mock(CommandSender.class); - - // when - commandService.send(sender, MessageKey.INVALID_EMAIL); - - // then - verify(messages).send(sender, MessageKey.INVALID_EMAIL); - } - - @Test - public void shouldSendMessageWithReplacements() { - // given - CommandSender sender = mock(Player.class); - - // when - commandService.send(sender, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE, "10"); - - // then - verify(messages).send(sender, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE, "10"); - } - - @Test - public void shouldRetrieveMessage() { - // given - MessageKey key = MessageKey.USAGE_CAPTCHA; - String[] givenMessages = new String[]{"Lorem ipsum...", "Test line test"}; - given(messages.retrieve(key)).willReturn(givenMessages); - - // when - String[] result = commandService.retrieveMessage(key); - - // then - assertThat(result, equalTo(givenMessages)); - verify(messages).retrieve(key); - } - - @Test - public void shouldRetrieveProperty() { - // given - Property property = SecuritySettings.CAPTCHA_LENGTH; - given(settings.getProperty(property)).willReturn(7); - - // when - int result = commandService.getProperty(property); - - // then - assertThat(result, equalTo(7)); - verify(settings).getProperty(property); - } - - @Test - public void shouldReturnSettings() { - // given/when - Settings result = commandService.getSettings(); - - // then - assertThat(result, equalTo(settings)); - } - - @Test - public void shouldValidateEmail() { - // given - String email = "test@example.tld"; - given(validationService.validateEmail(email)).willReturn(true); - - // when - boolean result = commandService.validateEmail(email); - - // then - assertThat(result, equalTo(true)); - verify(validationService).validateEmail(email); - } - - @Test - public void shouldCheckIfEmailCanBeUsed() { - // given - String email = "mail@example.com"; - CommandSender sender = mock(CommandSender.class); - given(validationService.isEmailFreeForRegistration(email, sender)) - .willReturn(true); - - // when - boolean result = commandService.isEmailFreeForRegistration(email, sender); - - // then - assertThat(result, equalTo(true)); - verify(validationService).isEmailFreeForRegistration(email, sender); - } - -} diff --git a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java index 815d8a21f..4f0c99974 100644 --- a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java @@ -10,10 +10,10 @@ import java.util.Collections; import java.util.List; import static org.hamcrest.Matchers.containsString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PlayerCommand}. @@ -27,7 +27,7 @@ public class PlayerCommandTest { PlayerCommandImpl command = new PlayerCommandImpl(); // when - command.executeCommand(sender, Collections.emptyList()); + command.executeCommand(sender, Collections.emptyList()); // then verify(sender).sendMessage(argThat(containsString("only for players"))); @@ -54,7 +54,7 @@ public class PlayerCommandTest { PlayerCommandWithAlt command = new PlayerCommandWithAlt(); // when - command.executeCommand(sender, Collections.emptyList()); + command.executeCommand(sender, Collections.emptyList()); // then verify(sender, times(1)).sendMessage(argThat(containsString("use /authme test instead"))); diff --git a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java index 3c57e2148..271f11741 100644 --- a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java @@ -11,7 +11,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.List; @@ -28,13 +28,13 @@ import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link HelpCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java index 9c645e4d2..29a07be88 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java @@ -1,17 +1,17 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -20,8 +20,8 @@ import java.util.List; import static fr.xephi.authme.TestHelper.runInnerRunnable; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -36,7 +36,7 @@ public class AccountsCommandTest { @InjectMocks private AccountsCommand command; @Mock - private CommandService service; + private CommonService service; @Mock private DataSource dataSource; @Mock @@ -83,7 +83,6 @@ public class AccountsCommandTest { CommandSender sender = mock(CommandSender.class); List arguments = Collections.singletonList("SomeUser"); given(dataSource.getAuth("someuser")).willReturn(mock(PlayerAuth.class)); - given(dataSource.getAllAuthsByIp(anyString())).willReturn(Collections.emptyList()); // when command.executeCommand(sender, arguments); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java index af8f4e96c..a4c80228f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -3,12 +3,12 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; @@ -17,13 +17,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import static fr.xephi.authme.TestHelper.runOptionallyAsyncTask; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -39,7 +39,7 @@ public class ChangePasswordAdminCommandTest { private ChangePasswordAdminCommand command; @Mock - private CommandService service; + private CommonService service; @Mock private PasswordSecurity passwordSecurity; @@ -84,7 +84,6 @@ public class ChangePasswordAdminCommandTest { String player = "player"; String password = "password"; given(playerCache.isAuthenticated(player)).willReturn(false); - given(dataSource.getAuth(player)).willReturn(null); given(validationService.validatePassword(password, player)).willReturn(new ValidationResult()); // when diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java index 32cc64897..cf306599d 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java @@ -2,10 +2,10 @@ package fr.xephi.authme.command.executable.authme; import ch.jalu.injector.Injector; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.util.StringUtils; import org.bukkit.command.CommandSender; import org.junit.BeforeClass; @@ -13,7 +13,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.HashSet; @@ -23,14 +23,14 @@ import java.util.Set; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link ConverterCommand}. @@ -42,7 +42,7 @@ public class ConverterCommandTest { private ConverterCommand command; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private BukkitService bukkitService; diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java index 000c7922d..3daaf7ad7 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java @@ -7,18 +7,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link FirstSpawnCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java index fc2602bb4..46671d8af 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java @@ -10,17 +10,16 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link ForceLoginCommand}. @@ -44,7 +43,7 @@ public class ForceLoginCommandTest { public void shouldRejectOfflinePlayer() { // given String playerName = "Bobby"; - Player player = mockPlayer(false, playerName); + Player player = mockPlayer(false); given(bukkitService.getPlayerExact(playerName)).willReturn(player); CommandSender sender = mock(CommandSender.class); @@ -53,7 +52,7 @@ public class ForceLoginCommandTest { // then verify(bukkitService).getPlayerExact(playerName); - verify(sender).sendMessage(argThat(equalTo("Player needs to be online!"))); + verify(sender).sendMessage("Player needs to be online!"); verifyZeroInteractions(management); } @@ -69,7 +68,7 @@ public class ForceLoginCommandTest { // then verify(bukkitService).getPlayerExact(playerName); - verify(sender).sendMessage(argThat(equalTo("Player needs to be online!"))); + verify(sender).sendMessage("Player needs to be online!"); verifyZeroInteractions(management); } @@ -77,7 +76,7 @@ public class ForceLoginCommandTest { public void shouldRejectPlayerWithMissingPermission() { // given String playerName = "testTest"; - Player player = mockPlayer(true, playerName); + Player player = mockPlayer(true); given(bukkitService.getPlayerExact(playerName)).willReturn(player); given(permissionsManager.hasPermission(player, PlayerPermission.CAN_LOGIN_BE_FORCED)).willReturn(false); CommandSender sender = mock(CommandSender.class); @@ -95,7 +94,7 @@ public class ForceLoginCommandTest { public void shouldForceLoginPlayer() { // given String playerName = "tester23"; - Player player = mockPlayer(true, playerName); + Player player = mockPlayer(true); given(bukkitService.getPlayerExact(playerName)).willReturn(player); given(permissionsManager.hasPermission(player, PlayerPermission.CAN_LOGIN_BE_FORCED)).willReturn(true); CommandSender sender = mock(CommandSender.class); @@ -112,24 +111,23 @@ public class ForceLoginCommandTest { public void shouldForceLoginSenderSelf() { // given String senderName = "tester23"; - Player player = mockPlayer(true, senderName); + Player player = mockPlayer(true); given(bukkitService.getPlayerExact(senderName)).willReturn(player); given(permissionsManager.hasPermission(player, PlayerPermission.CAN_LOGIN_BE_FORCED)).willReturn(true); CommandSender sender = mock(CommandSender.class); given(sender.getName()).willReturn(senderName); // when - command.executeCommand(sender, Collections.emptyList()); + command.executeCommand(sender, Collections.emptyList()); // then verify(bukkitService).getPlayerExact(senderName); verify(management).forceLogin(player); } - private static Player mockPlayer(boolean isOnline, String name) { + private static Player mockPlayer(boolean isOnline) { Player player = mock(Player.class); given(player.isOnline()).willReturn(isOnline); - given(player.getName()).willReturn(name); return player; } } diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java index f7d322605..1e3fe3915 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -1,23 +1,23 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; 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.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link GetEmailCommand}. @@ -32,7 +32,7 @@ public class GetEmailCommandTest { private DataSource dataSource; @Mock - private CommandService service; + private CommonService service; @Test public void shouldReportUnknownUser() { diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java index 75d8953a0..28c945e39 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java @@ -8,17 +8,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link GetIpCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java index 8ff1baff9..494de2cc0 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java @@ -1,16 +1,16 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.Date; @@ -39,7 +39,7 @@ public class LastLoginCommandTest { private DataSource dataSource; @Mock - private CommandService service; + private CommonService service; @Test diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java index 1927854ec..677ee1fe4 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java @@ -1,14 +1,14 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.task.purge.PurgeService; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.task.purge.PurgeService; import org.bukkit.OfflinePlayer; 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.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.HashSet; @@ -16,11 +16,11 @@ import java.util.Set; import static com.google.common.collect.Sets.newHashSet; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PurgeBannedPlayersCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java index 63e1008ad..08a1f6342 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java @@ -7,7 +7,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Calendar; @@ -17,11 +17,11 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PurgeCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java index 54a31734d..5f3aa6f13 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java @@ -1,24 +1,24 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; 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.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PurgeLastPositionCommand}. @@ -33,7 +33,7 @@ public class PurgeLastPositionCommandTest { private DataSource dataSource; @Mock - private CommandService service; + private CommonService service; @Test diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index 508010cf9..914199120 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -3,12 +3,12 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.limbo.LimboCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; @@ -19,15 +19,15 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import static fr.xephi.authme.TestHelper.runSyncTaskFromOptionallyAsyncTask; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -51,7 +51,7 @@ public class RegisterAdminCommandTest { private BukkitService bukkitService; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private ValidationService validationService; @@ -164,7 +164,7 @@ public class RegisterAdminCommandTest { Player player = mock(Player.class); given(bukkitService.getPlayerExact(user)).willReturn(player); String kickForAdminRegister = "Admin registered you -- log in again"; - given(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); + given(commandService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); CommandSender sender = mock(CommandSender.class); // when diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java index 07424e159..3d9cf354e 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java @@ -3,13 +3,13 @@ package fr.xephi.authme.command.executable.authme; import ch.jalu.injector.Injector; import fr.xephi.authme.AuthMe; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.SettingsDependent; -import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.output.LogLevel; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.PluginSettings; @@ -21,7 +21,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Arrays; @@ -29,13 +29,13 @@ import java.util.Collections; import java.util.List; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link ReloadCommand}. @@ -59,7 +59,7 @@ public class ReloadCommandTest { private DataSource dataSource; @Mock - private CommandService commandService; + private CommonService commandService; @BeforeClass public static void setUpLogger() { diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetEmailCommandTest.java index 033bd8819..30c97c89f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SetEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetEmailCommandTest.java @@ -2,22 +2,23 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import org.bukkit.command.CommandSender; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import static fr.xephi.authme.TestHelper.runOptionallyAsyncTask; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -37,27 +38,30 @@ public class SetEmailCommandTest { private DataSource dataSource; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private PlayerCache playerCache; @Mock private BukkitService bukkitService; + + @Mock + private ValidationService validationService; @Test public void shouldRejectInvalidMail() { // given String user = "somebody"; String email = "some.test@example.org"; - given(commandService.validateEmail(email)).willReturn(false); + given(validationService.validateEmail(email)).willReturn(false); CommandSender sender = mock(CommandSender.class); // when command.executeCommand(sender, Arrays.asList(user, email)); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(commandService).send(sender, MessageKey.INVALID_EMAIL); verifyZeroInteractions(dataSource); } @@ -67,7 +71,7 @@ public class SetEmailCommandTest { // given String user = "nonexistent"; String email = "mail@example.com"; - given(commandService.validateEmail(email)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); given(dataSource.getAuth(user)).willReturn(null); CommandSender sender = mock(CommandSender.class); @@ -76,7 +80,7 @@ public class SetEmailCommandTest { runOptionallyAsyncTask(bukkitService); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(dataSource).getAuth(user); verify(commandService).send(sender, MessageKey.UNKNOWN_USER); verifyNoMoreInteractions(dataSource); @@ -87,20 +91,20 @@ public class SetEmailCommandTest { // given String user = "someone"; String email = "mail@example.com"; - given(commandService.validateEmail(email)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); PlayerAuth auth = mock(PlayerAuth.class); given(dataSource.getAuth(user)).willReturn(auth); CommandSender sender = mock(CommandSender.class); - given(commandService.isEmailFreeForRegistration(email, sender)).willReturn(false); + given(validationService.isEmailFreeForRegistration(email, sender)).willReturn(false); // when command.executeCommand(sender, Arrays.asList(user, email)); runOptionallyAsyncTask(bukkitService); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(dataSource).getAuth(user); - verify(commandService).isEmailFreeForRegistration(email, sender); + verify(validationService).isEmailFreeForRegistration(email, sender); verify(commandService).send(sender, MessageKey.EMAIL_ALREADY_USED_ERROR); verifyNoMoreInteractions(dataSource); verifyZeroInteractions(auth); @@ -111,11 +115,11 @@ public class SetEmailCommandTest { // given String user = "Bobby"; String email = "new-addr@example.org"; - given(commandService.validateEmail(email)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); PlayerAuth auth = mock(PlayerAuth.class); given(dataSource.getAuth(user)).willReturn(auth); CommandSender sender = mock(CommandSender.class); - given(commandService.isEmailFreeForRegistration(email, sender)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, sender)).willReturn(true); given(dataSource.updateEmail(auth)).willReturn(false); // when @@ -123,9 +127,9 @@ public class SetEmailCommandTest { runOptionallyAsyncTask(bukkitService); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(dataSource).getAuth(user); - verify(commandService).isEmailFreeForRegistration(email, sender); + verify(validationService).isEmailFreeForRegistration(email, sender); verify(commandService).send(sender, MessageKey.ERROR); verify(dataSource).updateEmail(auth); verifyNoMoreInteractions(dataSource); @@ -136,11 +140,11 @@ public class SetEmailCommandTest { // given String user = "Bobby"; String email = "new-addr@example.org"; - given(commandService.validateEmail(email)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); PlayerAuth auth = mock(PlayerAuth.class); given(dataSource.getAuth(user)).willReturn(auth); CommandSender sender = mock(CommandSender.class); - given(commandService.isEmailFreeForRegistration(email, sender)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, sender)).willReturn(true); given(dataSource.updateEmail(auth)).willReturn(true); given(playerCache.getAuth(user)).willReturn(null); @@ -149,9 +153,9 @@ public class SetEmailCommandTest { runOptionallyAsyncTask(bukkitService); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(dataSource).getAuth(user); - verify(commandService).isEmailFreeForRegistration(email, sender); + verify(validationService).isEmailFreeForRegistration(email, sender); verify(commandService).send(sender, MessageKey.EMAIL_CHANGED_SUCCESS); verify(dataSource).updateEmail(auth); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); @@ -163,11 +167,11 @@ public class SetEmailCommandTest { // given String user = "Bobby"; String email = "new-addr@example.org"; - given(commandService.validateEmail(email)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); PlayerAuth auth = mock(PlayerAuth.class); given(dataSource.getAuth(user)).willReturn(auth); CommandSender sender = mock(CommandSender.class); - given(commandService.isEmailFreeForRegistration(email, sender)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, sender)).willReturn(true); given(dataSource.updateEmail(auth)).willReturn(true); given(playerCache.getAuth(user)).willReturn(mock(PlayerAuth.class)); @@ -176,9 +180,9 @@ public class SetEmailCommandTest { runOptionallyAsyncTask(bukkitService); // then - verify(commandService).validateEmail(email); + verify(validationService).validateEmail(email); verify(dataSource).getAuth(user); - verify(commandService).isEmailFreeForRegistration(email, sender); + verify(validationService).isEmailFreeForRegistration(email, sender); verify(commandService).send(sender, MessageKey.EMAIL_CHANGED_SUCCESS); verify(dataSource).updateEmail(auth); verify(playerCache).updatePlayer(auth); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java index 0974da728..f59dda891 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java @@ -7,15 +7,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link SetFirstSpawnCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java index 8522f997f..17f77e31b 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java @@ -7,15 +7,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link SetSpawnCommand}. @@ -39,7 +39,7 @@ public class SetSpawnCommandTest { given(spawnLoader.setSpawn(location)).willReturn(true); // when - command.executeCommand(player, Collections.emptyList()); + command.executeCommand(player, Collections.emptyList()); // then verify(spawnLoader).setSpawn(location); @@ -55,7 +55,7 @@ public class SetSpawnCommandTest { given(spawnLoader.setSpawn(location)).willReturn(false); // when - command.executeCommand(player, Collections.emptyList()); + command.executeCommand(player, Collections.emptyList()); // then verify(spawnLoader).setSpawn(location); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java index 81a7f416f..c4c6abcef 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java @@ -7,18 +7,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link SpawnCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java index 239ba33d2..03dd40773 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java @@ -1,26 +1,26 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.service.AntiBotService; import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.command.help.HelpProvider; +import fr.xephi.authme.service.AntiBotService; 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.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link SwitchAntiBotCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java index fd2e36883..4651b2beb 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java @@ -1,17 +1,17 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -33,7 +33,7 @@ public class UnregisterAdminCommandTest { private DataSource dataSource; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private BukkitService bukkitService; @@ -61,7 +61,6 @@ public class UnregisterAdminCommandTest { // given String user = "personaNonGrata"; given(dataSource.isAuthAvailable(user)).willReturn(true); - given(dataSource.removeAuth(user)).willReturn(false); Player player = mock(Player.class); given(bukkitService.getPlayerExact(user)).willReturn(player); CommandSender sender = mock(CommandSender.class); @@ -80,7 +79,6 @@ public class UnregisterAdminCommandTest { // given String user = "personaNonGrata"; given(dataSource.isAuthAvailable(user)).willReturn(true); - given(dataSource.removeAuth(user)).willReturn(false); given(bukkitService.getPlayerExact(user)).willReturn(null); CommandSender sender = mock(CommandSender.class); diff --git a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java index f2ba19c54..19920578d 100644 --- a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java @@ -12,7 +12,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -37,7 +37,7 @@ public class CaptchaCommandTest { private PlayerCache playerCache; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private LimboCache limboCache; diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java index 7ca0510b5..d306ff06b 100644 --- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java @@ -1,36 +1,32 @@ package fr.xephi.authme.command.executable.changepassword; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; -import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import static org.hamcrest.Matchers.containsString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link ChangePasswordCommand}. @@ -42,7 +38,7 @@ public class ChangePasswordCommandTest { private ChangePasswordCommand command; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private PlayerCache playerCache; @@ -53,22 +49,13 @@ public class ChangePasswordCommandTest { @Mock private Management management; - @Before - public void setSettings() { - when(commandService.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).thenReturn(2); - when(commandService.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).thenReturn(50); - // Only allow passwords with alphanumerical characters for the test - when(commandService.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).thenReturn("[a-zA-Z0-9]+"); - when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections.emptyList()); - } - @Test public void shouldRejectNonPlayerSender() { // given CommandSender sender = mock(BlockCommandSender.class); // when - command.executeCommand(sender, new ArrayList()); + command.executeCommand(sender, Collections.emptyList()); // then verify(sender).sendMessage(argThat(containsString("only for players"))); diff --git a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java index 985ce8151..75c10e7a4 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.email; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; +import fr.xephi.authme.service.CommonService; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -10,12 +10,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -30,7 +29,7 @@ public class AddEmailCommandTest { private AddEmailCommand command; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private Management management; @@ -41,7 +40,7 @@ public class AddEmailCommandTest { CommandSender sender = mock(BlockCommandSender.class); // when - command.executeCommand(sender, new ArrayList()); + command.executeCommand(sender, Collections.emptyList()); // then verifyZeroInteractions(management); @@ -52,7 +51,6 @@ public class AddEmailCommandTest { // given Player sender = mock(Player.class); String email = "mail@example"; - given(commandService.validateEmail(email)).willReturn(true); // when command.executeCommand(sender, Arrays.asList(email, email)); @@ -66,7 +64,6 @@ public class AddEmailCommandTest { // given Player sender = mock(Player.class); String email = "asdfasdf@example.com"; - given(commandService.validateEmail(email)).willReturn(true); // when command.executeCommand(sender, Arrays.asList(email, "wrongConf")); diff --git a/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java index 240ace312..8876abbfe 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java @@ -8,7 +8,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java index 8a3dfcf48..978a2bef9 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java @@ -3,15 +3,14 @@ package fr.xephi.authme.command.executable.email; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.RecoveryCodeService; import fr.xephi.authme.settings.properties.EmailSettings; -import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; import org.junit.BeforeClass; import org.junit.Test; @@ -19,17 +18,17 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; @@ -51,7 +50,7 @@ public class RecoverEmailCommandTest { private PasswordSecurity passwordSecurity; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private DataSource dataSource; @@ -169,13 +168,10 @@ public class RecoverEmailCommandTest { Player sender = mock(Player.class); given(sender.getName()).willReturn(name); given(sendMailSsl.hasAllInformation()).willReturn(true); + given(sendMailSsl.sendRecoveryCode(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "v@example.com"; given(dataSource.getAuth(name)).willReturn(newAuthWithEmail(email)); - int codeLength = 7; - given(commandService.getProperty(SecuritySettings.RECOVERY_CODE_LENGTH)).willReturn(codeLength); - int hoursValid = 12; - given(commandService.getProperty(SecuritySettings.RECOVERY_CODE_HOURS_VALID)).willReturn(hoursValid); String code = "a94f37"; given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.generateCode(name)).willReturn(code); @@ -202,7 +198,6 @@ public class RecoverEmailCommandTest { String email = "vulture@example.com"; PlayerAuth auth = newAuthWithEmail(email); given(dataSource.getAuth(name)).willReturn(auth); - given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.isCodeValid(name, "bogus")).willReturn(false); @@ -223,6 +218,7 @@ public class RecoverEmailCommandTest { Player sender = mock(Player.class); given(sender.getName()).willReturn(name); given(sendMailSsl.hasAllInformation()).willReturn(true); + given(sendMailSsl.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "vulture@example.com"; String code = "A6EF3AC8"; @@ -230,7 +226,7 @@ public class RecoverEmailCommandTest { given(dataSource.getAuth(name)).willReturn(auth); given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20); given(passwordSecurity.computeHash(anyString(), eq(name))) - .willAnswer(invocation -> new HashedPassword((String) invocation.getArguments()[0])); + .willAnswer(invocation -> new HashedPassword(invocation.getArgument(0))); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.isCodeValid(name, code)).willReturn(true); @@ -257,13 +253,14 @@ public class RecoverEmailCommandTest { Player sender = mock(Player.class); given(sender.getName()).willReturn(name); given(sendMailSsl.hasAllInformation()).willReturn(true); + given(sendMailSsl.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "shark@example.org"; PlayerAuth auth = newAuthWithEmail(email); given(dataSource.getAuth(name)).willReturn(auth); given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(20); given(passwordSecurity.computeHash(anyString(), eq(name))) - .willAnswer(invocation -> new HashedPassword((String) invocation.getArguments()[0])); + .willAnswer(invocation -> new HashedPassword(invocation.getArgument(0))); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(false); // when diff --git a/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java index 18513bf91..2825728a3 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java @@ -1,15 +1,15 @@ package fr.xephi.authme.command.executable.email; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -30,7 +30,7 @@ public class ShowEmailCommandTest { private ShowEmailCommand command; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private PlayerCache playerCache; diff --git a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java index 5e02b4b2c..2b6a5c192 100644 --- a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java @@ -8,17 +8,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Collections; import static org.hamcrest.Matchers.containsString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link LoginCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java index 7bc9eeb10..c5c66a161 100644 --- a/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java @@ -8,16 +8,16 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Collections; import static org.hamcrest.Matchers.containsString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link LogoutCommand}. diff --git a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java index a4dfe8f78..c63c8c960 100644 --- a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java @@ -1,11 +1,12 @@ package fr.xephi.authme.command.executable.register; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -19,20 +20,19 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link RegisterCommand}. @@ -44,7 +44,7 @@ public class RegisterCommandTest { private RegisterCommand command; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private Management management; @@ -52,6 +52,9 @@ public class RegisterCommandTest { @Mock private SendMailSSL sendMailSsl; + @Mock + private ValidationService validationService; + @BeforeClass public static void setup() { TestHelper.setupLogger(); @@ -70,7 +73,7 @@ public class RegisterCommandTest { CommandSender sender = mock(BlockCommandSender.class); // when - command.executeCommand(sender, new ArrayList()); + command.executeCommand(sender, Collections.emptyList()); // then verify(sender).sendMessage(argThat(containsString("Player only!"))); @@ -84,7 +87,7 @@ public class RegisterCommandTest { Player player = mock(Player.class); // when - command.executeCommand(player, Collections.emptyList()); + command.executeCommand(player, Collections.emptyList()); // then verify(management).performRegister(player, "", "", true); @@ -97,7 +100,7 @@ public class RegisterCommandTest { Player player = mock(Player.class); // when - command.executeCommand(player, Collections.emptyList()); + command.executeCommand(player, Collections.emptyList()); // then verify(commandService).send(player, MessageKey.USAGE_REGISTER); @@ -154,11 +157,10 @@ public class RegisterCommandTest { public void shouldRejectInvalidEmail() { // given String playerMail = "player@example.org"; - given(commandService.validateEmail(playerMail)).willReturn(false); + given(validationService.validateEmail(playerMail)).willReturn(false); given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); - given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); @@ -166,7 +168,7 @@ public class RegisterCommandTest { command.executeCommand(player, Arrays.asList(playerMail, playerMail)); // then - verify(commandService).validateEmail(playerMail); + verify(validationService).validateEmail(playerMail); verify(commandService).send(player, MessageKey.INVALID_EMAIL); verifyZeroInteractions(management); } @@ -175,11 +177,10 @@ public class RegisterCommandTest { public void shouldRejectInvalidEmailConfirmation() { // given String playerMail = "bobber@bobby.org"; - given(commandService.validateEmail(playerMail)).willReturn(true); + given(validationService.validateEmail(playerMail)).willReturn(true); given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); - given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); @@ -196,13 +197,12 @@ public class RegisterCommandTest { public void shouldPerformEmailRegistration() { // given String playerMail = "asfd@lakjgre.lds"; - given(commandService.validateEmail(playerMail)).willReturn(true); + given(validationService.validateEmail(playerMail)).willReturn(true); int passLength = 7; given(commandService.getProperty(EmailSettings.RECOVERY_PASSWORD_LENGTH)).willReturn(passLength); given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); - given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); @@ -210,7 +210,7 @@ public class RegisterCommandTest { command.executeCommand(player, Arrays.asList(playerMail, playerMail)); // then - verify(commandService).validateEmail(playerMail); + verify(validationService).validateEmail(playerMail); verify(sendMailSsl).hasAllInformation(); verify(management).performRegister(eq(player), argThat(stringWithLength(passLength)), eq(playerMail), eq(true)); } diff --git a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java index 336b17424..e646031f8 100644 --- a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java @@ -1,15 +1,15 @@ package fr.xephi.authme.command.executable.unregister; import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.command.CommandService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; +import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -31,7 +31,7 @@ public class UnregisterCommandTest { private Management management; @Mock - private CommandService commandService; + private CommonService commandService; @Mock private PlayerCache playerCache; diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java index 150bb42f5..292b0ec97 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java @@ -21,8 +21,8 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; /** * Test for {@link HelpMessagesService}. diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index e90a76120..99ec921c6 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -39,9 +39,9 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; diff --git a/src/test/java/fr/xephi/authme/data/SessionManagerTest.java b/src/test/java/fr/xephi/authme/data/SessionManagerTest.java index f778dd16b..f01311af1 100644 --- a/src/test/java/fr/xephi/authme/data/SessionManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/SessionManagerTest.java @@ -5,7 +5,7 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Map; diff --git a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java index 70aa11c76..28c2ab9ee 100644 --- a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java +++ b/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java @@ -5,15 +5,15 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.TempbanManager.TimedCounter; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Calendar; import java.util.Date; @@ -25,8 +25,8 @@ import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java b/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java index 54f408654..8eb8e9598 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java +++ b/src/test/java/fr/xephi/authme/data/limbo/LimboCacheTest.java @@ -12,7 +12,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Map; diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java index c8aeb7966..35ea12e3c 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java @@ -37,11 +37,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -105,7 +105,7 @@ public abstract class AbstractResourceClosingTest { given(settings.getProperty(any(Property.class))).willAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { - return ((Property) invocation.getArguments()[0]).getDefaultValue(); + return ((Property) invocation.getArguments()[0]).getDefaultValue(); } }); TestHelper.setupLogger(); diff --git a/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java index 7ce72f153..7e0bb1a51 100644 --- a/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java @@ -3,8 +3,6 @@ package fr.xephi.authme.datasource; import com.google.common.io.Files; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.FlatFile; import org.junit.Before; import org.junit.Rule; import org.junit.Test; diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java index 5484bd5e0..0edc804f7 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -4,9 +4,6 @@ import com.github.authme.configme.properties.Property; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.datasource.AbstractDataSourceIntegrationTest; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.MySQL; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import org.junit.After; @@ -22,7 +19,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java index 9398e4ed9..6caca39ca 100644 --- a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java @@ -3,9 +3,6 @@ package fr.xephi.authme.datasource; import com.github.authme.configme.properties.Property; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.AbstractDataSourceIntegrationTest; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import org.junit.After; @@ -25,7 +22,7 @@ import java.sql.Statement; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java b/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java index 897f36e93..14e59dff1 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java +++ b/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java @@ -13,10 +13,9 @@ import java.util.Arrays; import java.util.List; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; @@ -24,6 +23,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link AbstractDataSourceConverter}. diff --git a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java b/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java index e24953b96..453ede994 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java +++ b/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java @@ -23,11 +23,11 @@ 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.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link CrazyLoginConverter}. diff --git a/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java b/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java index d474ac6f5..cb1e6cb4c 100644 --- a/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java +++ b/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java @@ -14,7 +14,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; @@ -24,8 +24,8 @@ import java.util.concurrent.Executors; import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; diff --git a/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java b/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java index e798c4df1..6925343a4 100644 --- a/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java @@ -7,7 +7,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; diff --git a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java index 1f36fa8d4..f55f59f8c 100644 --- a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java @@ -16,11 +16,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index 6271f0922..766fb06ad 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -8,12 +8,12 @@ import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.service.AntiBotService; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.ValidationService; import org.bukkit.Server; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerLoginEvent; @@ -27,7 +27,7 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collection; @@ -175,10 +175,7 @@ public class OnJoinVerifierTest { @Test public void shouldNotKickRegisteredPlayer() throws FailedVerificationException { - // given - given(settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)).willReturn(true); - - // when + // given / when / then onJoinVerifier.checkKickNonRegistered(true); } @@ -206,7 +203,6 @@ public class OnJoinVerifierTest { @Test public void shouldRejectTooLongName() throws FailedVerificationException { // given - given(settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)).willReturn(4); given(settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH)).willReturn(8); given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+"); onJoinVerifier.reload(); // @PostConstruct method @@ -324,7 +320,6 @@ public class OnJoinVerifierTest { // given Player player = newPlayerWithName("MyPlayer"); PlayerAuth auth = null; - given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true); // when onJoinVerifier.checkNameCasing(player, auth); @@ -381,14 +376,27 @@ public class OnJoinVerifierTest { Player player = newPlayerWithName("Bobby"); boolean isAuthAvailable = false; given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false); - given(antiBotService.shouldKick(isAuthAvailable)).willReturn(false); + given(antiBotService.shouldKick()).willReturn(false); // when onJoinVerifier.checkAntibot(player, isAuthAvailable); // then verify(permissionsManager).hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT); - verify(antiBotService).shouldKick(isAuthAvailable); + verify(antiBotService).shouldKick(); + } + + @Test + public void shouldAllowUserWithAuth() throws FailedVerificationException { + // given + Player player = newPlayerWithName("Lacey"); + boolean isAuthAvailable = true; + + // when + onJoinVerifier.checkAntibot(player, isAuthAvailable); + + // then + verifyZeroInteractions(permissionsManager, antiBotService); } @Test @@ -397,13 +405,13 @@ public class OnJoinVerifierTest { Player player = newPlayerWithName("Steward"); boolean isAuthAvailable = false; given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(true); - given(antiBotService.shouldKick(isAuthAvailable)).willReturn(true); // when onJoinVerifier.checkAntibot(player, isAuthAvailable); // then verify(permissionsManager).hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT); + verifyZeroInteractions(antiBotService); } @Test @@ -412,7 +420,7 @@ public class OnJoinVerifierTest { Player player = newPlayerWithName("D3"); boolean isAuthAvailable = false; given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false); - given(antiBotService.shouldKick(isAuthAvailable)).willReturn(true); + given(antiBotService.shouldKick()).willReturn(true); // when / then try { @@ -420,7 +428,7 @@ public class OnJoinVerifierTest { fail("Expected exception to be thrown"); } catch (FailedVerificationException e) { verify(permissionsManager).hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT); - verify(antiBotService).shouldKick(isAuthAvailable); + verify(antiBotService).shouldKick(); } } @@ -447,7 +455,6 @@ public class OnJoinVerifierTest { // given String ip = "192.168.0.1"; given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true); - given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)).willReturn(false); given(validationService.isCountryAdmitted(ip)).willReturn(true); // when @@ -477,7 +484,6 @@ public class OnJoinVerifierTest { // given String ip = "192.168.40.0"; given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true); - given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)).willReturn(true); given(validationService.isCountryAdmitted(ip)).willReturn(false); // expect diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java new file mode 100644 index 000000000..b7b2c6057 --- /dev/null +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java @@ -0,0 +1,30 @@ +package fr.xephi.authme.listener; + +import org.bukkit.event.entity.EntityAirChangeEvent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; + +/** + * Test for {@link PlayerListener111}. + */ +@RunWith(MockitoJUnitRunner.class) +public class PlayerListener111Test { + + @InjectMocks + private PlayerListener111 listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldCancelEvent() { + withServiceMock(listenerService) + .check(listener::onPlayerAirChange, EntityAirChangeEvent.class); + } + +} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java index c60b8abff..5493a904d 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener16Test.java @@ -5,7 +5,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java index 9b93a270b..398e6f9ee 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener18Test.java @@ -5,7 +5,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java index 50dd179dc..b870ac069 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java @@ -5,7 +5,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index 15bf4c6ee..bc202cfbc 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -36,7 +36,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.net.InetAddress; import java.util.ArrayList; @@ -50,9 +50,9 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -379,7 +379,6 @@ public class PlayerListenerTest { Location to = new Location(world, 199, 70, 199); PlayerMoveEvent event = spy(new PlayerMoveEvent(player, from, to)); given(listenerService.shouldCancelEvent(player)).willReturn(true); - given(settings.getProperty(RestrictionSettings.REMOVE_SPEED)).willReturn(false); // when listener.onPlayerMove(event); @@ -565,7 +564,6 @@ public class PlayerListenerTest { verify(onJoinVerifier).checkKickNonRegistered(true); verify(onJoinVerifier).checkNameCasing(player, auth); verify(onJoinVerifier).checkPlayerCountry(true, ip); - verify(antiBotService).handlePlayerJoin(); verify(teleportationService).teleportOnJoin(player); verifyNoModifyingCalls(event); } diff --git a/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java b/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java index d40683c37..a2a326b66 100644 --- a/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java @@ -14,7 +14,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; diff --git a/src/test/java/fr/xephi/authme/mail/SendMailSSLTest.java b/src/test/java/fr/xephi/authme/mail/SendMailSSLTest.java new file mode 100644 index 000000000..7d3746ea6 --- /dev/null +++ b/src/test/java/fr/xephi/authme/mail/SendMailSSLTest.java @@ -0,0 +1,248 @@ +package fr.xephi.authme.mail; + +import ch.jalu.injector.testing.BeforeInjecting; +import ch.jalu.injector.testing.DelayedInjectionRunner; +import ch.jalu.injector.testing.InjectDelayed; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.apache.commons.mail.EmailException; +import org.apache.commons.mail.HtmlEmail; +import org.bukkit.Server; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link SendMailSSL}. + */ +@RunWith(DelayedInjectionRunner.class) +public class SendMailSSLTest { + + @InjectDelayed + private SendMailSSL sendMailSSL; + + @Mock + private Settings settings; + @Mock + private Server server; + @DataFolder + private File dataFolder; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @BeforeClass + public static void initLogger() { + TestHelper.setupLogger(); + } + + @BeforeInjecting + public void initFields() throws IOException { + dataFolder = temporaryFolder.newFolder(); + given(server.getServerName()).willReturn("serverName"); + given(settings.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("mail@example.org"); + given(settings.getProperty(EmailSettings.MAIL_PASSWORD)).willReturn("pass1234"); + } + + @Test + public void shouldHaveAllInformation() { + // given / when / then + assertThat(sendMailSSL.hasAllInformation(), equalTo(true)); + } + + @Test + public void shouldSendPasswordMail() throws EmailException { + // given + given(settings.getPasswordEmailMessage()) + .willReturn("Hi , your new password for is "); + given(settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)).willReturn(false); + SendMailSSL sendMailSpy = spy(sendMailSSL); + HtmlEmail email = mock(HtmlEmail.class); + doReturn(email).when(sendMailSpy).initializeMail(anyString()); + doReturn(true).when(sendMailSpy).sendEmail(anyString(), any(HtmlEmail.class)); + + // when + boolean result = sendMailSpy.sendPasswordMail("Player", "user@example.com", "new_password"); + + // then + assertThat(result, equalTo(true)); + verify(sendMailSpy).initializeMail("user@example.com"); + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); + verify(sendMailSpy).sendEmail(messageCaptor.capture(), eq(email)); + assertThat(messageCaptor.getValue(), + equalTo("Hi Player, your new password for serverName is new_password")); + } + + @Test + public void shouldHandleMailCreationError() throws EmailException { + // given + SendMailSSL sendMailSpy = spy(sendMailSSL); + doThrow(EmailException.class).when(sendMailSpy).initializeMail(anyString()); + + // when + boolean result = sendMailSpy.sendPasswordMail("Player", "user@example.com", "new_password"); + + // then + assertThat(result, equalTo(false)); + verify(sendMailSpy).initializeMail("user@example.com"); + verify(sendMailSpy, never()).sendEmail(anyString(), any(HtmlEmail.class)); + } + + @Test + public void shouldHandleMailSendingFailure() throws EmailException { + // given + given(settings.getPasswordEmailMessage()).willReturn("Hi , your new pass is "); + given(settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)).willReturn(false); + SendMailSSL sendMailSpy = spy(sendMailSSL); + HtmlEmail email = mock(HtmlEmail.class); + doReturn(email).when(sendMailSpy).initializeMail(anyString()); + doReturn(false).when(sendMailSpy).sendEmail(anyString(), any(HtmlEmail.class)); + + // when + boolean result = sendMailSpy.sendPasswordMail("bobby", "user@example.com", "myPassw0rd"); + + // then + assertThat(result, equalTo(false)); + verify(sendMailSpy).initializeMail("user@example.com"); + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); + verify(sendMailSpy).sendEmail(messageCaptor.capture(), eq(email)); + assertThat(messageCaptor.getValue(), equalTo("Hi bobby, your new pass is myPassw0rd")); + } + + @Test + public void shouldSendRecoveryCode() throws EmailException { + // given + given(settings.getProperty(SecuritySettings.RECOVERY_CODE_HOURS_VALID)).willReturn(7); + given(settings.getRecoveryCodeEmailMessage()) + .willReturn("Hi , your code on is (valid hours)"); + SendMailSSL sendMailSpy = spy(sendMailSSL); + HtmlEmail email = mock(HtmlEmail.class); + doReturn(email).when(sendMailSpy).initializeMail(anyString()); + doReturn(true).when(sendMailSpy).sendEmail(anyString(), any(HtmlEmail.class)); + + // when + boolean result = sendMailSpy.sendRecoveryCode("Timmy", "tim@example.com", "12C56A"); + + // then + assertThat(result, equalTo(true)); + verify(sendMailSpy).initializeMail("tim@example.com"); + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); + verify(sendMailSpy).sendEmail(messageCaptor.capture(), eq(email)); + assertThat(messageCaptor.getValue(), equalTo("Hi Timmy, your code on serverName is 12C56A (valid 7 hours)")); + } + + @Test + public void shouldHandleMailCreationErrorForRecoveryCode() throws EmailException { + // given + SendMailSSL sendMailSpy = spy(sendMailSSL); + doThrow(EmailException.class).when(sendMailSpy).initializeMail(anyString()); + + // when + boolean result = sendMailSpy.sendRecoveryCode("Player", "player@example.org", "ABC1234"); + + // then + assertThat(result, equalTo(false)); + verify(sendMailSpy).initializeMail("player@example.org"); + verify(sendMailSpy, never()).sendEmail(anyString(), any(HtmlEmail.class)); + } + + @Test + public void shouldHandleFailureToSendRecoveryCode() throws EmailException { + // given + given(settings.getProperty(SecuritySettings.RECOVERY_CODE_HOURS_VALID)).willReturn(7); + given(settings.getRecoveryCodeEmailMessage()).willReturn("Hi , your code is "); + SendMailSSL sendMailSpy = spy(sendMailSSL); + HtmlEmail email = mock(HtmlEmail.class); + doReturn(email).when(sendMailSpy).initializeMail(anyString()); + doReturn(false).when(sendMailSpy).sendEmail(anyString(), any(HtmlEmail.class)); + + // when + boolean result = sendMailSpy.sendRecoveryCode("John", "user@example.com", "1DEF77"); + + // then + assertThat(result, equalTo(false)); + verify(sendMailSpy).initializeMail("user@example.com"); + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); + verify(sendMailSpy).sendEmail(messageCaptor.capture(), eq(email)); + assertThat(messageCaptor.getValue(), equalTo("Hi John, your code is 1DEF77")); + } + + @Test + public void shouldCreateEmailObject() throws EmailException { + // given + given(settings.getProperty(EmailSettings.SMTP_PORT)).willReturn(465); + String smtpHost = "mail.example.com"; + given(settings.getProperty(EmailSettings.SMTP_HOST)).willReturn(smtpHost); + String senderMail = "sender@example.org"; + given(settings.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn(senderMail); + String senderName = "Server administration"; + given(settings.getProperty(EmailSettings.MAIL_SENDER_NAME)).willReturn(senderName); + + // when + HtmlEmail email = sendMailSSL.initializeMail("recipient@example.com"); + + // then + assertThat(email, not(nullValue())); + assertThat(email.getToAddresses(), hasSize(1)); + assertThat(email.getToAddresses().get(0).getAddress(), equalTo("recipient@example.com")); + assertThat(email.getFromAddress().getAddress(), equalTo(senderMail)); + assertThat(email.getFromAddress().getPersonal(), equalTo(senderName)); + assertThat(email.getHostName(), equalTo(smtpHost)); + assertThat(email.getSmtpPort(), equalTo("465")); + } + + @Test + public void shouldCreateEmailObjectWithOAuth2() throws EmailException { + // given + given(settings.getProperty(EmailSettings.SMTP_PORT)).willReturn(587); + given(settings.getProperty(EmailSettings.OAUTH2_TOKEN)).willReturn("oAuth2 token"); + String smtpHost = "mail.example.com"; + given(settings.getProperty(EmailSettings.SMTP_HOST)).willReturn(smtpHost); + String senderMail = "sender@example.org"; + given(settings.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn(senderMail); + + // when + HtmlEmail email = sendMailSSL.initializeMail("recipient@example.com"); + + // then + assertThat(email, not(nullValue())); + assertThat(email.getToAddresses(), hasSize(1)); + assertThat(email.getToAddresses().get(0).getAddress(), equalTo("recipient@example.com")); + assertThat(email.getFromAddress().getAddress(), equalTo(senderMail)); + assertThat(email.getHostName(), equalTo(smtpHost)); + assertThat(email.getSmtpPort(), equalTo("587")); + + Properties mailProperties = email.getMailSession().getProperties(); + assertThat(mailProperties.getProperty("mail.smtp.auth.mechanisms"), equalTo("XOAUTH2")); + assertThat(mailProperties.getProperty("mail.smtp.auth.plain.disable"), equalTo("true")); + assertThat(mailProperties.getProperty(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP), equalTo("oAuth2 token")); + } + +} diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index a8ff7ddb2..12949c5aa 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -19,14 +19,14 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link Messages}. @@ -160,7 +160,7 @@ public class MessagesIntegrationTest { messages.send(sender, key); // then - verify(sender).sendMessage(argThat(equalTo("Use /captcha THE_CAPTCHA to solve the captcha"))); + verify(sender).sendMessage("Use /captcha THE_CAPTCHA to solve the captcha"); } @Test diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java index 7fa6fecba..1499c976d 100644 --- a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java +++ b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; +import static tools.utils.FileIoUtils.listFilesOrThrow; import static org.junit.Assert.fail; /** @@ -28,11 +29,7 @@ public class YamlTextFileCheckerTest { @BeforeClass public static void loadMessagesFiles() { File folder = TestHelper.getJarFile(MESSAGES_FOLDER); - File[] files = folder.listFiles(); - if (files == null || files.length == 0) { - throw new IllegalStateException("Could not read folder '" + folder.getName() + "'"); - } - messageFiles = Arrays.asList(files); + messageFiles = Arrays.asList(listFilesOrThrow(folder)); } @Test @@ -82,17 +79,13 @@ public class YamlTextFileCheckerTest { * @param errors collection of errors to add to if the verification fails */ private void checkFile(File file, String mandatoryKey, List errors) { - String error = null; try { YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); if (StringUtils.isEmpty(configuration.getString(mandatoryKey))) { - error = "Message for '" + mandatoryKey + "' is empty"; + errors.add("Message for '" + mandatoryKey + "' is empty"); } } catch (Exception e) { - error = "Could not load file: " + StringUtils.formatException(e); - } - if (!StringUtils.isEmpty(error)) { - errors.add(file.getName() + ": " + error); + errors.add("Could not load file: " + StringUtils.formatException(e)); } } } diff --git a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java index f44ef2220..757319d55 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java @@ -2,6 +2,9 @@ package fr.xephi.authme.permission; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import fr.xephi.authme.ClassCollector; +import fr.xephi.authme.TestHelper; import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -12,9 +15,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static fr.xephi.authme.TestHelper.getJarFile; import static org.junit.Assert.fail; @@ -24,10 +29,6 @@ import static org.junit.Assert.fail; */ public class PermissionConsistencyTest { - /** All classes defining permission nodes. */ - private static final Set> PERMISSION_CLASSES = ImmutableSet - .of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class); - /** Wildcard permissions (present in plugin.yml but not in the codebase). */ private static final Set PLUGIN_YML_PERMISSIONS_WILDCARDS = ImmutableSet.of("authme.admin.*", "authme.player.*", "authme.player.email"); @@ -35,6 +36,9 @@ public class PermissionConsistencyTest { /** Name of the fields that make up a permission entry in plugin.yml. */ private static final Set PERMISSION_FIELDS = ImmutableSet.of("description", "default", "children"); + /** All classes defining permission nodes. */ + private static List> permissionClasses; + /** All known PermissionNode objects. */ private static List permissionNodes; @@ -43,6 +47,10 @@ public class PermissionConsistencyTest { @BeforeClass public static void gatherPermissionNodes() { + permissionClasses = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT + "permission") + .collectClasses(PermissionNode.class).stream() + .filter(clz -> !clz.isInterface()) + .collect(Collectors.toList()); permissionNodes = getPermissionsFromClasses(); pluginYmlPermissions = getPermissionsFromPluginYmlFile(); } @@ -109,7 +117,7 @@ public class PermissionConsistencyTest { */ private static List getPermissionsFromClasses() { List nodes = new ArrayList<>(); - for (Class clazz : PERMISSION_CLASSES) { + for (Class clazz : permissionClasses) { nodes.addAll(Arrays.asList(clazz.getEnumConstants())); } return Collections.unmodifiableList(nodes); @@ -163,15 +171,16 @@ public class PermissionConsistencyTest { // Replace ending .* in path if present, e.g. authme.player.* -> authme.player // Add ending '.' since we want all children to be children, i.e. authme.playertest would not be OK String root = definition.node.replaceAll("\\.\\*$", "") + "."; - List badChildren = new ArrayList<>(); - for (String child : definition.children) { - if (!child.startsWith(root)) { - badChildren.add(child); - } + Set expectedChildren = permissionNodes.stream().map(PermissionNode::getNode) + .filter(n -> n.startsWith(root)).collect(Collectors.toSet()); + Set missingChildren = Sets.difference(expectedChildren, definition.children); + Set unexpectedChildren = Sets.difference(definition.children, expectedChildren); + + if (!missingChildren.isEmpty()) { + errorList.add("Node '" + definition.node + "' has missing children: " + missingChildren); } - if (!badChildren.isEmpty()) { - errorList.add("Permission '" + definition.node + "' has children that are not logically below it: " - + String.join(", ", badChildren)); + if (!unexpectedChildren.isEmpty()) { + errorList.add("Node '" + definition.node + "' has unexpected children: " + unexpectedChildren); } } @@ -181,7 +190,7 @@ public class PermissionConsistencyTest { private static final class PermissionDefinition { private final String node; - private final List children; + private final Set children; private final DefaultPermission expectedDefault; PermissionDefinition(MemorySection memorySection) { @@ -193,7 +202,7 @@ public class PermissionConsistencyTest { collectChildren((MemorySection) memorySection.get("children"), children); this.children = removeStart(memorySection.getCurrentPath() + ".children.", children); } else { - this.children = Collections.emptyList(); + this.children = Collections.emptySet(); } } @@ -243,6 +252,10 @@ public class PermissionConsistencyTest { if (entry.getValue() instanceof MemorySection) { collectChildren((MemorySection) entry.getValue(), children); } else if (entry.getValue() instanceof Boolean) { + if (!Boolean.TRUE.equals(entry.getValue())) { + throw new IllegalStateException("Child entry '" + entry.getKey() + + "' has unexpected value '" + entry.getValue() + "'"); + } children.add(parentSection.getCurrentPath() + "." + entry.getKey()); } else { throw new IllegalStateException("Found child entry at '" + entry.getKey() + "' with value " @@ -258,8 +271,8 @@ public class PermissionConsistencyTest { * @param list the entries to modify * @return list with shortened entries */ - private static List removeStart(String start, List list) { - List result = new ArrayList<>(list.size()); + private static Set removeStart(String start, List list) { + Set result = new HashSet<>(list.size()); for (String entry : list) { result.add(entry.substring(start.length())); } diff --git a/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java b/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java index 8ede82b63..6c7efb452 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java +++ b/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java @@ -8,7 +8,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -62,7 +62,6 @@ public class PermissionsManagerTest { // given PermissionNode node = TestPermissions.WORLD_DOMINATION; CommandSender sender = mock(CommandSender.class); - given(sender.isOp()).willReturn(true); // when boolean result = permissionsManager.hasPermission(sender, node); @@ -116,7 +115,6 @@ public class PermissionsManagerTest { // given PermissionNode node = TestPermissions.WORLD_DOMINATION; Player player = mock(Player.class); - given(player.isOp()).willReturn(true); // when boolean result = permissionsManager.hasPermission(player, node); diff --git a/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java b/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java index 809014ada..ec63c5889 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java +++ b/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java @@ -23,15 +23,15 @@ public class PermissionsSystemTypeTest { List pluginNames = new ArrayList<>(PermissionsSystemType.values().length); for (PermissionsSystemType system : PermissionsSystemType.values()) { - assertThat("Name for enum entry '" + system + "' is not null", - system.getName(), not(nullValue())); + assertThat("Display name for enum entry '" + system + "' is not null", + system.getDisplayName(), not(nullValue())); assertThat("Plugin name for enum entry '" + system + "' is not null", system.getPluginName(), not(nullValue())); - assertThat("Only one enum entry has name '" + system.getName() + "'", - names, not(hasItem(system.getName()))); + assertThat("Only one enum entry has display name '" + system.getDisplayName() + "'", + names, not(hasItem(system.getDisplayName()))); assertThat("Only one enum entry has plugin name '" + system.getPluginName() + "'", pluginNames, not(hasItem(system.getPluginName()))); - names.add(system.getName()); + names.add(system.getDisplayName()); pluginNames.add(system.getPluginName()); } } diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java index e14c69f5e..c8c829622 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java @@ -5,7 +5,8 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.RegistrationSettings; import org.bukkit.entity.Player; import org.junit.BeforeClass; @@ -13,10 +14,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -40,7 +41,10 @@ public class AsyncAddEmailTest { private PlayerCache playerCache; @Mock - private ProcessService service; + private CommonService service; + + @Mock + private ValidationService validationService; @BeforeClass public static void setUp() { @@ -57,8 +61,8 @@ public class AsyncAddEmailTest { given(auth.getEmail()).willReturn(null); given(playerCache.getAuth("tester")).willReturn(auth); given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(true); - given(service.validateEmail(email)).willReturn(true); - given(service.isEmailFreeForRegistration(email, player)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, player)).willReturn(true); // when asyncAddEmail.addEmail(player, email); @@ -79,10 +83,9 @@ public class AsyncAddEmailTest { PlayerAuth auth = mock(PlayerAuth.class); given(auth.getEmail()).willReturn(null); given(playerCache.getAuth("tester")).willReturn(auth); - given(dataSource.countAuthsByEmail(email)).willReturn(0); given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(false); - given(service.validateEmail(email)).willReturn(true); - given(service.isEmailFreeForRegistration(email, player)).willReturn(true); + given(validationService.validateEmail(email)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, player)).willReturn(true); // when asyncAddEmail.addEmail(player, email); @@ -118,7 +121,7 @@ public class AsyncAddEmailTest { PlayerAuth auth = mock(PlayerAuth.class); given(auth.getEmail()).willReturn(null); given(playerCache.getAuth("my_player")).willReturn(auth); - given(service.validateEmail(email)).willReturn(false); + given(validationService.validateEmail(email)).willReturn(false); // when asyncAddEmail.addEmail(player, email); @@ -137,8 +140,8 @@ public class AsyncAddEmailTest { PlayerAuth auth = mock(PlayerAuth.class); given(auth.getEmail()).willReturn(null); given(playerCache.getAuth("testname")).willReturn(auth); - given(service.validateEmail(email)).willReturn(true); - given(service.isEmailFreeForRegistration(email, player)).willReturn(false); + given(validationService.validateEmail(email)).willReturn(true); + given(validationService.isEmailFreeForRegistration(email, player)).willReturn(false); // when asyncAddEmail.addEmail(player, email); diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java index ea18f86bb..145de51c7 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java @@ -4,17 +4,18 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.properties.RegistrationSettings; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -39,7 +40,10 @@ public class AsyncChangeEmailTest { private DataSource dataSource; @Mock - private ProcessService service; + private CommonService service; + + @Mock + private ValidationService validationService; @Test public void shouldAddEmail() { @@ -50,8 +54,8 @@ public class AsyncChangeEmailTest { PlayerAuth auth = authWithMail("old@mail.tld"); given(playerCache.getAuth("bobby")).willReturn(auth); given(dataSource.updateEmail(auth)).willReturn(true); - given(service.validateEmail(newEmail)).willReturn(true); - given(service.isEmailFreeForRegistration(newEmail, player)).willReturn(true); + given(validationService.validateEmail(newEmail)).willReturn(true); + given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -71,8 +75,8 @@ public class AsyncChangeEmailTest { PlayerAuth auth = authWithMail("old@mail.tld"); given(playerCache.getAuth("bobby")).willReturn(auth); given(dataSource.updateEmail(auth)).willReturn(false); - given(service.validateEmail(newEmail)).willReturn(true); - given(service.isEmailFreeForRegistration(newEmail, player)).willReturn(true); + given(validationService.validateEmail(newEmail)).willReturn(true); + given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(true); // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -108,7 +112,7 @@ public class AsyncChangeEmailTest { given(playerCache.isAuthenticated("bobby")).willReturn(true); PlayerAuth auth = authWithMail("old@mail.tld"); given(playerCache.getAuth("bobby")).willReturn(auth); - given(service.validateEmail(newEmail)).willReturn(false); + given(validationService.validateEmail(newEmail)).willReturn(false); // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -127,9 +131,7 @@ public class AsyncChangeEmailTest { given(playerCache.isAuthenticated("bobby")).willReturn(true); PlayerAuth auth = authWithMail("other@address.email"); given(playerCache.getAuth("bobby")).willReturn(auth); - given(service.validateEmail(newEmail)).willReturn(true); - given(service.isEmailFreeForRegistration(newEmail, player)).willReturn(true); - + given(validationService.validateEmail(newEmail)).willReturn(true); // when process.changeEmail(player, "old@mail.tld", newEmail); @@ -148,8 +150,8 @@ public class AsyncChangeEmailTest { given(playerCache.isAuthenticated("username")).willReturn(true); PlayerAuth auth = authWithMail("old@example.com"); given(playerCache.getAuth("username")).willReturn(auth); - given(service.validateEmail(newEmail)).willReturn(true); - given(service.isEmailFreeForRegistration(newEmail, player)).willReturn(false); + given(validationService.validateEmail(newEmail)).willReturn(true); + given(validationService.isEmailFreeForRegistration(newEmail, player)).willReturn(false); // when process.changeEmail(player, "old@example.com", newEmail); diff --git a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java index 33e8f05b1..d2a605297 100644 --- a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java +++ b/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java @@ -8,13 +8,13 @@ import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.LimboPlayerTaskManager; -import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; import org.junit.BeforeClass; import org.junit.Test; @@ -23,7 +23,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; @@ -31,9 +31,9 @@ import java.util.Collection; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -56,7 +56,7 @@ public class AsynchronousLoginTest { @Mock private PlayerCache playerCache; @Mock - private ProcessService processService; + private CommonService commonService; @Mock private LimboPlayerTaskManager limboPlayerTaskManager; @Mock @@ -81,7 +81,7 @@ public class AsynchronousLoginTest { // then verify(playerCache, only()).isAuthenticated(name); - verify(processService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + verify(commonService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); verifyZeroInteractions(dataSource); } @@ -98,7 +98,7 @@ public class AsynchronousLoginTest { // then verify(playerCache, only()).isAuthenticated(name); - verify(processService).send(player, MessageKey.USER_NOT_REGISTERED); + verify(commonService).send(player, MessageKey.USER_NOT_REGISTERED); verify(dataSource, only()).getAuth(name); } @@ -111,15 +111,15 @@ public class AsynchronousLoginTest { int groupId = 13; PlayerAuth auth = PlayerAuth.builder().name(name).groupId(groupId).build(); given(dataSource.getAuth(name)).willReturn(auth); - given(processService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn("group"); - given(processService.getProperty(HooksSettings.NON_ACTIVATED_USERS_GROUP)).willReturn(groupId); + given(commonService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn("group"); + given(commonService.getProperty(HooksSettings.NON_ACTIVATED_USERS_GROUP)).willReturn(groupId); // when asynchronousLogin.forceLogin(player); // then verify(playerCache, only()).isAuthenticated(name); - verify(processService).send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); + verify(commonService).send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); verify(dataSource, only()).getAuth(name); } @@ -133,7 +133,7 @@ public class AsynchronousLoginTest { given(playerCache.isAuthenticated(name)).willReturn(false); PlayerAuth auth = PlayerAuth.builder().name(name).build(); given(dataSource.getAuth(name)).willReturn(auth); - given(processService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn(""); + given(commonService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn(""); doReturn(true).when(asynchronousLogin).hasReachedMaxLoggedInPlayersForIp(any(Player.class), anyString()); // when @@ -141,7 +141,7 @@ public class AsynchronousLoginTest { // then verify(playerCache, only()).isAuthenticated(name); - verify(processService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + verify(commonService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); verify(dataSource, only()).getAuth(name); verify(asynchronousLogin).hasReachedMaxLoggedInPlayersForIp(player, ip); } @@ -156,13 +156,13 @@ public class AsynchronousLoginTest { given(playerCache.isAuthenticated(name)).willReturn(false); PlayerAuth auth = PlayerAuth.builder().name(name).build(); given(dataSource.getAuth(name)).willReturn(auth); - given(processService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn(""); - given(processService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); + given(commonService.getProperty(DatabaseSettings.MYSQL_COL_GROUP)).willReturn(""); + given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); doReturn(false).when(asynchronousLogin).hasReachedMaxLoggedInPlayersForIp(any(Player.class), anyString()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { - invocation.getArgumentAt(0, AuthMeAsyncPreLoginEvent.class).setCanLogin(false); + ((AuthMeAsyncPreLoginEvent) invocation.getArgument(0)).setCanLogin(false); return null; } }).when(bukkitService).callEvent(any(AuthMeAsyncPreLoginEvent.class)); @@ -181,7 +181,7 @@ public class AsynchronousLoginTest { public void shouldPassMaxLoginPerIpCheck() { // given Player player = mockPlayer("Carl"); - given(processService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); + given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(false); mockOnlinePlayersInBukkitService(); @@ -198,7 +198,7 @@ public class AsynchronousLoginTest { public void shouldSkipIpCheckForZeroThreshold() { // given Player player = mockPlayer("Fiona"); - given(processService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(0); + given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(0); // when boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "192.168.0.1"); @@ -212,7 +212,7 @@ public class AsynchronousLoginTest { public void shouldSkipIpCheckForPlayerWithMultipleAccountsPermission() { // given Player player = mockPlayer("Frank"); - given(processService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(1); + given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(1); given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true); // when @@ -228,7 +228,7 @@ public class AsynchronousLoginTest { public void shouldFailIpCheckForIpWithTooManyPlayersOnline() { // given Player player = mockPlayer("Ian"); - given(processService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); + given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(false); mockOnlinePlayersInBukkitService(); @@ -271,7 +271,6 @@ public class AsynchronousLoginTest { // 192.168.0.0: france (offline) Player playerF = mockPlayer("france"); TestHelper.mockPlayerIp(playerF, "192.168.0.0"); - given(dataSource.isLogged(playerF.getName())).willReturn(false); Collection onlinePlayers = Arrays.asList(playerA, playerB, playerC, playerD, playerE, playerF); given(bukkitService.getOnlinePlayers()).willReturn(onlinePlayers); diff --git a/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java index 318b782a7..d586b425a 100644 --- a/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java +++ b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java @@ -8,14 +8,14 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupType; -import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.service.CommonService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.LimboPlayerTaskManager; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.TeleportationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.BeforeClass; @@ -23,10 +23,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; @@ -45,7 +45,7 @@ public class AsynchronousUnregisterTest { @Mock private DataSource dataSource; @Mock - private ProcessService service; + private CommonService service; @Mock private PasswordSecurity passwordSecurity; @Mock @@ -185,7 +185,6 @@ public class AsynchronousUnregisterTest { Player player = mock(Player.class); String name = "Frank21"; given(player.getName()).willReturn(name); - given(player.isOnline()).willReturn(true); PlayerAuth auth = mock(PlayerAuth.class); given(playerCache.getAuth(name)).willReturn(auth); HashedPassword password = new HashedPassword("password", "in_auth_obj"); @@ -218,9 +217,6 @@ public class AsynchronousUnregisterTest { String userPassword = "pass"; given(passwordSecurity.comparePassword(userPassword, password, name)).willReturn(true); given(dataSource.removeAuth(name)).willReturn(true); - given(service.getProperty(RegistrationSettings.FORCE)).willReturn(true); - given(service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)).willReturn(true); - given(service.getProperty(RestrictionSettings.TIMEOUT)).willReturn(12); // when asynchronousUnregister.unregister(player, userPassword); @@ -238,7 +234,6 @@ public class AsynchronousUnregisterTest { // given Player player = mock(Player.class); String name = "Frank21"; - given(player.getName()).willReturn(name); given(player.isOnline()).willReturn(true); given(dataSource.removeAuth(name)).willReturn(true); given(service.getProperty(RegistrationSettings.FORCE)).willReturn(true); diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index fcfa4fad3..2d5368197 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -32,6 +32,7 @@ public class HashAlgorithmIntegrationTest { Settings settings = mock(Settings.class); given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8); given(settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH)).willReturn(16); + given(settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS)).willReturn(10_000); injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); injector.register(Settings.class, settings); } diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index 2e6adff4d..bca8b6d50 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -21,20 +21,26 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.newHashSet; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PasswordSecurity}. @@ -97,7 +103,7 @@ public class PasswordSecurityTest { given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true); - initSettings(HashAlgorithm.BCRYPT, false); + initSettings(HashAlgorithm.BCRYPT); PasswordSecurity security = newPasswordSecurity(); // when @@ -120,7 +126,7 @@ public class PasswordSecurityTest { given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); - initSettings(HashAlgorithm.CUSTOM, false); + initSettings(HashAlgorithm.CUSTOM); PasswordSecurity security = newPasswordSecurity(); // when @@ -140,7 +146,7 @@ public class PasswordSecurityTest { String clearTextPass = "tables"; given(dataSource.getPassword(playerName)).willReturn(null); - initSettings(HashAlgorithm.MD5, false); + initSettings(HashAlgorithm.MD5); PasswordSecurity security = newPasswordSecurity(); // when @@ -168,7 +174,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword); - initSettings(HashAlgorithm.MD5, true); + initSettings(HashAlgorithm.MD5); + given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(newArrayList(HashAlgorithm.BCRYPT.name())); PasswordSecurity security = newPasswordSecurity(); // when @@ -193,7 +200,7 @@ public class PasswordSecurityTest { String clearTextPass = "someInvalidPassword"; given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false); - initSettings(HashAlgorithm.MD5, true); + initSettings(HashAlgorithm.MD5); PasswordSecurity security = newPasswordSecurity(); // when @@ -212,7 +219,7 @@ public class PasswordSecurityTest { String usernameLowerCase = username.toLowerCase(); HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__"); given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword); - initSettings(HashAlgorithm.JOOMLA, true); + initSettings(HashAlgorithm.JOOMLA); PasswordSecurity security = newPasswordSecurity(); // when @@ -233,9 +240,8 @@ public class PasswordSecurityTest { String password = "?topSecretPass\\"; String username = "someone12"; HashedPassword hashedPassword = new HashedPassword("~T!est#Hash"); - given(method.computeHash(password, username)).willReturn(hashedPassword); given(method.hasSeparateSalt()).willReturn(true); - initSettings(HashAlgorithm.XAUTH, false); + initSettings(HashAlgorithm.XAUTH); PasswordSecurity security = newPasswordSecurity(); // when @@ -251,19 +257,21 @@ public class PasswordSecurityTest { @Test public void shouldReloadSettings() { // given - initSettings(HashAlgorithm.BCRYPT, false); + initSettings(HashAlgorithm.BCRYPT); PasswordSecurity passwordSecurity = newPasswordSecurity(); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.MD5); - given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(true); + List legacyHashes = newArrayList(HashAlgorithm.CUSTOM.name(), HashAlgorithm.BCRYPT.name()); + given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(legacyHashes); // when passwordSecurity.reload(); // then assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "algorithm"), - equalTo((Object) HashAlgorithm.MD5)); - assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "supportOldAlgorithm"), - equalTo((Object) Boolean.TRUE)); + equalTo(HashAlgorithm.MD5)); + Set legacyHashesSet = newHashSet(HashAlgorithm.CUSTOM, HashAlgorithm.BCRYPT); + assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "legacyAlgorithms"), + equalTo(legacyHashesSet)); } private PasswordSecurity newPasswordSecurity() { @@ -276,11 +284,10 @@ public class PasswordSecurityTest { return passwordSecurity; } - private void initSettings(HashAlgorithm algorithm, boolean supportOldPassword) { + private void initSettings(HashAlgorithm algorithm) { given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm); - given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(supportOldPassword); + given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(Collections.emptyList()); given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8); - given(settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH)).willReturn(16); } } diff --git a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java b/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java index 4c06a8cc1..24093721a 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java @@ -2,7 +2,9 @@ package fr.xephi.authme.security.crypts; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.security.crypts.description.AsciiRestricted; +import org.junit.BeforeClass; import org.junit.Test; import java.util.List; @@ -100,6 +102,11 @@ public abstract class AbstractEncryptionMethodTest { GIVEN_PASSWORDS[3], result3); } + @BeforeClass + public static void setupLogger() { + TestHelper.setupLogger(); + } + @Test public void testGivenPasswords() { // Start with the 2nd to last password if we skip long tests diff --git a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java index 3c0f1475a..1fe9ed086 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java @@ -1,9 +1,7 @@ package fr.xephi.authme.security.crypts; -import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.HooksSettings; -import org.junit.BeforeClass; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -13,11 +11,6 @@ import static org.mockito.Mockito.mock; */ public class BcryptTest extends AbstractEncryptionMethodTest { - @BeforeClass - public static void initializeLogger() { - TestHelper.setupLogger(); - } - public BcryptTest() { super(new BCRYPT(mockSettings()), "$2a$10$6iATmYgwJVc3YONhVcZFve3Cfb5GnwvKhJ20r.hMjmcNkIT9.Uh9K", // password diff --git a/src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2Test.java b/src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2Test.java deleted file mode 100644 index 36ce7106a..000000000 --- a/src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2Test.java +++ /dev/null @@ -1,20 +0,0 @@ -package fr.xephi.authme.security.crypts; - -import org.junit.Ignore; - -/** - * Test for {@link CryptPBKDF2}. - */ -@Ignore -// TODO #685: This algorithm seems broken -public class CryptPBKDF2Test extends AbstractEncryptionMethodTest { - - public CryptPBKDF2Test() { - super(new CryptPBKDF2(), - "pbkdf2_sha256$10000$dd9b1cd071f2$[82, -69, -58, -51, 101, 105, 61, -48, -49, 25, 50, -126, 115, 36, 16, -94, 4, 84, -94, 13, -115, -12, 94, -27, 94, -103, 115, -31, -56, -18, 8, 77, 36, 78, -61, 105, -7, -114, 41, 3, 48, 122, 27, 1, 56, 76, 126, 68, -120, 127, -95, 119, -7, 100, -87, -128, -77, 83, -118, 28, 43, 84, 73, 103]", // password - "pbkdf2_sha256$10000$4b3b650288cd$[99, 25, 45, 22, -66, -109, -109, 30, 117, 77, 22, 63, -36, -126, -116, -66, 35, 109, -33, -4, -112, 53, 48, 33, -20, 107, -100, -37, -89, 59, -29, -83, 57, -123, -40, 11, 98, 32, -74, 77, 107, -76, 95, -9, 110, -92, -31, -2, -18, 115, 43, -27, 16, 36, 75, -56, -11, 58, -62, 21, 0, 37, -59, -82]", // PassWord1 - "pbkdf2_sha256$10000$035205f5ab39$[-121, -15, 97, 35, -105, -57, -49, -60, -58, -106, 101, 78, -103, 2, -116, -120, 0, 106, -107, 10, 78, -97, 111, 98, -15, 40, -53, 84, 120, -86, 116, 12, -60, 19, 105, 1, 71, 99, 4, 43, -4, -36, 35, -110, 59, 73, -20, -8, 46, 102, 51, 84, 54, -92, -41, -84, 28, 36, 37, 26, 90, -6, -49, 70]", // &^%te$t?Pw@_ - "pbkdf2_sha256$10000$ca72ded579e9$[-81, 76, -103, 78, 68, -10, -58, -88, -57, 88, -38, 108, 115, -86, 13, -84, 80, 69, 48, 15, 105, 25, -2, 123, 9, 97, 23, -96, 95, -64, -56, 59, -124, 116, 36, 10, 96, -12, -76, -121, -51, 76, -96, -27, 84, 66, 85, 75, 95, -97, -60, -98, -41, -32, -58, 39, 82, -19, -25, 98, -15, -68, 59, -48]"); // âË_3(íù* - } - -} diff --git a/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java b/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java index 325d37d57..28c76a09f 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java +++ b/src/test/java/fr/xephi/authme/security/crypts/IPB4Test.java @@ -1,18 +1,10 @@ package fr.xephi.authme.security.crypts; -import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; - /** * Test for {@link IPB4}. */ public class IPB4Test extends AbstractEncryptionMethodTest { - @BeforeClass - public static void setUpSettings() { - TestHelper.setupLogger(); - } - public IPB4Test() { super(new IPB4(), new HashedPassword("$2a$13$leEvXu77OIwPwNvtZIJvaeAx8EItGHuR3nIlq8416g0gXeJaQdrr2", "leEvXu77OIwPwNvtZIJval"), //password diff --git a/src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2DjangoTest.java b/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java similarity index 58% rename from src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2DjangoTest.java rename to src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java index 1da3c5b91..a5a1af616 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/CryptPBKDF2DjangoTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java @@ -1,20 +1,12 @@ package fr.xephi.authme.security.crypts; -import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; - /** - * Test for {@link CryptPBKDF2Django}. + * Test for {@link Pbkdf2Django}. */ -public class CryptPBKDF2DjangoTest extends AbstractEncryptionMethodTest { +public class Pbkdf2DjangoTest extends AbstractEncryptionMethodTest { - @BeforeClass - public static void setupLogger() { - TestHelper.setupLogger(); - } - - public CryptPBKDF2DjangoTest() { - super(new CryptPBKDF2Django(), + public Pbkdf2DjangoTest() { + super(new Pbkdf2Django(), "pbkdf2_sha256$15000$50a7ff2d7e00$t7Qx2CfzMhGEbyCa3Wk5nJvNjj3N+FdxhpwJDerl4Fs=", // password "pbkdf2_sha256$15000$f9d8a58f3fe2$oMqmMGuJetdubW0cpubmT8CltQLjHT+L2GuwKsaWLx8=", // PassWord1 "pbkdf2_sha256$15000$1170bc7a31f5$Ex/2aQsXm4kogLIYARpUPn04ccK5LYYjyVPpl32ALjE=", // &^%te$t?Pw@_ diff --git a/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java b/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java new file mode 100644 index 000000000..8296e56ea --- /dev/null +++ b/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java @@ -0,0 +1,44 @@ +package fr.xephi.authme.security.crypts; + +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link Pbkdf2}. + */ +public class Pbkdf2Test extends AbstractEncryptionMethodTest { + + public Pbkdf2Test() { + super(new Pbkdf2(mockSettings()), + "pbkdf2_sha256$10000$b25801311edf$093E38B16DFF13FCE5CD64D5D888EE6E0376A3E572FE5DA6749515EA0F384413223A21C464B0BE899E64084D1FFEFD44F2AC768453C87F41B42CC6954C416900", // password + "pbkdf2_sha256$10000$fe705da06c57$A41527BD58FED9C9E6F452FC1BA8B0C4C4224ECC63E37F71EB1A0865D2AB81BBFEBCA9B7B6A6E8AEF4717B43F8EB6FB4EDEFFBB399D9D991EF7E23013595BAF0", // PassWord1 + "pbkdf2_sha256$10000$05603593cdda$1D30D1D90D826C866755969F06C312E21CC3E8DA0B777E2C764700E4E1FD890B731FAF44753D68F3FC025D3EAA709E800FBF2AF61DB23464311FCE7D35353A30", // &^%te$t?Pw@_ + "pbkdf2_sha256$10000$fb944d66d754$F7E3BF8CB07CE3B3C8C5C534F803252F7B4FD58832E33BA62BA46CA06F23BAE12BE03A9CB5874BCFD4469E42972406F920E59F002247B23C22A8CF3D0E7BFFE0"); // âË_3(íù* + } + + @Test + public void shouldDetectMatchForHashWithOtherRoundNumber() { + // given + Pbkdf2 pbkdf2 = new Pbkdf2(mockSettings()); + String hash = "pbkdf2_sha256$4128$3469b0d48b702046$DC8A54351008C6054E12FB19E0BF8A4EA6D4165E0EDC97A1ECD15231037C382DE5BF85D07D5BC9D1ADF9BBFE4CE257C6059FB1B9FF65DB69D8B205F064BE0DA9"; + String clearText = "PassWord1"; + + // when + boolean isMatch = pbkdf2.comparePassword(clearText, new HashedPassword(hash), ""); + + // then + assertThat(isMatch, equalTo(true)); + } + + private static Settings mockSettings() { + Settings settings = mock(Settings.class); + given(settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS)).willReturn(4128); + return settings; + } +} diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java index bda1c0f8c..4edafbdd0 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java @@ -1,18 +1,10 @@ package fr.xephi.authme.security.crypts; -import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; - /** * Test for {@link XFBCRYPT}. */ public class XFBCRYPTTest extends AbstractEncryptionMethodTest { - @BeforeClass - public static void setup() { - TestHelper.setupLogger(); - } - public XFBCRYPTTest() { super(new XFBCRYPT(), "$2a$10$UtuON/ZG.x8EWG/zQbryB.BHfQVrfxk3H7qykzP.UJQ8YiLjZyfqq", // password diff --git a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java index e40d5ca79..dd6b6c789 100644 --- a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java @@ -3,7 +3,6 @@ package fr.xephi.authme.service; import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.AdminPermission; @@ -12,6 +11,7 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -22,9 +22,9 @@ import java.util.List; import static fr.xephi.authme.TestHelper.runSyncDelayedTaskWithDelay; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.reset; @@ -52,6 +52,7 @@ public class AntiBotServiceTest { @BeforeInjecting public void initSettings() { given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(10); + given(settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL)).willReturn(5); given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(5); given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true); given(settings.getProperty(ProtectionSettings.ANTIBOT_DELAY)).willReturn(8); @@ -81,6 +82,7 @@ public class AntiBotServiceTest { } @Test + @Ignore // TODO ljacqu 20161030: Fix test public void shouldActivateAntibot() { // given - listening antibot runSyncDelayedTaskWithDelay(bukkitService); @@ -131,43 +133,12 @@ public class AntiBotServiceTest { runSyncDelayedTaskWithDelay(bukkitService); // when - boolean result = antiBotService.shouldKick(false); + boolean result = antiBotService.shouldKick(); // then assertThat(result, equalTo(false)); } - @Test - public void shouldRejectPlayerWithoutAuth() { - // given - active antibot - runSyncDelayedTaskWithDelay(bukkitService); - antiBotService.overrideAntiBotStatus(true); - - // when - boolean kickWithoutAuth = antiBotService.shouldKick(false); - boolean kickWithAuth = antiBotService.shouldKick(true); - - // then - assertThat(kickWithoutAuth, equalTo(true)); - assertThat(kickWithAuth, equalTo(false)); - } - - @Test - public void shouldIncreaseCountAndDecreaseAfterDelay() { - // given - listening antibot - runSyncDelayedTaskWithDelay(bukkitService); - reset(bukkitService); - assertThat(getAntiBotCount(antiBotService), equalTo(0)); - - // when - antiBotService.handlePlayerJoin(); - - // then - assertThat(getAntiBotCount(antiBotService), equalTo(1)); - runSyncDelayedTaskWithDelay(bukkitService); - assertThat(getAntiBotCount(antiBotService), equalTo(0)); - } - @Test public void shouldActivateAntibotAfterThreshold() { // given @@ -178,19 +149,19 @@ public class AntiBotServiceTest { runSyncDelayedTaskWithDelay(bukkitService); for (int i = 0; i < sensitivity; ++i) { - antiBotService.handlePlayerJoin(); + antiBotService.shouldKick(); } assertThat(antiBotService.getAntiBotStatus(), equalTo(AntiBotService.AntiBotStatus.LISTENING)); // when - antiBotService.handlePlayerJoin(); + antiBotService.shouldKick(); // then assertThat(antiBotService.getAntiBotStatus(), equalTo(AntiBotService.AntiBotStatus.ACTIVE)); } @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public void shouldInformPlayersOnActivation() { // given - listening antibot runSyncDelayedTaskWithDelay(bukkitService); @@ -222,7 +193,4 @@ public class AntiBotServiceTest { assertThat(antiBotService.getAntiBotStatus(), equalTo(AntiBotService.AntiBotStatus.LISTENING)); } - private static int getAntiBotCount(AntiBotService antiBotService) { - return ReflectionTestUtils.getFieldValue(AntiBotService.class, antiBotService, "antibotPlayers"); - } } diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java index d43f3c530..597f89a76 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java @@ -4,11 +4,16 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Collection; @@ -17,6 +22,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Test for {@link BukkitService}. @@ -24,10 +30,21 @@ import static org.mockito.Mockito.mock; @RunWith(MockitoJUnitRunner.class) public class BukkitServiceTest { + private BukkitService bukkitService; + @Mock private AuthMe authMe; @Mock private Settings settings; + @Mock + private Server server; + + @Before + public void constructBukkitService() { + ReflectionTestUtils.setField(Bukkit.class, null, "server", server); + given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); + bukkitService = new BukkitService(authMe, settings); + } /** * Checks that {@link BukkitService#getOnlinePlayersIsCollection} is initialized to {@code true} on startup; @@ -35,11 +52,7 @@ public class BukkitServiceTest { */ @Test public void shouldHavePlayerListAsCollectionMethod() { - // given - given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); - BukkitService bukkitService = new BukkitService(authMe, settings); - - // when + // given / when boolean doesMethodReturnCollection = ReflectionTestUtils .getFieldValue(BukkitService.class, bukkitService, "getOnlinePlayersIsCollection"); @@ -50,8 +63,6 @@ public class BukkitServiceTest { @Test public void shouldRetrieveListOfOnlinePlayersFromReflectedMethod() { // given - given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); - BukkitService bukkitService = new BukkitService(authMe, settings); ReflectionTestUtils.setField(BukkitService.class, bukkitService, "getOnlinePlayersIsCollection", false); ReflectionTestUtils.setField(BukkitService.class, bukkitService, "getOnlinePlayers", ReflectionTestUtils.getMethod(BukkitServiceTest.class, "onlinePlayersImpl")); @@ -63,6 +74,33 @@ public class BukkitServiceTest { assertThat(players, hasSize(2)); } + @Test + public void shouldDispatchCommand() { + // given + CommandSender sender = mock(CommandSender.class); + String command = "help test abc"; + + // when + bukkitService.dispatchCommand(sender, command); + + // then + verify(server).dispatchCommand(sender, command); + } + + @Test + public void shouldDispatchConsoleCommand() { + // given + ConsoleCommandSender consoleSender = mock(ConsoleCommandSender.class); + given(server.getConsoleSender()).willReturn(consoleSender); + String command = "my command"; + + // when + bukkitService.dispatchConsoleCommand(command); + + // then + verify(server).dispatchCommand(consoleSender, command); + } + // Note: This method is used through reflections public static Player[] onlinePlayersImpl() { return new Player[]{ diff --git a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java similarity index 65% rename from src/test/java/fr/xephi/authme/process/ProcessServiceTest.java rename to src/test/java/fr/xephi/authme/service/CommonServiceTest.java index 0c71cd510..ff992bfeb 100644 --- a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/CommonServiceTest.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.process; +package fr.xephi.authme.service; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -9,14 +9,13 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.service.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -25,16 +24,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; /** - * Test for {@link ProcessService}. + * Test for {@link CommonService}. */ @RunWith(MockitoJUnitRunner.class) -public class ProcessServiceTest { +public class CommonServiceTest { @InjectMocks - private ProcessService processService; - - @Mock - private ValidationService validationService; + private CommonService commonService; @Mock private Settings settings; @@ -54,22 +50,13 @@ public class ProcessServiceTest { given(settings.getProperty(SecuritySettings.CAPTCHA_LENGTH)).willReturn(8); // when - int result = processService.getProperty(SecuritySettings.CAPTCHA_LENGTH); + int result = commonService.getProperty(SecuritySettings.CAPTCHA_LENGTH); // then verify(settings).getProperty(SecuritySettings.CAPTCHA_LENGTH); assertThat(result, equalTo(8)); } - @Test - public void shouldReturnSettings() { - // given/when - Settings result = processService.getSettings(); - - // then - assertThat(result, equalTo(settings)); - } - @Test public void shouldSendMessageToPlayer() { // given @@ -77,7 +64,7 @@ public class ProcessServiceTest { MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; // when - processService.send(sender, key); + commonService.send(sender, key); // then verify(messages).send(sender, key); @@ -91,7 +78,7 @@ public class ProcessServiceTest { String[] replacements = new String[]{"test", "toast"}; // when - processService.send(sender, key, replacements); + commonService.send(sender, key, replacements); // then verify(messages).send(sender, key, replacements); @@ -105,7 +92,7 @@ public class ProcessServiceTest { given(messages.retrieve(key)).willReturn(lines); // when - String[] result = processService.retrieveMessage(key); + String[] result = commonService.retrieveMessage(key); // then assertThat(result, equalTo(lines)); @@ -120,43 +107,13 @@ public class ProcessServiceTest { given(messages.retrieveSingle(key)).willReturn(text); // when - String result = processService.retrieveSingleMessage(key); + String result = commonService.retrieveSingleMessage(key); // then assertThat(result, equalTo(text)); verify(messages).retrieveSingle(key); } - @Test - public void shouldValidateEmail() { - // given - String email = "test@example.tld"; - given(validationService.validateEmail(email)).willReturn(true); - - // when - boolean result = processService.validateEmail(email); - - // then - assertThat(result, equalTo(true)); - verify(validationService).validateEmail(email); - } - - @Test - public void shouldCheckIfEmailCanBeUsed() { - // given - String email = "mail@example.com"; - CommandSender sender = mock(CommandSender.class); - given(validationService.isEmailFreeForRegistration(email, sender)) - .willReturn(true); - - // when - boolean result = processService.isEmailFreeForRegistration(email, sender); - - // then - assertThat(result, equalTo(true)); - verify(validationService).isEmailFreeForRegistration(email, sender); - } - @Test public void shouldCheckPermission() { // given @@ -165,7 +122,7 @@ public class ProcessServiceTest { given(permissionsManager.hasPermission(player, permission)).willReturn(true); // when - boolean result = processService.hasPermission(player, permission); + boolean result = commonService.hasPermission(player, permission); // then verify(permissionsManager).hasPermission(player, permission); @@ -180,7 +137,7 @@ public class ProcessServiceTest { given(authGroupHandler.setGroup(player, type)).willReturn(true); // when - boolean result = processService.setGroup(player, type); + boolean result = commonService.setGroup(player, type); // then verify(authGroupHandler).setGroup(player, type); diff --git a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java b/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java index 5d1d9681f..60e3aa680 100644 --- a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java @@ -2,22 +2,21 @@ package fr.xephi.authme.service; import com.maxmind.geoip.Country; import com.maxmind.geoip.LookupService; -import fr.xephi.authme.service.GeoIpService; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java b/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java index 7283a4682..fa82b97ae 100644 --- a/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java @@ -13,7 +13,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; @@ -21,13 +21,13 @@ import java.util.Arrays; import static fr.xephi.authme.AuthMeMatchers.equalToHash; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link MigrationService}. @@ -126,7 +126,7 @@ public class MigrationServiceTest { given(sha256.computeHash(anyString(), anyString())).willAnswer(new Answer() { @Override public HashedPassword answer(InvocationOnMock invocation) { - String plainPassword = (String) invocation.getArguments()[0]; + String plainPassword = invocation.getArgument(0); return new HashedPassword(plainPassword.toUpperCase(), null); } }); diff --git a/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java b/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java index 970bcef45..d4e3f8cda 100644 --- a/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java @@ -5,7 +5,6 @@ import com.earth2me.essentials.User; import com.onarandombox.MultiverseCore.MultiverseCore; import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import fr.xephi.authme.service.PluginHookService; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import org.bukkit.Location; @@ -22,8 +21,8 @@ import java.io.File; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; diff --git a/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java b/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java index 30af5b0b2..3401738e8 100644 --- a/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java @@ -18,7 +18,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; @@ -26,9 +26,9 @@ import java.util.Arrays; import static fr.xephi.authme.TestHelper.runSyncDelayedTask; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -127,11 +127,6 @@ public class TeleportationServiceTest { public void shouldNotTeleportNewPlayer() { // given Player player = mock(Player.class); - given(player.hasPlayedBefore()).willReturn(false); - given(player.isOnline()).willReturn(true); - given(player.getWorld()).willReturn(mock(World.class)); - given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(false); - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); given(spawnLoader.getFirstSpawn()).willReturn(null); // when @@ -148,7 +143,6 @@ public class TeleportationServiceTest { public void shouldNotTeleportPlayerToFirstSpawnIfNoTeleportEnabled() { // given Player player = mock(Player.class); - given(player.hasPlayedBefore()).willReturn(false); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(true); // when @@ -163,7 +157,6 @@ public class TeleportationServiceTest { public void shouldNotTeleportNotNewPlayerToFirstSpawn() { // given Player player = mock(Player.class); - given(player.hasPlayedBefore()).willReturn(true); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); // when @@ -273,9 +266,7 @@ public class TeleportationServiceTest { given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(true); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(false); Player player = mock(Player.class); - given(player.isOnline()).willReturn(true); Location spawn = mockLocation(); - given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); PlayerAuth auth = mock(PlayerAuth.class); LimboPlayer limbo = mock(LimboPlayer.class); Location limboLocation = mockLocation(); @@ -293,7 +284,6 @@ public class TeleportationServiceTest { @Test public void shouldTeleportBackToPlayerAuthLocation() { // given - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); @@ -321,7 +311,6 @@ public class TeleportationServiceTest { @Test public void shouldTeleportAccordingToPlayerAuthAndPlayerWorldAsFallback() { // given - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); @@ -350,7 +339,6 @@ public class TeleportationServiceTest { @Test public void shouldTeleportWithLimboPlayerIfAuthYCoordIsNotSet() { // given - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); @@ -359,8 +347,6 @@ public class TeleportationServiceTest { auth.setWorld("authWorld"); Player player = mock(Player.class); given(player.isOnline()).willReturn(true); - World world = mock(World.class); - given(player.getWorld()).willReturn(world); LimboPlayer limbo = mock(LimboPlayer.class); Location location = mockLocation(); given(limbo.getLocation()).willReturn(location); @@ -377,15 +363,12 @@ public class TeleportationServiceTest { @Test public void shouldTeleportWithLimboPlayerIfSaveQuitLocIsDisabled() { // given - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(false); PlayerAuth auth = createAuthWithLocation(); Player player = mock(Player.class); given(player.isOnline()).willReturn(true); - World world = mock(World.class); - given(player.getWorld()).willReturn(world); LimboPlayer limbo = mock(LimboPlayer.class); Location location = mockLocation(); given(limbo.getLocation()).willReturn(location); @@ -403,7 +386,6 @@ public class TeleportationServiceTest { // given given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(false); given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); PlayerAuth auth = PlayerAuth.builder().name("bobby").build(); Player player = mock(Player.class); diff --git a/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java new file mode 100644 index 000000000..7123ac26b --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java @@ -0,0 +1,69 @@ +package fr.xephi.authme.settings; + +import com.github.authme.configme.knownproperties.ConfigurationData; +import com.github.authme.configme.properties.Property; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.fail; + +/** + * Tests the consistency of the settings configuration. + */ +public class SettingsConsistencyTest { + + /** + * Maximum characters one comment line may have (prevents horizontal scrolling). + */ + private static final int MAX_COMMENT_LENGTH = 90; + + private static ConfigurationData configurationData; + + @BeforeClass + public static void buildConfigurationData() { + configurationData = AuthMeSettingsRetriever.buildConfigurationData(); + } + + @Test + public void shouldHaveCommentOnEachProperty() { + // given + List> properties = configurationData.getProperties(); + + // when / then + for (Property property : properties) { + if (configurationData.getCommentsForSection(property.getPath()).length == 0) { + fail("No comment defined for '" + property + "'"); + } + } + } + + @Test + public void shouldNotHaveVeryLongCommentLines() { + // given + List> properties = configurationData.getProperties(); + List> badProperties = new ArrayList<>(); + + // when + for (Property property : properties) { + for (String comment : configurationData.getCommentsForSection(property.getPath())) { + if (comment.length() > MAX_COMMENT_LENGTH) { + badProperties.add(property); + break; + } + } + } + + // then + if (!badProperties.isEmpty()) { + fail("Comment lines should not be longer than " + MAX_COMMENT_LENGTH + " chars, " + + "but found too long comments for:\n- " + + badProperties.stream().map(Property::getPath).collect(Collectors.joining("\n- "))); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java b/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java index 9a6441979..d0d2c36a8 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java @@ -121,8 +121,7 @@ public class SettingsIntegrationTest { // given File configFile = temporaryFolder.newFile(); PropertyResource resource = new YamlFileResource(configFile); - Settings settings = new Settings(testPluginFolder, resource, - TestSettingsMigrationServices.alwaysFulfilled(), CONFIG_DATA); + Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA); // when assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.SECOND)); // default value diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java new file mode 100644 index 000000000..af109fbec --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java @@ -0,0 +1,88 @@ +package fr.xephi.authme.settings; + +import com.github.authme.configme.resource.PropertyResource; +import com.github.authme.configme.resource.YamlFileResource; +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.output.LogLevel; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static fr.xephi.authme.TestHelper.getJarFile; +import static fr.xephi.authme.settings.properties.PluginSettings.LOG_LEVEL; +import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE; +import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS; +import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN; +import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_ON_WORLDS; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link SettingsMigrationService}. + */ +public class SettingsMigrationServiceTest { + + private static final String OLD_CONFIG_FILE = TestHelper.PROJECT_ROOT + "settings/config-old.yml"; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldPerformMigrations() throws IOException { + // given + File dataFolder = temporaryFolder.newFolder(); + File configFile = new File(dataFolder, "config.yml"); + Files.copy(getJarFile(OLD_CONFIG_FILE), configFile); + PropertyResource resource = new YamlFileResource(configFile); + SettingsMigrationService migrationService = new SettingsMigrationService(dataFolder); + + // when + Settings settings = new Settings( + dataFolder, resource, migrationService, AuthMeSettingsRetriever.buildConfigurationData()); + + // then + assertThat(settings.getProperty(ALLOWED_NICKNAME_CHARACTERS), equalTo(ALLOWED_NICKNAME_CHARACTERS.getDefaultValue())); + assertThat(settings.getProperty(DELAY_JOIN_MESSAGE), equalTo(true)); + assertThat(settings.getProperty(FORCE_SPAWN_LOCATION_AFTER_LOGIN), equalTo(true)); + assertThat(settings.getProperty(FORCE_SPAWN_ON_WORLDS), contains("survival", "survival_nether", "creative")); + assertThat(settings.getProperty(LOG_LEVEL), equalTo(LogLevel.INFO)); + + // Check migration of old setting to email.html + assertThat(Files.readLines(new File(dataFolder, "email.html"), StandardCharsets.UTF_8), + contains("Dear ,

    This is your new AuthMe password for the server " + + "

    :



    Do not forget to " + + "change password after login!
    /changepassword newPassword")); + } + + @Test + public void shouldKeepOldForceCommandSettings() throws IOException { + // given + File dataFolder = temporaryFolder.newFolder(); + File configFile = new File(dataFolder, "config.yml"); + Files.copy(getJarFile(OLD_CONFIG_FILE), configFile); + PropertyResource resource = new YamlFileResource(configFile); + SettingsMigrationService migrationService = new SettingsMigrationService(dataFolder); + + // when + migrationService.performMigrations(resource, AuthMeSettingsRetriever.buildConfigurationData().getProperties()); + + // then + assertThat(migrationService.getOnLoginCommands(), contains("spawn")); + assertThat(migrationService.getOnLoginConsoleCommands(), contains("sethome %p:lastloc", "msg %p Welcome back")); + assertThat(migrationService.getOnRegisterCommands(), contains("me registers", "msg CONSOLE hi")); + assertThat(migrationService.getOnRegisterConsoleCommands(), contains("sethome %p:regloc")); + } +} diff --git a/src/test/java/fr/xephi/authme/settings/SettingsTest.java b/src/test/java/fr/xephi/authme/settings/SettingsTest.java index e839bb39f..3480bd0ca 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsTest.java @@ -55,8 +55,7 @@ public class SettingsTest { PropertyResource resource = mock(PropertyResource.class); given(resource.getBoolean(RegistrationSettings.USE_WELCOME_MESSAGE.getPath())).willReturn(true); - Settings settings = new Settings(testPluginFolder, resource, - TestSettingsMigrationServices.alwaysFulfilled(), CONFIG_DATA); + Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA); // when String[] result = settings.getWelcomeMessage(); @@ -75,8 +74,7 @@ public class SettingsTest { Files.write(emailFile.toPath(), emailMessage.getBytes()); PropertyResource resource = mock(PropertyResource.class); - Settings settings = new Settings(testPluginFolder, resource, - TestSettingsMigrationServices.alwaysFulfilled(), CONFIG_DATA); + Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA); // when String result = settings.getPasswordEmailMessage(); @@ -85,6 +83,24 @@ public class SettingsTest { assertThat(result, equalTo(emailMessage)); } + @Test + public void shouldLoadRecoveryCodeMessage() throws IOException { + // given + String emailMessage = "Your recovery code is %code."; + File emailFile = new File(testPluginFolder, "recovery_code_email.html"); + createFile(emailFile); + Files.write(emailFile.toPath(), emailMessage.getBytes()); + + PropertyResource resource = mock(PropertyResource.class); + Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA); + + // when + String result = settings.getRecoveryCodeEmailMessage(); + + // then + assertThat(result, equalTo(emailMessage)); + } + private static void createFile(File file) { try { file.getParentFile().mkdirs(); diff --git a/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java b/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java deleted file mode 100644 index f14a47831..000000000 --- a/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java +++ /dev/null @@ -1,21 +0,0 @@ -package fr.xephi.authme.settings; - -import com.github.authme.configme.migration.MigrationService; - -/** - * Provides {@link MigrationService} implementations for testing. - */ -public final class TestSettingsMigrationServices { - - private TestSettingsMigrationServices() { - } - - /** - * Returns a settings migration service which always answers that all data is up-to-date. - * - * @return test settings migration service - */ - public static MigrationService alwaysFulfilled() { - return (propertyResource, knownProperties) -> false; - } -} diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandConfigTestHelper.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandConfigTestHelper.java new file mode 100644 index 000000000..88ee90f3d --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandConfigTestHelper.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.settings.commandconfig; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * Helper class for tests around the command configuration. + */ +final class CommandConfigTestHelper { + + private CommandConfigTestHelper() { + } + + /** + * Returns a matcher for verifying a {@link Command} object. + * + * @param cmd the expected command line + * @param executor the expected executor + * @return the matcher + */ + static Matcher isCommand(String cmd, Executor executor) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Command item) { + return executor.equals(item.getExecutor()) && cmd.equals(item.getCommand()); + } + + @Override + public void describeTo(Description description) { + description.appendText("Command '" + cmd + "' run by '" + executor + "'"); + } + }; + } +} diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java new file mode 100644 index 000000000..fb8e4e40f --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java @@ -0,0 +1,174 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.google.common.io.Files; +import fr.xephi.authme.ReflectionTestUtils; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.settings.SettingsMigrationService; +import org.bukkit.entity.Player; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.io.IOException; +import java.util.function.BiConsumer; + +import static fr.xephi.authme.settings.commandconfig.CommandConfigTestHelper.isCommand; +import static java.lang.String.format; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Test for {@link CommandManager}. + */ +@RunWith(MockitoJUnitRunner.class) +public class CommandManagerTest { + + private static final String TEST_FILES_FOLDER = "/fr/xephi/authme/settings/commandconfig/"; + + private CommandManager manager; + @InjectMocks + private CommandMigrationService commandMigrationService; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Mock + private BukkitService bukkitService; + @Mock + private SettingsMigrationService settingsMigrationService; + + private File testFolder; + + @Before + public void setup() throws IOException { + testFolder = temporaryFolder.newFolder(); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldLoadCompleteFile() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + + // when + initManager(); + + // then + CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig"); + assertThat(commandConfig.getOnJoin().keySet(), contains("broadcast")); + assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE))); + assertThat(commandConfig.getOnRegister().keySet(), contains("announce", "notify")); + assertThat(commandConfig.getOnRegister().values(), contains( + isCommand("me I just registered", Executor.PLAYER), + isCommand("log %p registered", Executor.CONSOLE))); + assertThat(commandConfig.getOnLogin().keySet(), contains("welcome", "show_motd", "display_list")); + assertThat(commandConfig.getOnLogin().values(), contains( + isCommand("msg %p Welcome back", Executor.CONSOLE), + isCommand("motd", Executor.PLAYER), + isCommand("list", Executor.PLAYER))); + } + + @Test + public void shouldLoadIncompleteFile() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml"); + + // when + initManager(); + + // then + CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig"); + assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE))); + assertThat(commandConfig.getOnLogin().values(), contains( + isCommand("msg %p Welcome back", Executor.CONSOLE), + isCommand("list", Executor.PLAYER))); + assertThat(commandConfig.getOnRegister(), anEmptyMap()); + } + + @Test + public void shouldExecuteCommandsOnJoin() { + // given + String name = "Bobby1"; + + // when + testCommandExecution(name, CommandManager::runCommandsOnJoin); + + // then + verify(bukkitService, only()).dispatchConsoleCommand(format("broadcast %s has joined", name)); + } + + @Test + public void shouldExecuteCommandsOnRegister() { + // given + String name = "luis"; + + // when + testCommandExecution(name, CommandManager::runCommandsOnRegister); + + // then + verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered")); + verify(bukkitService).dispatchConsoleCommand(format("log %s registered", name)); + verifyNoMoreInteractions(bukkitService); + } + + @Test + public void shouldExecuteCommandsOnLogin() { + // given + String name = "plaYer01"; + + // when + testCommandExecution(name, CommandManager::runCommandsOnLogin); + + // then + verify(bukkitService).dispatchConsoleCommand(format("msg %s Welcome back", name)); + verify(bukkitService).dispatchCommand(any(Player.class), eq("motd")); + verify(bukkitService).dispatchCommand(any(Player.class), eq("list")); + verifyNoMoreInteractions(bukkitService); + } + + @Test + public void shouldHaveHiddenConstructorInSettingsHolderClass() { + // given / when / then + TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandSettingsHolder.class); + } + + + private void testCommandExecution(String playerName, BiConsumer testMethod) { + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + initManager(); + Player player = mock(Player.class); + given(player.getName()).willReturn(playerName); + + testMethod.accept(manager, player); + } + + private void initManager() { + manager = new CommandManager(testFolder, bukkitService, commandMigrationService); + } + + private void copyJarFileAsCommandsYml(String path) { + File source = TestHelper.getJarFile(path); + File destination = new File(testFolder, "commands.yml"); + try { + Files.copy(source, destination); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java new file mode 100644 index 000000000..1931ff2f2 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java @@ -0,0 +1,131 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.github.authme.configme.knownproperties.ConfigurationDataBuilder; +import com.github.authme.configme.resource.PropertyResource; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.settings.SettingsMigrationService; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static fr.xephi.authme.settings.commandconfig.CommandConfigTestHelper.isCommand; +import static java.util.Collections.emptyList; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link CommandMigrationService}. + */ +@RunWith(MockitoJUnitRunner.class) +public class CommandMigrationServiceTest { + + @InjectMocks + private CommandMigrationService commandMigrationService; + + @Mock + private SettingsMigrationService settingsMigrationService; + + private CommandConfig commandConfig = new CommandConfig(); + + @BeforeClass + public static void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldNotPerformAnyMigration() { + // given + given(settingsMigrationService.getOnLoginCommands()).willReturn(emptyList()); + given(settingsMigrationService.getOnLoginConsoleCommands()).willReturn(emptyList()); + given(settingsMigrationService.getOnRegisterCommands()).willReturn(emptyList()); + given(settingsMigrationService.getOnRegisterConsoleCommands()).willReturn(emptyList()); + commandConfig.getOnRegister().put("existing", new Command("existing cmd", Executor.PLAYER)); + CommandConfig configSpy = spy(commandConfig); + + // when + boolean result = commandMigrationService.transformOldCommands(configSpy); + + // then + assertThat(result, equalTo(false)); + verifyZeroInteractions(configSpy); + assertThat(configSpy.getOnRegister().keySet(), contains("existing")); + assertThat(configSpy.getOnLogin(), anEmptyMap()); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldPerformMigration() { + // given + List onLogin = Collections.singletonList("on login command"); + given(settingsMigrationService.getOnLoginCommands()).willReturn(onLogin); + List onLoginConsole = Arrays.asList("cmd1", "cmd2 %p", "cmd3"); + given(settingsMigrationService.getOnLoginConsoleCommands()).willReturn(onLoginConsole); + given(settingsMigrationService.getOnRegisterCommands()).willReturn(emptyList()); + List onRegisterConsole = Arrays.asList("log %p registered", "whois %p"); + given(settingsMigrationService.getOnRegisterConsoleCommands()).willReturn(onRegisterConsole); + + Map onLoginCommands = new LinkedHashMap<>(); + onLoginCommands.put("bcast", new Command("bcast %p returned", Executor.CONSOLE)); + commandConfig.setOnLogin(onLoginCommands); + Map onRegisterCommands = new LinkedHashMap<>(); + onRegisterCommands.put("ex_cmd", new Command("existing", Executor.CONSOLE)); + onRegisterCommands.put("ex_cmd2", new Command("existing2", Executor.PLAYER)); + commandConfig.setOnRegister(onRegisterCommands); + + // when + boolean result = commandMigrationService.transformOldCommands(commandConfig); + + // then + assertThat(result, equalTo(true)); + assertThat(commandConfig.getOnLogin(), sameInstance(onLoginCommands)); + Collection loginCmdList = onLoginCommands.values(); + assertThat(loginCmdList, contains( + equalTo(onLoginCommands.get("bcast")), + isCommand("on login command", Executor.PLAYER), + isCommand("cmd1", Executor.CONSOLE), + isCommand("cmd2 %p", Executor.CONSOLE), + isCommand("cmd3", Executor.CONSOLE))); + + assertThat(commandConfig.getOnRegister(), sameInstance(onRegisterCommands)); + Collection registerCmdList = onRegisterCommands.values(); + assertThat(registerCmdList, contains( + isCommand("existing", Executor.CONSOLE), + isCommand("existing2", Executor.PLAYER), + isCommand("log %p registered", Executor.CONSOLE), + isCommand("whois %p", Executor.CONSOLE))); + } + + @Test + @Ignore // TODO ConfigMe/#29: Create PropertyResource#getKeys + public void shouldRewriteForEmptyFile() { + // given + File commandFile = TestHelper.getJarFile("/fr/xephi/authme/settings/commandconfig/commands.empty.yml"); + PropertyResource resource = new YamlFileResource(commandFile); + + // when + boolean result = commandMigrationService.checkAndMigrate( + resource, ConfigurationDataBuilder.collectData(CommandSettingsHolder.class).getProperties()); + + // then + assertThat(result, equalTo(true)); + } +} diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandYmlConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandYmlConsistencyTest.java new file mode 100644 index 000000000..1ea214899 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandYmlConsistencyTest.java @@ -0,0 +1,60 @@ +package fr.xephi.authme.settings.commandconfig; + +import com.github.authme.configme.knownproperties.ConfigurationDataBuilder; +import com.github.authme.configme.resource.PropertyResource; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.settings.SettingsMigrationService; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; + +/** + * Tests that commands.yml is well-formed. + */ +@RunWith(MockitoJUnitRunner.class) +public class CommandYmlConsistencyTest { + + @InjectMocks + private CommandMigrationService commandMigrationService; + + @Mock + private SettingsMigrationService settingsMigrationService; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setUpSettingsMigrationService() { + given(settingsMigrationService.getOnLoginCommands()).willReturn(Collections.emptyList()); + given(settingsMigrationService.getOnLoginConsoleCommands()).willReturn(Collections.emptyList()); + given(settingsMigrationService.getOnRegisterCommands()).willReturn(Collections.emptyList()); + given(settingsMigrationService.getOnRegisterConsoleCommands()).willReturn(Collections.emptyList()); + } + + @Test + public void shouldLoadWithNoMigrations() { + // given + File commandFile = TestHelper.getJarFile("/commands.yml"); + PropertyResource resource = new YamlFileResource(commandFile); + + // when + boolean result = commandMigrationService.checkAndMigrate( + resource, ConfigurationDataBuilder.collectData(CommandSettingsHolder.class).getProperties()); + + // then + assertThat(result, equalTo(false)); + } +} diff --git a/src/test/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetrieverTest.java b/src/test/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetrieverTest.java new file mode 100644 index 000000000..a5bd8c905 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetrieverTest.java @@ -0,0 +1,32 @@ +package fr.xephi.authme.settings.properties; + +import com.github.authme.configme.knownproperties.ConfigurationData; +import fr.xephi.authme.TestHelper; +import org.junit.Test; + +import static org.hamcrest.Matchers.closeTo; +import static org.junit.Assert.assertThat; + +/** + * Test for {@link AuthMeSettingsRetriever}. + */ +public class AuthMeSettingsRetrieverTest { + + @Test + public void shouldReturnAllProperties() { + // given / when + ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); + + // then + // Note ljacqu 20161123: Check that the number of properties returned corresponds to what we expect with + // an error margin of 10: this prevents us from having to adjust the test every time the config is changed. + // If this test fails, replace the first argument in closeTo() with the new number of properties + assertThat((double) configurationData.getProperties().size(), + closeTo(150, 10)); + } + + @Test + public void shouldHaveHiddenConstructor() { + TestHelper.validateHasOnlyPrivateEmptyConstructor(AuthMeSettingsRetriever.class); + } +} diff --git a/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java b/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java index bb3bf37d9..c58e8276c 100644 --- a/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java +++ b/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java @@ -6,7 +6,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.List; diff --git a/src/test/java/fr/xephi/authme/task/LimboPlayerTaskManagerTest.java b/src/test/java/fr/xephi/authme/task/LimboPlayerTaskManagerTest.java index 8f0949f15..13e9c5761 100644 --- a/src/test/java/fr/xephi/authme/task/LimboPlayerTaskManagerTest.java +++ b/src/test/java/fr/xephi/authme/task/LimboPlayerTaskManagerTest.java @@ -6,10 +6,10 @@ import fr.xephi.authme.data.limbo.LimboCache; import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; -import fr.xephi.authme.service.BukkitService; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import org.junit.BeforeClass; @@ -17,13 +17,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.service.BukkitService.TICKS_PER_SECOND; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -174,11 +171,8 @@ public class LimboPlayerTaskManagerTest { @Test public void shouldNotRegisterTimeoutTaskForZeroTimeout() { // given - String name = "snail"; Player player = mock(Player.class); - given(player.getName()).willReturn(name); LimboPlayer limboPlayer = mock(LimboPlayer.class); - given(limboCache.getPlayerData(name)).willReturn(limboPlayer); given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(0); // when diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java b/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java index d6fe5b574..4d3923e63 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java +++ b/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java @@ -4,9 +4,9 @@ import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PurgeSettings; -import fr.xephi.authme.service.BukkitService; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -16,7 +16,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Calendar; @@ -32,15 +32,15 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.anyCollectionOf; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PurgeService}. @@ -100,7 +100,6 @@ public class PurgeServiceTest { given(settings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER)).willReturn(60); Set playerNames = newHashSet("alpha", "bravo", "charlie", "delta"); given(dataSource.getRecordsToPurge(anyLong(), eq(false))).willReturn(playerNames); - mockReturnedOfflinePlayers(); // when purgeService.runAutoPurge(); @@ -119,7 +118,7 @@ public class PurgeServiceTest { // given final long delay = 123012301L; final boolean includeLastLoginZeroEntries = true; - given(dataSource.getRecordsToPurge(delay, includeLastLoginZeroEntries)).willReturn(Collections.emptySet()); + given(dataSource.getRecordsToPurge(delay, includeLastLoginZeroEntries)).willReturn(Collections.emptySet()); CommandSender sender = mock(CommandSender.class); // when @@ -127,7 +126,7 @@ public class PurgeServiceTest { // then verify(dataSource).getRecordsToPurge(delay, includeLastLoginZeroEntries); - verify(dataSource, never()).purgeRecords(anyCollectionOf(String.class)); + verify(dataSource, never()).purgeRecords(anyCollection()); verify(sender).sendMessage("No players to purge"); verifyZeroInteractions(bukkitService, permissionsManager); } @@ -139,7 +138,6 @@ public class PurgeServiceTest { final boolean includeLastLoginZeroEntries = false; Set playerNames = newHashSet("charlie", "delta", "echo", "foxtrot"); given(dataSource.getRecordsToPurge(delay, includeLastLoginZeroEntries)).willReturn(playerNames); - mockReturnedOfflinePlayers(); Player sender = mock(Player.class); UUID uuid = UUID.randomUUID(); given(sender.getUniqueId()).willReturn(uuid); @@ -157,10 +155,10 @@ public class PurgeServiceTest { // given purgeService.setPurging(true); CommandSender sender = mock(CommandSender.class); - OfflinePlayer[] players = mockReturnedOfflinePlayers(); + OfflinePlayer[] offlinePlayers = new OfflinePlayer[]{mock(OfflinePlayer.class), mock(OfflinePlayer.class)}; // when - purgeService.purgePlayers(sender, newHashSet("test", "names"), players); + purgeService.purgePlayers(sender, newHashSet("test", "names"), offlinePlayers); // then verify(sender).sendMessage(argThat(containsString("Purge is already in progress"))); @@ -170,32 +168,15 @@ public class PurgeServiceTest { @Test public void shouldExecutePurgeActions() { // given - List players = Arrays.asList(mockReturnedOfflinePlayers()); List names = Arrays.asList("alpha", "bravo", "foxtrot"); + List offlinePlayers = Arrays.asList( + mock(OfflinePlayer.class), mock(OfflinePlayer.class), mock(OfflinePlayer.class)); // when - purgeService.executePurge(players, names); + purgeService.executePurge(offlinePlayers, names); // then - verify(executor).executePurge(players, names); - } - - /** - * Returns mock OfflinePlayer objects with names corresponding to A - G of the NATO phonetic alphabet, - * in various casing. - * - * @return list of offline players BukkitService is mocked to return - */ - private OfflinePlayer[] mockReturnedOfflinePlayers() { - String[] names = { "alfa", "Bravo", "charLIE", "delta", "ECHO", "Foxtrot", "golf" }; - OfflinePlayer[] players = new OfflinePlayer[names.length]; - for (int i = 0; i < names.length; ++i) { - OfflinePlayer player = mock(OfflinePlayer.class); - given(player.getName()).willReturn(names[i]); - players[i] = player; - } - given(bukkitService.getOfflinePlayers()).willReturn(players); - return players; + verify(executor).executePurge(offlinePlayers, names); } private void assertCorrectPurgeTimestamp(long timestamp, int configuredDays) { diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java b/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java index 2168756db..d371bad3e 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java +++ b/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java @@ -19,7 +19,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -35,16 +35,16 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link PurgeTask}. @@ -124,6 +124,27 @@ public class PurgeTaskTest { assertRanPurgeWithNames("foxtrot"); } + /** + * #1008: OfflinePlayer#getName may return null. + */ + @Test + public void shouldHandleOfflinePlayerWithNullName() { + // given + Set names = newHashSet("name1", "name2"); + OfflinePlayer[] players = asArray( + mockOfflinePlayer(null, false), mockOfflinePlayer("charlie", false), mockOfflinePlayer("name1", false)); + reset(purgeService, permissionsManager); + setPermissionsBehavior(); + + PurgeTask task = new PurgeTask(purgeService, permissionsManager, null, names, players); + + // when + task.run(); + + // then + assertRanPurgeWithPlayers(players[2]); + } + @Test public void shouldStopTaskAndInformSenderUponCompletion() { // given @@ -191,7 +212,7 @@ public class PurgeTaskTest { .willAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable { - OfflinePlayer player = (OfflinePlayer) invocationOnMock.getArguments()[0]; + OfflinePlayer player = invocationOnMock.getArgument(0); Boolean hasPermission = playerBypassAssignments.get(player); if (hasPermission == null) { throw new IllegalStateException("Unexpected check of '" + BYPASS_NODE diff --git a/src/test/java/fr/xephi/authme/security/RandomStringUtilsTest.java b/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/security/RandomStringUtilsTest.java rename to src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java index 217bc927b..c0f9cd0b5 100644 --- a/src/test/java/fr/xephi/authme/security/RandomStringUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java @@ -1,6 +1,5 @@ -package fr.xephi.authme.security; +package fr.xephi.authme.util; -import fr.xephi.authme.util.RandomStringUtils; import org.junit.Test; import java.util.regex.Pattern; diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/src/test/java/tools/checktestmocks/CheckTestMocks.java index 1e8817aa2..9af3ae040 100644 --- a/src/test/java/tools/checktestmocks/CheckTestMocks.java +++ b/src/test/java/tools/checktestmocks/CheckTestMocks.java @@ -4,8 +4,8 @@ import com.google.common.collect.Sets; import fr.xephi.authme.ClassCollector; import fr.xephi.authme.TestHelper; import org.mockito.Mock; +import tools.utils.AutoToolTask; import tools.utils.InjectorUtils; -import tools.utils.SimpleAutoTask; import java.lang.reflect.Field; import java.util.ArrayList; @@ -19,7 +19,7 @@ import java.util.stream.Collectors; * Task checking if all tests' {@code @Mock} fields have a corresponding * {@code @Inject} field in the class they are testing. */ -public class CheckTestMocks extends SimpleAutoTask { +public class CheckTestMocks implements AutoToolTask { private List errors = new ArrayList<>(); diff --git a/src/test/java/tools/docs/UpdateDocsTask.java b/src/test/java/tools/docs/UpdateDocsTask.java index cfdb69ef9..84f723c64 100644 --- a/src/test/java/tools/docs/UpdateDocsTask.java +++ b/src/test/java/tools/docs/UpdateDocsTask.java @@ -1,26 +1,20 @@ package tools.docs; -import com.google.common.collect.ImmutableSet; -import tools.docs.commands.CommandPageCreater; -import tools.docs.hashmethods.HashAlgorithmsDescriptionTask; -import tools.docs.permissions.PermissionsListWriter; -import tools.docs.translations.TranslationPageGenerator; +import fr.xephi.authme.ClassCollector; +import fr.xephi.authme.TestHelper; import tools.utils.AutoToolTask; import tools.utils.ToolTask; +import java.util.List; import java.util.Scanner; -import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * Task that runs all tasks which update files in the docs folder. */ public class UpdateDocsTask implements AutoToolTask { - private static final Set> TASKS = ImmutableSet - .of(CommandPageCreater.class, HashAlgorithmsDescriptionTask.class, - PermissionsListWriter.class, TranslationPageGenerator.class); - @Override public String getTaskName() { return "updateDocs"; @@ -40,19 +34,18 @@ public class UpdateDocsTask implements AutoToolTask { }); } - private static ToolTask instantiateTask(Class clazz) { - try { - return clazz.newInstance(); - } catch (IllegalAccessException | InstantiationException e) { - throw new UnsupportedOperationException("Could not instantiate task class '" + clazz + "'", e); - } - } - - private static void executeTasks(Consumer taskRunner) { - for (Class taskClass : TASKS) { - ToolTask task = instantiateTask(taskClass); + private void executeTasks(Consumer taskRunner) { + for (ToolTask task : getDocTasks()) { System.out.println("\nRunning " + task.getTaskName() + "\n-------------------"); taskRunner.accept(task); } } + + private List getDocTasks() { + ClassCollector classCollector = + new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools/docs"); + return classCollector.getInstancesOfType(ToolTask.class).stream() + .filter(task -> task.getClass() != getClass()) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/tools/docs/commands/CommandPageCreater.java b/src/test/java/tools/docs/commands/CommandPageCreater.java index eab18256a..ff1500a77 100644 --- a/src/test/java/tools/docs/commands/CommandPageCreater.java +++ b/src/test/java/tools/docs/commands/CommandPageCreater.java @@ -5,15 +5,15 @@ import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandInitializer; import fr.xephi.authme.command.CommandUtils; import fr.xephi.authme.permission.PermissionNode; -import tools.utils.FileUtils; -import tools.utils.SimpleAutoTask; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; import java.util.Collection; -public class CommandPageCreater extends SimpleAutoTask { +public class CommandPageCreater implements AutoToolTask { private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "commands.md"; @@ -29,7 +29,7 @@ public class CommandPageCreater extends SimpleAutoTask { NestedTagValue commandTags = new NestedTagValue(); addCommandsInfo(commandTags, baseCommands); - FileUtils.generateFileFromTemplate( + FileIoUtils.generateFileFromTemplate( ToolsConstants.TOOLS_SOURCE_ROOT + "docs/commands/commands.tpl.md", OUTPUT_FILE, TagValueHolder.create().put("commands", commandTags)); diff --git a/src/test/java/tools/docs/commands/commands.tpl.md b/src/test/java/tools/docs/commands/commands.tpl.md index c0f4da120..61018634c 100644 --- a/src/test/java/tools/docs/commands/commands.tpl.md +++ b/src/test/java/tools/docs/commands/commands.tpl.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` diff --git a/src/test/java/tools/docs/config/UpdateConfigPageTask.java b/src/test/java/tools/docs/config/UpdateConfigPageTask.java new file mode 100644 index 000000000..f9a249a70 --- /dev/null +++ b/src/test/java/tools/docs/config/UpdateConfigPageTask.java @@ -0,0 +1,49 @@ +package tools.docs.config; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import fr.xephi.authme.util.FileUtils; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; +import tools.utils.TagValueHolder; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.io.IOException; + +/** + * Task for updating the config docs page. + */ +public class UpdateConfigPageTask implements AutoToolTask { + + private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/config/config.tpl.md"; + private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "config.md"; + + @Override + public String getTaskName() { + return "updateConfigPage"; + } + + @Override + public void executeDefault() { + File config = null; + try { + // Create empty temporary .yml file and save the config to it + config = File.createTempFile("authme-config-", ".yml"); + SettingsManager settingsManager = new SettingsManager( + new YamlFileResource(config), null, AuthMeSettingsRetriever.buildConfigurationData()); + settingsManager.save(); + + // Get the contents and generate template file + TagValueHolder tagValueHolder = TagValueHolder.create() + .put("config", FileIoUtils.readFromFile(config.toPath())); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, OUTPUT_FILE, tagValueHolder); + System.out.println("Wrote to '" + OUTPUT_FILE + "'"); + } catch (IOException e) { + throw new IllegalStateException(e); + } finally { + FileUtils.delete(config); + } + } +} diff --git a/src/test/java/tools/docs/config/config.tpl.md b/src/test/java/tools/docs/config/config.tpl.md new file mode 100644 index 000000000..25cf8c2c0 --- /dev/null +++ b/src/test/java/tools/docs/config/config.tpl.md @@ -0,0 +1,16 @@ + + + +## AuthMe Configuration +The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, +with which you can configure various settings. This following is the initial contents of +the generated config.yml file. + +```yml +{config} +``` + +To change settings on a running server, save your changes to config.yml and use +`/authme reload`. + +{gen_footer} diff --git a/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java b/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java index 00809a73a..9cf98f338 100644 --- a/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java +++ b/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java @@ -20,7 +20,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java b/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java index d6ba721cc..b21d7fda3 100644 --- a/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java +++ b/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java @@ -1,8 +1,8 @@ package tools.docs.hashmethods; import fr.xephi.authme.security.HashAlgorithm; -import tools.utils.FileUtils; -import tools.utils.SimpleAutoTask; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; @@ -14,7 +14,7 @@ import java.util.Map; * * @see fr.xephi.authme.security.HashAlgorithm */ -public class HashAlgorithmsDescriptionTask extends SimpleAutoTask { +public class HashAlgorithmsDescriptionTask implements AutoToolTask { private static final String CUR_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/hashmethods/"; private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "hash_algorithms.md"; @@ -28,7 +28,8 @@ public class HashAlgorithmsDescriptionTask extends SimpleAutoTask { // Write to the docs file TagValueHolder tags = TagValueHolder.create().put("algorithms", methodRows); - FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags); + FileIoUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags); + System.out.println("Wrote to '" + OUTPUT_FILE + "'"); } private static NestedTagValue constructMethodRows(Map descriptions) { diff --git a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md b/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md index 17431eb5b..2f542b50f 100644 --- a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md +++ b/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md @@ -1,5 +1,5 @@ - + ## Hash Algorithms AuthMe supports the following hash algorithms for storing your passwords safely. diff --git a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java index 034677cb8..e36355121 100644 --- a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java +++ b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java @@ -2,7 +2,7 @@ package tools.docs.permissions; import fr.xephi.authme.ClassCollector; import fr.xephi.authme.permission.PermissionNode; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.ToolsConstants; import java.util.EnumSet; @@ -103,7 +103,7 @@ public class PermissionNodesGatherer { */ private static & PermissionNode> String getSourceForClass(Class clazz) { String classFile = ToolsConstants.MAIN_SOURCE_ROOT + clazz.getName().replace(".", "/") + ".java"; - return FileUtils.readFromFile(classFile); + return FileIoUtils.readFromFile(classFile); } } diff --git a/src/test/java/tools/docs/permissions/PermissionsListWriter.java b/src/test/java/tools/docs/permissions/PermissionsListWriter.java index ffd418e21..797e26e79 100644 --- a/src/test/java/tools/docs/permissions/PermissionsListWriter.java +++ b/src/test/java/tools/docs/permissions/PermissionsListWriter.java @@ -1,7 +1,7 @@ package tools.docs.permissions; -import tools.utils.FileUtils; -import tools.utils.SimpleAutoTask; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; @@ -12,7 +12,7 @@ import java.util.Map; * Task responsible for formatting a permissions node list and * for writing it to a file if desired. */ -public class PermissionsListWriter extends SimpleAutoTask { +public class PermissionsListWriter implements AutoToolTask { private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/permissions/permission_nodes.tpl.md"; private static final String PERMISSIONS_OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "permission_nodes.md"; @@ -27,9 +27,8 @@ public class PermissionsListWriter extends SimpleAutoTask { final NestedTagValue permissionsTagValue = generatePermissionsList(); TagValueHolder tags = TagValueHolder.create().put("nodes", permissionsTagValue); - FileUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags); System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'"); - System.out.println("Before committing, please verify the output!"); } private static NestedTagValue generatePermissionsList() { diff --git a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md b/src/test/java/tools/docs/permissions/permission_nodes.tpl.md index 8d2a2b4f4..8987f08bb 100644 --- a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md +++ b/src/test/java/tools/docs/permissions/permission_nodes.tpl.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. diff --git a/src/test/java/tools/docs/translations/TranslationPageGenerator.java b/src/test/java/tools/docs/translations/TranslationPageGenerator.java index 80619ba56..f24321f89 100644 --- a/src/test/java/tools/docs/translations/TranslationPageGenerator.java +++ b/src/test/java/tools/docs/translations/TranslationPageGenerator.java @@ -2,8 +2,8 @@ package tools.docs.translations; import com.google.common.collect.ImmutableMap; import tools.docs.translations.TranslationsGatherer.TranslationInfo; -import tools.utils.FileUtils; -import tools.utils.SimpleAutoTask; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; @@ -17,7 +17,7 @@ import static com.google.common.base.Objects.firstNonNull; /** * Generates the translations page in docs. */ -public class TranslationPageGenerator extends SimpleAutoTask { +public class TranslationPageGenerator implements AutoToolTask { private static final String DOCS_PAGE = ToolsConstants.DOCS_FOLDER + "translations.md"; private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/translations/translations.tpl.md"; @@ -34,8 +34,6 @@ public class TranslationPageGenerator extends SimpleAutoTask { private static final int[] COLOR_1 = {12, 9, 0}; private static final int[] COLOR_2 = { 6, 15, 6}; - private final TranslationsGatherer gatherer = new TranslationsGatherer(); - @Override public String getTaskName() { return "updateTranslations"; @@ -43,6 +41,7 @@ public class TranslationPageGenerator extends SimpleAutoTask { @Override public void executeDefault() { + TranslationsGatherer gatherer = new TranslationsGatherer(); NestedTagValue translationValuesHolder = new NestedTagValue(); for (TranslationInfo translation : gatherer.getTranslationInfo()) { @@ -57,7 +56,8 @@ public class TranslationPageGenerator extends SimpleAutoTask { } TagValueHolder tags = TagValueHolder.create().put("languages", translationValuesHolder); - FileUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags); + System.out.println("Wrote to '" + DOCS_PAGE + "'"); } /** @@ -124,6 +124,7 @@ public class TranslationPageGenerator extends SimpleAutoTask { .put("nl", "Dutch") .put("pl", "Polish") .put("pt", "Portuguese") + .put("ro", "Romanian") .put("ru", "Russian") .put("sk", "Slovakian") .put("tr", "Turkish") diff --git a/src/test/java/tools/docs/translations/TranslationsGatherer.java b/src/test/java/tools/docs/translations/TranslationsGatherer.java index 233b93324..f54843a9c 100644 --- a/src/test/java/tools/docs/translations/TranslationsGatherer.java +++ b/src/test/java/tools/docs/translations/TranslationsGatherer.java @@ -11,6 +11,8 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static tools.utils.FileIoUtils.listFilesOrThrow; + /** * Gathers all available translations of AuthMe. */ @@ -31,10 +33,7 @@ public class TranslationsGatherer { } private void gatherTranslations() { - File[] files = new File(MESSAGES_FOLDER).listFiles(); - if (files == null) { - throw new IllegalStateException("Cannot read files of '" + MESSAGES_FOLDER + "'"); - } + File[] files = listFilesOrThrow(new File(MESSAGES_FOLDER)); for (File file : files) { String code = getLanguageCode(file.getName()); if (code != null) { diff --git a/src/test/java/tools/docs/translations/translations.tpl.md b/src/test/java/tools/docs/translations/translations.tpl.md index 199d4029c..5308b6554 100644 --- a/src/test/java/tools/docs/translations/translations.tpl.md +++ b/src/test/java/tools/docs/translations/translations.tpl.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code diff --git a/src/test/java/tools/filegeneration/GenerateCommandsYml.java b/src/test/java/tools/filegeneration/GenerateCommandsYml.java new file mode 100644 index 000000000..e6e691335 --- /dev/null +++ b/src/test/java/tools/filegeneration/GenerateCommandsYml.java @@ -0,0 +1,57 @@ +package tools.filegeneration; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.resource.YamlFileResource; +import com.google.common.collect.ImmutableMap; +import fr.xephi.authme.settings.commandconfig.Command; +import fr.xephi.authme.settings.commandconfig.CommandConfig; +import fr.xephi.authme.settings.commandconfig.CommandSettingsHolder; +import fr.xephi.authme.settings.commandconfig.Executor; +import tools.utils.AutoToolTask; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.util.Scanner; + +/** + * Generates the commands.yml file that corresponds to the default in the code. + */ +public class GenerateCommandsYml implements AutoToolTask { + + private static final String COMMANDS_YML_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "commands.yml"; + + @Override + public void execute(Scanner scanner) { + executeDefault(); + } + + @Override + public void executeDefault() { + File file = new File(COMMANDS_YML_FILE); + + // Get default and add sample entry + CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getDefaultValue(); + commandConfig.setOnLogin( + ImmutableMap.of("welcome", newCommand("msg %p Welcome back!", Executor.PLAYER))); + + // Export the value to the file + SettingsManager settingsManager = new SettingsManager( + new YamlFileResource(file), null, CommandSettingsHolder.class); + settingsManager.setProperty(CommandSettingsHolder.COMMANDS, commandConfig); + settingsManager.save(); + + System.out.println("Updated " + COMMANDS_YML_FILE); + } + + @Override + public String getTaskName() { + return "generateCommandsYml"; + } + + private static Command newCommand(String commandLine, Executor executor) { + Command command = new Command(); + command.setCommand(commandLine); + command.setExecutor(executor); + return command; + } +} diff --git a/src/test/java/tools/filegeneration/GeneratePluginYml.java b/src/test/java/tools/filegeneration/GeneratePluginYml.java index 633c5aac8..b4a017c62 100644 --- a/src/test/java/tools/filegeneration/GeneratePluginYml.java +++ b/src/test/java/tools/filegeneration/GeneratePluginYml.java @@ -9,8 +9,8 @@ import fr.xephi.authme.permission.PermissionNode; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import tools.docs.permissions.PermissionNodesGatherer; -import tools.utils.FileUtils; -import tools.utils.SimpleAutoTask; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; import tools.utils.ToolsConstants; import java.io.StringReader; @@ -26,7 +26,7 @@ import java.util.stream.Collectors; /** * Generates the command and permission section of plugin.yml. */ -public class GeneratePluginYml extends SimpleAutoTask { +public class GeneratePluginYml implements AutoToolTask { private static final String PLUGIN_YML_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "plugin.yml"; @@ -46,7 +46,7 @@ public class GeneratePluginYml extends SimpleAutoTask { configuration.set("commands", generateCommands()); configuration.set("permissions", generatePermissions()); - FileUtils.writeToFile(PLUGIN_YML_FILE, + FileIoUtils.writeToFile(PLUGIN_YML_FILE, pluginYmlStart + "\n" + configuration.saveToString()); } @@ -63,7 +63,7 @@ public class GeneratePluginYml extends SimpleAutoTask { * @return file configuration with the lower part of the plugin.yml file */ private FileConfiguration loadPartialPluginYmlFile() { - List pluginYmlLines = FileUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); + List pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); int lineNr = 0; for (String line : pluginYmlLines) { if (line.equals("commands:")) { diff --git a/src/test/java/tools/helptranslation/HelpTranslationVerifier.java b/src/test/java/tools/helptranslation/HelpTranslationVerifier.java new file mode 100644 index 000000000..9cbf12b5c --- /dev/null +++ b/src/test/java/tools/helptranslation/HelpTranslationVerifier.java @@ -0,0 +1,171 @@ +package tools.helptranslation; + +import com.google.common.collect.Sets; +import de.bananaco.bpermissions.imp.YamlConfiguration; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandInitializer; +import fr.xephi.authme.command.CommandUtils; +import fr.xephi.authme.command.help.HelpMessage; +import fr.xephi.authme.command.help.HelpSection; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * Verifies a help messages translation. + */ +public class HelpTranslationVerifier { + + private final FileConfiguration configuration; + + // missing and unknown HelpSection and HelpMessage entries + private final List missingSections = new ArrayList<>(); + private final List unknownSections = new ArrayList<>(); + // missing and unknown command entries + private final List missingCommands = new ArrayList<>(); + private final List unknownCommands = new ArrayList<>(); + + public HelpTranslationVerifier(File translation) { + this.configuration = YamlConfiguration.loadConfiguration(translation); + checkFile(); + } + + private void checkFile() { + checkHelpSections(); + checkCommands(); + } + + public List getMissingSections() { + return missingSections; + } + + public List getUnknownSections() { + return unknownSections; + } + + public List getMissingCommands() { + // All entries start with "command.", so remove that + return missingCommands.stream() + .map(s -> s.substring(9)).collect(Collectors.toList()); + } + + public List getUnknownCommands() { + // All entries start with "command.", so remove that + return unknownCommands.stream() + .map(s -> s.substring(9)).collect(Collectors.toList()); + } + + /** + * Verifies that the file has the expected entries for {@link HelpSection} and {@link HelpMessage}. + */ + private void checkHelpSections() { + Set knownSections = Arrays.stream(HelpSection.values()) + .map(HelpSection::getKey).collect(Collectors.toSet()); + knownSections.addAll(Arrays.stream(HelpMessage.values()).map(HelpMessage::getKey).collect(Collectors.toSet())); + knownSections.addAll(Arrays.asList("common.defaultPermissions.notAllowed", + "common.defaultPermissions.opOnly", "common.defaultPermissions.allowed")); + Set sectionKeys = getLeafKeys("section"); + sectionKeys.addAll(getLeafKeys("common")); + + if (sectionKeys.isEmpty()) { + missingSections.addAll(knownSections); + } else { + missingSections.addAll(Sets.difference(knownSections, sectionKeys)); + unknownSections.addAll(Sets.difference(sectionKeys, knownSections)); + } + } + + /** + * Verifies that the file has the expected entries for AuthMe commands. + */ + private void checkCommands() { + Set commandPaths = buildCommandPaths(); + Set existingKeys = getLeafKeys("commands"); + if (existingKeys.isEmpty()) { + missingCommands.addAll(commandPaths); // commandPaths should be empty in this case + } else { + missingCommands.addAll(Sets.difference(commandPaths, existingKeys)); + unknownCommands.addAll(Sets.difference(existingKeys, commandPaths)); + } + } + + private Set buildCommandPaths() { + Set commandPaths = new LinkedHashSet<>(); + for (CommandDescription command : new CommandInitializer().getCommands()) { + commandPaths.addAll(getYamlPaths(command)); + command.getChildren().forEach(child -> commandPaths.addAll(getYamlPaths(child))); + } + return commandPaths; + } + + private List getYamlPaths(CommandDescription command) { + // e.g. commands.authme.register + String commandPath = "commands." + CommandUtils.constructParentList(command).stream() + .map(cmd -> cmd.getLabels().get(0)) + .collect(Collectors.joining(".")); + // The entire command is not present, so just add it as a missing command and don't return any YAML path + if (!configuration.contains(commandPath)) { + missingCommands.add(commandPath); + return Collections.emptyList(); + } + + // Entries each command can have + List paths = newArrayList(commandPath + ".description", commandPath + ".detailedDescription"); + + // Add argument entries that may exist + for (int argIndex = 1; argIndex <= command.getArguments().size(); ++argIndex) { + String argPath = String.format("%s.arg%d", commandPath, argIndex); + paths.add(argPath + ".label"); + paths.add(argPath + ".description"); + } + return paths; + } + + /** + * Returns the leaf keys of the section at the given path of the file configuration. + * + * @param path the path whose leaf keys should be retrieved + * @return leaf keys of the memory section, + * empty set if the configuration does not have a memory section at the given path + */ + private Set getLeafKeys(String path) { + if (!(configuration.get(path) instanceof MemorySection)) { + return Collections.emptySet(); + } + MemorySection memorySection = (MemorySection) configuration.get(path); + + // MemorySection#getKeys(true) returns all keys on all levels, e.g. if the configuration has + // 'commands.authme.register' then it also has 'commands.authme' and 'commands'. We can traverse each node and + // build its parents (e.g. for commands.authme.register.description: commands.authme.register, commands.authme, + // and commands, which we can remove from the collection since we know they are not a leaf. + Set leafKeys = memorySection.getKeys(true); + Set allKeys = new HashSet<>(leafKeys); + + for (String key : allKeys) { + List pathParts = Arrays.asList(key.split("\\.")); + + // We perform construction of parents & their removal in reverse order so we can build the lowest-level + // parent of a node first. As soon as the parent doesn't exist in the set already, we know we can continue + // with the next node since another node has already removed the concerned parents. + for (int i = pathParts.size() - 1; i > 0; --i) { + // e.g. for commands.authme.register -> i = {2, 1} => {commands.authme, commands} + String parentPath = String.join(".", pathParts.subList(0, i)); + if (!leafKeys.remove(parentPath)) { + break; + } + } + } + return leafKeys.stream().map(leaf -> path + "." + leaf).collect(Collectors.toSet()); + } +} diff --git a/src/test/java/tools/helptranslation/VerifyHelpTranslations.java b/src/test/java/tools/helptranslation/VerifyHelpTranslations.java new file mode 100644 index 000000000..a8a8daa9a --- /dev/null +++ b/src/test/java/tools/helptranslation/VerifyHelpTranslations.java @@ -0,0 +1,71 @@ +package tools.helptranslation; + +import tools.utils.ToolTask; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static tools.utils.FileIoUtils.listFilesOrThrow; + +/** + * Verifies the help translations for validity and completeness. + */ +public class VerifyHelpTranslations implements ToolTask { + + private static final Pattern HELP_MESSAGE_PATTERN = Pattern.compile("help_[a-z]{2,7}\\.yml"); + private static final String FOLDER = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/"; + + @Override + public String getTaskName() { + return "verifyHelpTranslations"; + } + + @Override + public void execute(Scanner scanner) { + System.out.println("Check specific language file?"); + System.out.println("Enter the language code for a specific file (e.g. 'it' for help_it.yml)"); + System.out.println("Empty line will check all files in the resources messages folder (default)"); + + String language = scanner.nextLine(); + if (language.isEmpty()) { + getHelpTranslations().forEach(this::processFile); + } else { + processFile(new File(FOLDER, "help_" + language + ".yml")); + } + } + + private void processFile(File file) { + System.out.println("Checking '" + file.getName() + "'"); + HelpTranslationVerifier verifier = new HelpTranslationVerifier(file); + + // Check and output errors + if (!verifier.getMissingSections().isEmpty()) { + System.out.println("Missing sections: " + String.join(", ", verifier.getMissingSections())); + } + if (!verifier.getUnknownSections().isEmpty()) { + System.out.println("Unknown sections: " + String.join(", ", verifier.getUnknownSections())); + } + if (!verifier.getMissingCommands().isEmpty()) { + System.out.println("Missing command entries: " + String.join(", ", verifier.getMissingCommands())); + } + if (!verifier.getUnknownCommands().isEmpty()) { + System.out.println("Unknown command entries: " + String.join(", ", verifier.getUnknownCommands())); + } + } + + private static List getHelpTranslations() { + File[] files = listFilesOrThrow(new File(FOLDER)); + List helpFiles = Arrays.stream(files) + .filter(file -> HELP_MESSAGE_PATTERN.matcher(file.getName()).matches()) + .collect(Collectors.toList()); + if (helpFiles.isEmpty()) { + throw new IllegalStateException("Could not get any matching files!"); + } + return helpFiles; + } +} diff --git a/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java b/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java new file mode 100644 index 000000000..24e99a4fe --- /dev/null +++ b/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java @@ -0,0 +1,50 @@ +package tools.messages; + +import fr.xephi.authme.message.MessageKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import tools.utils.AutoToolTask; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Task to add English messages as javadoc comment to each MessageKey enum entry. + */ +public class AddJavaDocToMessageEnumTask implements AutoToolTask { + + private static final String MESSAGES_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/messages_en.yml"; + + private FileConfiguration configuration; + + @Override + public void executeDefault() { + configuration = YamlConfiguration.loadConfiguration(new File(MESSAGES_FILE)); + + List entries = new ArrayList<>(); + for (MessageKey entry : MessageKey.values()) { + String tags = entry.getTags().length == 0 + ? "" + : ", \"" + String.join("\", \"", entry.getTags()) + "\""; + + entries.add("/** " + getMessageForJavaDoc(entry) + " */" + + "\n\t" + entry.name() + "(\"" + entry.getKey() + "\"" + tags + ")"); + } + + System.out.println("\t" + String.join(",\n\n\t", entries) + ";"); + } + + @Override + public String getTaskName() { + return "addJavaDocToMessageEnum"; + } + + private String getMessageForJavaDoc(MessageKey key) { + return configuration.getString(key.getKey()) + .replaceAll("&[0-9a-f]", "") + .replace("&", "&") + .replace("<", ">"); + } +} diff --git a/src/test/java/tools/messages/MessageFileVerifier.java b/src/test/java/tools/messages/MessageFileVerifier.java index f0be6bb44..b151228c1 100644 --- a/src/test/java/tools/messages/MessageFileVerifier.java +++ b/src/test/java/tools/messages/MessageFileVerifier.java @@ -8,7 +8,7 @@ import com.google.common.collect.Multimap; import fr.xephi.authme.message.MessageKey; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import java.io.File; import java.util.ArrayList; @@ -104,7 +104,7 @@ public class MessageFileVerifier { * @param defaultMessages The collection of default messages */ public void addMissingKeys(FileConfiguration defaultMessages) { - final List fileLines = FileUtils.readLinesFromFile(messagesFile.toPath()); + final List fileLines = FileIoUtils.readLinesFromFile(messagesFile.toPath()); List keysToAdd = new ArrayList<>(); for (MissingKey entry : missingKeys) { @@ -135,7 +135,7 @@ public class MessageFileVerifier { addCommentForMissingTags(fileLines, key, entry.getValue()); } - FileUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines)); + FileIoUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines)); } /** diff --git a/src/test/java/tools/messages/VerifyMessagesTask.java b/src/test/java/tools/messages/VerifyMessagesTask.java index 5343d2310..661ff9d86 100644 --- a/src/test/java/tools/messages/VerifyMessagesTask.java +++ b/src/test/java/tools/messages/VerifyMessagesTask.java @@ -17,6 +17,8 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static tools.utils.FileIoUtils.listFilesOrThrow; + /** * Task to verify the keys in the messages files. */ @@ -124,12 +126,7 @@ public final class VerifyMessagesTask implements ToolTask { } private static List getMessagesFiles() { - File folder = new File(MESSAGES_FOLDER); - File[] files = folder.listFiles(); - if (files == null) { - throw new IllegalStateException("Could not read files from folder '" + folder.getName() + "'"); - } - + File[] files = listFilesOrThrow(new File(MESSAGES_FOLDER)); List messageFiles = new ArrayList<>(); for (File file : files) { if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) { diff --git a/src/test/java/tools/messages/translation/AuthMeYamlConfiguration.java b/src/test/java/tools/messages/translation/AuthMeYamlConfiguration.java deleted file mode 100644 index 21564b577..000000000 --- a/src/test/java/tools/messages/translation/AuthMeYamlConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -package tools.messages.translation; - -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; - -import java.io.File; -import java.io.IOException; -import java.util.regex.Pattern; - -/** - * Extension of {@link YamlConfiguration} to customize the writing style. - */ -public class AuthMeYamlConfiguration extends YamlConfiguration { - - // Differences to YamlConfiguration: Texts are always in single quotes - // and line breaks are only applied after 200 chars - @Override - public String saveToString() { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); - options.setPrettyFlow(true); - options.setWidth(200); - Yaml yaml = new Yaml(options); - - String header = buildHeader(); - String dump = yaml.dump(getValues(false)); - - if (dump.equals(BLANK_CONFIG)) { - dump = ""; - } - // By setting the scalar style to SINGLE_QUOTED both keys and values will be enclosed in single quotes. - // We want all texts wrapped in single quotes, but not the keys. Seems like this is not possible in SnakeYAML - dump = Pattern.compile("^'([a-zA-Z0-9-_]+)': ", Pattern.MULTILINE) - .matcher(dump).replaceAll("$1: "); - - return header + dump; - } - - /** - * Behaves similarly to {@link YamlConfiguration#loadConfiguration(File)} but returns an object - * of this class instead. - * - * @param file the file to load - * @return the constructed AuthMeYamlConfiguration instance - */ - public static AuthMeYamlConfiguration loadConfiguration(File file) { - AuthMeYamlConfiguration config = new AuthMeYamlConfiguration(); - try { - config.load(file); - } catch (IOException | InvalidConfigurationException ex) { - throw new IllegalStateException(ex); - } - return config; - } -} diff --git a/src/test/java/tools/messages/translation/ExportMessagesTask.java b/src/test/java/tools/messages/translation/ExportMessagesTask.java deleted file mode 100644 index 7ea2a4196..000000000 --- a/src/test/java/tools/messages/translation/ExportMessagesTask.java +++ /dev/null @@ -1,106 +0,0 @@ -package tools.messages.translation; - -import com.google.common.io.CharStreams; -import com.google.gson.Gson; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.util.StringUtils; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import tools.utils.ToolTask; -import tools.utils.ToolsConstants; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -/** - * Task to export a language's messages to the remote translation service. - */ -public class ExportMessagesTask implements ToolTask { - - /** The folder containing the messages files. */ - protected static final String MESSAGES_FOLDER = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/"; - /** The remote URL to send an updated file to. */ - private static final String UPDATE_URL = "http://jalu.ch/ext/authme/update.php"; - private final Gson gson = new Gson(); - - @Override - public String getTaskName() { - return "exportMessages"; - } - - @Override - public void execute(Scanner scanner) { - System.out.println("Enter language code of messages to export:"); - String languageCode = scanner.nextLine().trim(); - - File file = new File(MESSAGES_FOLDER + "messages_" + languageCode + ".yml"); - if (!file.exists()) { - throw new IllegalStateException("File '" + file.getAbsolutePath() + "' does not exist"); - } - - FileConfiguration configuration = YamlConfiguration.loadConfiguration(file); - String json = convertToJson(languageCode, loadDefaultMessages(), configuration); - - String result = sendJsonToRemote(languageCode, json); - System.out.println("Answer: " + result); - } - - protected String convertToJson(String code, FileConfiguration defaultMessages, FileConfiguration messageFile) { - List list = new ArrayList<>(); - for (MessageKey key : MessageKey.values()) { - list.add(new MessageExport(key.getKey(), key.getTags(), getString(key, defaultMessages), - getString(key, messageFile))); - } - - return gson.toJson(new LanguageExport(code, list)); - } - - protected FileConfiguration loadDefaultMessages() { - return YamlConfiguration.loadConfiguration(new File(MESSAGES_FOLDER + "messages_en.yml")); - } - - private static String getString(MessageKey key, FileConfiguration configuration) { - return configuration.getString(key.getKey(), ""); - } - - private static String sendJsonToRemote(String language, String json) { - try { - String encodedData = "file=" + URLEncoder.encode(json, "UTF-8") - + "&language=" + URLEncoder.encode(language, "UTF-8"); - - URL url = new URL(UPDATE_URL); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.setRequestProperty("Content-Length", String.valueOf(encodedData.length())); - OutputStream os = conn.getOutputStream(); - os.write(encodedData.getBytes()); - os.flush(); - os.close(); - - return "Response code: " + conn.getResponseCode() - + "\n" + inputStreamToString(conn.getInputStream()); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private static String inputStreamToString(InputStream is) { - try (InputStreamReader isr = new InputStreamReader(is)) { - return CharStreams.toString(isr); - } catch (IOException e) { - return "Failed to read output - " + StringUtils.formatException(e); - } - } - -} diff --git a/src/test/java/tools/messages/translation/ImportMessagesTask.java b/src/test/java/tools/messages/translation/ImportMessagesTask.java deleted file mode 100644 index c70caaa3a..000000000 --- a/src/test/java/tools/messages/translation/ImportMessagesTask.java +++ /dev/null @@ -1,116 +0,0 @@ -package tools.messages.translation; - -import com.google.common.io.Resources; -import com.google.gson.Gson; -import fr.xephi.authme.message.MessageKey; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import tools.messages.MessageFileVerifier; -import tools.messages.VerifyMessagesTask; -import tools.utils.FileUtils; -import tools.utils.ToolTask; -import tools.utils.ToolsConstants; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.HashSet; -import java.util.Scanner; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * Imports a message file from a remote JSON export and validates the resulting file. - *

    - * Comments at the top of an existing file should remain after the import, but it is important - * to verify that no unwanted changes have been applied to the file. Note that YAML comments - * tend to disappear if there is no space between the # and the first character. - */ -public class ImportMessagesTask implements ToolTask { - - private static final String MESSAGES_FOLDER = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/"; - private Gson gson = new Gson(); - private Set messageCodes; - - @Override - public String getTaskName() { - return "importMessages"; - } - - @Override - public void execute(Scanner scanner) { - System.out.println("Enter URL to import from"); - // Dirty trick: replace https:// with http:// so we don't have to worry about installing certificates... - String url = scanner.nextLine().replace("https://", "http://"); - - LanguageExport languageExport = getLanguageExportFromUrl(url); - if (languageExport == null) { - throw new IllegalStateException("An error occurred: constructed language export is null"); - } - - mergeExportIntoFile(languageExport); - System.out.println("Saved to messages file for code '" + languageExport.code + "'"); - } - - private LanguageExport getLanguageExportFromUrl(String location) { - try { - URL url = new URL(location); - String json = Resources.toString(url, StandardCharsets.UTF_8); - return gson.fromJson(json, LanguageExport.class); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private void mergeExportIntoFile(LanguageExport export) { - String languageCode = export.code; - String fileName = MESSAGES_FOLDER + "messages_" + languageCode + ".yml"; - File file = new File(fileName); - FileConfiguration fileConfiguration; - if (file.exists()) { - removeAllTodoComments(fileName); - fileConfiguration = AuthMeYamlConfiguration.loadConfiguration(file); - } else { - fileConfiguration = new AuthMeYamlConfiguration(); - } - - buildMessageCodeList(); - for (MessageExport messageExport : export.messages) { - if (!messageCodes.contains(messageExport.key)) { - throw new IllegalStateException("Message key '" + messageExport.key + "' does not exist"); - } else if (!messageExport.translatedMessage.isEmpty()) { - fileConfiguration.set(messageExport.key, messageExport.translatedMessage); - } - } - try { - fileConfiguration.save(file); - } catch (IOException e) { - throw new IllegalStateException(e); - } - - MessageFileVerifier verifier = new MessageFileVerifier(file); - VerifyMessagesTask.verifyFileAndAddKeys(verifier, YamlConfiguration.loadConfiguration( - new File(MESSAGES_FOLDER + "messages_en.yml"))); - } - - private void buildMessageCodeList() { - messageCodes = new HashSet<>(MessageKey.values().length); - for (MessageKey messageKey : MessageKey.values()) { - messageCodes.add(messageKey.getKey()); - } - } - - /** - * Removes all to-do comments written by {@link VerifyMessagesTask}. This is helpful as the YamlConfiguration - * moves those comments otherwise upon saving. - * - * @param file The file whose to-do comments should be removed - */ - private static void removeAllTodoComments(String file) { - String contents = FileUtils.readFromFile(file); - String regex = "^# TODO .*$"; - contents = Pattern.compile(regex, Pattern.MULTILINE).matcher(contents).replaceAll(""); - FileUtils.writeToFile(file, contents); - } -} diff --git a/src/test/java/tools/messages/translation/LanguageExport.java b/src/test/java/tools/messages/translation/LanguageExport.java deleted file mode 100644 index c15a54bde..000000000 --- a/src/test/java/tools/messages/translation/LanguageExport.java +++ /dev/null @@ -1,19 +0,0 @@ -package tools.messages.translation; - -import java.util.Collections; -import java.util.List; - -/** - * Export of a language's messages. - */ -public class LanguageExport { - - public final String code; - public final List messages; - - public LanguageExport(String code, List messages) { - this.code = code; - this.messages = Collections.unmodifiableList(messages); - } - -} diff --git a/src/test/java/tools/messages/translation/MessageExport.java b/src/test/java/tools/messages/translation/MessageExport.java deleted file mode 100644 index 1b37347c8..000000000 --- a/src/test/java/tools/messages/translation/MessageExport.java +++ /dev/null @@ -1,20 +0,0 @@ -package tools.messages.translation; - -/** - * Container class for one translatable message. - */ -public class MessageExport { - - public final String key; - public final String tags; - public final String defaultMessage; - public final String translatedMessage; - - public MessageExport(String key, String[] tags, String defaultMessage, String translatedMessage) { - this.key = key; - this.tags = String.join(",", tags); - this.defaultMessage = defaultMessage; - this.translatedMessage = translatedMessage; - } - -} diff --git a/src/test/java/tools/messages/translation/WriteAllExportsTask.java b/src/test/java/tools/messages/translation/WriteAllExportsTask.java deleted file mode 100644 index f5f3825bc..000000000 --- a/src/test/java/tools/messages/translation/WriteAllExportsTask.java +++ /dev/null @@ -1,37 +0,0 @@ -package tools.messages.translation; - -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import tools.utils.FileUtils; -import tools.utils.ToolsConstants; - -import java.io.File; -import java.util.Scanner; - -/** - * Task which exports all messages to a local folder. - */ -public class WriteAllExportsTask extends ExportMessagesTask { - - private static final String OUTPUT_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "messages/translation/export/"; - - @Override - public String getTaskName() { - return "writeAllExports"; - } - - @Override - public void execute(Scanner scanner) { - File[] messageFiles = new File(MESSAGES_FOLDER).listFiles(); - if (messageFiles == null || messageFiles.length == 0) { - throw new IllegalStateException("Could not read messages folder"); - } - - final FileConfiguration defaultMessages = loadDefaultMessages(); - for (File file : messageFiles) { - String code = file.getName().substring("messages_".length(), file.getName().length() - ".yml".length()); - String json = convertToJson(code, defaultMessages, YamlConfiguration.loadConfiguration(file)); - FileUtils.writeToFile(OUTPUT_FOLDER + "messages_" + code + ".json", json); - } - } -} diff --git a/src/test/java/tools/utils/AutoToolTask.java b/src/test/java/tools/utils/AutoToolTask.java index b635fe5fe..61b909a52 100644 --- a/src/test/java/tools/utils/AutoToolTask.java +++ b/src/test/java/tools/utils/AutoToolTask.java @@ -1,5 +1,7 @@ package tools.utils; +import java.util.Scanner; + /** * Interface for tasks that can be run automatically, i.e. without any user input. */ @@ -10,4 +12,9 @@ public interface AutoToolTask extends ToolTask { */ void executeDefault(); + @Override + default void execute(Scanner scanner) { + executeDefault(); + } + } diff --git a/src/test/java/tools/utils/FileUtils.java b/src/test/java/tools/utils/FileIoUtils.java similarity index 66% rename from src/test/java/tools/utils/FileUtils.java rename to src/test/java/tools/utils/FileIoUtils.java index 44bf5a296..eadefd2d5 100644 --- a/src/test/java/tools/utils/FileUtils.java +++ b/src/test/java/tools/utils/FileIoUtils.java @@ -1,5 +1,6 @@ package tools.utils; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -9,11 +10,11 @@ import java.nio.file.StandardOpenOption; import java.util.List; /** - * Utility class for reading from and writing to files. + * Utility class for I/O operations on files. */ -public final class FileUtils { +public final class FileIoUtils { - private FileUtils() { + private FileIoUtils() { } public static void generateFileFromTemplate(String templateFile, String destinationFile, TagValueHolder tags) { @@ -43,8 +44,12 @@ public final class FileUtils { } public static String readFromFile(String file) { + return readFromFile(Paths.get(file)); + } + + public static String readFromFile(Path file) { try { - return new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8); + return new String(Files.readAllBytes(file), StandardCharsets.UTF_8); } catch (IOException e) { throw new UnsupportedOperationException("Could not read from file '" + file + "'", e); } @@ -58,4 +63,19 @@ public final class FileUtils { } } + /** + * Returns a folder's files or throws an exception if the folder could not be read or if it is empty. + * + * @param folder the folder to read + * @return the files in the folder + */ + public static File[] listFilesOrThrow(File folder) { + File[] files = folder.listFiles(); + if (files == null) { + throw new IllegalStateException("Could not read folder '" + folder + "'"); + } else if (files.length == 0) { + throw new IllegalStateException("Folder '" + folder + "' is empty"); + } + return files; + } } diff --git a/src/test/java/tools/utils/SimpleAutoTask.java b/src/test/java/tools/utils/SimpleAutoTask.java deleted file mode 100644 index 4c0c936d0..000000000 --- a/src/test/java/tools/utils/SimpleAutoTask.java +++ /dev/null @@ -1,15 +0,0 @@ -package tools.utils; - -import java.util.Scanner; - -/** - * Abstract class for auto tool tasks that perform exactly the same action for - * {@link ToolTask#execute(Scanner)} and {@link AutoToolTask#executeDefault()}. - */ -public abstract class SimpleAutoTask implements AutoToolTask { - - @Override - public final void execute(Scanner scanner) { - executeDefault(); - } -} diff --git a/src/test/java/tools/utils/ToolsConstants.java b/src/test/java/tools/utils/ToolsConstants.java index 5986c378b..e7ba3e05a 100644 --- a/src/test/java/tools/utils/ToolsConstants.java +++ b/src/test/java/tools/utils/ToolsConstants.java @@ -9,9 +9,6 @@ public final class ToolsConstants { public static final String MAIN_RESOURCES_ROOT = "src/main/resources/"; - // Add specific `fr.xephi.authme` package as not to include the tool tasks in the `tools` package - public static final String TEST_SOURCE_ROOT = "src/test/java/fr/xephi/authme"; - public static final String TOOLS_SOURCE_ROOT = "src/test/java/tools/"; public static final String DOCS_FOLDER = "docs/"; diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml new file mode 100644 index 000000000..8c6bf79de --- /dev/null +++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml @@ -0,0 +1,23 @@ +# Sample commands.yml file + +onJoin: + broadcast: + command: 'broadcast %p has joined' + executor: CONSOLE +onRegister: + announce: + command: 'me I just registered' + executor: PLAYER + notify: + command: 'log %p registered' + executor: CONSOLE +onLogin: + welcome: + command: 'msg %p Welcome back' + executor: CONSOLE + show_motd: + command: 'motd' + executor: PLAYER + display_list: + command: 'list' + executor: PLAYER diff --git a/src/test/java/tools/messages/translation/export/export-is-written-here b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml similarity index 100% rename from src/test/java/tools/messages/translation/export/export-is-written-here rename to src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml new file mode 100644 index 000000000..2eef86b03 --- /dev/null +++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml @@ -0,0 +1,20 @@ +# Sample commands.yml file with wrong and missing sections + +onJoin: + broadcast: + command: 'broadcast %p has joined' + executor: CONSOLE +onLogin: + welcome: + command: 'msg %p Welcome back' + executor: CONSOLE + show_motd: + # command: 'motd' <-- mandatory property, so entry should be ignored + executor: PLAYER + display_list: + command: 'list' + executor: WRONG_EXECUTOR +doesNotExist: + wrongEntry: + command: 'should be ignored' + executor: PLAYER diff --git a/src/test/resources/fr/xephi/authme/settings/config-old.yml b/src/test/resources/fr/xephi/authme/settings/config-old.yml new file mode 100644 index 000000000..ff2b2918e --- /dev/null +++ b/src/test/resources/fr/xephi/authme/settings/config-old.yml @@ -0,0 +1,414 @@ +# Old default config file, based on commit 1f001f2 (2015-12-06) + +DataSource: + # What type of database do you want to use? + # Valid values: sqlite, mysql + backend: sqlite + # Enable database caching, should improve database performance + caching: true + # Database location + mySQLHost: 127.0.0.1 + # Database Port + mySQLPort: '3306' + # Username about Database Connection Infos + mySQLUsername: authme + # Password about Database Connection Infos + mySQLPassword: '12345' + # Database Name, use with converters or as SQLITE database name + mySQLDatabase: authme + # Table of the database + mySQLTablename: authme + # Column of IDs to sort data + mySQLColumnId: id + # Column for storing or checking players nickname + mySQLColumnName: username + # Column for storing players passwords + mySQLColumnPassword: password + # Column for storing players emails + mySQLColumnEmail: email + # Column for Saving if a player is logged in or not + mySQLColumnLogged: isLogged + # Column for storing players IPs + mySQLColumnIp: ip + # Column for storing players lastlogins + mySQLColumnLastLogin: lastlogin + # Column for SaveQuitLocation - X + mySQLlastlocX: x + # Column for SaveQuitLocation - Y + mySQLlastlocY: y + # Column for SaveQuitLocation - Z + mySQLlastlocZ: z + # Column for SaveQuitLocation - World name + mySQLlastlocWorld: world + # Column for RealName + mySQLRealName: realname + # Enable this when you allow registration through a website + mySQLWebsite: false +settings: + # The name shown in the help messages. + helpHeader: AuthMeReloaded + sessions: + # Do you want to enable the session feature? + # If enabled, when a player authenticates successfully, + # his IP and his nickname is saved. + # The next time the player joins the server, if his IP + # is the same of the last time, and the timeout time + # hasn't expired, he will not need to authenticate. + enabled: false + # After how many minutes a session should expire? + # 0 for unlimited time (Very dangerous, use it at your own risk!) + # Consider that session will end only after the timeout time, and + # if the player's ip has changed but the timeout hasn't expired, + # player will be kicked out of sever due to invalidSession! + timeout: 10 + # Should the session expire if the player try to login with an + # another IP Address? + sessionExpireOnIpChange: true + restrictions: + # Can not authenticated players chat and see the chat log? + # Care that this feature blocks also all the commands not + # listed in the list below. + allowChat: false + # Commands allowed when a player is not authenticated + allowCommands: + - /login + - /register + - /l + - /reg + - /email + - /captcha + # Max number of allowed registrations per IP (default: 1) + maxRegPerIp: 1 + # Max allowed username length + maxNicknameLength: 16 + # When this setting is enabled, online players can't be kicked out + # due to "Logged in from another Location" + # This setting will prevent potetial security exploits. + ForceSingleSession: true + # If enabled, every player will be teleported to the world spawnpoint + # after successful authentication. + # The quit location of the player will be overwritten. + # This is different from "teleportUnAuthedToSpawn" that teleport player + # back to his quit location after the authentication. + ForceSpawnLocOnJoinEnabled: true + # This option will save the quit location of the players. + SaveQuitLocation: false + # To activate the restricted user feature you need + # to enable this option and configure the + # AllowedRestrctedUser field. + AllowRestrictedUser: false + # The restricted user feature will kick players listed below + # if they dont match of the defined ip address. + # Example: + # AllowedRestrictedUser: + # - playername;127.0.0.1 + AllowedRestrictedUser: + - playername;127.0.0.1 + # Should unregistered players be kicked immediatly? + kickNonRegistered: false + # Should players be kicked on wrong password? + kickOnWrongPassword: false + # Should not logged in players be teleported to the spawn? + # After the authentication they will be teleported back to + # their normal position. + teleportUnAuthedToSpawn: false + # Minimum allowed nick length + minNicknameLength: 4 + # Can unregistered players walk around? + allowMovement: false + # Should not authenticated players have speed = 0? + # This will reset the fly/walk speed to default value after the login. + removeSpeed: true + # After how many time players who fail to login or register + # should be kicked? Set to 0 to disable. + timeout: 30 + # Regex sintax of allowed characters in the player name. + allowedNicknameCharacters: '[a-zA-Z0-9_?]*' + # How far can unregistered players walk? Set to 0 + # for unlimited radius + allowedMovementRadius: 100 + # Enable double check of password when you register + # when it's true, registration require that kind of command: + # /register + enablePasswordVerifier: true + # Should we protect the player inventory before logging in? + ProtectInventoryBeforeLogIn: true + # Should we display all other accounts from a player when he joins? + # permission: /authme.admin.accounts + displayOtherAccounts: true + # WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled + # CASE SENSITIVE + ForceSpawnOnTheseWorlds: + - survival + - survival_nether + - creative + # Ban ip when the ip is not the ip registered in database + banUnsafedIP: false + # Spawn Priority, Values : authme, essentials, multiverse, default + spawnPriority: authme,essentials,multiverse,default + # Maximum Login authorized by IP + maxLoginPerIp: 0 + # Maximum Join authorized by IP + maxJoinPerIp: 0 + # AuthMe will NEVER teleport players ! + noTeleport: false + # Regex sintax for allowed Chars in passwords. + allowedPasswordCharacters: '[\x21-\x7E]*' + GameMode: + # ForceSurvivalMode to player when join ? + ForceSurvivalMode: false + # if player join with CreativeMode and ForceSurvivalMode: true + # inventory will be wipped + ResetInventoryIfCreative: false + # Do we need to force the survival mode ONLY after /login process ? + ForceOnlyAfterLogin: false + security: + # minimum Length of password + minPasswordLength: 5 + # this is very important options, + # every time player join the server, + # if they are registered, AuthMe will switch him + # to unLoggedInGroup, this + # should prevent all major exploit. + # So you can set up on your Permission Plugin + # this special group with 0 permissions, or permissions to chat, + # or permission to + # send private message or all other perms that you want, + # the better way is to set up + # this group with few permissions, + # so if player try to exploit some account, + # they can + # do anything except what you set in perm Group. + # After a correct logged-in player will be + # moved to his correct permissions group! + # Pay attention group name is case sensitive, + # so Admin is different from admin, + # otherwise your group will be wiped, + # and player join in default group []! + # Example unLoggedinGroup: NotLogged + unLoggedinGroup: unLoggedinGroup + # possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, + # PLAINTEXT ( unhashed password), + # MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, + # DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only) + passwordHash: SHA256 + # salt length for the SALTED2MD5 MD5(MD5(password)+salt) + doubleMD5SaltLength: 8 + # If password checking return false , do we need to check with all + # other password algorithm to check an old password ? + # AuthMe will update the password to the new passwordHash ! + supportOldPasswordHash: false + # Cancel unsafe passwords for being used, put them on lowercase! + #unsafePasswords: + #- '123456' + #- 'password' + unsafePasswords: + - '123456' + - 'password' + - 'qwerty' + - '12345' + - '54321' + registration: + # enable registration on the server? + enabled: true + # Send every X seconds a message to a player to + # remind him that he has to login/register + messageInterval: 5 + # Only registered and logged in players can play. + # See restrictions for exceptions + force: true + # Does we replace password registration by an Email registration method ? + enableEmailRegistrationSystem: false + # Enable double check of email when you register + # when it's true, registration require that kind of command: + # /register + doubleEmailCheck: false + # Do we force kicking player after a successful registration ? + # Do not use with login feature below + forceKickAfterRegister: false + # Does AuthMe need to enforce a /login after a successful registration ? + forceLoginAfterRegister: false + unrestrictions: + # below you can list all your account name, that + # AuthMe will ignore for registration or login, configure it + # at your own risk!! Remember that if you are going to add + # nickname with [], you have to delimit name with ' '. + # this option add compatibility with BuildCraft and some + # other mods. + # It is CaseSensitive! + UnrestrictedName: [] + # Message language, available : en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt + messagesLanguage: en + # Force these commands after /login, without any '/', use %p for replace with player name + forceCommands: + - spawn + # Force these commands after /login as a server console, without any '/', use %p for replace with player name + forceCommandsAsConsole: + - sethome %p:lastloc + - msg %p Welcome back + # Force these commands after /register, without any '/', use %p for replace with player name + forceRegisterCommands: + - me registers + - msg CONSOLE hi + # Force these commands after /register as a server console, without any '/', use %p for replace with player name + forceRegisterCommandsAsConsole: + - sethome %p:regloc + # Do we need to display the welcome message (welcome.txt) after a register or a login? + # You can use colors in this welcome.txt + some replaced strings : + # {PLAYER} : player name, {ONLINE} : display number of online players, {MAXPLAYERS} : display server slots, + # {IP} : player ip, {LOGINS} : number of players logged, {WORLD} : player current world, {SERVER} : server name + # {VERSION} : get current bukkit version, {COUNTRY} : player country + useWelcomeMessage: true + # Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player + broadcastWelcomeMessage: false + # Do we need to delay the join/leave message to be displayed only when the player is authenticated ? + delayJoinLeaveMessages: true + # Do we need to add potion effect Blinding before login/register ? + applyBlindEffect: false +ExternalBoardOptions: + # MySQL column for the salt , needed for some forum/cms support + mySQLColumnSalt: '' + # MySQL column for the group, needed for some forum/cms support + mySQLColumnGroup: '' + # -1 mean disabled. If u want that only + # activated player can login in your server + # u can put in this options the group number + # of unactivated user, needed for some forum/cms support + nonActivedUserGroup: -1 + # Other MySQL columns where we need to put the Username (case sensitive) + mySQLOtherUsernameColumns: [] + # How much Log to Round needed in BCrypt(do not change it if you do not know what's your doing) + bCryptLog2Round: 10 + # phpBB prefix defined during phpbb installation process + phpbbTablePrefix: 'phpbb_' + # phpBB activated group id , 2 is default registered group defined by phpbb + phpbbActivatedGroupId: 2 + # WordPress prefix defined during WordPress installation process + wordpressTablePrefix: 'wp_' +permission: + # Take care with this options, if you dont want + # to use Vault and Group Switching of + # AuthMe for unloggedIn players put true + # below, default is false. + EnablePermissionCheck: false +BackupSystem: + # Enable or Disable Automatic Backup + ActivateBackup: false + # set Backup at every start of Server + OnServerStart: false + # set Backup at every stop of Server + OnServerStop: true + # Windows only mysql installation Path + MysqlWindowsPath: 'C:\\Program Files\\MySQL\\MySQL Server 5.1\\' +Security: + SQLProblem: + # Stop the server if we can't contact the sql database + # Take care with this, if you set that to false, + # AuthMe automatically disable and the server is not protected! + stopServer: true + ReloadCommand: + # /reload support + useReloadCommandSupport: true + console: + # Remove spam console + noConsoleSpam: true + # Replace passwords in the console when player type a command like /login + removePassword: true + captcha: + # Player need to put a captcha when he fails too lot the password + useCaptcha: false + # Max allowed tries before request a captcha + maxLoginTry: 5 + # Captcha length + captchaLength: 5 +Converter: + Rakamak: + # Rakamak file name + fileName: users.rak + # Rakamak use ip ? + useIP: false + # IP file name for rakamak + ipFileName: UsersIp.rak + CrazyLogin: + # CrazyLogin database file + fileName: accounts.db +Email: + # Email SMTP server host + mailSMTP: smtp.gmail.com + # Email SMTP server port + mailPort: 465 + # Email account that send the mail + mailAccount: '' + # Email account password + mailPassword: '' + # Custom SenderName, that replace the mailAccount name in the email + mailSenderName: '' + # Random password length + RecoveryPasswordLength: 8 + # Email subject of password get + mailSubject: 'Your new AuthMe Password' + # Email text here + mailText: 'Dear ,

    This is your new AuthMe password for the server

    %servername% :



    Do not forget to change password after login!
    /changepassword %generatedpass% newPassword' + # Like maxRegPerIp but with email + maxRegPerEmail: 1 + # Recall players to add an email ? + recallPlayers: false + # Delay in minute for the recall scheduler + delayRecall: 5 + # Blacklist these domains for emails + emailBlacklisted: + - 10minutemail.com + # WhiteList only these domains for emails + emailWhitelisted: [] + # Do we need to send new password draw in an image ? + generateImage: false +Hooks: + # Do we need to hook with multiverse for spawn checking? + multiverse: true + # Do we need to hook with BungeeCord for get the real Player ip ? + bungeecord: false + # Do we need to disable Essentials SocialSpy on join ? + disableSocialSpy: true + # Do we need to force /motd Essentials command on join ? + useEssentialsMotd: false + # Do we need to cache custom Attributes ? + customAttributes: false +Purge: + # On Enable , does AuthMe need to purge automatically old accounts unused ? + useAutoPurge: false + # Number of Days an account become Unused + daysBeforeRemovePlayer: 60 + # Do we need to remove the player.dat file during purge process ? + removePlayerDat: false + # Do we need to remove the Essentials/users/player.yml file during purge process ? + removeEssentialsFile: false + # World where are players.dat stores + defaultWorld: 'world' + # Do we need to remove LimitedCreative/inventories/player.yml , player_creative.yml files during purge process ? + removeLimitedCreativesInventories: false + # Do we need to remove the AntiXRayData/PlayerData/player file during purge process ? + removeAntiXRayFile: false + # Do we need to remove permissions ? + removePermissions: false +Protection: + # Enable some servers protection ( country based login, antibot ) + enableProtection: false + # Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes + # PLEASE USE QUOTES! + countries: + - 'US' + - 'GB' + # Countries blacklisted automatically ( without any needed to enable protection ) + # PLEASE USE QUOTES! + countriesBlacklist: + - 'A1' + # Do we need to enable automatic antibot system? + enableAntiBot: false + # Max number of player allowed to login in 5 secs before enable AntiBot system automatically + antiBotSensibility: 5 + # Duration in minutes of the antibot automatic system + antiBotDuration: 10 +VeryGames: + # These features are only available on VeryGames Server Provider + enableIpCheck: false +