Pārlūkot izejas kodu

fix #944 Admin API: /api/admin/accesstoken

Shinsuke Sugaya 8 gadi atpakaļ
vecāks
revīzija
8031b37d99

+ 1 - 0
.gitignore

@@ -25,3 +25,4 @@
 /tomcat.8080/
 dbflute_fess/output/doc/lastadoc-fess.html
 dbflute_fess/schema/project-lastadoc-fess.json
+src/main/resources/fess_indices/.fess_config/access_token.bulk

+ 1 - 1
pom.xml

@@ -40,7 +40,7 @@
 
 		<!-- Main Framework -->
 		<dbflute.version>1.1.2</dbflute.version>
-		<lastaflute.version>0.9.2-RC1</lastaflute.version>
+		<lastaflute.version>0.9.2-RC2</lastaflute.version>
 		<lasta.taglib.version>0.7.1</lasta.taglib.version>
 		<lasta.job.version>0.4.0</lasta.job.version>
 		<mailflute.version>0.5.2</mailflute.version>

+ 3 - 3
src/main/java/org/codelibs/fess/app/web/admin/accesstoken/AdminAccesstokenAction.java

@@ -44,11 +44,11 @@ import org.lastaflute.web.ruts.process.ActionRuntime;
  */
 public class AdminAccesstokenAction extends FessAdminAction {
 
-    private static final String TOKEN = "token";
+    public static final String TOKEN = "token";
 
-    private static final String EXPIRES = "expires";
+    public static final String EXPIRES = "expires";
 
-    private static final String EXPIRED_TIME = "expiredTime";
+    public static final String EXPIRED_TIME = "expiredTime";
 
     // ===================================================================================
     //                                                                           Attribute

+ 42 - 3
src/main/java/org/codelibs/fess/app/web/api/ApiResult.java

@@ -15,6 +15,7 @@
  */
 package org.codelibs.fess.app.web.api;
 
+import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
 
@@ -59,11 +60,49 @@ public class ApiResult {
         }
     }
 
+    public static class ApiUpdateResponse extends ApiResponse {
+        protected String id;
+        protected boolean created;
+
+        public ApiUpdateResponse id(String id) {
+            this.id = id;
+            return this;
+        }
+
+        public ApiUpdateResponse created(boolean created) {
+            this.created = created;
+            return this;
+        }
+
+        public ApiResult result() {
+            return new ApiResult(this);
+        }
+    }
+
     public static class ApiConfigResponse extends ApiResponse {
-        protected Object config;
+        protected Object setting;
+
+        public ApiConfigResponse setting(Object setting) {
+            this.setting = setting;
+            return this;
+        }
+
+        public ApiResult result() {
+            return new ApiResult(this);
+        }
+    }
+
+    public static class ApiConfigsResponse<T> extends ApiResponse {
+        protected List<T> settings;
+        protected long total = 0;
+
+        public ApiConfigsResponse<T> settings(List<T> settings) {
+            this.settings = settings;
+            return this;
+        }
 
-        public ApiConfigResponse config(Object config) {
-            this.config = config;
+        public ApiConfigsResponse<T> total(long total) {
+            this.total = total;
             return this;
         }
 

+ 195 - 0
src/main/java/org/codelibs/fess/app/web/api/admin/accesstoken/ApiAdminAccesstokenAction.java

@@ -0,0 +1,195 @@
+/*
+ * Copyright 2012-2017 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.app.web.api.admin.accesstoken;
+
+import static org.codelibs.core.stream.StreamUtil.split;
+import static org.codelibs.core.stream.StreamUtil.stream;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.app.pager.AccessTokenPager;
+import org.codelibs.fess.app.service.AccessTokenService;
+import org.codelibs.fess.app.web.CrudMode;
+import org.codelibs.fess.app.web.admin.accesstoken.AdminAccesstokenAction;
+import org.codelibs.fess.app.web.admin.accesstoken.CreateForm;
+import org.codelibs.fess.app.web.admin.accesstoken.EditForm;
+import org.codelibs.fess.app.web.api.ApiResult;
+import org.codelibs.fess.app.web.api.ApiResult.ApiConfigResponse;
+import org.codelibs.fess.app.web.api.ApiResult.ApiConfigsResponse;
+import org.codelibs.fess.app.web.api.ApiResult.ApiResponse;
+import org.codelibs.fess.app.web.api.ApiResult.ApiUpdateResponse;
+import org.codelibs.fess.app.web.api.ApiResult.Status;
+import org.codelibs.fess.app.web.api.admin.FessApiAdminAction;
+import org.codelibs.fess.es.config.exentity.AccessToken;
+import org.codelibs.fess.helper.PermissionHelper;
+import org.codelibs.fess.util.ComponentUtil;
+import org.dbflute.optional.OptionalEntity;
+import org.lastaflute.web.Execute;
+import org.lastaflute.web.response.JsonResponse;
+
+/**
+ * @author shinsuke
+ */
+public class ApiAdminAccesstokenAction extends FessApiAdminAction {
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    private AccessTokenService accessTokenService;
+
+    // ===================================================================================
+    //                                                                      Search Execute
+    //                                                                      ==============
+
+    // GET /api/admin/accesstoken
+    // POST /api/admin/accesstoken
+    @Execute
+    public JsonResponse<ApiResult> settings(SearchBody body) {
+        validateApi(body, messages -> {});
+        final AccessTokenPager pager = new AccessTokenPager();
+        pager.setPageSize(body.size);
+        pager.setCurrentPageNumber(body.page);
+        final List<AccessToken> list = accessTokenService.getAccessTokenList(pager);
+        return asJson(new ApiConfigsResponse<EditBody>()
+                .settings(list.stream().map(entity -> createEditBody(entity)).collect(Collectors.toList()))
+                .total(pager.getAllRecordCount()).status(Status.OK).result());
+    }
+
+    // GET /api/admin/accesstoken/setting/{id}
+    @Execute
+    public JsonResponse<ApiResult> get$setting(String id) {
+        return asJson(new ApiConfigResponse()
+                .setting(accessTokenService.getAccessToken(id).map(entity -> createEditBody(entity)).orElseGet(() -> {
+                    throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id));
+                    return null;
+                })).status(Status.OK).result());
+    }
+
+    // PUT /api/admin/accesstoken/setting
+    @Execute
+    public JsonResponse<ApiResult> put$setting(CreateBody body) {
+        validateApi(body, messages -> {});
+        body.crudMode = CrudMode.CREATE;
+        final AccessToken accessToken = getAccessToken(body).map(entity -> {
+            entity.setToken(systemHelper.generateAccessToken());
+            try {
+                accessTokenService.store(entity);
+            } catch (final Exception e) {
+                throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)));
+            }
+            return entity;
+        }).orElseGet(() -> {
+            throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL));
+            return null;
+        });
+        return asJson(new ApiUpdateResponse().id(accessToken.getId()).created(true).status(Status.OK).result());
+    }
+
+    // POST /api/admin/accesstoken/setting
+    @Execute
+    public JsonResponse<ApiResult> post$setting(final EditBody body) {
+        validateApi(body, messages -> {});
+        body.crudMode = CrudMode.EDIT;
+        final AccessToken accessToken = getAccessToken(body).map(entity -> {
+            try {
+                accessTokenService.store(entity);
+            } catch (final Exception e) {
+                throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)));
+            }
+            return entity;
+        }).orElseGet(() -> {
+            throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, body.id));
+            return null;
+        });
+        return asJson(new ApiUpdateResponse().id(accessToken.getId()).created(false).status(Status.OK).result());
+    }
+
+    // DELETE /api/admin/accesstoken/setting/{id}
+    @Execute
+    public JsonResponse<ApiResult> delete$setting(String id) {
+        accessTokenService.getAccessToken(id).ifPresent(entity -> {
+            try {
+                accessTokenService.delete(entity);
+                saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            } catch (final Exception e) {
+                throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)));
+            }
+        }).orElse(() -> {
+            throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id));
+        });
+        return asJson(new ApiResponse().status(Status.OK).result());
+    }
+
+    protected EditBody createEditBody(final AccessToken entity) {
+        final EditBody form = new EditBody();
+        copyBeanToBean(entity, form, copyOp -> copyOp.exclude(Constants.PERMISSIONS, AdminAccesstokenAction.EXPIRED_TIME).excludeNull()
+                .dateConverter(Constants.DEFAULT_DATETIME_FORMAT, AdminAccesstokenAction.EXPIRES));
+        final PermissionHelper permissionHelper = ComponentUtil.getPermissionHelper();
+        form.permissions =
+                stream(entity.getPermissions()).get(
+                        stream -> stream.map(permissionHelper::decode).filter(StringUtil::isNotBlank).distinct()
+                                .collect(Collectors.joining("\n")));
+        form.crudMode = null;
+        return form;
+
+    }
+
+    protected OptionalEntity<AccessToken> getAccessToken(final CreateForm body) {
+        final String username = systemHelper.getUsername();
+        final long currentTime = systemHelper.getCurrentTimeAsLong();
+        return getEntity(body, username, currentTime).map(
+                entity -> {
+                    entity.setUpdatedBy(username);
+                    entity.setUpdatedTime(currentTime);
+                    copyBeanToBean(
+                            body,
+                            entity,
+                            op -> op.exclude(Constants.COMMON_CONVERSION_RULE)
+                                    .exclude(AdminAccesstokenAction.TOKEN, Constants.PERMISSIONS, AdminAccesstokenAction.EXPIRED_TIME)
+                                    .dateConverter(Constants.DEFAULT_DATETIME_FORMAT, AdminAccesstokenAction.EXPIRES));
+                    final PermissionHelper permissionHelper = ComponentUtil.getPermissionHelper();
+                    entity.setPermissions(split(body.permissions, "\n").get(
+                            stream -> stream.map(s -> permissionHelper.encode(s)).filter(StringUtil::isNotBlank).distinct()
+                                    .toArray(n -> new String[n])));
+                    return entity;
+                });
+    }
+
+    private OptionalEntity<AccessToken> getEntity(final CreateForm form, final String username, final long currentTime) {
+        switch (form.crudMode) {
+        case CrudMode.CREATE:
+            return OptionalEntity.of(new AccessToken()).map(entity -> {
+                entity.setCreatedBy(username);
+                entity.setCreatedTime(currentTime);
+                return entity;
+            });
+        case CrudMode.EDIT:
+            if (form instanceof EditForm) {
+                return accessTokenService.getAccessToken(((EditForm) form).id);
+            }
+            break;
+        default:
+            break;
+        }
+        return OptionalEntity.empty();
+    }
+}

