View Javadoc
1   /*
2    * Copyright 2012-2025 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.webauth;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Locale;
22  import java.util.Map;
23  
24  import org.apache.logging.log4j.LogManager;
25  import org.apache.logging.log4j.Logger;
26  import org.codelibs.fess.Constants;
27  import org.codelibs.fess.annotation.Secured;
28  import org.codelibs.fess.app.pager.WebAuthPager;
29  import org.codelibs.fess.app.service.WebAuthenticationService;
30  import org.codelibs.fess.app.service.WebConfigService;
31  import org.codelibs.fess.app.web.CrudMode;
32  import org.codelibs.fess.app.web.base.FessAdminAction;
33  import org.codelibs.fess.helper.SystemHelper;
34  import org.codelibs.fess.opensearch.config.exentity.WebAuthentication;
35  import org.codelibs.fess.opensearch.config.exentity.WebConfig;
36  import org.codelibs.fess.util.ComponentUtil;
37  import org.codelibs.fess.util.RenderDataUtil;
38  import org.dbflute.optional.OptionalEntity;
39  import org.dbflute.optional.OptionalThing;
40  import org.lastaflute.web.Execute;
41  import org.lastaflute.web.response.HtmlResponse;
42  import org.lastaflute.web.response.render.RenderData;
43  import org.lastaflute.web.ruts.process.ActionRuntime;
44  
45  import jakarta.annotation.Resource;
46  
47  /**
48   * Admin action for Web Authentication management.
49   *
50   */
51  public class AdminWebauthAction extends FessAdminAction {
52  
53      /**
54       * Default constructor.
55       */
56      public AdminWebauthAction() {
57          super();
58      }
59  
60      /** Role name for admin web auth operations */
61      public static final String ROLE = "admin-webauth";
62  
63      private static final Logger logger = LogManager.getLogger(AdminWebauthAction.class);
64  
65      // ===================================================================================
66      //                                                                           Attribute
67      //                                                                           =========
68      /** Service for managing web authentication configurations */
69      @Resource
70      private WebAuthenticationService webAuthenticationService;
71      /** Pager for paginating web authentication results */
72      @Resource
73      private WebAuthPager webAuthPager;
74      /** Service for accessing and modifying web configuration settings */
75      @Resource
76      protected WebConfigService webConfigService;
77  
78      // ===================================================================================
79      //                                                                               Hook
80      //                                                                              ======
81      @Override
82      protected void setupHtmlData(final ActionRuntime runtime) {
83          super.setupHtmlData(runtime);
84          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameWebauth()));
85      }
86  
87      @Override
88      protected String getActionRole() {
89          return ROLE;
90      }
91  
92      // ===================================================================================
93      //                                                                      Search Execute
94      //                                                                      ==============
95      /**
96       * Displays the web authentication management index page.
97       *
98       * @param form the search form for filtering
99       * @return HTML response for the web authentication list page
100      */
101     @Execute
102     @Secured({ ROLE, ROLE + VIEW })
103     public HtmlResponse index(final SearchForm form) {
104         return asListHtml();
105     }
106 
107     /**
108      * Displays a paginated list of web authentication configurations.
109      *
110      * @param pageNumber the page number to display (optional)
111      * @param form the search form containing filter criteria
112      * @return HTML response with the web authentication list
113      */
114     @Execute
115     @Secured({ ROLE, ROLE + VIEW })
116     public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
117         pageNumber.ifPresent(num -> {
118             webAuthPager.setCurrentPageNumber(pageNumber.get());
119         }).orElse(() -> {
120             webAuthPager.setCurrentPageNumber(0);
121         });
122         return asHtml(path_AdminWebauth_AdminWebauthJsp).renderWith(data -> {
123             searchPaging(data, form);
124         });
125     }
126 
127     /**
128      * Searches for web authentication configurations based on the provided search criteria.
129      *
130      * @param form the search form containing search criteria
131      * @return HTML response with filtered web authentication results
132      */
133     @Execute
134     @Secured({ ROLE, ROLE + VIEW })
135     public HtmlResponse search(final SearchForm form) {
136         copyBeanToBean(form, webAuthPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
137         return asHtml(path_AdminWebauth_AdminWebauthJsp).renderWith(data -> {
138             searchPaging(data, form);
139         });
140     }
141 
142     /**
143      * Resets the search criteria and displays all web authentication configurations.
144      *
145      * @param form the search form to reset
146      * @return HTML response with the reset web authentication list
147      */
148     @Execute
149     @Secured({ ROLE, ROLE + VIEW })
150     public HtmlResponse reset(final SearchForm form) {
151         webAuthPager.clear();
152         return asHtml(path_AdminWebauth_AdminWebauthJsp).renderWith(data -> {
153             searchPaging(data, form);
154         });
155     }
156 
157     /**
158      * Sets up pagination data for the web authentication search results.
159      * Registers web authentication items and determines if the create link should be displayed.
160      *
161      * @param data the render data to populate with search results
162      * @param form the search form containing filter criteria
163      */
164     protected void searchPaging(final RenderData data, final SearchForm form) {
165         RenderDataUtil.register(data, "webAuthenticationItems", webAuthenticationService.getWebAuthenticationList(webAuthPager)); // page navi
166         RenderDataUtil.register(data, "displayCreateLink", !crawlingConfigHelper.getAllWebConfigList(false, false, false, null).isEmpty());
167         // restore from pager
168         copyBeanToBean(webAuthPager, form, op -> op.include("id"));
169     }
170 
171     // ===================================================================================
172     //                                                                        Edit Execute
173     //                                                                        ============
174     // -----------------------------------------------------
175     //                                            Entry Page
176     //                                            ----------
177     /**
178      * Displays the form for creating a new web authentication configuration.
179      *
180      * @return HTML response for the web authentication creation form
181      */
182     @Execute
183     @Secured({ ROLE })
184     public HtmlResponse createnew() {
185         saveToken();
186         return asHtml(path_AdminWebauth_AdminWebauthEditJsp).useForm(CreateForm.class, op -> {
187             op.setup(form -> {
188                 form.initialize();
189                 form.crudMode = CrudMode.CREATE;
190             });
191         }).renderWith(data -> {
192             registerProtocolSchemeItems(data);
193             registerWebConfigItems(data);
194         });
195     }
196 
197     /**
198      * Displays the form for editing an existing web authentication configuration.
199      *
200      * @param form the edit form containing web authentication ID
201      * @return HTML response for the web authentication edit form
202      */
203     @Execute
204     @Secured({ ROLE })
205     public HtmlResponse edit(final EditForm form) {
206         validate(form, messages -> {}, this::asListHtml);
207         final String id = form.id;
208         webAuthenticationService.getWebAuthentication(id).ifPresent(entity -> {
209             copyBeanToBean(entity, form, op -> {});
210         }).orElse(() -> {
211             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
212         });
213         saveToken();
214         if (form.crudMode.intValue() == CrudMode.EDIT) {
215             // back
216             form.crudMode = CrudMode.DETAILS;
217             return asDetailsHtml();
218         }
219         form.crudMode = CrudMode.EDIT;
220         return asEditHtml();
221     }
222 
223     // -----------------------------------------------------
224     //                                               Details
225     //                                               -------
226     /**
227      * Displays the details of a web authentication configuration.
228      *
229      * @param crudMode the CRUD mode for the operation
230      * @param id the ID of the web authentication to display
231      * @return HTML response for the web authentication details page
232      */
233     @Execute
234     @Secured({ ROLE, ROLE + VIEW })
235     public HtmlResponse details(final int crudMode, final String id) {
236         verifyCrudMode(crudMode, CrudMode.DETAILS, this::asListHtml);
237         saveToken();
238         return asHtml(path_AdminWebauth_AdminWebauthDetailsJsp).useForm(EditForm.class, op -> {
239             op.setup(form -> {
240                 webAuthenticationService.getWebAuthentication(id).ifPresent(entity -> {
241                     copyBeanToBean(entity, form, copyOp -> {
242                         copyOp.excludeNull();
243                     });
244                     form.crudMode = crudMode;
245                 }).orElse(() -> {
246                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
247                 });
248             });
249         }).renderWith(data -> {
250             registerProtocolSchemeItems(data);
251             registerWebConfigItems(data);
252         });
253     }
254 
255     // -----------------------------------------------------
256     //                                         Actually Crud
257     //                                         -------------
258     /**
259      * Creates a new web authentication configuration.
260      *
261      * @param form the create form containing the new web authentication data
262      * @return HTML response redirecting to the list page after creation
263      */
264     @Execute
265     @Secured({ ROLE })
266     public HtmlResponse create(final CreateForm form) {
267         verifyCrudMode(form.crudMode, CrudMode.CREATE, this::asListHtml);
268         validate(form, messages -> {}, this::asEditHtml);
269         verifyToken(this::asEditHtml);
270         getWebAuthentication(form).ifPresent(entity -> {
271             try {
272                 webAuthenticationService.store(entity);
273                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
274             } catch (final Exception e) {
275                 logger.warn("Failed to process a request.", e);
276                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
277                         this::asEditHtml);
278             }
279         }).orElse(() -> {
280             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
281         });
282         return redirect(getClass());
283     }
284 
285     /**
286      * Updates an existing web authentication configuration.
287      *
288      * @param form the edit form containing the updated web authentication data
289      * @return HTML response redirecting to the list page after update
290      */
291     @Execute
292     @Secured({ ROLE })
293     public HtmlResponse update(final EditForm form) {
294         verifyCrudMode(form.crudMode, CrudMode.EDIT, this::asListHtml);
295         validate(form, messages -> {}, this::asEditHtml);
296         verifyToken(this::asEditHtml);
297         getWebAuthentication(form).ifPresent(entity -> {
298             try {
299                 webAuthenticationService.store(entity);
300                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
301             } catch (final Exception e) {
302                 logger.warn("Failed to process a request.", e);
303                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
304                         this::asEditHtml);
305             }
306         }).orElse(() -> {
307             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
308         });
309         return redirect(getClass());
310     }
311 
312     /**
313      * Deletes a web authentication configuration.
314      *
315      * @param form the edit form containing the ID of the web authentication to delete
316      * @return HTML response redirecting to the list page after deletion
317      */
318     @Execute
319     @Secured({ ROLE })
320     public HtmlResponse delete(final EditForm form) {
321         verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
322         validate(form, messages -> {}, this::asDetailsHtml);
323         verifyToken(this::asDetailsHtml);
324         final String id = form.id;
325         webAuthenticationService.getWebAuthentication(id).ifPresent(entity -> {
326             try {
327                 webAuthenticationService.delete(entity);
328                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
329             } catch (final Exception e) {
330                 logger.warn("Failed to process a request.", e);
331                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
332                         this::asEditHtml);
333             }
334         }).orElse(() -> {
335             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asDetailsHtml);
336         });
337         return redirect(getClass());
338     }
339 
340     //===================================================================================
341     //                                                                        Assist Logic
342     //                                                                        ============
343     /**
344      * Retrieves or creates a WebAuthentication entity based on the form's CRUD mode.
345      *
346      * @param form the form containing the web authentication data
347      * @param username the username of the current user
348      * @param currentTime the current timestamp
349      * @return an optional WebAuthentication entity
350      */
351     public static OptionalEntity<WebAuthentication> getEntity(final CreateForm form, final String username, final long currentTime) {
352         switch (form.crudMode) {
353         case CrudMode.CREATE:
354             return OptionalEntity.of(new WebAuthentication()).map(entity -> {
355                 entity.setCreatedBy(username);
356                 entity.setCreatedTime(currentTime);
357                 return entity;
358             });
359         case CrudMode.EDIT:
360             if (form instanceof EditForm) {
361                 return ComponentUtil.getComponent(WebAuthenticationService.class).getWebAuthentication(((EditForm) form).id);
362             }
363             break;
364         default:
365             break;
366         }
367         return OptionalEntity.empty();
368     }
369 
370     /**
371      * Converts a form to a WebAuthentication entity with proper user and timestamp information.
372      *
373      * @param form the form containing the web authentication data
374      * @return an optional WebAuthentication entity with updated metadata
375      */
376     public static OptionalEntity<WebAuthentication> getWebAuthentication(final CreateForm form) {
377         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
378         final String username = systemHelper.getUsername();
379         final long currentTime = systemHelper.getCurrentTimeAsLong();
380         return getEntity(form, username, currentTime).map(entity -> {
381             entity.setUpdatedBy(username);
382             entity.setUpdatedTime(currentTime);
383             copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
384             return entity;
385         });
386     }
387 
388     /**
389      * Registers available protocol scheme items for web authentication forms.
390      * Includes Basic, Digest, NTLM, and Form authentication schemes.
391      *
392      * @param data the render data to register the protocol scheme items with
393      */
394     protected void registerProtocolSchemeItems(final RenderData data) {
395         final List<Map<String, String>> itemList = new ArrayList<>();
396         final Locale locale = ComponentUtil.getRequestManager().getUserLocale();
397         itemList.add(createItem(ComponentUtil.getMessageManager().getMessage(locale, "labels.webauth_scheme_basic"), Constants.BASIC));
398         itemList.add(createItem(ComponentUtil.getMessageManager().getMessage(locale, "labels.webauth_scheme_digest"), Constants.DIGEST));
399         itemList.add(createItem(ComponentUtil.getMessageManager().getMessage(locale, "labels.webauth_scheme_ntlm"), Constants.NTLM));
400         itemList.add(createItem(ComponentUtil.getMessageManager().getMessage(locale, "labels.webauth_scheme_form"), Constants.FORM));
401         RenderDataUtil.register(data, "protocolSchemeItems", itemList);
402     }
403 
404     /**
405      * Registers available web configuration items for use in web authentication forms.
406      * Retrieves all web configurations and creates form items from them.
407      *
408      * @param data the render data to register the web configuration items with
409      */
410     protected void registerWebConfigItems(final RenderData data) {
411         final List<Map<String, String>> itemList = new ArrayList<>();
412         final List<WebConfig> webConfigList = crawlingConfigHelper.getAllWebConfigList(false, false, false, null);
413         for (final WebConfig webConfig : webConfigList) {
414             itemList.add(createItem(webConfig.getName(), webConfig.getId().toString()));
415         }
416         RenderDataUtil.register(data, "webConfigItems", itemList);
417     }
418 
419     /**
420      * Creates a map item with label and value for use in dropdown lists and form options.
421      *
422      * @param label the display label for the item
423      * @param value the value associated with the item
424      * @return a map containing the label and value
425      */
426     protected Map<String, String> createItem(final String label, final String value) {
427         final Map<String, String> map = new HashMap<>(2);
428         map.put(Constants.ITEM_LABEL, label);
429         map.put(Constants.ITEM_VALUE, value);
430         return map;
431     }
432 
433     // ===================================================================================
434     //                                                                        Small Helper
435     //                                                                        ============
436     //                                                                              JSP
437     //                                                                           =========
438 
439     private HtmlResponse asListHtml() {
440         return asHtml(path_AdminWebauth_AdminWebauthJsp).renderWith(data -> {
441             RenderDataUtil.register(data, "webAuthenticationItems", webAuthenticationService.getWebAuthenticationList(webAuthPager)); // page navi
442             RenderDataUtil.register(data, "displayCreateLink",
443                     !crawlingConfigHelper.getAllWebConfigList(false, false, false, null).isEmpty());
444         }).useForm(SearchForm.class, setup -> {
445             setup.setup(form -> {
446                 copyBeanToBean(webAuthPager, form, op -> op.include("id"));
447             });
448         });
449     }
450 
451     private HtmlResponse asEditHtml() {
452         return asHtml(path_AdminWebauth_AdminWebauthEditJsp).renderWith(data -> {
453             registerProtocolSchemeItems(data);
454             registerWebConfigItems(data);
455         });
456     }
457 
458     private HtmlResponse asDetailsHtml() {
459         return asHtml(path_AdminWebauth_AdminWebauthDetailsJsp).renderWith(data -> {
460             registerProtocolSchemeItems(data);
461             registerWebConfigItems(data);
462         });
463     }
464 }