Commit dfb6e9b5 authored by Artem Hrybeniuk's avatar Artem Hrybeniuk
Browse files

Reset password improvements

-Liquibase: updated reset password templates, added reset confirmation template
-Sending password reset confirmation email
parent 807fac81
......@@ -209,7 +209,14 @@ public class MeController extends ApiBaseController {
}
try {
eMailVerificationService.changeSysUserPassword(tokenUuid, key, password);
String referer = request.getHeader("Referer");
OAuthClient client = oauthClientRepository.findByClientId(defaultOAuthClientId);
Optional<String> origin = client.getAllowedOrigins().stream().filter(referer::startsWith).findFirst();
if (origin.isEmpty()) {
LOG.warn("Invalid origin provided for: {}", referer);
throw new InvalidApiUsageException("Provided origin is not allowed: " + referer);
}
eMailVerificationService.changeSysUserPassword(tokenUuid, key, password, origin.get());
return true;
} catch (final TokenVerificationService.NoSuchVerificationTokenException e) {
throw new UserException("No such verification token!");
......
......@@ -319,7 +319,14 @@ public class WebUserController extends ApiBaseController {
}
try {
eMailVerificationService.changeWebUserPassword(tokenUuid, key, password);
String referer = request.getHeader("Referer");
OAuthClient client = oauthClientRepository.findByClientId(defaultOAuthClientId);
Optional<String> origin = client.getAllowedOrigins().stream().filter(referer::startsWith).findFirst();
if (origin.isEmpty()) {
LOG.warn("Invalid origin provided for: {}", referer);
throw new InvalidApiUsageException("Provided origin is not allowed: " + referer);
}
eMailVerificationService.changeWebUserPassword(tokenUuid, key, password, origin.get());
return true;
} catch (final TokenVerificationService.NoSuchVerificationTokenException e) {
throw new UserException("No such verification token!");
......
......@@ -29,11 +29,11 @@ public interface EMailVerificationService {
void sendPasswordResetEmail(String email, String username, String userResetTemplate, String origin);
void changeWebUserPassword(String tokenUuid, String key, String password)
void changeWebUserPassword(String tokenUuid, String key, String password, String origin)
throws TokenVerificationService.NoSuchVerificationTokenException, TokenVerificationService.TokenExpiredException;
void changeSysUserPassword(String tokenUuid, String key, String password)
throws TokenVerificationService.NoSuchVerificationTokenException, TokenVerificationService.TokenExpiredException, NoUserFoundException, UserException;
void changeSysUserPassword(String tokenUuid, String key, String password, String origin)
throws TokenVerificationService.NoSuchVerificationTokenException, TokenVerificationService.TokenExpiredException, UserException;
void cancelPasswordReset(String tokenUuid) throws Exception;
......
......@@ -27,6 +27,7 @@ public interface TemplatingService {
String EMAIL_WEBUSER_CONFIRMATION = "email.webuser.confirmation";
String PASSWORD_WEBUSER_RESET = "password.webuser.reset";
String PASSWORD_USER_RESET = "password.user.reset";
String PASSWORD_RESET_CONFIRMATION = "password.reset.confirmation";
String fillTemplate(String template, Map<String, Object> params);
......
......@@ -16,6 +16,8 @@
package org.gringlobal.service.impl;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
......@@ -164,7 +166,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
@Override
@Transactional(rollbackFor = Throwable.class)
public void changeWebUserPassword(final String tokenUuid, final String key, final String password) throws NoSuchVerificationTokenException, TokenExpiredException {
public void changeWebUserPassword(final String tokenUuid, final String key, final String password, final String origin) throws NoSuchVerificationTokenException, TokenExpiredException {
final VerificationToken consumedToken = tokenVerificationService.consumeToken("password-reset", tokenUuid, key);
final WebUser user = (WebUser) webUserService.loadUserByUsername(consumedToken.getData());
......@@ -174,6 +176,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
final UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
webUserService.setPassword(user.getId(), password);
sendResetConfirmationEmail(user.getUsername(), user.getWebCooperator().getEmail(), origin);
} finally {
LOG.warn("Restoring authorization away from {}", user.getUsername());
SecurityContextHolder.getContext().setAuthentication(prevAuth);
......@@ -182,7 +185,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
@Override
@Transactional(rollbackFor = Throwable.class)
public void changeSysUserPassword(final String tokenUuid, final String key, final String password) throws NoSuchVerificationTokenException, TokenExpiredException, UserException {
public void changeSysUserPassword(final String tokenUuid, final String key, final String password, final String origin) throws NoSuchVerificationTokenException, TokenExpiredException, UserException {
final VerificationToken consumedToken = tokenVerificationService.consumeToken("password-reset", tokenUuid, key);
final SysUser user = userService.loadSysUserByEmail(consumedToken.getData());
......@@ -192,6 +195,7 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
final UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
userService.setPassword(user, password);
sendResetConfirmationEmail(user.getUsername(), consumedToken.getData(), origin);
} finally {
LOG.warn("Restoring authorization away from {}", user.getUsername());
SecurityContextHolder.getContext().setAuthentication(prevAuth);
......@@ -209,6 +213,26 @@ public class EMailVerificationServiceImpl implements EMailVerificationService {
}
}
private void sendResetConfirmationEmail(String username, String email, String url) {
Date nowDate = new Date();
String resetDate = new SimpleDateFormat("MMM dd, yyyy 'at' HH:mm z").format(nowDate);
final Map<String, Object> scopes = new HashMap<>();
scopes.put("username", username);
scopes.put("resetDate", resetDate);
scopes.put("url", url);
final AppResource resource = appResourceService.getResource(AppResourceService.APP_NAME_GG_CE, TemplatingService.PASSWORD_RESET_CONFIRMATION, Locale.ENGLISH);
if (resource != null) {
final String mailBody = templatingService.fillTemplate(resource.getDescription(), scopes);
emailService.sendMail(resource.getDisplayMember(), mailBody, email);
} else {
LOG.warn("{} app resource not found. Not sending verification email", TemplatingService.PASSWORD_RESET_CONFIRMATION);
}
}
private <T> T asAdmin(Callable<T> callable) throws Exception {
UserDetails administrator = userService.loadUserByUsername("administrator");
List<GrantedAuthority> authorities = Lists.newArrayList(new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
......
......@@ -9286,3 +9286,26 @@ databaseChangeLog:
columnName: normal_count
columnDataType: int
 
- changeSet:
id: 1649159035545-1
author: ahrybeniuk
comment: Update reset password templates and add password reset confirmation template
dbms: mssql
changes:
- sql:
splitStatements: true
sql: >-
update app_resource set
description = '<p>Hi there,</p><p>We received a request to change the password of your GG-CE account <strong>{{username}}.</strong></p><p>Click <a href="{{url}}/user/pwdreset/{{tokenUUID}}?key={{tokenKey}}" rel="nofollow">this link</a> to set a new password and use this validation key: <strong>{{tokenKey}}</strong></p><p>If you did not make this request, <a href="{{url}}/user/pwdreset/{{tokenUUID}}/cancel" rel="nofollow">click here to cancel</a>. If you have any questions, please contact your GGCE administrator, and please do not reply to this email.</p><p>Thank you,</p><p>The GG-CE team</p>'
where sys_lang_id = 1 and app_name = 'gg-ce' and form_name = 'password_reset' and app_resource_name = 'password.user.reset';
update app_resource set
description = '<p>Hi there,</p><p>We received a request to change the password of your GG-CE account <strong>{{username}}.</strong></p><p>Click <a href="{{url}}/#/pwdreset/{{tokenUUID}}?key={{tokenKey}}" rel="nofollow">this link</a> to set a new password and use this validation key: <strong>{{tokenKey}}</strong></p><p>If you did not make this request, <a href="{{url}}/#/pwdreset/{{tokenUUID}}/cancel" rel="nofollow">click here to cancel</a>. If you have any questions, please contact your GGCE administrator, and please do not reply to this email.</p><p>Thank you,</p><p>The GG-CE team</p>'
where sys_lang_id = 1 and app_name = 'gg-ce' and form_name = 'password_reset' and app_resource_name = 'password.webuser.reset';
insert into app_resource
(sys_lang_id, app_name, form_name, app_resource_name,
description,
display_member, created_by, created_date, owned_by, owned_date, modified_by, modified_date)
values
(1, 'gg-ce', 'password_reset', 'password.reset.confirmation',
'<p>Dear {{username}}</p><p>The password for your GGCE account on <a href="{{url}}">{{url}}</a> was successfully reset on {{resetDate}}.</p><p>If you have any questions, please contact your GGCE administrator.</p><p>Regards</p><p>The GG-CE team</p>',
'Reset password confirmation', 1, GETDATE(), 1, GETDATE(), 1, GETDATE());
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment