View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    * 
5    *  Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  package org.melati.login;
46  
47  import javax.servlet.http.Cookie;
48  import javax.servlet.http.HttpSession;
49  
50  import org.melati.Melati;
51  import org.melati.poem.AccessPoemException;
52  import org.melati.poem.Field;
53  import org.melati.poem.PoemThread;
54  import org.melati.poem.User;
55  import org.melati.poem.UserTable;
56  import org.melati.servlet.Form;
57  import org.melati.servlet.TemplateServlet;
58  import org.melati.template.ServletTemplateContext;
59  import org.melati.util.HttpServletRequestParameters;
60  import org.melati.util.MD5Util;
61  import org.melati.util.UTF8URLEncoder;
62  
63  /**
64   * An object which sets up the login process.
65   *
66   */
67  public class LoginHandler {
68  
69    static int ONEYEARINSECONDS = 60 * 60 * 24 * 365;
70  
71    protected TemplateServlet servlet;
72  
73    /**
74     * Constructor.
75     * 
76     * @param servlet to set
77     */
78    public LoginHandler(TemplateServlet servlet) {
79      this.servlet = servlet;
80    }
81  
82    protected String loginTemplate(String name) {
83      /*
84      // Fails to find templates in jars!!
85      return "org" + File.separatorChar + 
86             "melati" + File.separatorChar + 
87             "login" + File.separatorChar + 
88             name;
89      */
90      return "org/melati/login/" + name;
91      }
92  
93    protected String loginPageTemplate() {
94      return loginTemplate("Login");
95    }
96  
97    protected String usernameUnknownTemplate() {
98      return loginTemplate("LoginFailure");
99    }
100 
101   protected String passwordIncorrectTemplate() {
102     return loginTemplate("LoginFailure");
103   }
104 
105   protected String loginSuccessTemplate () {
106     return loginTemplate("LoginSuccess");
107   }
108 
109   /**
110    * Extract current values from context and add fields to context.
111    * 
112    * @param context the ServletTemplateContext to modify 
113    */
114   public void setupContext(ServletTemplateContext context) {
115     HttpSession session = context.getSession();
116 
117     AccessPoemException triggeringException = null;
118     if (session != null) triggeringException = 
119         (AccessPoemException)session.getAttribute(Login.TRIGGERING_EXCEPTION);
120 
121     if (triggeringException != null)
122       context.put("triggeringException", triggeringException);
123 
124     String username = context.getFormField("field_login");
125     String password = context.getFormField("field_password");
126     UserTable<User> users = PoemThread.database().getUserTable();
127 
128     context.put("login", new Field<String>(username, users.getLoginColumn()));
129     context.put("password", new Field<String>(password, users.getPasswordColumn()));
130 
131     context.put("loginUnknown", Boolean.FALSE);
132     context.put("passwordWrong", Boolean.FALSE);
133   }
134 
135   /**
136    * Set cookies if requested, remove any leftovers from any 
137    * triggering {@link AccessPoemException}.
138    * 
139    * @param melati the melati
140    * @param templateContext context to augment  
141    * @param user the established User
142    * @return the name of the success template
143    */
144   public String loginSuccessfullyAs (Melati melati, 
145                                      ServletTemplateContext templateContext, 
146                                      User user) {
147     // Arrange for the original parameters from the request that triggered the
148     // login to be overlaid on the next request that comes in if it's a match
149     // (this allows POSTed fields to be recovered without converting the
150     // request into a GET that the browser will repeat on reload without giving
151     // any warning).
152     
153     // if we have asked that our password be remembered, set the cookies
154     if (Form.getFieldNulled(templateContext,"rememberme") != null) {
155       String ldb = melati.getPoemContext().getLogicalDatabase();
156       melati.getResponse().addCookie(makeCookie(ldb, user.getLogin_unsafe()));
157       melati.getResponse().addCookie(makeCookie(ldb+user.getLogin_unsafe(), 
158                            MD5Util.encode(user.getPassword_unsafe())));
159     }
160 
161     HttpSession session = templateContext.getSession();
162 
163     HttpServletRequestParameters triggeringParams =
164         (HttpServletRequestParameters)session.getAttribute(
165                                           Login.TRIGGERING_REQUEST_PARAMETERS);
166 
167     if (triggeringParams != null) {
168       session.setAttribute(HttpSessionAccessHandler.OVERLAY_PARAMETERS,
169                        triggeringParams);
170       session.removeAttribute(Login.TRIGGERING_REQUEST_PARAMETERS);
171       session.removeAttribute(Login.TRIGGERING_EXCEPTION);
172       templateContext.put("continuationURL", 
173                           triggeringParams.continuationURL());
174     } else {
175       if (Form.getFieldNulled(templateContext,"continuationURL") 
176           != null) {
177         templateContext.put("continuationURL",
178                             templateContext.getFormField("continuationURL"));
179       }
180     }
181 
182     session.setAttribute(HttpSessionAccessHandler.USER, user);
183 
184     return loginSuccessTemplate();
185   }
186   
187   /**
188    * Make a cookie, using default (UTF-8) encoding, regardless of user's 
189    * encoding. 
190    */
191   private Cookie makeCookie(String key, String value) {
192     Cookie c =  new Cookie(UTF8URLEncoder.encode(key), UTF8URLEncoder.encode(value));
193 
194     c.setPath("/");
195     c.setMaxAge(ONEYEARINSECONDS);
196     c.setComment("This cookie is used to automatically log you back into " +
197                  "this site when you return.");
198     return c;
199   }
200     
201   /**
202    * Action the login.
203    * 
204    * @param melati the Melati
205    * @param templateContext the context
206    * @return a template name
207    * @throws Exception
208    */
209   public String doTemplateRequest(Melati melati, 
210                                   ServletTemplateContext templateContext)
211      throws Exception {
212 
213     setupContext(templateContext);
214 
215     String username = templateContext.getFormField("field_login");
216     String password = templateContext.getFormField("field_password");
217 
218     if (username == null)
219       return loginPageTemplate();
220 
221     User user = (User)PoemThread.database().getUserTable().getLoginColumn().
222                                                       firstWhereEq(username);
223     if (user == null) {
224       templateContext.put("loginUnknown", Boolean.TRUE);
225       return usernameUnknownTemplate();
226     }
227 
228     if (!user.getPassword_unsafe().equals(password)) {
229       templateContext.put("passwordWrong", Boolean.TRUE);
230       return passwordIncorrectTemplate();
231     }
232 
233     return loginSuccessfullyAs(melati, templateContext, user);
234   }
235 }