View Javadoc
1   package fr.ifremer.reefdb.security;
2   
3   /*
4    * #%L
5    * Reef DB :: 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.quadrige3.core.dao.administration.user.PrivilegeCode;
28  import fr.ifremer.quadrige3.core.dao.referential.StatusCode;
29  import fr.ifremer.quadrige3.core.dao.technical.Assert;
30  import fr.ifremer.quadrige3.core.dao.technical.hibernate.TemporaryDataHelper;
31  import fr.ifremer.quadrige3.core.security.Encryption;
32  import fr.ifremer.quadrige3.core.security.QuadrigeUserDetails;
33  import fr.ifremer.reefdb.config.ReefDbConfiguration;
34  import fr.ifremer.reefdb.dao.administration.user.ReefDbQuserDao;
35  import fr.ifremer.reefdb.dao.technical.Daos;
36  import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
37  import fr.ifremer.reefdb.dto.ReefDbBeans;
38  import fr.ifremer.reefdb.dto.referential.PersonDTO;
39  import fr.ifremer.reefdb.service.ReefDbTechnicalException;
40  import org.apache.commons.lang3.StringUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.nuiton.i18n.I18n;
44  import org.springframework.beans.factory.annotation.Autowired;
45  import org.springframework.context.annotation.Lazy;
46  import org.springframework.security.core.userdetails.UserDetails;
47  import org.springframework.security.core.userdetails.UserDetailsService;
48  import org.springframework.security.core.userdetails.UsernameNotFoundException;
49  import org.springframework.stereotype.Service;
50  import org.springframework.transaction.annotation.Transactional;
51  
52  import javax.annotation.Resource;
53  import java.util.List;
54  
55  /**
56   * <p>ReefDbUserDetailsServiceImpl class.</p>
57   *
58   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
59   */
60  @Lazy
61  @Transactional
62  @Service("reefDbUserDetailsService")
63  public class ReefDbUserDetailsServiceImpl implements UserDetailsService {
64  
65      private static final Log LOG = LogFactory.getLog(ReefDbUserDetailsServiceImpl.class);
66  
67      private boolean disabled;
68  
69      private final String mockUsername;
70      private final String mockCryptPassword;
71      private final Integer mockUserId;
72  
73      @Resource
74      protected ReefDbQuserDao reefDbQuserDao;
75  
76      /**
77       * <p>Constructor for ReefDbUserDetailsServiceImpl.</p>
78       *
79       * @param config a {@link fr.ifremer.reefdb.config.ReefDbConfiguration} object.
80       */
81      @Autowired
82      public ReefDbUserDetailsServiceImpl(ReefDbConfiguration config) {
83          disabled = config.isAuthenticationDisabled();
84  
85          // If authentication is disable
86          if (disabled) {
87              Assert.notBlank(config.getAuthenticationMockUsername(),
88                      "Mock username must be set, when authentication is disable");
89              Assert.notBlank(config.getAuthenticationMockPassword(),
90                      "Mock username must be set, when authentication is disable");
91  
92              mockUsername = config.getAuthenticationMockUsername();
93              mockCryptPassword = Encryption.sha(config.getAuthenticationMockPassword());
94              mockUserId = config.getAuthenticationMockUserId();
95  
96              LOG.debug(String.format("Authentication disable. Only this login/password is allowed: [%s/%s]",
97                      mockUsername,
98                      config.getAuthenticationMockPassword()));
99          } else {
100             mockUsername = null;
101             mockCryptPassword = null;
102             mockUserId = null;
103         }
104     }
105 
106     /**
107      * <p>Setter for the field <code>disabled</code>.</p>
108      *
109      * @param disabled a boolean.
110      */
111     public void setDisabled(boolean disabled) {
112         this.disabled = disabled;
113 
114         if (disabled) {
115             LOG.warn("Authentication has been disabled by the configuration. Please contact your administrator to ask the credentials for test.");
116         }
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
122 
123         if (StringUtils.isBlank(username)) {
124             String error = "username must not be blank";
125             LOG.error(error);
126             throw new UsernameNotFoundException(error);
127         }
128 
129         if (disabled) {
130             return loadTestUser(username);
131         }
132 
133         PersonDTO person;
134         try {
135             // Only allowed user with status enable (or local enable)
136             person = reefDbQuserDao.getUserByLogin(Lists.newArrayList(
137                     StatusCode.ENABLE.value(),
138                     StatusCode.LOCAL_ENABLE.value()),
139                     username);
140         } catch (Exception e) {
141             // Log here, because Spring security is in silent mode
142             LOG.error(e.getMessage(), e);
143             throw new ReefDbTechnicalException(e);
144         }
145 
146         if (person == null) {
147             String error = I18n.t("reefdb.error.authentication.userNotFoundOrDisable", username);
148             LOG.error(error);
149             throw new UsernameNotFoundException(error);
150         }
151 
152         String cryptedPassword = reefDbQuserDao.getPasswordByUserId(person.getId());
153         List<String> privilegeCodes = reefDbQuserDao.getPrivilegeCodesByUserId(person.getId());
154         boolean localUser = TemporaryDataHelper.isTemporaryId(person.getId()) || ReefDbBeans.isLocalStatus(person.getStatus());
155 
156         return new QuadrigeUserDetails(person.getId(), person.getFirstName() + " " + person.getName(), cryptedPassword, privilegeCodes, localUser);
157     }
158 
159     private UserDetails loadTestUser(String username) throws UsernameNotFoundException {
160 
161         if (!mockUsername.equals(username)) {
162             String error = String.format("Bad username [%s]. Only [%s] is allow, because authentication is disable.", username, mockUsername);
163             LOG.error(error);
164             throw new UsernameNotFoundException(error);
165         }
166 
167         PersonDTO person = ReefDbBeanFactory.newPersonDTO();
168         person.setId(mockUserId);
169         person.setFirstName(mockUsername);
170         person.setName("ReefDb");
171         person.setDepartment(null);
172 
173         // add user privileges
174         List<String> privilegesCodes = reefDbQuserDao.getPrivilegeCodesByUserId(person.getId());
175 
176         // add other privileges (useful for unit testing)
177         if (!privilegesCodes.contains(PrivilegeCode.LOCAL_ADMINISTRATOR.value()))
178             privilegesCodes.add(PrivilegeCode.LOCAL_ADMINISTRATOR.value());
179         if (!privilegesCodes.contains(PrivilegeCode.QUALIFIER.value()))
180             privilegesCodes.add(PrivilegeCode.QUALIFIER.value());
181 
182         return new QuadrigeUserDetails(mockUserId, mockUsername, mockCryptPassword, privilegesCodes, TemporaryDataHelper.isTemporaryId(mockUserId));
183     }
184 }