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.api.admin.accesstoken;
17  
18  import static org.codelibs.core.stream.StreamUtil.stream;
19  import static org.codelibs.fess.app.web.admin.accesstoken.AdminAccesstokenAction.getAccessToken;
20  
21  import java.util.List;
22  import java.util.stream.Collectors;
23  
24  import org.apache.logging.log4j.LogManager;
25  import org.apache.logging.log4j.Logger;
26  import org.codelibs.core.lang.StringUtil;
27  import org.codelibs.fess.Constants;
28  import org.codelibs.fess.app.pager.AccessTokenPager;
29  import org.codelibs.fess.app.web.CrudMode;
30  import org.codelibs.fess.app.web.admin.accesstoken.AdminAccesstokenAction;
31  import org.codelibs.fess.app.web.api.ApiResult;
32  import org.codelibs.fess.app.web.api.ApiResult.ApiConfigResponse;
33  import org.codelibs.fess.app.web.api.ApiResult.ApiConfigsResponse;
34  import org.codelibs.fess.app.web.api.ApiResult.ApiResponse;
35  import org.codelibs.fess.app.web.api.ApiResult.ApiUpdateResponse;
36  import org.codelibs.fess.app.web.api.ApiResult.Status;
37  import org.codelibs.fess.app.web.api.admin.FessApiAdminAction;
38  import org.codelibs.fess.helper.PermissionHelper;
39  import org.codelibs.fess.opensearch.config.exentity.AccessToken;
40  import org.codelibs.fess.util.ComponentUtil;
41  import org.lastaflute.web.Execute;
42  import org.lastaflute.web.response.JsonResponse;
43  
44  /**
45   * API action for admin access token.
46   *
47   */
48  public class ApiAdminAccesstokenAction extends FessApiAdminAction {
49  
50      /**
51       * Default constructor.
52       */
53      public ApiAdminAccesstokenAction() {
54          super();
55      }
56  
57      private static final Logger logger = LogManager.getLogger(ApiAdminAccesstokenAction.class);
58  
59      // ===================================================================================
60      //                                                                           Attribute
61      //                                                                           =========
62  
63      // ===================================================================================
64      //                                                                      Search Execute
65      //                                                                      ==============
66  
67      // GET /api/admin/accesstoken
68      // PUT /api/admin/accesstoken
69      /**
70       * Retrieves a list of access token settings.
71       *
72       * @param body the search body containing filter criteria
73       * @return JSON response with access token list
74       */
75      @Execute
76      public JsonResponse<ApiResult> settings(final SearchBody body) {
77          validateApi(body, messages -> {});
78          final AccessTokenPager pager = copyBeanToNewBean(body, AccessTokenPager.class);
79          final List<AccessToken> list = accessTokenService.getAccessTokenList(pager);
80          return asJson(new ApiConfigsResponse<EditBody>().settings(list.stream().map(this::createEditBody).collect(Collectors.toList()))
81                  .total(pager.getAllRecordCount())
82                  .status(Status.OK)
83                  .result());
84      }
85  
86      // GET /api/admin/accesstoken/setting/{id}
87      /**
88       * Retrieves a specific access token setting by ID.
89       *
90       * @param id the access token ID to retrieve
91       * @return JSON response with the access token setting
92       */
93      @Execute
94      public JsonResponse<ApiResult> get$setting(final String id) {
95          return asJson(new ApiConfigResponse().setting(accessTokenService.getAccessToken(id).map(this::createEditBody).orElseGet(() -> {
96              throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id));
97              return null;
98          })).status(Status.OK).result());
99      }
100 
101     // POST /api/admin/accesstoken/setting
102     /**
103      * Creates a new access token setting.
104      *
105      * @param body the create body containing access token data
106      * @return JSON response with the created access token ID
107      */
108     @Execute
109     public JsonResponse<ApiResult> post$setting(final CreateBody body) {
110         validateApi(body, messages -> {});
111         body.crudMode = CrudMode.CREATE;
112         final AccessToken accessToken = getAccessToken(body).map(entity -> {
113             entity.setToken(accessTokenHelper.generateAccessToken());
114             try {
115                 accessTokenService.store(entity);
116             } catch (final Exception e) {
117                 logger.warn("Failed to process a request.", e);
118                 throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)));
119             }
120             return entity;
121         }).orElseGet(() -> {
122             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL));
123             return null;
124         });
125         return asJson(new ApiUpdateResponse().id(accessToken.getId()).created(true).status(Status.OK).result());
126     }
127 
128     // PUT /api/admin/accesstoken/setting
129     /**
130      * Updates an existing access token setting.
131      *
132      * @param body the edit body containing updated access token data
133      * @return JSON response with the updated access token ID
134      */
135     @Execute
136     public JsonResponse<ApiResult> put$setting(final EditBody body) {
137         validateApi(body, messages -> {});
138         body.crudMode = CrudMode.EDIT;
139         final AccessToken accessToken = getAccessToken(body).map(entity -> {
140             try {
141                 accessTokenService.store(entity);
142             } catch (final Exception e) {
143                 logger.warn("Failed to process a request.", e);
144                 throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)));
145             }
146             return entity;
147         }).orElseGet(() -> {
148             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, body.id));
149             return null;
150         });
151         return asJson(new ApiUpdateResponse().id(accessToken.getId()).created(false).status(Status.OK).result());
152     }
153 
154     // DELETE /api/admin/accesstoken/setting/{id}
155     /**
156      * Deletes an access token setting by ID.
157      *
158      * @param id the access token ID to delete
159      * @return JSON response confirming deletion
160      */
161     @Execute
162     public JsonResponse<ApiResult> delete$setting(final String id) {
163         accessTokenService.getAccessToken(id).ifPresent(entity -> {
164             try {
165                 accessTokenService.delete(entity);
166                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
167             } catch (final Exception e) {
168                 logger.warn("Failed to process a request.", e);
169                 throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)));
170             }
171         }).orElse(() -> {
172             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id));
173         });
174         return asJson(new ApiResponse().status(Status.OK).result());
175     }
176 
177     /**
178      * Creates an EditBody from an AccessToken entity for API responses.
179      * Converts permissions and handles date formatting.
180      *
181      * @param entity the AccessToken entity to convert
182      * @return the EditBody representation of the entity
183      */
184     protected EditBody createEditBody(final AccessToken entity) {
185         final EditBody body = new EditBody();
186         copyBeanToBean(entity, body,
187                 copyOp -> copyOp.exclude(Constants.PERMISSIONS, AdminAccesstokenAction.EXPIRED_TIME)
188                         .excludeNull()
189                         .dateConverter(Constants.DEFAULT_DATETIME_FORMAT, AdminAccesstokenAction.EXPIRES));
190         final PermissionHelper permissionHelper = ComponentUtil.getPermissionHelper();
191         body.permissions = stream(entity.getPermissions()).get(
192                 stream -> stream.map(permissionHelper::decode).filter(StringUtil::isNotBlank).distinct().collect(Collectors.joining("\n")));
193         body.crudMode = null;
194         return body;
195 
196     }
197 }