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
@@ -44,7 +44,7 @@ public class CommandInitializerTest {
// 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 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, "register"), equalTo(true));
assertThat(commandsIncludeLabel(commands, "help"), equalTo(false));
@@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.changepassword;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
@@ -40,11 +41,14 @@ public class ChangePasswordCommandTest {
private ChangePasswordCommand command;
@Mock
private CommonService commandService;
private CommonService commonService;
@Mock
private PlayerCache playerCache;
@Mock
private VerificationCodeManager codeManager;
@Mock
private ValidationService validationService;
@@ -72,23 +76,24 @@ public class ChangePasswordCommandTest {
command.executeCommand(sender, Arrays.asList("pass", "pass"));
// then
verify(commandService).send(sender, MessageKey.NOT_LOGGED_IN);
verify(commonService).send(sender, MessageKey.NOT_LOGGED_IN);
}
@Test
public void shouldRejectInvalidPassword() {
// given
CommandSender sender = initPlayerWithName("abc12", true);
Player sender = initPlayerWithName("abc12", true);
String password = "newPW";
given(validationService.validatePassword(password, "abc12"))
.willReturn(new ValidationResult(MessageKey.INVALID_PASSWORD_LENGTH));
given(validationService.validatePassword(password, "abc12")).willReturn(new ValidationResult(MessageKey.INVALID_PASSWORD_LENGTH));
given(codeManager.isVerificationRequired(sender)).willReturn(false);
// when
command.executeCommand(sender, Arrays.asList("tester", password));
// then
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
@@ -98,14 +103,16 @@ public class ChangePasswordCommandTest {
String newPass = "abc123";
Player player = initPlayerWithName("parker", true);
given(validationService.validatePassword("abc123", "parker")).willReturn(new ValidationResult());
given(codeManager.isVerificationRequired(player)).willReturn(false);
// when
command.executeCommand(player, Arrays.asList(oldPass, newPass));
// then
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(codeManager).isVerificationRequired(player);
}
@Test
@@ -1,7 +1,9 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -16,9 +18,11 @@ 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;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/**
* Test for {@link ChangeEmailCommand}.
@@ -32,6 +36,12 @@ public class ChangeEmailCommandTest {
@Mock
private Management management;
@Mock
private CommonService commonService;
@Mock
private VerificationCodeManager codeManager;
@Test
public void shouldRejectNonPlayerSender() {
@@ -45,16 +55,34 @@ public class ChangeEmailCommandTest {
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
public void shouldForwardData() {
// given
Player sender = mock(Player.class);
Player sender = initPlayerWithName("AmATest");
given(codeManager.isVerificationRequired(sender)).willReturn(false);
// when
command.executeCommand(sender, Arrays.asList("new.mail@example.org", "old_mail@example.org"));
// then
verify(management).performChangeEmail(sender, "new.mail@example.org", "old_mail@example.org");
verify(codeManager).isVerificationRequired(sender);
}
@Test
@@ -62,4 +90,10 @@ public class ChangeEmailCommandTest {
// given / when / then
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;
}
}
@@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.unregister;
import fr.xephi.authme.data.VerificationCodeManager;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.message.MessageKey;
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.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/**
* Test for {@link UnregisterCommand}.
@@ -36,11 +37,14 @@ public class UnregisterCommandTest {
private Management management;
@Mock
private CommonService commandService;
private CommonService commonService;
@Mock
private PlayerCache playerCache;
@Mock
private VerificationCodeManager codeManager;
@Test
public void shouldCatchUnauthenticatedUser() {
// given
@@ -55,7 +59,26 @@ public class UnregisterCommandTest {
// then
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);
}
@@ -67,6 +90,7 @@ public class UnregisterCommandTest {
Player player = mock(Player.class);
given(player.getName()).willReturn(name);
given(playerCache.isAuthenticated(name)).willReturn(true);
given(codeManager.isVerificationRequired(player)).willReturn(false);
// when
command.executeCommand(player, Collections.singletonList(password));
@@ -74,6 +98,7 @@ public class UnregisterCommandTest {
// then
verify(playerCache).isAuthenticated(name);
verify(management).performUnregister(player, password);
verify(codeManager).isVerificationRequired(player);
}
@Test
@@ -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;
}
}