Email verification code (#1382)

* Base verification code implementation, must define command, manager, events
* VerificationManager, verification command and messages, handled some sensible commands, configuration values
* Improved manager and sensible commands trigger
* Updated messages
* Updated verification code manager, fixed tests
* Switched to a permission based command
* Verification manager and command improved and added tests
* Edited messages
This commit is contained in:
HexelDev 2017-10-28 12:23:14 +02:00 committed by ljacqu
parent 8422581a82
commit ba65633182
58 changed files with 1227 additions and 67 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Oct 08 19:56:41 CEST 2017. See docs/commands/commands.tpl.md --> <!-- File auto-generated on Sat Oct 28 10:39:36 CEST 2017. See docs/commands/commands.tpl.md -->
## AuthMe Commands ## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
@ -32,7 +32,7 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.admin.firstspawn` <br />Requires `authme.admin.firstspawn`
- **/authme setfirstspawn**: Change the first player's spawn to your current position. - **/authme setfirstspawn**: Change the first player's spawn to your current position.
<br />Requires `authme.admin.setfirstspawn` <br />Requires `authme.admin.setfirstspawn`
- **/authme purge** &lt;days> [all]: Purge old AuthMeReloaded data longer than the specified number of days ago. - **/authme purge** &lt;days>: Purge old AuthMeReloaded data longer than the specified number of days ago.
<br />Requires `authme.admin.purge` <br />Requires `authme.admin.purge`
- **/authme purgeplayer** &lt;player> [options]: Purges data of the given player. - **/authme purgeplayer** &lt;player> [options]: Purges data of the given player.
<br />Requires `authme.admin.purgeplayer` <br />Requires `authme.admin.purgeplayer`
@ -86,8 +86,11 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
- **/captcha** &lt;captcha>: Captcha command for AuthMeReloaded. - **/captcha** &lt;captcha>: Captcha command for AuthMeReloaded.
<br />Requires `authme.player.captcha` <br />Requires `authme.player.captcha`
- **/captcha help** [query]: View detailed help for /captcha commands. - **/captcha help** [query]: View detailed help for /captcha commands.
- **/verification** &lt;code>: Command to complete the verification process for AuthMeReloaded.
<br />Requires `authme.player.security.verificationcode`
- **/verification help** [query]: View detailed help for /verification commands.
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 08 19:56:41 CEST 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 28 10:39:36 CEST 2017

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Tue Oct 10 13:51:56 CEST 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Sat Oct 28 10:39:36 CEST 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -43,10 +43,14 @@ DataSource:
mySQLColumnLogged: 'isLogged' mySQLColumnLogged: 'isLogged'
# Column for storing if a player has a valid session or not # Column for storing if a player has a valid session or not
mySQLColumnHasSession: 'hasSession' mySQLColumnHasSession: 'hasSession'
# Column for storing players ips # Column for storing the player's last IP
mySQLColumnIp: 'ip' mySQLColumnIp: 'ip'
# Column for storing players lastlogins # Column for storing players lastlogins
mySQLColumnLastLogin: 'lastlogin' mySQLColumnLastLogin: 'lastlogin'
# Column storing the registration date
mySQLColumnRegisterDate: 'regdate'
# Column for storing the IP address at the time of registration
mySQLColumnRegisterIp: 'regip'
# Column for storing player LastLocation - X # Column for storing player LastLocation - X
mySQLlastlocX: 'x' mySQLlastlocX: 'x'
# Column for storing player LastLocation - Y # Column for storing player LastLocation - Y
@ -226,10 +230,11 @@ settings:
minPasswordLength: 5 minPasswordLength: 5
# Maximum length of password # Maximum length of password
passwordMaxLength: 30 passwordMaxLength: 30
# Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512, WHIRLPOOL, # Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,
# MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB, # MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,
# PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only). See full list at # PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md # https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md
# If you use ARGON2, check that you have the argon2 c library on your system
passwordHash: 'SHA256' passwordHash: 'SHA256'
# If a password check fails, AuthMe will also try to check with the following hash methods. # 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. # Use this setting when you change from one hash method to another.
@ -470,6 +475,8 @@ Security:
# original email: my.email@example.com # original email: my.email@example.com
# hidden email: my.***@***mple.com # hidden email: my.***@***mple.com
enableEmailMasking: false enableEmailMasking: false
# Minutes after which a verification code will expire
verificationCodeExpiration: 10
# Before a user logs in, various properties are temporarily removed from the player, # Before a user logs in, various properties are temporarily removed from the player,
# such as OP status, ability to fly, and walk/fly speed. # such as OP status, ability to fly, and walk/fly speed.
# Once the user is logged in, we add back the properties we previously saved. # Once the user is logged in, we add back the properties we previously saved.
@ -547,4 +554,4 @@ To change settings on a running server, save your changes to config.yml and use
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Tue Oct 10 13:51:56 CEST 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 28 10:39:36 CEST 2017

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sat Sep 02 12:24:17 CEST 2017. See docs/permissions/permission_nodes.tpl.md --> <!-- File auto-generated on Sat Oct 28 12:12:40 CEST 2017. See docs/permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes ## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds. The following are the permission nodes that are currently supported by the latest dev builds.
@ -40,6 +40,7 @@ The following are the permission nodes that are currently supported by the lates
- **authme.debug.group** Permission to view permission groups. - **authme.debug.group** Permission to view permission groups.
- **authme.debug.limbo** Permission to use the limbo data viewer. - **authme.debug.limbo** Permission to use the limbo data viewer.
- **authme.debug.mail** Permission to use the test email sender. - **authme.debug.mail** Permission to use the test email sender.
- **authme.debug.mysqldef** Permission to change nullable status of MySQL columns.
- **authme.debug.perm** Permission to use the permission checker. - **authme.debug.perm** Permission to use the permission checker.
- **authme.debug.spawn** Permission to view spawn information. - **authme.debug.spawn** Permission to view spawn information.
- **authme.debug.stats** Permission to use the stats section. - **authme.debug.stats** Permission to use the stats section.
@ -56,6 +57,7 @@ The following are the permission nodes that are currently supported by the lates
- **authme.player.login** Command permission to login. - **authme.player.login** Command permission to login.
- **authme.player.logout** Command permission to logout. - **authme.player.logout** Command permission to logout.
- **authme.player.register** Command permission to register. - **authme.player.register** Command permission to register.
- **authme.player.security.verificationcode** Permission to use the email verification codes feature.
- **authme.player.seeownaccounts** Permission to use to see own other accounts. - **authme.player.seeownaccounts** Permission to use to see own other accounts.
- **authme.player.unregister** Command permission to unregister. - **authme.player.unregister** Command permission to unregister.
- **authme.vip** When the server is full and someone with this permission joins the server, someone will be kicked. - **authme.vip** When the server is full and someone with this permission joins the server, someone will be kicked.
@ -63,4 +65,4 @@ The following are the permission nodes that are currently supported by the lates
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Sep 02 12:24:17 CEST 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 28 12:12:40 CEST 2017

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Sep 17 11:29:08 CEST 2017. See docs/translations/translations.tpl.md --> <!-- File auto-generated on Sat Oct 28 11:30:40 CEST 2017. See docs/translations/translations.tpl.md -->
# AuthMe Translations # AuthMe Translations
The following translations are available in AuthMe. Set `messagesLanguage` to the language code The following translations are available in AuthMe. Set `messagesLanguage` to the language code
@ -8,37 +8,37 @@ in your config.yml to use the language, or use another language code to start a
Code | Language | Translated | &nbsp; Code | Language | Translated | &nbsp;
---- | -------- | ---------: | ------ ---- | -------- | ---------: | ------
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" /> [bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 53% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=53&h=5&txtpad=1" alt="bar" /> [eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 49% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=49&h=5&txtpad=1" alt="bar" />
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 56% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=56&h=5&txtpad=1" alt="bar" /> [fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 52% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=52&h=5&txtpad=1" alt="bar" />
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 60% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=60&h=5&txtpad=1" alt="bar" /> [gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 55% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=55&h=5&txtpad=1" alt="bar" />
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 91% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=91&h=5&txtpad=1" alt="bar" />
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 59% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=59&h=5&txtpad=1" alt="bar" /> [id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 54% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=54&h=5&txtpad=1" alt="bar" />
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" /> [ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 78% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=78&h=5&txtpad=1" alt="bar" />
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 45% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=45&h=5&txtpad=1" alt="bar" /> [lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 41% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa4400&w=41&h=5&txtpad=1" alt="bar" />
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" /> [tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 79% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=79&h=5&txtpad=1" alt="bar" /> [uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 73% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=73&h=5&txtpad=1" alt="bar" />
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 96% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=96&h=5&txtpad=1" alt="bar" /> [vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 89% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=89&h=5&txtpad=1" alt="bar" />
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" /> [zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 68% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=68&h=5&txtpad=1" alt="bar" /> [zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 81% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=81&h=5&txtpad=1" alt="bar" /> [zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 75% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=cc9900&w=75&h=5&txtpad=1" alt="bar" />
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 68% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=68&h=5&txtpad=1" alt="bar" /> [zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 63% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=63&h=5&txtpad=1" alt="bar" />
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Sep 17 11:29:08 CEST 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 28 11:30:40 CEST 2017

View File

@ -40,6 +40,7 @@ import fr.xephi.authme.command.executable.login.LoginCommand;
import fr.xephi.authme.command.executable.logout.LogoutCommand; import fr.xephi.authme.command.executable.logout.LogoutCommand;
import fr.xephi.authme.command.executable.register.RegisterCommand; import fr.xephi.authme.command.executable.register.RegisterCommand;
import fr.xephi.authme.command.executable.unregister.UnregisterCommand; import fr.xephi.authme.command.executable.unregister.UnregisterCommand;
import fr.xephi.authme.command.executable.verification.VerificationCommand;
import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.permission.PlayerPermission;
@ -136,13 +137,24 @@ public class CommandInitializer {
CommandDescription captchaBase = CommandDescription.builder() CommandDescription captchaBase = CommandDescription.builder()
.parent(null) .parent(null)
.labels("captcha") .labels("captcha")
.description("Captcha Command") .description("Captcha command")
.detailedDescription("Captcha command for AuthMeReloaded.") .detailedDescription("Captcha command for AuthMeReloaded.")
.withArgument("captcha", "The Captcha", false) .withArgument("captcha", "The Captcha", false)
.permission(PlayerPermission.CAPTCHA) .permission(PlayerPermission.CAPTCHA)
.executableCommand(CaptchaCommand.class) .executableCommand(CaptchaCommand.class)
.register(); .register();
// Register the base verification code command
CommandDescription verificationBase = CommandDescription.builder()
.parent(null)
.labels("verification")
.description("Verification command")
.detailedDescription("Command to complete the verification process for AuthMeReloaded.")
.withArgument("code", "The code", false)
.permission(PlayerPermission.VERIFICATION_CODE)
.executableCommand(VerificationCommand.class)
.register();
List<CommandDescription> baseCommands = ImmutableList.of( List<CommandDescription> baseCommands = ImmutableList.of(
authMeBase, authMeBase,
emailBase, emailBase,
@ -151,7 +163,8 @@ public class CommandInitializer {
registerBase, registerBase,
unregisterBase, unregisterBase,
changePasswordBase, changePasswordBase,
captchaBase); captchaBase,
verificationBase);
setHelpOnAllBases(baseCommands); setHelpOnAllBases(baseCommands);
commands = baseCommands; commands = baseCommands;

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.command.executable.changepassword; package fr.xephi.authme.command.executable.changepassword;
import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -29,17 +30,28 @@ public class ChangePasswordCommand extends PlayerCommand {
@Inject @Inject
private Management management; private Management management;
@Inject
private VerificationCodeManager codeManager;
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
String oldPassword = arguments.get(0);
String newPassword = arguments.get(1);
String name = player.getName().toLowerCase(); String name = player.getName().toLowerCase();
if (!playerCache.isAuthenticated(name)) { if (!playerCache.isAuthenticated(name)) {
commonService.send(player, MessageKey.NOT_LOGGED_IN); commonService.send(player, MessageKey.NOT_LOGGED_IN);
return; return;
} }
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(name);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
String oldPassword = arguments.get(0);
String newPassword = arguments.get(1);
// Make sure the password is allowed // Make sure the password is allowed
ValidationResult passwordValidation = validationService.validatePassword(newPassword, name); ValidationResult passwordValidation = validationService.validatePassword(newPassword, name);
if (passwordValidation.hasError()) { if (passwordValidation.hasError()) {

View File

@ -1,8 +1,10 @@
package fr.xephi.authme.command.executable.email; package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
@ -16,11 +18,24 @@ public class ChangeEmailCommand extends PlayerCommand {
@Inject @Inject
private Management management; private Management management;
@Inject
private CommonService commonService;
@Inject
private VerificationCodeManager codeManager;
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
final String playerName = player.getName();
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(playerName);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
String playerMailOld = arguments.get(0); String playerMailOld = arguments.get(0);
String playerMailNew = arguments.get(1); String playerMailNew = arguments.get(1);
management.performChangeEmail(player, playerMailOld, playerMailNew); management.performChangeEmail(player, playerMailOld, playerMailNew);
} }

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.command.executable.unregister; package fr.xephi.authme.command.executable.unregister;
import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -24,6 +25,9 @@ public class UnregisterCommand extends PlayerCommand {
@Inject @Inject
private PlayerCache playerCache; private PlayerCache playerCache;
@Inject
private VerificationCodeManager codeManager;
@Override @Override
public void runCommand(Player player, List<String> arguments) { public void runCommand(Player player, List<String> arguments) {
String playerPass = arguments.get(0); String playerPass = arguments.get(0);
@ -35,6 +39,13 @@ public class UnregisterCommand extends PlayerCommand {
return; return;
} }
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(playerName);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
// Unregister the player // Unregister the player
management.performUnregister(player, playerPass); management.performUnregister(player, playerPass);
} }

View File

@ -0,0 +1,58 @@
package fr.xephi.authme.command.executable.verification;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
/**
* Used to complete the email verification process.
*/
public class VerificationCommand extends PlayerCommand {
@Inject
private CommonService commonService;
@Inject
private VerificationCodeManager codeManager;
@Override
public void runCommand(Player player, List<String> arguments) {
final String playerName = player.getName();
if (!codeManager.canSendMail()) {
ConsoleLogger.warning("Mail API is not set");
commonService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return;
}
if (codeManager.isVerificationRequired(player)) {
if (codeManager.isCodeRequired(playerName)) {
if (codeManager.checkCode(playerName, arguments.get(0))) {
commonService.send(player, MessageKey.VERIFICATION_CODE_VERIFIED);
} else {
commonService.send(player, MessageKey.INCORRECT_VERIFICATION_CODE);
}
} else {
commonService.send(player, MessageKey.VERIFICATION_CODE_EXPIRED);
}
} else {
if (codeManager.hasEmail(playerName)) {
commonService.send(player, MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED);
} else {
commonService.send(player, MessageKey.VERIFICATION_CODE_EMAIL_NEEDED);
commonService.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
@Override
public MessageKey getArgumentsMismatchMessage() {
return MessageKey.USAGE_VERIFICATION_CODE;
}
}

View File

@ -0,0 +1,189 @@
package fr.xephi.authme.data;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceResult;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.mail.EmailService;
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.util.RandomStringUtils;
import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.expiring.ExpiringMap;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class VerificationCodeManager implements SettingsDependent, HasCleanup {
private final EmailService emailService;
private final DataSource dataSource;
private final PermissionsManager permissionsManager;
private final ExpiringMap<String, String> verificationCodes;
private final Set<String> verifiedPlayers;
private boolean canSendMail;
@Inject
VerificationCodeManager(Settings settings, DataSource dataSource, EmailService emailService,
PermissionsManager permissionsManager) {
this.emailService = emailService;
this.dataSource = dataSource;
this.permissionsManager = permissionsManager;
verifiedPlayers = new HashSet<>();
long countTimeout = settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES);
verificationCodes = new ExpiringMap<>(countTimeout, TimeUnit.MINUTES);
reload(settings);
}
/**
* Returns if it is possible to send emails
*
* @return true if the service is enabled, false otherwise
*/
public boolean canSendMail() {
return canSendMail;
}
/**
* Returns whether the given player is able to verify his identity
*
* @param player the player to verify
* @return true if the player has not been verified yet, false otherwise
*/
public boolean isVerificationRequired(Player player) {
final String name = player.getName();
return canSendMail
&& !isPlayerVerified(name)
&& permissionsManager.hasPermission(player, PlayerPermission.VERIFICATION_CODE)
&& hasEmail(name);
}
/**
* Returns whether the given player is required to verify his identity through a command
*
* @param name the name of the player to verify
* @return true if the player has an existing code and has not been verified yet, false otherwise
*/
public boolean isCodeRequired(String name) {
return canSendMail && hasCode(name) && !isPlayerVerified(name);
}
/**
* Returns whether the given player has been verified or not
*
* @param name the name of the player to verify
* @return true if the player has been verified, false otherwise
*/
private boolean isPlayerVerified(String name) {
return verifiedPlayers.contains(name.toLowerCase());
}
/**
* Returns if a code exists for the player
*
* @param name the name of the player to verify
* @return true if the code exists, false otherwise
*/
public boolean hasCode(String name) {
return (verificationCodes.get(name.toLowerCase()) != null);
}
/**
* Returns whether the given player is able to receive emails
*
* @param name the name of the player to verify
* @return true if the player is able to receive emails, false otherwise
*/
public boolean hasEmail(String name) {
boolean result = false;
DataSourceResult<String> emailResult = dataSource.getEmail(name);
if (emailResult.playerExists()) {
final String email = emailResult.getValue();
if (!Utils.isEmailEmpty(email)) {
result = true;
}
}
return result;
}
/**
* Check if a code exists for the player or generates and saves a new one.
*
* @param name the player's name
*/
public void codeExistOrGenerateNew(String name) {
if (!hasCode(name)) {
generateCode(name);
}
}
/**
* Generates a code for the player and returns it.
*
* @param name the name of the player to generate a code for
*/
private void generateCode(String name) {
DataSourceResult<String> emailResult = dataSource.getEmail(name);
if (emailResult.playerExists()) {
final String email = emailResult.getValue();
if (!Utils.isEmailEmpty(email)) {
String code = RandomStringUtils.generateNum(6); // 6 digits code
verificationCodes.put(name.toLowerCase(), code);
emailService.sendVerificationMail(name, email, code);
}
}
}
/**
* Checks the given code against the existing one.
*
* @param name the name of the player to check
* @param code the supplied code
* @return true if the code matches, false otherwise
*/
public boolean checkCode(String name, String code) {
boolean correct = false;
if (code.equals(verificationCodes.get(name.toLowerCase()))) {
verify(name);
correct = true;
}
return correct;
}
/**
* Add the user to the set of verified users
*
* @param name the name of the player to generate a code for
*/
public void verify(String name){
verifiedPlayers.add(name.toLowerCase());
}
/**
* Remove the user from the set of verified users
*
* @param name the name of the player to generate a code for
*/
public void unverify(String name){
verifiedPlayers.remove(name.toLowerCase());
}
@Override
public void reload(Settings settings) {
canSendMail = emailService.hasAllInformation();
long countTimeout = settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES);
verificationCodes.setExpiration(countTimeout, TimeUnit.MINUTES);
}
@Override
public void performCleanup() {
verificationCodes.removeExpiredEntries();
}
}

View File

@ -67,7 +67,7 @@ public class EmailService {
File file = null; File file = null;
if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) {
try { try {
file = generateImage(name, newPass); file = generatePasswordImage(name, newPass);
mailText = embedImageIntoEmailContent(file, email, mailText); mailText = embedImageIntoEmailContent(file, email, mailText);
} catch (IOException | EmailException e) { } catch (IOException | EmailException e) {
ConsoleLogger.logException( ConsoleLogger.logException(
@ -80,6 +80,33 @@ public class EmailService {
return couldSendEmail; return couldSendEmail;
} }
/**
* Sends an email to the user with the temporary verification code.
*
* @param name the name of the player
* @param mailAddress the player's email
* @param code the verification code
* @return true if email could be sent, false otherwise
*/
public boolean sendVerificationMail(String name, String mailAddress, String code) {
if (!hasAllInformation()) {
ConsoleLogger.warning("Cannot send verification email: not all email settings are complete");
return false;
}
HtmlEmail email;
try {
email = sendMailSsl.initializeMail(mailAddress);
} catch (EmailException e) {
ConsoleLogger.logException("Failed to create verification email with the given settings:", e);
return false;
}
String mailText = replaceTagsForVerificationEmail(settings.getVerificationEmailMessage(), name, code,
settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES));
return sendMailSsl.sendEmail(mailText, email);
}
/** /**
* Sends an email to the user with a recovery code for the password recovery process. * Sends an email to the user with a recovery code for the password recovery process.
* *
@ -102,7 +129,7 @@ public class EmailService {
return sendMailSsl.sendEmail(message, htmlEmail); return sendMailSsl.sendEmail(message, htmlEmail);
} }
private File generateImage(String name, String newPass) throws IOException { private File generatePasswordImage(String name, String newPass) throws IOException {
ImageGenerator gen = new ImageGenerator(newPass); ImageGenerator gen = new ImageGenerator(newPass);
File file = new File(dataFolder, name + "_new_pass.jpg"); File file = new File(dataFolder, name + "_new_pass.jpg");
ImageIO.write(gen.generateImage(), "jpg", file); ImageIO.write(gen.generateImage(), "jpg", file);
@ -123,6 +150,14 @@ public class EmailService {
.replace("<generatedpass />", newPass); .replace("<generatedpass />", newPass);
} }
private String replaceTagsForVerificationEmail(String mailText, String name, String code, int minutesValid) {
return mailText
.replace("<playername />", name)
.replace("<servername />", serverName)
.replace("<generatedcode />", code)
.replace("<minutesvalid />", String.valueOf(minutesValid));
}
private String replaceTagsForRecoveryCodeMail(String mailText, String name, String code, int hoursValid) { private String replaceTagsForRecoveryCodeMail(String mailText, String name, String code, int hoursValid) {
return mailText return mailText
.replace("<playername />", name) .replace("<playername />", name)

View File

@ -239,6 +239,36 @@ public enum MessageKey {
/** An email was already sent recently. You must wait %time before you can send a new one. */ /** An email was already sent recently. You must wait %time before you can send a new one. */
EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"), EMAIL_COOLDOWN_ERROR("email_cooldown_error", "%time"),
/**
* The command you are trying to execute is sensitive and requires a verification!
* A verification code has been sent to your email,
* run the command "/verification [code]" to verify your identity.
*/
VERIFICATION_CODE_REQUIRED("verification_code_required"),
/** Usage: /verification &lt;code&gt; */
USAGE_VERIFICATION_CODE("usage_verification_code"),
/** Incorrect code, please type "/verification &lt;code&gt;" into the chat! */
INCORRECT_VERIFICATION_CODE("incorrect_verification_code"),
/**
* Your identity has been verified!
* You can now execute every sensitive command within the current session!
*/
VERIFICATION_CODE_VERIFIED("verification_code_verified"),
/**
* You can already execute every sensitive command within the current session!
*/
VERIFICATION_CODE_ALREADY_VERIFIED("verification_code_already_verified"),
/** Your code has expired! Execute another sensitive command to get a new code! */
VERIFICATION_CODE_EXPIRED("verification_code_expired"),
/** To verify your identity you need to link an email address with your account! */
VERIFICATION_CODE_EMAIL_NEEDED("verification_code_email_needed"),
/** second */ /** second */
SECOND("second"), SECOND("second"),

View File

@ -63,7 +63,12 @@ public enum PlayerPermission implements PermissionNode {
/** /**
* Permission to use to see own other accounts. * Permission to use to see own other accounts.
*/ */
SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"); SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"),
/**
* Permission to use the email verification codes feature.
*/
VERIFICATION_CODE("authme.player.security.verificationcode");
/** /**
* The permission node. * The permission node.

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.process.logout; package fr.xephi.authme.process.logout;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
@ -26,6 +27,9 @@ public class AsynchronousLogout implements AsynchronousProcess {
@Inject @Inject
private PlayerCache playerCache; private PlayerCache playerCache;
@Inject
private VerificationCodeManager codeManager;
@Inject @Inject
private SyncProcessManager syncProcessManager; private SyncProcessManager syncProcessManager;
@ -52,6 +56,7 @@ public class AsynchronousLogout implements AsynchronousProcess {
} }
playerCache.removePlayer(name); playerCache.removePlayer(name);
codeManager.unverify(name);
database.setUnlogged(name); database.setUnlogged(name);
database.revokeSession(name); database.revokeSession(name);
syncProcessManager.processSyncPlayerLogout(player); syncProcessManager.processSyncPlayerLogout(player);

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.process.quit; package fr.xephi.authme.process.quit;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.CacheDataSource;
@ -44,6 +45,9 @@ public class AsynchronousQuit implements AsynchronousProcess {
@Inject @Inject
private ValidationService validationService; private ValidationService validationService;
@Inject
private VerificationCodeManager codeManager;
AsynchronousQuit() { AsynchronousQuit() {
} }
@ -80,6 +84,7 @@ public class AsynchronousQuit implements AsynchronousProcess {
//always unauthenticate the player - use session only for auto logins on the same ip //always unauthenticate the player - use session only for auto logins on the same ip
playerCache.removePlayer(name); playerCache.removePlayer(name);
codeManager.unverify(name);
//always update the database when the player quit the game (if sessions are disabled) //always update the database when the player quit the game (if sessions are disabled)
if (wasLoggedIn) { if (wasLoggedIn) {

View File

@ -20,6 +20,7 @@ public class Settings extends SettingsManager {
private final File pluginFolder; private final File pluginFolder;
private String passwordEmailMessage; private String passwordEmailMessage;
private String verificationEmailMessage;
private String recoveryCodeEmailMessage; private String recoveryCodeEmailMessage;
/** /**
@ -46,6 +47,15 @@ public class Settings extends SettingsManager {
return passwordEmailMessage; return passwordEmailMessage;
} }
/**
* Return the text for verification emails (before sensitive commands can be used).
*
* @return The email message
*/
public String getVerificationEmailMessage() {
return verificationEmailMessage;
}
/** /**
* Return the text to use when someone requests to receive a recovery code. * Return the text to use when someone requests to receive a recovery code.
* *
@ -57,6 +67,7 @@ public class Settings extends SettingsManager {
private void loadSettingsFromFiles() { private void loadSettingsFromFiles() {
passwordEmailMessage = readFile("email.html"); passwordEmailMessage = readFile("email.html");
verificationEmailMessage = readFile("verification_code_email.html");
recoveryCodeEmailMessage = readFile("recovery_code_email.html"); recoveryCodeEmailMessage = readFile("recovery_code_email.html");
} }

View File

@ -147,6 +147,10 @@ public final class SecuritySettings implements SettingsHolder {
public static final Property<Boolean> USE_EMAIL_MASKING = public static final Property<Boolean> USE_EMAIL_MASKING =
newProperty("Security.privacy.enableEmailMasking", false); newProperty("Security.privacy.enableEmailMasking", false);
@Comment("Minutes after which a verification code will expire")
public static final Property<Integer> VERIFICATION_CODE_EXPIRATION_MINUTES =
newProperty("Security.privacy.verificationCodeExpiration", 10);
private SecuritySettings() { private SecuritySettings() {
} }

View File

@ -10,8 +10,9 @@ public final class RandomStringUtils {
private static final char[] CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); private static final char[] CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final Random RANDOM = new SecureRandom(); private static final Random RANDOM = new SecureRandom();
private static final int HEX_MAX_INDEX = 16; private static final int NUM_INDEX = 10;
private static final int LOWER_ALPHANUMERIC_INDEX = 36; private static final int LOWER_ALPHANUMERIC_INDEX = 36;
private static final int HEX_MAX_INDEX = 16;
// Utility class // Utility class
private RandomStringUtils() { private RandomStringUtils() {
@ -38,6 +39,17 @@ public final class RandomStringUtils {
return generateString(length, HEX_MAX_INDEX); return generateString(length, HEX_MAX_INDEX);
} }
/**
* Generate a random numbers string of the given length. In other words, the generated string
* contains characters only within the range [0-9].
*
* @param length The length of the random string to generate
* @return The random numbers string
*/
public static String generateNum(int length) {
return generateString(length, NUM_INDEX);
}
/** /**
* Generate a random string with digits and lowercase and uppercase letters. The result of this * Generate a random string with digits and lowercase and uppercase letters. The result of this
* method matches the pattern [0-9a-zA-Z]. * method matches the pattern [0-9a-zA-Z].

View File

@ -96,6 +96,15 @@ usage_captcha: '&3Моля въведе цифрите/буквите от ка
wrong_captcha: '&cКода е грешен, използвайте: "/captcha THE_CAPTCHA" в чата!' wrong_captcha: '&cКода е грешен, използвайте: "/captcha THE_CAPTCHA" в чата!'
valid_captcha: '&2Кода е валиден!' valid_captcha: '&2Кода е валиден!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'секунда' second: 'секунда'
seconds: 'секунди' seconds: 'секунди'

View File

@ -100,6 +100,15 @@ usage_captcha: '&3Para iniciar sessão você tem que resolver um código captcha
wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha THE_CAPTCHA" no chat!' wrong_captcha: '&cCaptcha errado, por favor, escreva "/captcha THE_CAPTCHA" no chat!'
valid_captcha: '&2Código Captcha resolvido corretamente!' valid_captcha: '&2Código Captcha resolvido corretamente!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'segundo' second: 'segundo'
seconds: 'segundos' seconds: 'segundos'

View File

@ -95,6 +95,15 @@ usage_captcha: '&cPoužij: /captcha <theCaptcha>'
wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA' wrong_captcha: '&cŠpatné opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA'
valid_captcha: '&cZadaná captcha je v pořádku!' valid_captcha: '&cZadaná captcha je v pořádku!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'sekundy' second: 'sekundy'
seconds: 'sekund' seconds: 'sekund'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha <th
wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA' wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA'
valid_captcha: '&2Das Captcha ist korrekt!' valid_captcha: '&2Das Captcha ist korrekt!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'Sekunde' second: 'Sekunde'
seconds: 'Sekunden' seconds: 'Sekunden'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3To login you have to solve a captcha code, please use the comm
wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
valid_captcha: '&2Captcha code solved correctly!' valid_captcha: '&2Captcha code solved correctly!'
# Verification code
verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
usage_verification_code: '&cUsage: /verification <code>'
incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'second' second: 'second'
seconds: 'seconds' seconds: 'seconds'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Ensaluti vi devas solvi captcha kodo, bonvolu uzi la komando:
wrong_captcha: '&cMalĝusta captcha, bonvolu tajpi "/captcha THE_CAPTCHA" en la babilejo!' wrong_captcha: '&cMalĝusta captcha, bonvolu tajpi "/captcha THE_CAPTCHA" en la babilejo!'
valid_captcha: '&2Captcha kodo solvita ĝuste!' valid_captcha: '&2Captcha kodo solvita ĝuste!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'sekundo' second: 'sekundo'
seconds: 'sekundoj' seconds: 'sekundoj'

View File

@ -99,6 +99,15 @@ usage_captcha: '&cUso: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha incorrecto, por favor usa: /captcha THE_CAPTCHA'
valid_captcha: '&c¡ Captcha ingresado correctamente !' valid_captcha: '&c¡ Captcha ingresado correctamente !'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'segundo' second: 'segundo'
seconds: 'segundos' seconds: 'segundos'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Sisselogimiseks pead lahendama captcha. Tee seda kasutades kä
wrong_captcha: '&cVale captcha, kirjuta "/captcha THE_CAPTCHA" chatti!' wrong_captcha: '&cVale captcha, kirjuta "/captcha THE_CAPTCHA" chatti!'
valid_captcha: '&2Captcha lahendatud!' valid_captcha: '&2Captcha lahendatud!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'sekund' second: 'sekund'
seconds: 'sekundit' seconds: 'sekundit'

View File

@ -96,6 +96,15 @@ recovery_email: '&cPasahitza ahaztu duzu? Erabili /email recovery <zureemaila>'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!' # TODO valid_captcha: '&2Captcha code solved correctly!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -96,6 +96,15 @@ usage_captcha: '&cKäyttötapa: /captcha <theCaptcha>'
wrong_captcha: '&cVäärä varmistus, käytä : /captcha THE_CAPTCHA' wrong_captcha: '&cVäärä varmistus, käytä : /captcha THE_CAPTCHA'
valid_captcha: '&cSinun varmistus onnistui.!' valid_captcha: '&cSinun varmistus onnistui.!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -101,6 +101,15 @@ usage_captcha: '&cTrop de tentatives de connexion échouées, utilisez: /captcha
wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha incorrect, écrivez de nouveau: /captcha THE_CAPTCHA'
valid_captcha: '&aCaptché validé! Veuillez maintenant vous connecter.' valid_captcha: '&aCaptché validé! Veuillez maintenant vous connecter.'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Unités de temps # Unités de temps
second: 'seconde' second: 'seconde'
seconds: 'secondes' seconds: 'secondes'

View File

@ -96,6 +96,15 @@ usage_captcha: '&cNecesitas escribir un captcha, por favor escribe: /captcha <th
wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha equivocado, por favor usa: /captcha THE_CAPTCHA'
valid_captcha: '&cO teu captcha é válido !' valid_captcha: '&cO teu captcha é válido !'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -80,7 +80,6 @@ email_added: '&2Az email címed rögzítése sikeresen megtörtént!'
email_confirm: '&cKérlek, ellenőrízd az email címedet!' email_confirm: '&cKérlek, ellenőrízd az email címedet!'
email_changed: '&2Az email cím cseréje sikeresen megtörtént!' email_changed: '&2Az email cím cseréje sikeresen megtörtént!'
email_send: '&2A jelszó visszaállításhoz szükséges emailt elküldtük! Ellenőrizd a leveleidet!' email_send: '&2A jelszó visszaállításhoz szükséges emailt elküldtük! Ellenőrizd a leveleidet!'
email_exists: '&cA visszaállító emailt elküldtük! Hiba esetén újra kérheted az alábbi parancs segítségével:'
email_show: '&2A jelenlegi email-ed a következő: &f%email' email_show: '&2A jelenlegi email-ed a következő: &f%email'
incomplete_email_settings: 'Hiba: nem lett beállítva az összes szükséges beállítás az email küldéshez. Vedd fel a kapcsolatot egy adminnal.' incomplete_email_settings: 'Hiba: nem lett beállítva az összes szükséges beállítás az email küldéshez. Vedd fel a kapcsolatot egy adminnal.'
email_already_used: '&4Ez az email cím már használatban van!' email_already_used: '&4Ez az email cím már használatban van!'
@ -88,6 +87,7 @@ email_send_failure: 'Nem sikerült elküldeni az emailt. Lépj kapcsolatba egy a
show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.'
add_email: '&3Kérlek, rendeld hozzá a felhasználódhoz az email címedet "&7/email add <email címed> <email címed ismét>&3".' add_email: '&3Kérlek, rendeld hozzá a felhasználódhoz az email címedet "&7/email add <email címed> <email címed ismét>&3".'
recovery_email: '&3Ha elfelejtetted a jelszavad, használd az "&7/email recovery <regisztrált email címed>&3".' recovery_email: '&3Ha elfelejtetted a jelszavad, használd az "&7/email recovery <regisztrált email címed>&3".'
# TODO change_password_expired: 'You cannot change your password using this command anymore.'
email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.' email_cooldown_error: '&cEgy emailt már kiküldtünk. Következő email küldése előtt várnod kell: %time.'
# Captcha # Captcha
@ -95,6 +95,15 @@ usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérlek, használd a
wrong_captcha: '&cHibás CAPTCHA, kérlek, írd be a következő parancsot: "&7/captcha THE_CAPTCHA&c"!' wrong_captcha: '&cHibás CAPTCHA, kérlek, írd be a következő parancsot: "&7/captcha THE_CAPTCHA&c"!'
valid_captcha: '&2A CAPTCHA sikeresen feloldva!' valid_captcha: '&2A CAPTCHA sikeresen feloldva!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'másodperc' second: 'másodperc'
seconds: 'másodperc' seconds: 'másodperc'

View File

@ -96,6 +96,15 @@ usage_captcha: '&3Kamu harus menyelesaikan kode captcha untuk login, silahkan gu
wrong_captcha: '&cCaptcha salah, gunakan command "/captcha THE_CAPTCHA" pada chat!' wrong_captcha: '&cCaptcha salah, gunakan command "/captcha THE_CAPTCHA" pada chat!'
valid_captcha: '&2Kode captcha terselesaikan!' valid_captcha: '&2Kode captcha terselesaikan!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -97,6 +97,15 @@ usage_captcha: '&3Per poterti autenticare devi risolvere un captcha, per favore
wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo: "/captcha THE_CAPTCHA" in chat!' wrong_captcha: '&cCaptcha sbagliato, per favore riprova scrivendo: "/captcha THE_CAPTCHA" in chat!'
valid_captcha: '&2Il captcha inserito è valido!' valid_captcha: '&2Il captcha inserito è valido!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Unità di tempo # Unità di tempo
second: 'secondo' second: 'secondo'
seconds: 'secondi' seconds: 'secondi'

View File

@ -34,7 +34,6 @@ tempban_max_logins: '&c너무 많이 로그인에 실패하여 잠시 서버에
max_reg: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!' max_reg: '&c당신은 가입할 수 있는 계정 한도를 초과했습니다 (%reg_count/%max_acc %reg_names)!'
no_perm: '&4이 작업을 수행할 수 있는 권한이 없습니다!' no_perm: '&4이 작업을 수행할 수 있는 권한이 없습니다!'
error: '&4예기치 않은 오류가 발생했습니다, 관리자에게 알려주세요!' error: '&4예기치 않은 오류가 발생했습니다, 관리자에게 알려주세요!'
unsafe_spawn: '&c마지막으로 접속한 위치가 안전하지 않습니다, 이 세계의 스폰 지점으로 텔레포트 됩니다.'
kick_forvip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!' kick_forvip: '&3서버가 꽉 차있을땐 VIP 플레이어만 접속이 가능합니다!'
# AntiBot # AntiBot
@ -48,7 +47,11 @@ accounts_owned_self: '%count 개의 계정을 소유하고 있습니다:'
accounts_owned_other: '플레이어 %name 는 %count 개의 계정을 소유하고 있습니다:' accounts_owned_other: '플레이어 %name 는 %count 개의 계정을 소유하고 있습니다:'
two_factor_create: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다' two_factor_create: '&2당신의 비밀 코드는 %code 입니다. %url 에서 스캔할 수 있습니다'
recovery_code_sent: '비밀번호 재설정을 위한 복구 코드가 이메일로 전송되었습니다.' recovery_code_sent: '비밀번호 재설정을 위한 복구 코드가 이메일로 전송되었습니다.'
# TODO: Missing tags %count
recovery_code_incorrect: '복구 코드가 올바르지 않습니다! "/email recovery [이메일 주소]"를 이용하여 새로 생성하세요' recovery_code_incorrect: '복구 코드가 올바르지 않습니다! "/email recovery [이메일 주소]"를 이용하여 새로 생성하세요'
# TODO recovery_tries_exceeded: 'You have exceeded the maximum number attempts to enter the recovery code. Use "/email recovery [email]" to generate a new one.'
# TODO recovery_code_correct: 'Recovery code entered correctly!'
# TODO recovery_change_password: 'Please use the command /email setpassword <new password> to change your password immediately.'
vb_nonActiv: '&c계정이 아직 활성화되지 않았습니다, 이메일을 확인해보세요!' vb_nonActiv: '&c계정이 아직 활성화되지 않았습니다, 이메일을 확인해보세요!'
usage_unreg: '&c사용법: /unregister <비밀번호>' usage_unreg: '&c사용법: /unregister <비밀번호>'
pwd_changed: '&2비밀번호가 변경되었습니다!' pwd_changed: '&2비밀번호가 변경되었습니다!'
@ -82,7 +85,6 @@ email_added: '&2계정에 이메일 주소를 추가했습니다!'
email_confirm: '&c이메일 주소를 확인해주세요!' email_confirm: '&c이메일 주소를 확인해주세요!'
email_changed: '&2이메일 주소가 변경되었습니다!' email_changed: '&2이메일 주소가 변경되었습니다!'
email_send: '&2복구 이메일을 보냈습니다! 메일함을 확인해보세요!' email_send: '&2복구 이메일을 보냈습니다! 메일함을 확인해보세요!'
email_exists: '&c복구 이메일을 이미 보냈습니다! 아래 명령어를 사용하여 취소하고 다시 보낼 수 있습니다:'
email_show: '&2현재 이메일 주소: &f%email' email_show: '&2현재 이메일 주소: &f%email'
incomplete_email_settings: '오류: 메일을 보내기 위해 필요한 설정이 되어 있지 않습니다. 관리자에게 알려주세요.' incomplete_email_settings: '오류: 메일을 보내기 위해 필요한 설정이 되어 있지 않습니다. 관리자에게 알려주세요.'
email_already_used: '&4이메일 주소가 이미 사용 중입니다.' email_already_used: '&4이메일 주소가 이미 사용 중입니다.'
@ -90,8 +92,29 @@ email_send_failure: '이메일을 보낼 수 없습니다. 관리자에게 알
show_no_email: '&2현재 이 계정과 연결된 이메일 주소가 없습니다.' show_no_email: '&2현재 이 계정과 연결된 이메일 주소가 없습니다.'
add_email: '&3다음 명령어로 계정에 이메일 주소를 추가하세요: /email add <이메일 주소> <이메일 주소 확인>' add_email: '&3다음 명령어로 계정에 이메일 주소를 추가하세요: /email add <이메일 주소> <이메일 주소 확인>'
recovery_email: '&3비밀번호를 잊으셨나요? 이 명령어를 사용해보세요: /email recovery <이메일 주소>' recovery_email: '&3비밀번호를 잊으셨나요? 이 명령어를 사용해보세요: /email recovery <이메일 주소>'
# TODO change_password_expired: 'You cannot change your password using this command anymore.'
# TODO email_cooldown_error: '&cAn email was already sent recently. You must wait %time before you can send a new one.'
# Captcha # Captcha
usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다, 이 명령어를 사용하세요: /captcha <theCaptcha>' usage_captcha: '&3로그인 하려면 CAPTCHA 코드를 입력해야 합니다, 이 명령어를 사용하세요: /captcha <theCaptcha>'
wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다, "/captcha THE_CAPTCHA"을 입력해주세요!' wrong_captcha: '&c잘못된 CAPTCHA 코드 입니다, "/captcha THE_CAPTCHA"을 입력해주세요!'
valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!' valid_captcha: '&2CAPTCHA 코드가 확인되었습니다!'
# Verification Code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units
# TODO second: 'second'
# TODO seconds: 'seconds'
# TODO minute: 'minute'
# TODO minutes: 'minutes'
# TODO hour: 'hour'
# TODO hours: 'hours'
# TODO day: 'day'
# TODO days: 'days'

View File

@ -96,6 +96,15 @@ usage_captcha: '&cPanaudojimas: /captcha <theCaptcha>'
wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha THE_CAPTCHA' wrong_captcha: '&cNeteisinga Captcha, naudokite : /captcha THE_CAPTCHA'
valid_captcha: '&cJusu captcha Teisinga!' valid_captcha: '&cJusu captcha Teisinga!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Om in te loggen moet je een captcha-code oplossen, gebruik het
wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha THE_CAPTCHA" in de chat!' wrong_captcha: '&cVerkeerde captcha-code, typ alsjeblieft "/captcha THE_CAPTCHA" in de chat!'
valid_captcha: '&2De captcha-code is geldig!' valid_captcha: '&2De captcha-code is geldig!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'seconde' second: 'seconde'
seconds: 'seconden' seconds: 'seconden'

View File

@ -95,6 +95,15 @@ usage_captcha: '&cWpisz: /captcha <theCaptcha>'
wrong_captcha: '&cZły kod, proszę wpisać: /captcha THE_CAPTCHA' wrong_captcha: '&cZły kod, proszę wpisać: /captcha THE_CAPTCHA'
valid_captcha: '&cTwój kod jest nieprawidłowy!' valid_captcha: '&cTwój kod jest nieprawidłowy!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'sekundy' second: 'sekundy'
seconds: 'sekund' seconds: 'sekund'

View File

@ -97,6 +97,15 @@ usage_captcha: '&cPrecisa digitar um captcha, escreva: /captcha <theCaptcha>'
wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha THE_CAPTCHA' wrong_captcha: '&cCaptcha errado, por favor escreva: /captcha THE_CAPTCHA'
valid_captcha: '&cO seu captcha é válido!' valid_captcha: '&cO seu captcha é válido!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'segundo' second: 'segundo'
seconds: 'segundos' seconds: 'segundos'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Pentru a te autentifica, te rugam sa folosesti comanda "/captc
wrong_captcha: '&cCodul de verificare este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!' wrong_captcha: '&cCodul de verificare este gresit, te rugam foloseste comanda "/captcha THE_CAPTCHA"!'
valid_captcha: '&2Codul de verificare a fost scris corect!' valid_captcha: '&2Codul de verificare a fost scris corect!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'secunda' second: 'secunda'
seconds: 'secunde' seconds: 'secunde'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Необходимо ввести текст с каптчи.
wrong_captcha: '&cНеверно! Используйте «/captcha THE_CAPTCHA».' wrong_captcha: '&cНеверно! Используйте «/captcha THE_CAPTCHA».'
valid_captcha: '&2Вы успешно решили каптчу!' valid_captcha: '&2Вы успешно решили каптчу!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Единицы времени # Единицы времени
second: 'с.' second: 'с.'
seconds: 'с.' seconds: 'с.'

View File

@ -102,6 +102,15 @@ usage_captcha: '&3Pre prihlásenie musíš vyriešiť captcha kód, prosím pou
wrong_captcha: '&cNesprávny kód captcha, prosím napíš "/captcha THE_CAPTCHA" do chatu!' wrong_captcha: '&cNesprávny kód captcha, prosím napíš "/captcha THE_CAPTCHA" do chatu!'
valid_captcha: '&2Správne si vyriešil captcha kód!' valid_captcha: '&2Správne si vyriešil captcha kód!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'sek.' second: 'sek.'
seconds: 'sek.' seconds: 'sek.'

View File

@ -96,6 +96,15 @@ usage_captcha: '&3Giris yapmak icin guvenlik kodunu komut yazarak girin "/captch
wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha THE_CAPTCHA" sohbete yazin!' wrong_captcha: '&cYanlis guvenlik kodu, kullanim sekli "/captcha THE_CAPTCHA" sohbete yazin!'
valid_captcha: '&2Guvenlik kodu dogrulandi!' valid_captcha: '&2Guvenlik kodu dogrulandi!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Zaman birimleri # Zaman birimleri
second: 'saniye' second: 'saniye'
seconds: 'saniye' seconds: 'saniye'

View File

@ -95,6 +95,15 @@ usage_captcha: '&3Для продовження доведеться ввест
wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha THE_CAPTCHA"' wrong_captcha: '&cНевірно введена капча! Спробуйте ще раз — "/captcha THE_CAPTCHA"'
valid_captcha: '&2Капчу прийнято.' valid_captcha: '&2Капчу прийнято.'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -96,6 +96,15 @@ usage_captcha: '&eĐể đăng nhập vui lòng hãy nhập mã Captcha, gõ l
wrong_captcha: '&cSai mã captcha, Vui lòng gõ lệnh "/captcha THE_CAPTCHA"!' wrong_captcha: '&cSai mã captcha, Vui lòng gõ lệnh "/captcha THE_CAPTCHA"!'
valid_captcha: '&2Mã captcha đã được xác nhận!' valid_captcha: '&2Mã captcha đã được xác nhận!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: 'giây' second: 'giây'
seconds: 'giây' seconds: 'giây'

View File

@ -97,6 +97,15 @@ usage_captcha: '&8[&6玩家系统&8] &c正确用法/captcha <theCaptcha>'
wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码请输入“/captcha THE_CAPTCHA”' wrong_captcha: '&8[&6玩家系统&8] &c错误的验证码请输入“/captcha THE_CAPTCHA”'
valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的' valid_captcha: '&8[&6玩家系统&8] &c你的验证码是有效的'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# 时间单位 # 时间单位
second: '秒' second: '秒'
seconds: '秒' seconds: '秒'

View File

@ -99,6 +99,15 @@ usage_captcha: '&8[&6用戶系統&8] &f用法《 /captcha <theCaptcha> 》'
wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效請使用 《 /captcha THE_CAPTCHA 》 再次輸入。' wrong_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效請使用 《 /captcha THE_CAPTCHA 》 再次輸入。'
valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 ' valid_captcha: '&8[&6用戶系統&8] &c你所輸入的驗證碼無效 '
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
second: '秒' second: '秒'
seconds: '秒' seconds: '秒'

View File

@ -96,6 +96,15 @@ usage_captcha: '&3T要登錄您必須使用captcha驗證碼,請使用命令: "/c
wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha THE_CAPTCHA"' wrong_captcha: '&c驗證碼錯誤!請按T在聊天中輸入 "/captcha THE_CAPTCHA"'
valid_captcha: '&2驗證碼正確!' valid_captcha: '&2驗證碼正確!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -100,6 +100,15 @@ usage_captcha: '&b【AuthMe】&6請用 &c"/captcha <theCaptcha>" &6來輸入你
wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼請使用 《 /captcha THE_CAPTCHA 》 再試一次吧。' wrong_captcha: '&b【AuthMe】&6錯誤的驗證碼請使用 《 /captcha THE_CAPTCHA 》 再試一次吧。'
valid_captcha: '&b【AuthMe】&6驗證碼無效!' valid_captcha: '&b【AuthMe】&6驗證碼無效!'
# Verification code
# TODO verification_code_required: '&3This command is sensitive and requires an email verification! Check your inbox and follow the email''s instructions.'
# TODO usage_verification_code: '&cUsage: /verification <code>'
# TODO incorrect_verification_code: '&cIncorrect code, please type "/verification <code>" into the chat, using the code you received by email'
# TODO verification_code_verified: '&2Your identity has been verified! You can now execute all commands within the current session!'
# TODO verification_code_already_verified: '&2You can already execute every sensitive command within the current session!'
# TODO verification_code_expired: '&3Your code has expired! Execute an other sensitive command to get a new code!'
# TODO verification_code_email_needed: '&3To verify your identity you need to link an email address with your account!!'
# Time units # Time units
# TODO second: 'second' # TODO second: 'second'
# TODO seconds: 'seconds' # TODO seconds: 'seconds'

View File

@ -47,8 +47,11 @@ commands:
- changepass - changepass
- cp - cp
captcha: captcha:
description: Captcha Command description: Captcha command
usage: /captcha <captcha> usage: /captcha <captcha>
verification:
description: Verification command
usage: /verification <code>
permissions: permissions:
authme.admin.*: authme.admin.*:
description: Gives access to all admin commands description: Gives access to all admin commands
@ -224,6 +227,7 @@ permissions:
authme.player.login: true authme.player.login: true
authme.player.logout: true authme.player.logout: true
authme.player.register: true authme.player.register: true
authme.player.security.verificationcode: true
authme.player.seeownaccounts: true authme.player.seeownaccounts: true
authme.player.unregister: true authme.player.unregister: true
authme.player.canbeforced: authme.player.canbeforced:
@ -263,6 +267,9 @@ permissions:
authme.player.register: authme.player.register:
description: Command permission to register. description: Command permission to register.
default: true default: true
authme.player.security.verificationcode:
description: Permission to use the email verification codes feature.
default: true
authme.player.seeownaccounts: authme.player.seeownaccounts:
description: Permission to use to see own other accounts. description: Permission to use to see own other accounts.
default: true default: true

View File

@ -0,0 +1,20 @@
<h1>
Dear <playername />,
</h1>
<p>
This is your temporary verification code for the server <servername />:
</p>
<p>
<generatedcode />
</p>
<p>
This code will be valid for the next <minutesvalid /> mins!<br />
Use the command
/verification <generatedcode />
to complete the verification process.
</p>
<p>
See you on <servername />!
</p>

View File

@ -44,7 +44,7 @@ public class CommandInitializerTest {
// It obviously doesn't make sense to test much of the concrete data // It obviously doesn't make sense to test much of the concrete data
// that is being initialized; we just want to guarantee with this test // that is being initialized; we just want to guarantee with this test
// that data is indeed being initialized and we take a few "probes" // that data is indeed being initialized and we take a few "probes"
assertThat(commands, hasSize(8)); assertThat(commands, hasSize(9));
assertThat(commandsIncludeLabel(commands, "authme"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "authme"), equalTo(true));
assertThat(commandsIncludeLabel(commands, "register"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "register"), equalTo(true));
assertThat(commandsIncludeLabel(commands, "help"), equalTo(false)); assertThat(commandsIncludeLabel(commands, "help"), equalTo(false));

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.changepassword; package fr.xephi.authme.command.executable.changepassword;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -40,11 +41,14 @@ public class ChangePasswordCommandTest {
private ChangePasswordCommand command; private ChangePasswordCommand command;
@Mock @Mock
private CommonService commandService; private CommonService commonService;
@Mock @Mock
private PlayerCache playerCache; private PlayerCache playerCache;
@Mock
private VerificationCodeManager codeManager;
@Mock @Mock
private ValidationService validationService; private ValidationService validationService;
@ -72,23 +76,24 @@ public class ChangePasswordCommandTest {
command.executeCommand(sender, Arrays.asList("pass", "pass")); command.executeCommand(sender, Arrays.asList("pass", "pass"));
// then // then
verify(commandService).send(sender, MessageKey.NOT_LOGGED_IN); verify(commonService).send(sender, MessageKey.NOT_LOGGED_IN);
} }
@Test @Test
public void shouldRejectInvalidPassword() { public void shouldRejectInvalidPassword() {
// given // given
CommandSender sender = initPlayerWithName("abc12", true); Player sender = initPlayerWithName("abc12", true);
String password = "newPW"; String password = "newPW";
given(validationService.validatePassword(password, "abc12")) given(validationService.validatePassword(password, "abc12")).willReturn(new ValidationResult(MessageKey.INVALID_PASSWORD_LENGTH));
.willReturn(new ValidationResult(MessageKey.INVALID_PASSWORD_LENGTH)); given(codeManager.isVerificationRequired(sender)).willReturn(false);
// when // when
command.executeCommand(sender, Arrays.asList("tester", password)); command.executeCommand(sender, Arrays.asList("tester", password));
// then // then
verify(validationService).validatePassword(password, "abc12"); verify(validationService).validatePassword(password, "abc12");
verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH, new String[0]); verify(commonService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH, new String[0]);
verify(codeManager).isVerificationRequired(sender);
} }
@Test @Test
@ -98,14 +103,16 @@ public class ChangePasswordCommandTest {
String newPass = "abc123"; String newPass = "abc123";
Player player = initPlayerWithName("parker", true); Player player = initPlayerWithName("parker", true);
given(validationService.validatePassword("abc123", "parker")).willReturn(new ValidationResult()); given(validationService.validatePassword("abc123", "parker")).willReturn(new ValidationResult());
given(codeManager.isVerificationRequired(player)).willReturn(false);
// when // when
command.executeCommand(player, Arrays.asList(oldPass, newPass)); command.executeCommand(player, Arrays.asList(oldPass, newPass));
// then // then
verify(validationService).validatePassword(newPass, "parker"); verify(validationService).validatePassword(newPass, "parker");
verify(commandService, never()).send(eq(player), any(MessageKey.class)); verify(commonService, never()).send(eq(player), any(MessageKey.class));
verify(management).performPasswordChange(player, oldPass, newPass); verify(management).performPasswordChange(player, oldPass, newPass);
verify(codeManager).isVerificationRequired(player);
} }
@Test @Test

View File

@ -1,7 +1,9 @@
package fr.xephi.authme.command.executable.email; package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.BlockCommandSender; import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -16,9 +18,11 @@ import java.util.Collections;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/** /**
* Test for {@link ChangeEmailCommand}. * Test for {@link ChangeEmailCommand}.
@ -32,6 +36,12 @@ public class ChangeEmailCommandTest {
@Mock @Mock
private Management management; private Management management;
@Mock
private CommonService commonService;
@Mock
private VerificationCodeManager codeManager;
@Test @Test
public void shouldRejectNonPlayerSender() { public void shouldRejectNonPlayerSender() {
@ -45,16 +55,34 @@ public class ChangeEmailCommandTest {
verifyZeroInteractions(management); verifyZeroInteractions(management);
} }
@Test
public void shouldStopIfVerificationIsRequired() {
// given
String name = "Testeroni";
Player player = initPlayerWithName(name);
given(codeManager.isVerificationRequired(player)).willReturn(true);
// when
command.executeCommand(player, Arrays.asList("mail@example.org", "otherMail@example.com"));
// then
verify(codeManager).codeExistOrGenerateNew(name);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
verifyZeroInteractions(management);
}
@Test @Test
public void shouldForwardData() { public void shouldForwardData() {
// given // given
Player sender = mock(Player.class); Player sender = initPlayerWithName("AmATest");
given(codeManager.isVerificationRequired(sender)).willReturn(false);
// when // when
command.executeCommand(sender, Arrays.asList("new.mail@example.org", "old_mail@example.org")); command.executeCommand(sender, Arrays.asList("new.mail@example.org", "old_mail@example.org"));
// then // then
verify(management).performChangeEmail(sender, "new.mail@example.org", "old_mail@example.org"); verify(management).performChangeEmail(sender, "new.mail@example.org", "old_mail@example.org");
verify(codeManager).isVerificationRequired(sender);
} }
@Test @Test
@ -62,4 +90,10 @@ public class ChangeEmailCommandTest {
// given / when / then // given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_CHANGE_EMAIL)); assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_CHANGE_EMAIL));
} }
private Player initPlayerWithName(String name) {
Player player = mock(Player.class);
when(player.getName()).thenReturn(name);
return player;
}
} }

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.unregister; package fr.xephi.authme.command.executable.unregister;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -17,11 +18,11 @@ import java.util.Collections;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/** /**
* Test for {@link UnregisterCommand}. * Test for {@link UnregisterCommand}.
@ -36,11 +37,14 @@ public class UnregisterCommandTest {
private Management management; private Management management;
@Mock @Mock
private CommonService commandService; private CommonService commonService;
@Mock @Mock
private PlayerCache playerCache; private PlayerCache playerCache;
@Mock
private VerificationCodeManager codeManager;
@Test @Test
public void shouldCatchUnauthenticatedUser() { public void shouldCatchUnauthenticatedUser() {
// given // given
@ -55,7 +59,26 @@ public class UnregisterCommandTest {
// then // then
verify(playerCache).isAuthenticated(name); verify(playerCache).isAuthenticated(name);
verify(commandService).send(player, MessageKey.NOT_LOGGED_IN); verify(commonService).send(player, MessageKey.NOT_LOGGED_IN);
verifyZeroInteractions(management);
}
@Test
public void shouldStopForMissingVerificationCode() {
// given
String name = "asldjf";
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
given(playerCache.isAuthenticated(name)).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(true);
// when
command.executeCommand(player, Collections.singletonList("blergh"));
// then
verify(playerCache).isAuthenticated(name);
verify(codeManager).codeExistOrGenerateNew(name);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
verifyZeroInteractions(management); verifyZeroInteractions(management);
} }
@ -67,6 +90,7 @@ public class UnregisterCommandTest {
Player player = mock(Player.class); Player player = mock(Player.class);
given(player.getName()).willReturn(name); given(player.getName()).willReturn(name);
given(playerCache.isAuthenticated(name)).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(false);
// when // when
command.executeCommand(player, Collections.singletonList(password)); command.executeCommand(player, Collections.singletonList(password));
@ -74,6 +98,7 @@ public class UnregisterCommandTest {
// then // then
verify(playerCache).isAuthenticated(name); verify(playerCache).isAuthenticated(name);
verify(management).performUnregister(player, password); verify(management).performUnregister(player, password);
verify(codeManager).isVerificationRequired(player);
} }
@Test @Test

View File

@ -0,0 +1,161 @@
package fr.xephi.authme.command.executable.verification;
import fr.xephi.authme.data.VerificationCodeManager;
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.junit.MockitoJUnitRunner;
import java.util.Collections;
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 VerificationCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class VerificationCommandTest {
@InjectMocks
private VerificationCommand command;
@Mock
private CommonService commonService;
@Mock
private VerificationCodeManager codeManager;
@Test
public void shouldDetectIfMailHasASetup() {
// given
String name = "Alligator";
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(false);
// when
command.executeCommand(player, Collections.singletonList("code"));
// then
verify(commonService).send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
}
@Test
public void shouldRequireAndAcceptCode() {
// given
String name = "Duck";
String code = "123932";
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(true);
given(codeManager.isCodeRequired(name)).willReturn(true);
given(codeManager.checkCode(name, code)).willReturn(true);
// when
command.executeCommand(player, Collections.singletonList(code));
// then
verify(codeManager).isVerificationRequired(player);
verify(codeManager).isCodeRequired(name);
verify(codeManager).checkCode(name, code);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_VERIFIED);
}
@Test
public void shouldRejectCode() {
// given
String name = "Spider";
String code = "98345222"; // more than 6 digits
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(true);
given(codeManager.isCodeRequired(name)).willReturn(true);
given(codeManager.checkCode(name, code)).willReturn(false);
// when
command.executeCommand(player, Collections.singletonList(code));
// then
verify(codeManager).isVerificationRequired(player);
verify(codeManager).isCodeRequired(name);
verify(codeManager).checkCode(name, code);
verify(commonService).send(player, MessageKey.INCORRECT_VERIFICATION_CODE);
}
@Test
public void shouldRejectVerificationDueToExpiration() {
// given
String name = "Dog";
String code = "131552";
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(true);
given(codeManager.isCodeRequired(name)).willReturn(false);
// when
command.executeCommand(player, Collections.singletonList(code));
// then
verify(codeManager).isVerificationRequired(player);
verify(codeManager).isCodeRequired(name);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_EXPIRED);
}
@Test
public void shouldRejectVerificationDueToVerifiedIdentity() {
// given
String name = "Cow";
String code = "973583";
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(false);
given(codeManager.hasEmail(name)).willReturn(true);
// when
command.executeCommand(player, Collections.singletonList(code));
// then
verify(codeManager).isVerificationRequired(player);
verify(codeManager).hasEmail(name);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_ALREADY_VERIFIED);
}
@Test
public void shouldRejectVerificationDueToUndefinedEmail() {
// given
String name = "Frog";
String code = "774543";
Player player = mockPlayerWithName(name);
given(codeManager.canSendMail()).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(false);
given(codeManager.hasEmail(name)).willReturn(false);
// when
command.executeCommand(player, Collections.singletonList(code));
// then
verify(codeManager).isVerificationRequired(player);
verify(codeManager).hasEmail(name);
verify(commonService).send(player, MessageKey.VERIFICATION_CODE_EMAIL_NEEDED);
verify(commonService).send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
@Test
public void shouldDefineArgumentMismatchMessage() {
// given / when / then
assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_VERIFICATION_CODE));
}
private static Player mockPlayerWithName(String name) {
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
return player;
}
}

View File

@ -0,0 +1,171 @@
package fr.xephi.authme.data;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceResult;
import fr.xephi.authme.mail.EmailService;
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 org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.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.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link VerificationCodeManager}.
*/
@RunWith(MockitoJUnitRunner.class)
public class VerificationCodeManagerTest {
@Mock
private Settings settings;
@Mock
private DataSource dataSource;
@Mock
private EmailService emailService;
@Mock
private PermissionsManager permissionsManager;
@Before
public void setUpBasicBehavior() {
given(emailService.hasAllInformation()).willReturn(true);
given(settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES)).willReturn(1);
}
@Test
public void shouldRequireVerification() {
// given
String name1 = "ILoveTests";
Player player1 = mockPlayerWithName(name1);
given(dataSource.getEmail(name1)).willReturn(DataSourceResult.of("ilovetests@test.com"));
given(permissionsManager.hasPermission(player1, PlayerPermission.VERIFICATION_CODE)).willReturn(true);
String name2 = "StillLovingTests";
Player player2 = mockPlayerWithName(name2);
VerificationCodeManager codeManager = createCodeManager();
codeManager.verify(name2);
// when
boolean test1 = codeManager.isVerificationRequired(player1);
boolean test2 = codeManager.isVerificationRequired(player2);
// then
assertThat(test1, equalTo(true));
assertThat(test2, equalTo(false));
verify(dataSource, only()).getEmail(name1);
verify(permissionsManager, only()).hasPermission(player1, PlayerPermission.VERIFICATION_CODE);
}
@Test
public void shouldNotRequireVerificationIfEmailSettingsAreIncomplete() {
// given
given(emailService.hasAllInformation()).willReturn(false);
VerificationCodeManager codeManager = createCodeManager();
Player player = mock(Player.class);
// when
boolean result = codeManager.isVerificationRequired(player);
// then
assertThat(result, equalTo(false));
verifyZeroInteractions(permissionsManager, dataSource);
}
@Test
public void shouldNotRequireVerificationForMissingPermission() {
// given
Player player = mockPlayerWithName("ILoveTests");
given(permissionsManager.hasPermission(player, PlayerPermission.VERIFICATION_CODE)).willReturn(false);
VerificationCodeManager codeManager = createCodeManager();
// when
boolean result = codeManager.isVerificationRequired(player);
// then
assertThat(result, equalTo(false));
verify(permissionsManager).hasPermission(player, PlayerPermission.VERIFICATION_CODE);
verifyZeroInteractions(dataSource);
}
@Test
public void shouldGenerateCode() {
// given
String player = "ILoveTests";
String email = "ilovetests@test.com";
given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email));
VerificationCodeManager codeManager1 = createCodeManager();
VerificationCodeManager codeManager2 = createCodeManager();
codeManager2.codeExistOrGenerateNew(player);
// when
boolean test1 = codeManager1.hasCode(player);
boolean test2 = codeManager2.hasCode(player);
// then
assertThat(test1, equalTo(false));
assertThat(test2, equalTo(true));
}
@Test
public void shouldRequireCode() {
// given
String player = "ILoveTests";
String email = "ilovetests@test.com";
given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email));
VerificationCodeManager codeManager1 = createCodeManager();
VerificationCodeManager codeManager2 = createCodeManager();
codeManager2.codeExistOrGenerateNew(player);
// when
boolean test1 = codeManager1.isCodeRequired(player);
boolean test2 = codeManager2.isCodeRequired(player);
// then
assertThat(test1, equalTo(false));
assertThat(test2, equalTo(true));
}
@Test
public void shouldVerifyCode() {
// given
String player = "ILoveTests";
String code = "193458";
String email = "ilovetests@test.com";
given(dataSource.getEmail(player)).willReturn(DataSourceResult.of(email));
VerificationCodeManager codeManager1 = createCodeManager();
VerificationCodeManager codeManager2 = createCodeManager();
codeManager1.codeExistOrGenerateNew(player);
// when
boolean test1 = codeManager1.checkCode(player, code);
boolean test2 = codeManager2.checkCode(player, code);
// then
assertThat(test1, equalTo(false));
assertThat(test2, equalTo(false));
}
private VerificationCodeManager createCodeManager() {
return new VerificationCodeManager(settings, dataSource, emailService, permissionsManager);
}
private static Player mockPlayerWithName(String name) {
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
return player;
}
}

View File

@ -77,6 +77,24 @@ public class SettingsTest {
assertThat(result, equalTo(emailMessage)); assertThat(result, equalTo(emailMessage));
} }
@Test
public void shouldLoadVerificationMessage() throws IOException {
// given
String emailMessage = "Please verify your identity with <recoverycode />.";
File emailFile = new File(testPluginFolder, "verification_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.getVerificationEmailMessage();
// then
assertThat(result, equalTo(emailMessage));
}
private static void createFile(File file) { private static void createFile(File file) {
try { try {
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();

View File

@ -61,6 +61,22 @@ public class RandomStringUtilsTest {
} }
} }
@Test
public void shouldGenerateRandomNumberString() {
// given
int[] lengths = {0, 1, 18, 147, 1833};
Pattern badChars = Pattern.compile(".*[^0-9].*");
// when / then
for (int length : lengths) {
String result = RandomStringUtils.generateNum(length);
assertThat("Result '" + result + "' should have length " + length,
result.length(), equalTo(length));
assertThat("Result '" + result + "' should only have characters 0-9",
badChars.matcher(result).matches(), equalTo(false));
}
}
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void shouldThrowForInvalidLength() { public void shouldThrowForInvalidLength() {
// given/when // given/when