Skip to content

Commit

Permalink
#35 allow password reset by username OR email
Browse files Browse the repository at this point in the history
  • Loading branch information
Brutus5000 committed Oct 17, 2017
1 parent 2259ed2 commit 62d496f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 11 deletions.
3 changes: 2 additions & 1 deletion src/main/java/com/faforever/api/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public enum ErrorCode {
MOD_UID_EXISTS(159, "Duplicate mod UID", "A mod with UID ''{0}'' already exists."),
MOD_STRUCTURE_INVALID(160, "Invalid file structure for mod", "Files in the the root level of the zip file are not allowed. Please ensure all files reside inside a folder."),
MOD_VERSION_NOT_A_NUMBER(161, "Mod version is not a number", "The mod version has to be a whole number like 123, but was ''{0}''"),
USERNAME_RESERVED(162, "Invalid account data", "The entered username is currently reserved: {0} (Maximum reservation time is {1} months)");
USERNAME_RESERVED(162, "Invalid account data", "The entered username is currently reserved: {0} (Maximum reservation time is {1} months)"),
UNKNOWN_IDENTIFIER(163, "Unable to resolve user", "The identifier does neither match a username nor an email: {0}");

private final int code;
private final String title;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/faforever/api/user/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ public void changeLogin(@RequestParam("newLogin") String newLogin, Authenticatio
userService.changeLogin(newLogin, userService.getUser(authentication));
}

@ApiOperation("Sends a password reset to the email linked by this account.")
@ApiOperation("Sends a password reset to the username OR email linked by this account.")
@RequestMapping(path = "/resetPassword", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public void resetPassword(@RequestParam("email") String email) {
userService.resetPassword(email);
public void resetPassword(@RequestParam("identifier") String identifier) {
userService.resetPassword(identifier);
}

@ApiOperation("Sets a new password for an account.")
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/com/faforever/api/user/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,17 @@ void changeLogin(String newLogin, User user) {
userRepository.save(user);
}

void resetPassword(String email) {
log.debug("Registration requested for user: {}", email);
void resetPassword(String identifier) {
log.debug("Password reset requested for user-identifier: {}", identifier);

User user = userRepository.findOneByEmailIgnoreCase(email);
User user = userRepository.findOneByLoginIgnoreCase(identifier);

if (user == null) {
throw new ApiException(new Error(ErrorCode.USERNAME_INVALID));
user = userRepository.findOneByEmailIgnoreCase(identifier);
}

if (user == null) {
throw new ApiException(new Error(ErrorCode.UNKNOWN_IDENTIFIER));
}

String token = createPasswordResetToken(user.getId());
Expand Down
33 changes: 30 additions & 3 deletions src/test/java/com/faforever/api/user/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,34 @@ public void changeLoginUsernameReservedBySelf() {
@Test
@SneakyThrows
@SuppressWarnings("unchecked")
public void resetPassword() {
public void resetPasswordByLogin() {
properties.getPasswordReset().setPasswordResetUrlFormat("http://www.example.com/resetPassword/%s");

User user = createUser(TEST_USERID, TEST_USERNAME, TEST_CURRENT_PASSWORD, TEST_EMAIL);

when(userRepository.findOneByLoginIgnoreCase(TEST_USERNAME)).thenReturn(user);
instance.resetPassword(TEST_USERNAME);

verify(userRepository).findOneByLoginIgnoreCase(TEST_USERNAME);

ArgumentCaptor<String> urlCaptor = ArgumentCaptor.forClass(String.class);
verify(emailService).sendPasswordResetMail(eq(TEST_USERNAME), eq(TEST_EMAIL), urlCaptor.capture());

String passwordResetUrl = urlCaptor.getValue();
assertThat(passwordResetUrl, startsWith("http://www.example.com/resetPassword/"));

String token = passwordResetUrl.split("/")[4];
HashMap<String, String> claims = objectMapper.readValue(JwtHelper.decode(token).getClaims(), HashMap.class);

assertThat(claims.get(UserService.KEY_ACTION), is("reset_password"));
assertThat(claims.get(UserService.KEY_USER_ID), is(user.getId()));
assertThat(Instant.parse(claims.get(UserService.KEY_EXPIRY)).isAfter(Instant.now()), is(true));
}

@Test
@SneakyThrows
@SuppressWarnings("unchecked")
public void resetPasswordByEmail() {
properties.getPasswordReset().setPasswordResetUrlFormat("http://www.example.com/resetPassword/%s");

User user = createUser(TEST_USERID, TEST_USERNAME, TEST_CURRENT_PASSWORD, TEST_EMAIL);
Expand All @@ -307,8 +334,8 @@ public void resetPassword() {
@Test
@SneakyThrows
@SuppressWarnings("unchecked")
public void resetPasswordUnknownUser() {
expectedException.expect(ApiExceptionWithCode.apiExceptionWithCode(ErrorCode.USERNAME_INVALID));
public void resetPasswordUnknownUsernameAndEmail() {
expectedException.expect(ApiExceptionWithCode.apiExceptionWithCode(ErrorCode.UNKNOWN_IDENTIFIER));

when(userRepository.findOneByEmailIgnoreCase(TEST_EMAIL)).thenReturn(null);
instance.resetPassword(TEST_EMAIL);
Expand Down

0 comments on commit 62d496f

Please sign in to comment.