Commit 52104ad7 authored by Daniel Gerhardt's avatar Daniel Gerhardt
Browse files

Merge branch 'account-activation-reset' into 'master'

Add new activation reset POST Route to UserController

Closes #55

See merge request !160
parents 7d2e4959 bd2a4dee
......@@ -63,6 +63,7 @@ public class SecurityProperties {
private Jwt jwt;
private List<String> adminAccounts;
private int loginTryLimit;
private int resendMailLimit;
private List<String> corsOrigins;
public Jwt getJwt() {
......@@ -89,6 +90,14 @@ public class SecurityProperties {
this.loginTryLimit = loginTryLimit;
}
public int getResendMailLimit() {
return resendMailLimit;
}
public void setResendMailLimit(final int resendMailLimit) {
this.resendMailLimit = resendMailLimit;
}
public List<String> getCorsOrigins() {
return corsOrigins;
}
......
......@@ -41,6 +41,7 @@ public class UserController extends AbstractEntityController<UserProfile> {
protected static final String REQUEST_MAPPING = "/user";
private static final String REGISTER_MAPPING = "/register";
private static final String ACTIVATE_MAPPING = DEFAULT_ID_MAPPING + "/activate";
private static final String RESET_ACTIVATE_MAPPING = DEFAULT_ID_MAPPING + "/resetactivation";
private static final String RESET_PASSWORD_MAPPING = DEFAULT_ID_MAPPING + "/resetpassword";
private static final String ROOM_HISTORY_MAPPING = DEFAULT_ID_MAPPING + "/roomHistory";
......@@ -111,6 +112,13 @@ public class UserController extends AbstractEntityController<UserProfile> {
}
}
@PostMapping(RESET_ACTIVATE_MAPPING)
public void resetActivation(
@PathVariable final String id,
final HttpServletRequest request) {
userService.resetActivation(id, request.getRemoteAddr());
}
@PostMapping(RESET_PASSWORD_MAPPING)
public void resetPassword(
@PathVariable final String id,
......
......@@ -45,8 +45,12 @@ public interface UserService extends EntityService<UserProfile> {
boolean isBannedFromLogin(String addr);
boolean isBannedFromSendingActivationMail(String addr);
void increaseFailedLoginCount(String addr);
void increaseSentMailCount(String addr);
String getUserIdToSocketId(UUID socketId);
void putUserIdToSocketId(UUID socketId, String userId);
......@@ -100,4 +104,6 @@ public interface UserService extends EntityService<UserProfile> {
void addWsSessionToJwtMapping(String wsSessionId, String jwt);
User getAuthenticatedUserByWsSession(String wsSessionId);
UserProfile resetActivation(String id, String clientAddress);
}
......@@ -91,6 +91,10 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
private static final int LOGIN_BAN_RESET_DELAY_MS = 2 * 60 * 1000;
private static final int MAIL_RESEND_TRY_RESET_DELAY_MS = 30 * 1000;
private static final int MAIL_RESEND_BAN_RESET_DELAY_MS = 2 * 60 * 1000;
private static final int REPEATED_PASSWORD_RESET_DELAY_MS = 3 * 60 * 1000;
private static final int PASSWORD_RESET_KEY_DURABILITY_MS = 2 * 60 * 60 * 1000;
......@@ -137,10 +141,14 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
private BCryptPasswordEncoder encoder;
private ConcurrentHashMap<String, Byte> loginTries;
private Set<String> loginBans;
private ConcurrentHashMap<String, Byte> resentMailCount;
private Set<String> resendMailBans;
{
loginTries = new ConcurrentHashMap<>();
loginBans = Collections.synchronizedSet(new HashSet<String>());
resentMailCount = new ConcurrentHashMap<>();
resendMailBans = Collections.synchronizedSet(new HashSet<String>());
}
public UserServiceImpl(
......@@ -178,6 +186,22 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
}
}
@Scheduled(fixedDelay = MAIL_RESEND_TRY_RESET_DELAY_MS)
public void resetResendingActivationTries() {
if (!resentMailCount.isEmpty()) {
logger.info("Reset failed mail activation resending counters.");
resentMailCount.clear();
}
}
@Scheduled(fixedDelay = MAIL_RESEND_BAN_RESET_DELAY_MS)
public void resetResendingActivationBan() {
if (!resendMailBans.isEmpty()) {
logger.info("Reset temporary bans from resending activation mail.");
resendMailBans.clear();
}
}
@Scheduled(fixedDelay = ACTIVATION_KEY_CHECK_INTERVAL_MS)
public void deleteInactiveUsers() {
logger.info("Delete inactive users.");
......@@ -229,6 +253,11 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
return loginBans.contains(addr);
}
@Override
public boolean isBannedFromSendingActivationMail(final String addr) {
return resendMailBans.contains(addr);
}
@Override
public void increaseFailedLoginCount(final String addr) {
Byte tries = loginTries.get(addr);
......@@ -244,6 +273,22 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
}
}
@Override
public void increaseSentMailCount(final String addr) {
Byte tries = resentMailCount.get(addr);
if (null == tries) {
tries = 0;
}
if (tries < securityProperties.getResendMailLimit()) {
resentMailCount.put(addr, ++tries);
if (securityProperties.getResendMailLimit() == tries) {
logger.info("Temporarily banned {} from resending activation"
+ " mails in due to too many resent activation mails.", addr);
resendMailBans.add(addr);
}
}
}
@Override
public String getUserIdToSocketId(final UUID socketId) {
return socketIdToUserId.get(socketId);
......@@ -523,6 +568,25 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
}
}
@Override
@Secured({"ROLE_ANONYMOUS", "ROLE_USER", "RUN_AS_ACCOUNT_MANAGEMENT"})
public UserProfile resetActivation(final String id, final String clientAddress) {
if (isBannedFromSendingActivationMail(clientAddress)) {
return null;
}
final UserProfile userProfile = get(id, true);
if (null == userProfile) {
logger.info("Reset of account activation failed. User {} does not exist.", id);
increaseFailedLoginCount(clientAddress);
throw new NotFoundException();
}
sendActivationEmail(userProfile);
return userProfile;
}
@Override
@Secured({"ROLE_ANONYMOUS", "ROLE_USER", "RUN_AS_ACCOUNT_MANAGEMENT"})
public boolean activateAccount(final String id, final String key, final String clientAddress) {
if (isBannedFromLogin(clientAddress)) {
......@@ -530,7 +594,7 @@ public class UserServiceImpl extends DefaultEntityServiceImpl<UserProfile> imple
}
final UserProfile userProfile = get(id, true);
if (userProfile == null || !key.equals(userProfile.getAccount().getActivationKey())) {
increaseFailedLoginCount(clientAddress);
increaseSentMailCount(clientAddress);
return false;
}
......
......@@ -77,6 +77,8 @@ arsnova:
# After the specified number of login tries the client IP will be banned for
# several minutes
login-try-limit: 50
# After the specific number of resent emails the client IP will be banned for several minutes from resending
resend-mail-limit: 5
# JSON Web Tokens
jwt:
......
Markdown is supported
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