View Javadoc
1   package fr.ifremer.dali.security;
2   
3   /*
4    * #%L
5    * Dali :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.google.common.collect.Lists;
27  import fr.ifremer.dali.config.DaliConfiguration;
28  import fr.ifremer.dali.dao.administration.user.DaliQuserDao;
29  import fr.ifremer.dali.dto.referential.PersonDTO;
30  import fr.ifremer.dali.service.DaliTechnicalException;
31  import fr.ifremer.quadrige3.core.dao.administration.user.PrivilegeCode;
32  import fr.ifremer.quadrige3.core.dao.referential.StatusCode;
33  import fr.ifremer.quadrige3.core.dao.technical.Assert;
34  import fr.ifremer.quadrige3.core.dao.technical.hibernate.TemporaryDataHelper;
35  import fr.ifremer.quadrige3.core.security.Encryption;
36  import fr.ifremer.quadrige3.core.security.QuadrigeUserDetails;
37  import fr.ifremer.quadrige3.ui.core.dto.QuadrigeBeans;
38  import org.apache.commons.lang3.StringUtils;
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.nuiton.i18n.I18n;
42  import org.springframework.beans.factory.annotation.Autowired;
43  import org.springframework.context.annotation.Lazy;
44  import org.springframework.security.core.userdetails.UserDetails;
45  import org.springframework.security.core.userdetails.UserDetailsService;
46  import org.springframework.security.core.userdetails.UsernameNotFoundException;
47  import org.springframework.stereotype.Service;
48  import org.springframework.transaction.annotation.Transactional;
49  
50  import javax.annotation.Resource;
51  import java.util.List;
52  
53  /**
54   * <p>DaliUserDetailsServiceImpl class.</p>
55   *
56   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
57   */
58  @Lazy
59  @Transactional
60  @Service("daliUserDetailsService")
61  public class DaliUserDetailsServiceImpl implements UserDetailsService {
62  
63      private static final Log LOG = LogFactory.getLog(DaliUserDetailsServiceImpl.class);
64  
65      private boolean disabled;
66  
67      private final String mockUsername;
68      private final String mockCryptPassword;
69      private final Integer mockUserId;
70  
71      @Resource
72      protected DaliQuserDao daliQuserDao;
73  
74      /**
75       * <p>Constructor for DaliUserDetailsServiceImpl.</p>
76       *
77       * @param config a {@link DaliConfiguration} object.
78       */
79      @Autowired
80      public DaliUserDetailsServiceImpl(DaliConfiguration config) {
81          disabled = config.isAuthenticationDisabled();
82  
83          // If authentication is disable
84          if (disabled) {
85              Assert.notBlank(config.getAuthenticationMockUsername(),
86                      "Mock username must be set, when authentication is disable");
87              Assert.notBlank(config.getAuthenticationMockPassword(),
88                      "Mock username must be set, when authentication is disable");
89  
90              mockUsername = config.getAuthenticationMockUsername();
91              mockCryptPassword = Encryption.sha(config.getAuthenticationMockPassword());
92              mockUserId = config.getAuthenticationMockUserId();
93  
94              LOG.debug(String.format("Authentication disable. Only this login/password is allowed: [%s/%s]",
95                      mockUsername,
96                      config.getAuthenticationMockPassword()));
97          } else {
98              mockUsername = null;
99              mockCryptPassword = null;
100             mockUserId = null;
101         }
102     }
103 
104     /**
105      * <p>Setter for the field <code>disabled</code>.</p>
106      *
107      * @param disabled a boolean.
108      */
109     public void setDisabled(boolean disabled) {
110         this.disabled = disabled;
111 
112         if (disabled) {
113             LOG.warn("Authentication has been disabled by the configuration. Please contact your administrator to ask the credentials for test.");
114         }
115     }
116 
117     /** {@inheritDoc} */
118     @Override
119     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
120 
121         if (StringUtils.isBlank(username)) {
122             String error = "username must not be blank";
123             LOG.error(error);
124             throw new UsernameNotFoundException(error);
125         }
126 
127         if (disabled) {
128             return loadTestUser(username);
129         }
130 
131         PersonDTO person;
132         try {
133             // Only allowed user with status enable (or local enable)
134             person = daliQuserDao.getUserByLogin(
135                     Lists.newArrayList(StatusCode.ENABLE.value()),
136                     username);
137         } catch (Exception e) {
138             // Log here, because Spring security is in silent mode
139             LOG.error(e.getMessage(), e);
140             throw new DaliTechnicalException(e);
141         }
142 
143         if (person == null) {
144             String error = I18n.t("dali.error.authentication.userNotFoundOrDisable", username);
145             LOG.error(error);
146             throw new UsernameNotFoundException(error);
147         }
148 
149         String cryptedPassword = daliQuserDao.getPasswordByUserId(person.getId());
150         List<String> privilegeCodes = daliQuserDao.getPrivilegeCodesByUserId(person.getId());
151         boolean localUser = TemporaryDataHelper.isTemporaryId(person.getId()) || QuadrigeBeans.isLocalStatus(person.getStatus());
152 
153         return new QuadrigeUserDetails(person.getId(), person.getFirstName() + " " + person.getName(), cryptedPassword, privilegeCodes, localUser);
154     }
155 
156     private UserDetails loadTestUser(String username) throws UsernameNotFoundException {
157 
158         if (!mockUsername.equals(username)) {
159             String error = String.format("Bad username [%s]. Only [%s] is allow, because authentication is disable.", username, mockUsername);
160             LOG.error(error);
161             throw new UsernameNotFoundException(error);
162         }
163 
164         // add user privileges
165         List<String> privilegesCodes = daliQuserDao.getPrivilegeCodesByUserId(mockUserId);
166 
167         // add other privileges (useful for unit testing)
168         if (!privilegesCodes.contains(PrivilegeCode.REFERENTIAL_ADMINISTRATOR.value()))
169             privilegesCodes.add(PrivilegeCode.REFERENTIAL_ADMINISTRATOR.value());
170         if (!privilegesCodes.contains(PrivilegeCode.QUALIFIER.value()))
171             privilegesCodes.add(PrivilegeCode.QUALIFIER.value());
172 
173         return new QuadrigeUserDetails(mockUserId, mockUsername, mockCryptPassword, privilegesCodes, TemporaryDataHelper.isTemporaryId(mockUserId));
174     }
175 }