View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.struts.apps.mailreader.actions;
18  
19  import java.lang.reflect.InvocationTargetException;
20  
21  import jakarta.servlet.ServletException;
22  import jakarta.servlet.http.HttpServletRequest;
23  import jakarta.servlet.http.HttpServletResponse;
24  import jakarta.servlet.http.HttpSession;
25  
26  import org.apache.commons.beanutils.PropertyUtils;
27  import org.apache.struts.action.ActionForm;
28  import org.apache.struts.action.ActionForward;
29  import org.apache.struts.action.ActionMapping;
30  import org.apache.struts.action.ActionMessage;
31  import org.apache.struts.action.ActionMessages;
32  import org.apache.struts.action.DynaActionForm;
33  import org.apache.struts.apps.mailreader.Constants;
34  import org.apache.struts.apps.mailreader.dao.ExpiredPasswordException;
35  import org.apache.struts.apps.mailreader.dao.User;
36  import org.apache.struts.apps.mailreader.dao.UserDatabase;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * <p>
42   * Provide an Edit method for retrieving an existing user,
43   * and a Save method for updating or inserting a user.
44   * </p><p>
45   * Both methods utilize a RegistrationForm to obtain or expose User details.
46   * If Save is used to create a user,
47   * additional validations ensure input is nominal.
48   * When a user is created,
49   * Save also handles the initial logon.
50   * </p>
51   */
52  public final class RegistrationAction extends BaseAction {
53      private static final long serialVersionUID = -7647634599126416947L;
54  
55      /**
56       * The {@code Log} instance for this class.
57       */
58      private final static Logger LOG =
59          LoggerFactory.getLogger(RegistrationAction.class);
60  
61      // --- Public Constants --
62  
63      /**
64       * <p>
65       * Name of fromAddress field ["fromAddress"].
66       * </p>
67       */
68      public final static String FROM_ADDRESS = "fromAddress";
69  
70      /**
71       * <p>
72       * Name of fullName field ["fullName"].
73       * </p>
74       */
75      public final static String FULL_NAME = "fullName";
76  
77      /**
78       * <p>
79       * Name of password confirmation field ["password2"].
80       * </p>
81       */
82      public final static String PASSWORD2 = "password2";
83  
84      /**
85       * <p>
86       * Name of replyToAddress field ["replyToAddress"].
87       * </p>
88       */
89      public final static String REPLY_TO_ADDRESS = "replyToAddress";
90  
91      // ---- Private Methods ----
92  
93      /**
94       * <p>
95       * The message prefix to use when populating a Registration Form.
96       * </p>
97       */
98      final String LOG_REGISTRATION_POPULATE = "RegistrationForm.populate";
99  
100     /**
101      * <p>
102      * Helper method to post error message when user already exists.
103      * </p>
104      *
105      * @param username Existing username
106      * @param errors   Our ActionMessages collection
107      */
108     private void errorUsernameUnique(String username,
109                                      ActionMessages errors) {
110         errors.add(
111                 USERNAME,
112                 new org.apache.struts.action.ActionMessage(
113                         "error.username.unique", username));
114     }
115 
116     /**
117      * <p>
118      * Verify input for creating a new user,
119      * create the user, and process the login.
120      * </p>
121      *
122      * @param form    The input form
123      * @param request The HttpRequest being served
124      * @param errors  The ActionMessages collection for any errors
125      * @return A new User and empty Errors if create succeeds,
126      *         or null and Errors if create fails
127      */
128     private User doCreateUser(
129             ActionForm form,
130             HttpServletRequest request,
131             ActionMessages errors) {
132 
133         LOG.trace(" Perform additional validations on Create");
134 
135         UserDatabase database = doGetUserDatabase();
136         String username = doGet(form, USERNAME);
137         try {
138             if (database.findUser(username) != null) {
139                 errorUsernameUnique(username, errors);
140             }
141         }
142         catch (ExpiredPasswordException e) {
143             errorUsernameUnique(username, errors);
144             errors.add("errors.literal", new ActionMessage(e.getMessage()));
145         }
146 
147         String password = doGet(form, PASSWORD);
148         if ((password == null) || (password.length() < 1)) {
149             errors.add(PASSWORD, new ActionMessage("error.password.required"));
150 
151             String password2 = doGet(form, PASSWORD2);
152             if ((password2 == null) || (password2.length() < 1)) {
153                 errors.add(
154                         PASSWORD2,
155                         new ActionMessage("error.password2.required"));
156             }
157         }
158 
159         if (!errors.isEmpty()) {
160             return null;
161         }
162 
163         User user = database.createUser(username);
164 
165         // Log the user in
166         HttpSession session = request.getSession();
167         session.setAttribute(Constants.USER_KEY, user);
168         LOG.trace(" User: '{}' logged on in session: {}",
169             user.getUsername(), session.getId());
170 
171         return user;
172     }
173 
174     /**
175      * <p>
176      * Helper method to populate the input form from the User object.
177      * </p>
178      *
179      * @param form Form with incoming values
180      * @param user User object to populate
181      * @throws ServletException On any error
182      */
183     private void doPopulate(ActionForm form, User user)
184             throws ServletException {
185 
186         final String title = Constants.EDIT;
187 
188         LOG.trace("{}{}", Constants.LOG_POPULATE_FORM, user);
189 
190         try {
191             PropertyUtils.copyProperties(form, user);
192             DynaActionForm dyna = (DynaActionForm) form;
193             dyna.set(TASK, title);
194             dyna.set(PASSWORD, null);
195             dyna.set(PASSWORD2, null);
196         } catch (InvocationTargetException e) {
197             Throwable t = e.getTargetException();
198             if (t == null) {
199                 t = e;
200             }
201             LOG.error(LOG_REGISTRATION_POPULATE, t);
202             throw new ServletException(LOG_REGISTRATION_POPULATE, t);
203         } catch (Throwable t) {
204             LOG.error(LOG_REGISTRATION_POPULATE, t);
205             throw new ServletException(LOG_REGISTRATION_POPULATE, t);
206         }
207     }
208 
209     /**
210      * <p>
211      * Helper method to populate the User object from the input form.
212      * </p>
213      *
214      * @param user User object to populate
215      * @param form Form with incoming values
216      * @throws ServletException On any error
217      */
218     private void doPopulate(User user, ActionForm form)
219             throws ServletException {
220 
221         LOG.trace("{}{}", Constants.LOG_POPULATE_USER, user);
222 
223         try {
224             String oldPassword = user.getPassword();
225             PropertyUtils.copyProperties(user, form);
226             String password = doGet(form, PASSWORD);
227             if ((password == null)
228                     || (password.length() < 1)) {
229 
230                 user.setPassword(oldPassword);
231             }
232 
233         } catch (InvocationTargetException e) {
234             Throwable t = e.getTargetException();
235             if (t == null) {
236                 t = e;
237             }
238 
239             LOG.error(LOG_REGISTRATION_POPULATE, t);
240             throw new ServletException(LOG_REGISTRATION_POPULATE, t);
241 
242         } catch (Throwable t) {
243             LOG.error(LOG_REGISTRATION_POPULATE, t);
244             throw new ServletException(LOG_REGISTRATION_POPULATE, t);
245         }
246     }
247 
248     /**
249      * <p>
250      * Validate and clear the transactional token,
251      * creating logging statements as needed.
252      * </p>
253      *
254      * @param request Our HttpServletRequest
255      * @param errors  ActionErrors to transfer any messages
256      */
257     private void doValidateToken(HttpServletRequest request,
258                                  ActionMessages errors) {
259 
260         LOG.trace(Constants.LOG_TOKEN_CHECK);
261 
262         if (!isTokenValid(request)) {
263             errors.add(
264                     ActionMessages.GLOBAL_MESSAGE,
265                     new ActionMessage(Constants.MSG_TRANSACTION_TOKEN));
266         }
267 
268         resetToken(request);
269     }
270 
271     // ----- Public Methods ----
272 
273     /**
274      * <p>
275      * Retrieve the User object to edit or null if the User does not exist,
276      * and set an transactional token to later detect multiple Save commands.
277      * </p>
278      *
279      * @param mapping  Our ActionMapping
280      * @param form     Our ActionForm
281      * @param request  Our HttpServletRequest
282      * @param response Our HttpServletResponse
283      * @return The "Success" result for this mapping
284      * @throws Exception on any error
285      */
286     public ActionForward Edit(
287             ActionMapping mapping,
288             ActionForm form,
289             HttpServletRequest request,
290             HttpServletResponse response)
291             throws Exception {
292 
293         final String method = Constants.EDIT;
294         doLogProcess(mapping, method);
295 
296         HttpSession session = request.getSession();
297         User user = doGetUser(session);
298         boolean updating = (user != null);
299         if (updating) {
300             doPopulate(form, user);
301         }
302 
303         doSaveToken(request);
304         return doFindSuccess(mapping);
305     }
306 
307     /**
308      * <p>
309      * Insert or update a User object to the persistent store.
310      * </p><p>
311      * If a User is not logged in,
312      * then a new User is created and automatically logged in.
313      * Otherwise, the existing User is updated.
314      * </p>
315      *
316      * @param mapping  Our ActionMapping
317      * @param form     Our ActionForm
318      * @param request  Our HttpServletRequest
319      * @param response Our HttpServletResponse
320      * @return The "Success" result for this mapping
321      * @throws Exception on any error
322      */
323     public ActionForward Save(
324             ActionMapping mapping,
325             ActionForm form,
326             HttpServletRequest request,
327             HttpServletResponse response)
328             throws Exception {
329 
330         final String method = Constants.SAVE;
331         doLogProcess(mapping, method);
332 
333         HttpSession session = request.getSession();
334         if (isCancelled(request)) {
335             doCancel(session, method, Constants.SUBSCRIPTION_KEY);
336             return doFindSuccess(mapping);
337         }
338 
339         ActionMessages errors = new ActionMessages();
340         doValidateToken(request, errors);
341 
342         if (!errors.isEmpty()) {
343             return doInputForward(mapping, request, errors);
344         }
345 
346         User user = doGetUser(session);
347         if (user == null) {
348             user = doCreateUser(form, request, errors);
349             if (!errors.isEmpty()) {
350                 return doInputForward(mapping, request, errors);
351             }
352         }
353 
354         doPopulate(user, form);
355         doSaveUser(user);
356 
357         return doFindSuccess(mapping);
358     }
359 }