View Javadoc
1   /*
2    * Copyright 2012-2017 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.app.web.admin.user;
17  
18  import java.util.Base64;
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import javax.annotation.Resource;
23  
24  import org.apache.commons.lang3.ArrayUtils;
25  import org.codelibs.core.lang.StringUtil;
26  import org.codelibs.fess.Constants;
27  import org.codelibs.fess.app.pager.UserPager;
28  import org.codelibs.fess.app.service.GroupService;
29  import org.codelibs.fess.app.service.RoleService;
30  import org.codelibs.fess.app.service.UserService;
31  import org.codelibs.fess.app.web.CrudMode;
32  import org.codelibs.fess.app.web.base.FessAdminAction;
33  import org.codelibs.fess.app.web.base.login.FessLoginAssist;
34  import org.codelibs.fess.es.user.exentity.User;
35  import org.codelibs.fess.util.ComponentUtil;
36  import org.codelibs.fess.util.RenderDataUtil;
37  import org.dbflute.optional.OptionalEntity;
38  import org.dbflute.optional.OptionalThing;
39  import org.lastaflute.web.Execute;
40  import org.lastaflute.web.response.HtmlResponse;
41  import org.lastaflute.web.response.render.RenderData;
42  import org.lastaflute.web.ruts.process.ActionRuntime;
43  import org.lastaflute.web.validation.VaErrorHook;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  /**
48   * @author shinsuke
49   * @author Keiichi Watanabe
50   */
51  public class AdminUserAction extends FessAdminAction {
52  
53      private static final Logger logger = LoggerFactory.getLogger(AdminUserAction.class);
54  
55      // ===================================================================================
56      //                                                                           Attribute
57      //                                                                           =========
58      @Resource
59      private UserService userService;
60      @Resource
61      private RoleService roleService;
62      @Resource
63      private GroupService groupService;
64      @Resource
65      private UserPager userPager;
66  
67      // ===================================================================================
68      //                                                                               Hook
69      //                                                                              ======
70      @Override
71      protected void setupHtmlData(final ActionRuntime runtime) {
72          super.setupHtmlData(runtime);
73          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameUser()));
74          runtime.registerData("ldapAdminEnabled", fessConfig.isLdapAdminEnabled());
75      }
76  
77      // ===================================================================================
78      //                                                                      Search Execute
79      //                                                                      ==============
80      @Execute
81      public HtmlResponse index() {
82          return asListHtml();
83      }
84  
85      @Execute
86      public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
87          pageNumber.ifPresent(num -> {
88              userPager.setCurrentPageNumber(pageNumber.get());
89          }).orElse(() -> {
90              userPager.setCurrentPageNumber(0);
91          });
92          return asHtml(path_AdminUser_AdminUserJsp).renderWith(data -> {
93              searchPaging(data, form);
94          });
95      }
96  
97      @Execute
98      public HtmlResponse search(final SearchForm form) {
99          copyBeanToBean(form, userPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
100         return asHtml(path_AdminUser_AdminUserJsp).renderWith(data -> {
101             searchPaging(data, form);
102         });
103     }
104 
105     @Execute
106     public HtmlResponse reset(final SearchForm form) {
107         userPager.clear();
108         return asHtml(path_AdminUser_AdminUserJsp).renderWith(data -> {
109             searchPaging(data, form);
110         });
111     }
112 
113     protected void searchPaging(final RenderData data, final SearchForm form) {
114         RenderDataUtil.register(data, "userItems", userService.getUserList(userPager)); // page navi
115         // restore from pager
116         copyBeanToBean(userPager, form, op -> op.include("id"));
117     }
118 
119     private void registerForms(final RenderData data) {
120         RenderDataUtil.register(data, "roleItems", roleService.getAvailableRoleList());
121         RenderDataUtil.register(data, "groupItems", groupService.getAvailableGroupList());
122     }
123 
124     // ===================================================================================
125     //                                                                        Edit Execute
126     //                                                                        ============
127     // -----------------------------------------------------
128     //                                            Entry Page
129     //                                            ----------
130     @Execute
131     public HtmlResponse createnew() {
132         saveToken();
133         return asHtml(path_AdminUser_AdminUserEditJsp).useForm(CreateForm.class, op -> {
134             op.setup(form -> {
135                 form.initialize();
136                 form.crudMode = CrudMode.CREATE;
137             });
138         }).renderWith(data -> {
139             registerForms(data);
140         });
141     }
142 
143     @Execute
144     public HtmlResponse edit(final EditForm form) {
145         validate(form, messages -> {}, () -> asListHtml());
146         final String id = form.id;
147         userService.getUser(id).ifPresent(entity -> {
148             copyBeanToBean(entity, form, op -> {});
149         }).orElse(() -> {
150             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asListHtml());
151         });
152         resetPassword(form);
153         saveToken();
154         if (form.crudMode.intValue() == CrudMode.EDIT) {
155             // back
156             form.crudMode = CrudMode.DETAILS;
157             return asDetailsHtml();
158         } else {
159             form.crudMode = CrudMode.EDIT;
160             return asEditHtml();
161         }
162     }
163 
164     // -----------------------------------------------------
165     //                                               Details
166     //                                               -------
167     @Execute
168     public HtmlResponse details(final int crudMode, final String id) {
169         verifyCrudMode(crudMode, CrudMode.DETAILS);
170         saveToken();
171         return asHtml(path_AdminUser_AdminUserDetailsJsp).useForm(EditForm.class, op -> {
172             op.setup(form -> {
173                 userService.getUser(id).ifPresent(entity -> {
174                     copyBeanToBean(entity, form, copyOp -> {
175                         copyOp.excludeNull();
176                     });
177                     form.crudMode = crudMode;
178                 }).orElse(() -> {
179                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asListHtml());
180                 });
181                 resetPassword(form);
182             });
183         }).renderWith(data -> {
184             registerForms(data);
185         });
186     }
187 
188     // -----------------------------------------------------
189     //                                         Actually Crud
190     //                                         -------------
191     @Execute
192     public HtmlResponse create(final CreateForm form) {
193         verifyCrudMode(form.crudMode, CrudMode.CREATE);
194         validate(form, messages -> {}, () -> asEditHtml());
195         verifyPassword(form, () -> asEditHtml());
196         verifyToken(() -> asEditHtml());
197         getUser(form).ifPresent(
198                 entity -> {
199                     try {
200                         userService.store(entity);
201                         saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
202                     } catch (final Exception e) {
203                         logger.error("Failed to add " + entity, e);
204                         throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
205                                 () -> asEditHtml());
206                     }
207                 }).orElse(() -> {
208             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), () -> asEditHtml());
209         });
210         return redirect(getClass());
211     }
212 
213     @Execute
214     public HtmlResponse update(final EditForm form) {
215         verifyCrudMode(form.crudMode, CrudMode.EDIT);
216         validate(form, messages -> {}, () -> asEditHtml());
217         verifyPassword(form, () -> asEditHtml());
218         verifyToken(() -> asEditHtml());
219         getUser(form).ifPresent(
220                 entity -> {
221                     try {
222                         userService.store(entity);
223                         saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
224                     } catch (final Exception e) {
225                         logger.error("Failed to update " + entity, e);
226                         throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
227                                 () -> asEditHtml());
228                     }
229                 }).orElse(() -> {
230             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
231         });
232         return redirect(getClass());
233     }
234 
235     @Execute
236     public HtmlResponse delete(final EditForm form) {
237         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
238         validate(form, messages -> {}, () -> asDetailsHtml());
239         getUserBean().ifPresent(u -> {
240             if (u.getFessUser() instanceof User && form.name.equals(u.getUserId())) {
241                 throwValidationError(messages -> messages.addErrorsCouldNotDeleteLoggedInUser(GLOBAL), () -> asDetailsHtml());
242             }
243         });
244         verifyToken(() -> asDetailsHtml());
245         final String id = form.id;
246         userService
247                 .getUser(id)
248                 .ifPresent(
249                         entity -> {
250                             try {
251                                 userService.delete(entity);
252                                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
253                             } catch (final Exception e) {
254                                 logger.error("Failed to delete " + entity, e);
255                                 throwValidationError(
256                                         messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
257                                         () -> asDetailsHtml());
258                             }
259                         }).orElse(() -> {
260                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
261                 });
262         return redirect(getClass());
263     }
264 
265     //===================================================================================
266     //                                                                       Assist Logic
267     //                                                                       ============
268     private static OptionalEntity<User> getEntity(final CreateForm form) {
269         switch (form.crudMode) {
270         case CrudMode.CREATE:
271             return OptionalEntity.of(new User()).map(entity -> {
272                 entity.setId(Base64.getUrlEncoder().encodeToString(form.name.getBytes(Constants.CHARSET_UTF_8)));
273                 return entity;
274             });
275         case CrudMode.EDIT:
276             if (form instanceof EditForm) {
277                 return ComponentUtil.getComponent(UserService.class).getUser(((EditForm) form).id);
278             }
279             break;
280         default:
281             break;
282         }
283         return OptionalEntity.empty();
284     }
285 
286     public static OptionalEntity<User> getUser(final CreateForm form) {
287         return getEntity(form).map(entity -> {
288             copyBeanToBean(form, entity, op -> op.exclude(ArrayUtils.addAll(Constants.COMMON_CONVERSION_RULE, "password")));
289             if (form.crudMode.intValue() == CrudMode.CREATE || StringUtil.isNotBlank(form.password)) {
290                 final String encodedPassword = ComponentUtil.getComponent(FessLoginAssist.class).encryptPassword(form.password);
291                 entity.setOriginalPassword(form.password);
292                 entity.setPassword(encodedPassword);
293             }
294             return entity;
295         });
296     }
297 
298     protected Map<String, String> createItem(final String label, final String value) {
299         final Map<String, String> map = new HashMap<>(2);
300         map.put(Constants.ITEM_LABEL, label);
301         map.put(Constants.ITEM_VALUE, value);
302         return map;
303     }
304 
305     // ===================================================================================
306     //                                                                        Small Helper
307     //                                                                        ============
308 
309     protected void verifyCrudMode(final int crudMode, final int expectedMode) {
310         if (crudMode != expectedMode) {
311             throwValidationError(messages -> {
312                 messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(crudMode));
313             }, () -> asListHtml());
314         }
315     }
316 
317     protected void verifyPassword(final CreateForm form, final VaErrorHook validationErrorLambda) {
318         if (form.crudMode == CrudMode.CREATE && StringUtil.isBlank(form.password)) {
319             resetPassword(form);
320             throwValidationError(messages -> {
321                 messages.addErrorsBlankPassword("password");
322             }, validationErrorLambda);
323         }
324         if (form.password != null && !form.password.equals(form.confirmPassword)) {
325             form.confirmPassword = null;
326             throwValidationError(messages -> {
327                 messages.addErrorsInvalidConfirmPassword("confirmPassword");
328             }, validationErrorLambda);
329         }
330     }
331 
332     public static void resetPassword(final CreateForm form) {
333         form.password = null;
334         form.confirmPassword = null;
335     }
336 
337     // ===================================================================================
338     //                                                                              JSP
339     //                                                                           =========
340 
341     private HtmlResponse asListHtml() {
342         return asHtml(path_AdminUser_AdminUserJsp).renderWith(data -> {
343             RenderDataUtil.register(data, "userItems", userService.getUserList(userPager)); // page navi
344             }).useForm(SearchForm.class, setup -> {
345             setup.setup(form -> {
346                 copyBeanToBean(userPager, form, op -> op.include("id"));
347             });
348         });
349     }
350 
351     private HtmlResponse asEditHtml() {
352         return asHtml(path_AdminUser_AdminUserEditJsp).renderWith(data -> {
353             registerForms(data);
354         });
355     }
356 
357     private HtmlResponse asDetailsHtml() {
358         return asHtml(path_AdminUser_AdminUserDetailsJsp).renderWith(data -> {
359             registerForms(data);
360         });
361     }
362 }