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

This commit is contained in:
Shinsuke Sugaya 2017-03-24 06:49:11 +09:00
parent bcffa82995
commit 8031b37d99
11 changed files with 336 additions and 27 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -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 ApiConfigResponse extends ApiResponse {
protected Object config;
public static class ApiUpdateResponse extends ApiResponse {
protected String id;
protected boolean created;
public ApiConfigResponse config(Object config) {
this.config = config;
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 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 ApiConfigsResponse<T> total(long total) {
this.total = total;
return this;
}

View file

@ -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();
}
}

View file

@ -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 {
}

View file

@ -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 {
}

View file

@ -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;
}

View file

@ -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

View file

@ -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
// ========

View file

@ -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();
}
}