+ 22 - 0
src/main/java/org/codelibs/fess/app/web/api/admin/accesstoken/CreateBody.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012-2017 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.app.web.api.admin.accesstoken;
+
+import org.codelibs.fess.app.web.admin.accesstoken.CreateForm;
+
+public class CreateBody extends CreateForm {
+
+}

+ 22 - 0
src/main/java/org/codelibs/fess/app/web/api/admin/accesstoken/EditBody.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012-2017 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.app.web.api.admin.accesstoken;
+
+import org.codelibs.fess.app.web.admin.accesstoken.EditForm;
+
+public class EditBody extends EditForm {
+
+}

+ 30 - 0
src/main/java/org/codelibs/fess/app/web/api/admin/accesstoken/SearchBody.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012-2017 CodeLibs Project and the Others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.codelibs.fess.app.web.api.admin.accesstoken;
+
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.util.ComponentUtil;
+import org.lastaflute.web.validation.Required;
+
+public class SearchBody {
+
+    @Required
+    public Integer size = ComponentUtil.getFessConfig().getPagingPageSizeAsInteger();
+
+    @Required
+    public Integer page = Constants.DEFAULT_ADMIN_PAGE_NUMBER;
+
+}

+ 1 - 1
src/main/java/org/codelibs/fess/app/web/api/admin/general/ApiAdminGeneralAction.java

@@ -47,7 +47,7 @@ public class ApiAdminGeneralAction extends FessApiAdminAction {
         final EditBody form = new EditBody();
         AdminGeneralAction.updateForm(fessConfig, form);
         form.ldapAdminSecurityCredentials = null;
-        return asJson(new ApiConfigResponse().config(form).status(Status.OK).result());
+        return asJson(new ApiConfigResponse().setting(form).status(Status.OK).result());
     }
 
     // POST /api/admin/general

+ 0 - 18
src/main/java/org/codelibs/fess/app/web/base/FessAdminAction.java

@@ -15,12 +15,8 @@
  */
 package org.codelibs.fess.app.web.base;
 
-import java.util.function.Consumer;
-
 import javax.servlet.ServletContext;
 
-import org.codelibs.core.beans.util.BeanUtil;
-import org.codelibs.core.beans.util.CopyOptions;
 import org.codelibs.fess.exception.UserRoleLoginException;
 import org.dbflute.optional.OptionalThing;
 import org.lastaflute.di.util.LdiFileUtil;
@@ -49,24 +45,10 @@ public abstract class FessAdminAction extends FessBaseAction {
         LdiFileUtil.write(path, data);
     }
 
-    protected void copyBeanToBean(final Object src, final Object dest, final Consumer<CopyOptions> option) {
-        BeanUtil.copyBeanToBean(src, dest, option);
-    }
-
     protected ServletContext getServletContext() {
         return LaServletContextUtil.getServletContext();
     }
 
-    protected String buildThrowableMessage(final Throwable t) {
-        final StringBuilder buf = new StringBuilder(100);
-        Throwable current = t;
-        while (current != null) {
-            buf.append(current.getLocalizedMessage()).append(' ');
-            current = current.getCause();
-        }
-        return buf.toString();
-    }
-
     // ===================================================================================
     //                                                                            Document
     //                                                                            ========

+ 18 - 0
src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java

@@ -15,8 +15,12 @@
  */
 package org.codelibs.fess.app.web.base;
 
+import java.util.function.Consumer;
+
 import javax.annotation.Resource;
 
+import org.codelibs.core.beans.util.BeanUtil;
+import org.codelibs.core.beans.util.CopyOptions;
 import org.codelibs.fess.app.web.base.login.FessLoginAssist;
 import org.codelibs.fess.helper.ActivityHelper;
 import org.codelibs.fess.helper.SystemHelper;
@@ -184,4 +188,18 @@ public abstract class FessBaseAction extends TypicalAction // has several interf
         validationMessagesLambda.message(messages);
         sessionManager.errors().saveMessages(messages);
     }
+
+    protected void copyBeanToBean(final Object src, final Object dest, final Consumer<CopyOptions> option) {
+        BeanUtil.copyBeanToBean(src, dest, option);
+    }
+
+    protected String buildThrowableMessage(final Throwable t) {
+        final StringBuilder buf = new StringBuilder(100);
+        Throwable current = t;
+        while (current != null) {
+            buf.append(current.getLocalizedMessage()).append(' ');
+            current = current.getCause();
+        }
+        return buf.toString();
+    }
 }