Project

General

Profile

Download (6.8 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2021 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.cdm.api.service.security;
10

    
11
import java.util.HashMap;
12
import java.util.Map;
13
import java.util.Optional;
14

    
15
import javax.mail.internet.AddressException;
16
import javax.mail.internet.InternetAddress;
17

    
18
import org.apache.logging.log4j.LogManager;
19
import org.apache.logging.log4j.Logger;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.beans.factory.annotation.Qualifier;
22
import org.springframework.dao.DataAccessException;
23
import org.springframework.mail.MailException;
24
import org.springframework.mail.MailPreparationException;
25
import org.springframework.scheduling.annotation.Async;
26
import org.springframework.scheduling.annotation.AsyncResult;
27
import org.springframework.security.core.userdetails.UserDetails;
28
import org.springframework.security.core.userdetails.UsernameNotFoundException;
29
import org.springframework.stereotype.Service;
30
import org.springframework.transaction.annotation.Transactional;
31
import org.springframework.util.Assert;
32
import org.springframework.util.concurrent.ListenableFuture;
33

    
34
import eu.etaxonomy.cdm.api.security.AbstractRequestToken;
35
import eu.etaxonomy.cdm.api.security.IAbstractRequestTokenStore;
36
import eu.etaxonomy.cdm.api.security.PasswordResetRequest;
37
import eu.etaxonomy.cdm.model.permission.User;
38

    
39
/**
40
 * @author a.kohlbecker
41
 * @since Oct 26, 2021
42
 */
43
@Service
44
@Transactional(readOnly = false)
45
public class PasswordResetService extends AccountSelfManagementService implements IPasswordResetService {
46

    
47
    private static Logger logger = LogManager.getLogger();
48

    
49
    @Autowired
50
    @Qualifier("passwordResetTokenStore")
51
    private IAbstractRequestTokenStore<PasswordResetRequest, User> passwordResetTokenStore;
52

    
53
    @Override
54
    @Async
55
    public ListenableFuture<Boolean> emailResetToken(String userNameOrEmail, String passwordRequestFormUrlTemplate) throws MailException, EmailAddressNotFoundException {
56

    
57
        try {
58
            // give calling methods a bit time to register the Listeners to the Future
59
            Thread.sleep(10);
60
        } catch (InterruptedException e1) {
61
            // IGNORE
62
        }
63
        if(logger.isTraceEnabled()) {
64
            logger.trace("emailResetToken trying to aquire from rate limiter [rate: " + emailResetToken_rateLimiter.getRate() + ", timeout: " + getRateLimiterTimeout().toMillis() + "ms]");
65
        }
66
        if (emailResetToken_rateLimiter.tryAcquire(getRateLimiterTimeout())) {
67
            logger.trace("emailResetToken allowed by rate limiter");
68
            try {
69
                User user = findUser(userNameOrEmail);
70
                String emailAddress = user.getEmailAddress();
71
                if(emailAddress == null) {
72
                    throw new MailPreparationException("no email address found for " + userNameOrEmail);
73
                }
74
                AbstractRequestToken resetRequest = passwordResetTokenStore.create(emailAddress, user);
75
                String passwordRequestFormUrl = String.format(passwordRequestFormUrlTemplate, resetRequest.getToken());
76
                Map<String, String> additionalValues = new HashMap<>();
77
                additionalValues.put("linkUrl", passwordRequestFormUrl);
78
                sendEmail(emailAddress, user.getUsername(),
79
                        UserAccountEmailTemplates.RESET_REQUEST_EMAIL_SUBJECT_TEMPLATE,
80
                        UserAccountEmailTemplates.REGISTRATION_REQUEST_EMAIL_BODY_TEMPLATE, additionalValues);
81
                logger.info("A password reset request for  " + user.getUsername() + " has been send to "
82
                        + emailAddress);
83
            } catch (EmailAddressNotFoundException e) {
84
                throw e;
85
            } catch (UsernameNotFoundException e) {
86
                logger.warn("Password reset request for unknown user, cause: " + e.getMessage());
87
            } catch (MailException e) {
88
                throw e;
89
            }
90
            return new AsyncResult<Boolean>(true);
91
        } else {
92
            logger.trace("blocked by rate limiter");
93
            return new AsyncResult<Boolean>(false);
94
        }
95
    }
96

    
97
   @Override
98
   @Async
99
   public ListenableFuture<Boolean> resetPassword(String token, String newPassword) throws AccountSelfManagementException, MailException {
100

    
101
       if (resetPassword_rateLimiter.tryAcquire(getRateLimiterTimeout())) {
102

    
103
           Optional<PasswordResetRequest> resetRequest = passwordResetTokenStore.findRequest(token);
104
           if (resetRequest.isPresent()) {
105
               try {
106
                   UserDetails user = userService.loadUserByUsername(resetRequest.get().getUserName());
107
                   Assert.isAssignable(user.getClass(), User.class);
108
                   userService.encodeUserPassword((User)user, newPassword);
109
                   userDao.saveOrUpdate((User)user);
110
                   passwordResetTokenStore.remove(token);
111
                   sendEmail(resetRequest.get().getUserEmail(), resetRequest.get().getUserName(),
112
                           UserAccountEmailTemplates.RESET_SUCCESS_EMAIL_SUBJECT_TEMPLATE,
113
                           UserAccountEmailTemplates.RESET_SUCCESS_EMAIL_BODY_TEMPLATE, null);
114
                   return new AsyncResult<Boolean>(true);
115
               } catch (DataAccessException | IllegalArgumentException | UsernameNotFoundException e) {
116
                   logger.error("Failed to change password of User " + resetRequest.get().getUserName(), e);
117
                   sendEmail(resetRequest.get().getUserEmail(), resetRequest.get().getUserName(),
118
                           UserAccountEmailTemplates.RESET_FAILED_EMAIL_SUBJECT_TEMPLATE,
119
                           UserAccountEmailTemplates.RESET_FAILED_EMAIL_BODY_TEMPLATE, null);
120
               }
121
           } else {
122
               throw new AccountSelfManagementException("Invalid password reset token");
123
           }
124
       }
125
       return new AsyncResult<Boolean>(false);
126
   }
127

    
128
    protected User findUser(String userNameOrEmail) throws UsernameNotFoundException, EmailAddressNotFoundException {
129

    
130
        User user;
131
        try {
132
            InternetAddress emailAddr = new InternetAddress(userNameOrEmail);
133
            emailAddr.validate();
134
            user = userDao.findByEmailAddress(userNameOrEmail);
135
            if (user == null) {
136
                throw new EmailAddressNotFoundException(
137
                        "No user with the email address'" + userNameOrEmail + "' found.");
138
            }
139
        } catch (AddressException ex) {
140
            user = userDao.findUserByUsername(userNameOrEmail);
141
            if (user == null) {
142
                throw new UsernameNotFoundException("No user with the user name: '" + userNameOrEmail + "' found.");
143
            }
144
        }
145
        return user;
146
    }
147
}
(9-9/10)