Project

General

Profile

Download (7.05 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.scheduling.annotation.Async;
25
import org.springframework.scheduling.annotation.AsyncResult;
26
import org.springframework.stereotype.Service;
27
import org.springframework.transaction.annotation.Transactional;
28
import org.springframework.util.concurrent.ListenableFuture;
29

    
30
import eu.etaxonomy.cdm.api.security.AbstractRequestToken;
31
import eu.etaxonomy.cdm.api.security.AccountCreationRequest;
32
import eu.etaxonomy.cdm.api.security.IAbstractRequestTokenStore;
33
import eu.etaxonomy.cdm.model.permission.Group;
34
import eu.etaxonomy.cdm.model.permission.User;
35
import eu.etaxonomy.cdm.persistence.dao.permission.IGroupDao;
36

    
37
/**
38
 * @author a.kohlbecker
39
 * @since Oct 26, 2021
40
 */
41
@Service
42
@Transactional(readOnly = true)
43
public class AccountRegistrationService extends AccountSelfManagementService implements IAccountRegistrationService {
44

    
45
    private static Logger logger = LogManager.getLogger();
46

    
47
    protected static final String EMAIL_EXISTS = "An account for this email address already exits.";
48

    
49
    protected static final String USER_NAME_EXISTS_MSG = "This user name is already being used by someone else.";
50

    
51
    @Autowired
52
    protected IGroupDao groupDao;
53

    
54
    @Autowired
55
    @Qualifier("accountCreationRequestTokenStore")
56
    private IAbstractRequestTokenStore<AccountCreationRequest, Object> accountRegistrationTokenStore;
57

    
58
    @Override
59
    @Async
60
    public ListenableFuture<Boolean> emailAccountRegistrationRequest(String emailAddress, String accountCreationRequestFormUrlTemplate) throws MailException, AddressException, AccountSelfManagementException {
61

    
62
        if(logger.isTraceEnabled()) {
63
            logger.trace("emailAccountRegistrationConfirmation() trying to aquire from rate limiter [rate: " + emailResetToken_rateLimiter.getRate() + ", timeout: " + getRateLimiterTimeout().toMillis() + "ms]");
64
        }
65
        if (emailResetToken_rateLimiter.tryAcquire(getRateLimiterTimeout())) {
66
            logger.trace("emailAccountRegistrationConfirmation() allowed by rate limiter");
67
            try {
68
                emailAddressValidAndUnused(emailAddress);
69
                AbstractRequestToken resetRequest = accountRegistrationTokenStore.create(emailAddress, null);
70
                String passwordRequestFormUrl = String.format(accountCreationRequestFormUrlTemplate, resetRequest.getToken());
71
                Map<String, String> additionalValues = new HashMap<>();
72
                additionalValues.put("linkUrl", passwordRequestFormUrl);
73
                sendEmail(emailAddress, null,
74
                        UserAccountEmailTemplates.REGISTRATION_REQUEST_EMAIL_SUBJECT_TEMPLATE,
75
                        UserAccountEmailTemplates.REGISTRATION_REQUEST_EMAIL_BODY_TEMPLATE, additionalValues);
76
                logger.info("An account creation request has been send to " + emailAddress);
77
                return new AsyncResult<Boolean>(true);
78
            } catch (MailException e) {
79
                throw e;
80
            }
81
        } else {
82
            logger.trace("blocked by rate limiter");
83
            return new AsyncResult<Boolean>(false);
84
        }
85
    }
86

    
87
    @Override
88
    @Async
89
    @Transactional(readOnly = false)
90
    public ListenableFuture<Boolean> createUserAccount(String token, String userName, String password, String givenName, String familyName, String prefix)
91
            throws MailException, AccountSelfManagementException, AddressException {
92

    
93
        if (resetPassword_rateLimiter.tryAcquire(getRateLimiterTimeout())) {
94

    
95
            Optional<AccountCreationRequest> creationRequest = accountRegistrationTokenStore.findRequest(token);
96
            if (creationRequest.isPresent()) {
97
                try {
98
                    // check again if the email address is still unused
99
                    emailAddressValidAndUnused(creationRequest.get().getUserEmail());
100
                    if(userNameExists(userName)) {
101
                        throw new AccountSelfManagementException(USER_NAME_EXISTS_MSG);
102
                    }
103
                    User newUser = User.NewInstance(userName, password);
104
                    userService.encodeUserPassword(newUser, password);
105
                    //for Phycobank only (preliminary, should be handled in Phycobank explicitly)
106
                    Group submitterGroup = groupDao.findGroupByName(Group.GROUP_SUBMITTER);
107
                    if (submitterGroup != null) {
108
                        submitterGroup.addMember(newUser);
109
                    }
110
                    userDao.saveOrUpdate(newUser);
111

    
112
                    accountRegistrationTokenStore.remove(token);
113
                    sendEmail(creationRequest.get().getUserEmail(), userName,
114
                            UserAccountEmailTemplates.REGISTRATION_SUCCESS_EMAIL_SUBJECT_TEMPLATE,
115
                            UserAccountEmailTemplates.REGISTRATION_SUCCESS_EMAIL_BODY_TEMPLATE, null);
116
                    return new AsyncResult<Boolean>(true);
117
                } catch (DataAccessException e) {
118
                    String message = "Failed to create a new user [userName: " + userName + ", email: " + creationRequest.get().getUserEmail() + "]";
119
                    logger.error(message, e);
120
                    throw new AccountSelfManagementException(message);
121
                }
122
            } else {
123
                throw new AccountSelfManagementException("Invalid account creation token");
124
            }
125
        }
126
        return new AsyncResult<Boolean>(false);
127
    }
128

    
129
    /**
130
     * Throws exceptions in case of any problems, returns silently in case
131
     * everything is OK.
132
     *
133
     * @param userNameOrEmail
134
     * @throws AddressException
135
     *             in case the <code>emailAddress</code> is invalid
136
     * @throws EmailAddressAlreadyInUseException
137
     *             in case the <code>emailAddress</code> is in use
138
     */
139
    protected void emailAddressValidAndUnused(String emailAddress)
140
            throws AddressException, EmailAddressAlreadyInUseException {
141
        InternetAddress emailAddr = new InternetAddress(emailAddress);
142
        emailAddr.validate();
143
        if (emailAddressExists(emailAddr.toString())) {
144
            throw new EmailAddressAlreadyInUseException(EMAIL_EXISTS);
145
        }
146
    }
147

    
148
    @Override
149
    public boolean emailAddressExists(String emailAddress) {
150
        return userDao.emailAddressExists(emailAddress);
151
    }
152

    
153
    @Override
154
    public boolean userNameExists(String userName) {
155
        return userDao.userNameExists(userName);
156
    }
157

    
158
}
(1-1/10)