1    package org.torweg.pulse.accesscontrol.authentication;
2    
3    import java.util.Map;
4    
5    import org.hibernate.Session;
6    import org.hibernate.Transaction;
7    import org.slf4j.Logger;
8    import org.slf4j.LoggerFactory;
9    import org.torweg.pulse.accesscontrol.User;
10   import org.torweg.pulse.bundle.ResultSet;
11   import org.torweg.pulse.configuration.Configurable;
12   import org.torweg.pulse.configuration.PoorMansCache;
13   import org.torweg.pulse.invocation.lifecycle.Lifecycle;
14   import org.torweg.pulse.service.event.RedirectEvent;
15   import org.torweg.pulse.service.event.RedirectSafelyEvent;
16   import org.torweg.pulse.service.request.Command;
17   import org.torweg.pulse.service.request.Parameter;
18   import org.torweg.pulse.service.request.ServiceRequest;
19   import org.torweg.pulse.service.request.ServiceSession;
20   
21   /**
22    * is responsible for the authentication of a {@code User} and the
23    * login/logout-procedure.
24    * 
25    * @author Christian Schatt, Thomas Weber
26    * @version $Revision: 2043 $
27    */
28   public final class Authentication {
29   
30       /**
31        * the logger.
32        */
33       private static final Logger LOGGER = LoggerFactory
34               .getLogger(Authentication.class);
35   
36       /**
37        * hidden constructor.
38        */
39       private Authentication() {
40           super();
41       }
42   
43       /**
44        * returns the configuration.
45        * 
46        * @return the configuration
47        */
48       private static AuthenticationConfig getConfiguration() {
49           return (AuthenticationConfig) PoorMansCache
50                   .getConfig(Authentication.class);
51       }
52   
53       /**
54        * performs the user authentication tasks and adds the authenticated user to
55        * the request.
56        * 
57        * @param request
58        *            the current request
59        * @param rs
60        *            the result set
61        * 
62        */
63       public static void authenticate(final ServiceRequest request,
64               final ResultSet rs) {
65           Command command = request.getCommand();
66   
67           Map<String, String> mappings = Authentication.getConfiguration()
68                   .getParameterMappings();
69   
70           Parameter login = command.getParameter(mappings.get("login"));
71           command.removeParameter(login);
72           Parameter logout = command.getParameter(mappings.get("logout"));
73           command.removeParameter(logout);
74           if ((login == null) && (logout == null)) {
75               return;
76           }
77   
78           Parameter username = command.getParameter(mappings.get("username"));
79           command.removeParameter(username);
80           Parameter password = command.getParameter(mappings.get("password"));
81           command.removeParameter(password);
82   
83           AuthenticationResult result = new AuthenticationResult();
84   
85           if ((login != null)
86                   && Authentication.login(request, result, username, password)) {
87               redirectAfterLogin(request);
88               return;
89           } else if (logout != null) {
90               Authentication.logout(request, request.getSession());
91               request.getEventManager().addEvent(new RedirectSafelyEvent());
92               return;
93           }
94   
95           if (request.getCommand().getParameter("redirect") != null) {
96               result.setRedirectURI(request.getCommand().getParameter("redirect")
97                       .getFirstValue());
98           }
99   
100          result.setLoginStatus(request.getSession().getAttribute(
101                  User.class.getCanonicalName()) != null);
102          result.setParameterMappings(mappings);
103          result.setLoginModes(Authentication.getConfiguration().getLoginModes());
104  
105          // add result
106          rs.addResult(result);
107      }
108  
109      /**
110       * Actually tries to login a user.
111       * 
112       * @param request
113       *            the current request
114       * @param result
115       *            the {@code AuthenticationResult} to record login failures
116       * @param username
117       *            the name of the {@code User} to be authenticated
118       * @param password
119       *            the password of the {@code User} to be authenticated
120       * @return {@code true}, if the login was successful. Otherwise
121       *         {@code false}.
122       */
123      private static boolean login(final ServiceRequest request,
124              final AuthenticationResult result, final Parameter username,
125              final Parameter password) {
126          if ((username != null) && (username.getFirstValue() != null)
127                  && (password != null) && (password.getFirstValue() != null)) {
128              User user = null;
129              Session s = Lifecycle.getHibernateDataSource().createNewSession();
130              Transaction tx = s.beginTransaction();
131              try {
132                  user = (User) s.createQuery("from User as u where u.name = ?")
133                          .setString(0, username.getFirstValue()).uniqueResult();
134                  tx.commit();
135              } catch (Exception exception) {
136                  tx.rollback();
137                  throw new AuthenticationException(exception);
138              } finally {
139                  s.close();
140              }
141              return postProcessLoadedUser(request, result, password, user);
142          } else {
143              result.setError(1, getConfiguration().getErrorCodes().get(1));
144              return false;
145          }
146      }
147  
148      /**
149       * checks, if the loaded {@code User} is not {@code null} and active and if
150       * the password is correct.
151       * 
152       * @param request
153       *            the service session
154       * @param result
155       *            the result
156       * @param password
157       *            the password
158       * @param user
159       *            the user
160       * @return {@code true}, if the {@code User} has been successfully
161       *         authenticated. Otherwise {@code false}.
162       */
163      private static boolean postProcessLoadedUser(final ServiceRequest request,
164              final AuthenticationResult result, final Parameter password,
165              final User user) {
166          if ((user != null) && (!user.isExpunged())) {
167              if (user.isActive()) {
168                  if (user.checkPassword(password.getFirstValue())) {
169                      // add milliseconds since last login to session
170                      if (user.getLastLoginTime() != null) {
171                          request.getSession()
172                                  .setAttribute(
173                                          new StringBuilder(Authentication.class
174                                                  .getCanonicalName())
175                                                  .append('#')
176                                                  .append("lastLogin").toString(),
177                                          Long.valueOf(user.getLastLoginTime()
178                                                  .getTime()));
179                      }
180  
181                      user.setLastLoginTime();
182                      Session sess = Lifecycle.getHibernateDataSource()
183                              .createNewSession();
184                      Transaction trans = sess.beginTransaction();
185                      try {
186                          sess.saveOrUpdate(user);
187                          trans.commit();
188                          // keep session open for SSO-Tasks
189                          if (getConfiguration().isSingleSignOn()) {
190                              singleSignOnLogin(user, request, password);
191                          }
192                      } catch (Exception exception) {
193                          trans.rollback();
194                          throw new AuthenticationException(
195                                  "Error post-processing user.", exception);
196                      } finally {
197                          sess.close();
198                      }
199                      request.getSession().setAttribute(
200                              User.class.getCanonicalName(), user.getId());
201                      request.refreshUser();
202                      return true;
203                  } else {
204                      result.setError(4, getConfiguration().getErrorCodes()
205                              .get(4));
206                      return false;
207                  }
208              } else {
209                  result.setError(3, getConfiguration().getErrorCodes().get(3));
210                  return false;
211              }
212          } else {
213              result.setError(2, getConfiguration().getErrorCodes().get(2));
214              return false;
215          }
216      }
217  
218      /**
219       * performs the redirect after a successful login.
220       * 
221       * @param request
222       *            the current request
223       */
224      private static void redirectAfterLogin(final ServiceRequest request) {
225          String redirectURI = request.getHttpServletRequest().getHeader(
226                  "referer");
227          if (request.getCommand().getParameter("redirect") != null) {
228              redirectURI = request.getCommand().getParameter("redirect")
229                      .getFirstValue();
230  
231          }
232          if (!redirectURI.equals("")) {
233              request.getEventManager().addEvent(new RedirectEvent(redirectURI));
234          } else {
235              request.getEventManager().addEvent(new RedirectSafelyEvent());
236          }
237      }
238  
239      /**
240       * Actually logs out the current user.
241       * 
242       * @param request
243       *            the request
244       * @param session
245       *            the service session
246       */
247      private static void logout(final ServiceRequest request,
248              final ServiceSession session) {
249          User user = request.getUser();
250          session.removeAttribute(User.class.getCanonicalName());
251          request.refreshUser();
252          if (getConfiguration().isSingleSignOn()) {
253              singleSignOnLogout(user, request);
254          }
255      }
256  
257      /**
258       * processes the {@code ISingleSignOnTask}s during login.
259       * 
260       * @param request
261       *            the request
262       * @param user
263       *            the user
264       * @param password
265       *            the password, or {@code null} on sign out
266       */
267      private static void singleSignOnLogin(final User user,
268              final ServiceRequest request, final Parameter password) {
269          for (Class<ISingleSignOnTask> task : getConfiguration()
270                  .getSingleSignOnTasks()) {
271              ISingleSignOnTask taskInstance = prepareTask(task);
272              if (taskInstance != null) {
273                  taskInstance.signOn(user, password.getFirstValue(), request);
274              }
275          }
276      }
277  
278      /**
279       * processes the {@code ISingleSignOnTask}s during log out.
280       * 
281       * @param user
282       *            the user
283       * @param request
284       *            the request
285       */
286      private static void singleSignOnLogout(final User user,
287              final ServiceRequest request) {
288          /* nothing to do for Everybody */
289          if (user instanceof User.Everybody) {
290              return;
291          }
292          for (Class<ISingleSignOnTask> task : getConfiguration()
293                  .getSingleSignOnTasks()) {
294              ISingleSignOnTask taskInstance = prepareTask(task);
295              if (taskInstance != null) {
296                  taskInstance.signOff(user, request);
297              }
298          }
299  
300      }
301  
302      /**
303       * prepares a {@code ISingleSignOnTask} for execution.
304       * 
305       * @param task
306       *            the task
307       * @return the configured instance
308       */
309      private static ISingleSignOnTask prepareTask(
310              final Class<ISingleSignOnTask> task) {
311          try {
312              ISingleSignOnTask taskInstance = task.newInstance();
313  
314              configureTask(task, taskInstance);
315              return taskInstance;
316          } catch (InstantiationException e) {
317              LOGGER.error(
318                      "Error processing task '{}':{}",
319                      new Object[] { task.getCanonicalName(),
320                              e.getLocalizedMessage() });
321              return null;
322          } catch (IllegalAccessException e) {
323              LOGGER.error(
324                      "Error processing task '{}':{}",
325                      new Object[] { task.getCanonicalName(),
326                              e.getLocalizedMessage() });
327              return null;
328          }
329      }
330  
331      /**
332       * configures the task if it is an instance of {@code Configurable}.
333       * 
334       * @param task
335       *            the task class
336       * @param taskInstance
337       *            the task instance
338       */
339      @SuppressWarnings("all")
340      private static void configureTask(final Class<ISingleSignOnTask> task,
341              final ISingleSignOnTask taskInstance) {
342          if (taskInstance instanceof Configurable) {
343              ((Configurable) taskInstance).initialize(PoorMansCache
344                      .getConfiguration(task));
345          }
346      }
347  }
348