Bladeren bron

#738 add expiredTime

Shinsuke Sugaya 8 jaren geleden
bovenliggende
commit
048cc7bfab
20 gewijzigde bestanden met toevoegingen van 441 en 74 verwijderingen
  1. 3 0
      src/main/config/es/fess_config.json
  2. 6 0
      src/main/java/org/codelibs/fess/api/gsa/GsaApiManager.java
  3. 9 0
      src/main/java/org/codelibs/fess/api/json/JsonApiManager.java
  4. 6 0
      src/main/java/org/codelibs/fess/api/suggest/SuggestApiManager.java
  5. 99 15
      src/main/java/org/codelibs/fess/app/web/admin/accesstoken/AdminAccesstokenAction.java
  6. 11 1
      src/main/java/org/codelibs/fess/app/web/admin/accesstoken/CreateForm.java
  7. 1 0
      src/main/java/org/codelibs/fess/es/config/bsbhv/BsAccessTokenBhv.java
  8. 17 0
      src/main/java/org/codelibs/fess/es/config/bsentity/BsAccessToken.java
  9. 9 0
      src/main/java/org/codelibs/fess/es/config/bsentity/dbmeta/AccessTokenDbm.java
  10. 4 0
      src/main/java/org/codelibs/fess/es/config/cbean/bs/BsAccessTokenCB.java
  11. 174 0
      src/main/java/org/codelibs/fess/es/config/cbean/cq/bs/BsAccessTokenCQ.java
  12. 12 0
      src/main/java/org/codelibs/fess/es/config/exentity/AccessToken.java
  13. 18 7
      src/main/java/org/codelibs/fess/helper/RoleQueryHelper.java
  14. 9 0
      src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java
  15. 3 0
      src/main/resources/fess_indices/.fess_config/access_token.json
  16. 3 0
      src/main/resources/fess_label.properties
  17. 3 0
      src/main/resources/fess_label_en.properties
  18. 3 0
      src/main/resources/fess_label_ja.properties
  19. 17 51
      src/main/webapp/WEB-INF/view/admin/accesstoken/admin_accesstoken_details.jsp
  20. 34 0
      src/main/webapp/WEB-INF/view/admin/accesstoken/admin_accesstoken_edit.jsp

+ 3 - 0
src/main/config/es/fess_config.json

@@ -22,6 +22,9 @@
             "type": "string",
             "index": "not_analyzed"
           },
+          "expiredTime" : {
+            "type" : "long"
+          },
           "createdBy": {
             "type": "string",
             "index": "not_analyzed"

+ 6 - 0
src/main/java/org/codelibs/fess/api/gsa/GsaApiManager.java

@@ -49,6 +49,7 @@ import org.codelibs.fess.entity.FacetInfo;
 import org.codelibs.fess.entity.GeoInfo;
 import org.codelibs.fess.entity.SearchRenderData;
 import org.codelibs.fess.entity.SearchRequestParams;
+import org.codelibs.fess.exception.InvalidAccessTokenException;
 import org.codelibs.fess.mylasta.direction.FessConfig;
 import org.codelibs.fess.util.ComponentUtil;
 import org.dbflute.optional.OptionalThing;
@@ -264,6 +265,11 @@ public class GsaApiManager extends BaseApiManager implements WebApiManager {
             if (logger.isDebugEnabled()) {
                 logger.debug("Failed to process a search request.", e);
             }
+            if (e instanceof InvalidAccessTokenException) {
+                final InvalidAccessTokenException iate = (InvalidAccessTokenException) e;
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                response.setHeader("WWW-Authenticate", "Bearer error=\"" + iate.getType() + "\"");
+            }
         }
 
         writeXmlResponse(status, xmlDtd, buf.toString(), errMsg);

+ 9 - 0
src/main/java/org/codelibs/fess/api/json/JsonApiManager.java

@@ -45,6 +45,7 @@ import org.codelibs.fess.entity.SearchRenderData;
 import org.codelibs.fess.entity.SearchRequestParams;
 import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
 import org.codelibs.fess.es.client.FessEsClient;
+import org.codelibs.fess.exception.InvalidAccessTokenException;
 import org.codelibs.fess.exception.WebApiException;
 import org.codelibs.fess.helper.LabelTypeHelper;
 import org.codelibs.fess.helper.PopularWordHelper;
@@ -62,6 +63,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.script.Script;
 import org.lastaflute.web.util.LaRequestUtil;
+import org.lastaflute.web.util.LaResponseUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -538,6 +540,13 @@ public class JsonApiManager extends BaseApiManager {
             return;
         }
 
+        if (t instanceof InvalidAccessTokenException) {
+            final InvalidAccessTokenException e = (InvalidAccessTokenException) t;
+            final HttpServletResponse response = LaResponseUtil.getResponse();
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            response.setHeader("WWW-Authenticate", "Bearer error=\"" + e.getType() + "\"");
+        }
+
         final StringBuilder sb = new StringBuilder();
         if (StringUtil.isBlank(t.getMessage())) {
             sb.append(t.getClass().getName());

+ 6 - 0
src/main/java/org/codelibs/fess/api/suggest/SuggestApiManager.java

@@ -35,6 +35,7 @@ import org.codelibs.fess.entity.FacetInfo;
 import org.codelibs.fess.entity.GeoInfo;
 import org.codelibs.fess.entity.SearchRequestParams;
 import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
+import org.codelibs.fess.exception.InvalidAccessTokenException;
 import org.codelibs.fess.helper.RoleQueryHelper;
 import org.codelibs.fess.helper.SuggestHelper;
 import org.codelibs.fess.suggest.entity.SuggestItem;
@@ -148,6 +149,11 @@ public class SuggestApiManager extends BaseApiManager {
             if (logger.isDebugEnabled()) {
                 logger.debug("Failed to process a suggest request.", e);
             }
+            if (e instanceof InvalidAccessTokenException) {
+                final InvalidAccessTokenException iate = (InvalidAccessTokenException) e;
+                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                response.setHeader("WWW-Authenticate", "Bearer error=\"" + iate.getType() + "\"");
+            }
         }
 
         JsonApiManager.writeJsonResponse(status, buf.toString(), errMsg);

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

@@ -15,14 +15,22 @@
  */
 package org.codelibs.fess.app.web.admin.accesstoken;
 
+import static org.codelibs.core.stream.StreamUtil.split;
+import static org.codelibs.core.stream.StreamUtil.stream;
+
+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.base.FessAdminAction;
 import org.codelibs.fess.es.config.exentity.AccessToken;
+import org.codelibs.fess.helper.PermissionHelper;
+import org.codelibs.fess.util.ComponentUtil;
 import org.codelibs.fess.util.RenderDataUtil;
 import org.dbflute.optional.OptionalEntity;
 import org.dbflute.optional.OptionalThing;
@@ -36,6 +44,12 @@ import org.lastaflute.web.ruts.process.ActionRuntime;
  */
 public class AdminAccesstokenAction extends FessAdminAction {
 
+    private static final String TOKEN = "token";
+
+    private static final String EXPIRES = "expires";
+
+    private static final String EXPIRED_TIME = "expiredTime";
+
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
@@ -120,18 +134,61 @@ public class AdminAccesstokenAction extends FessAdminAction {
     public HtmlResponse details(final int crudMode, final String id) {
         verifyCrudMode(crudMode, CrudMode.DETAILS);
         saveToken();
-        return asDetailsHtml().useForm(EditForm.class, op -> {
-            op.setup(form -> {
-                accessTokenService.getAccessToken(id).ifPresent(entity -> {
-                    copyBeanToBean(entity, form, copyOp -> {
-                        copyOp.excludeNull();
+        return asDetailsHtml().useForm(
+                EditForm.class,
+                op -> {
+                    op.setup(form -> {
+                        accessTokenService
+                                .getAccessToken(id)
+                                .ifPresent(
+                                        entity -> {
+                                            copyBeanToBean(entity, form, copyOp -> copyOp.exclude(Constants.PERMISSIONS, EXPIRED_TIME)
+                                                    .excludeNull().dateConverter(Constants.DEFAULT_DATETIME_FORMAT, 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 = crudMode;
+                                        })
+                                .orElse(() -> {
+                                    throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id),
+                                            () -> asListHtml());
+                                });
                     });
-                    form.crudMode = crudMode;
-                }).orElse(() -> {
+                });
+    }
+
+    @Execute
+    public HtmlResponse edit(final EditForm form) {
+        validate(form, messages -> {}, () -> asListHtml());
+        final String id = form.id;
+        accessTokenService
+                .getAccessToken(id)
+                .ifPresent(
+                        entity -> {
+                            copyBeanToBean(
+                                    entity,
+                                    form,
+                                    op -> op.exclude(Constants.PERMISSIONS, EXPIRED_TIME).dateConverter(Constants.DEFAULT_DATETIME_FORMAT,
+                                            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")));
+                        }).orElse(() -> {
                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asListHtml());
                 });
-            });
-        });
+        saveToken();
+        if (form.crudMode.intValue() == CrudMode.EDIT) {
+            // back
+            form.crudMode = CrudMode.DETAILS;
+            return asDetailsHtml();
+        } else {
+            form.crudMode = CrudMode.EDIT;
+            return asEditHtml();
+        }
     }
 
     // -----------------------------------------------------
@@ -158,6 +215,26 @@ public class AdminAccesstokenAction extends FessAdminAction {
         return redirect(getClass());
     }
 
+    @Execute
+    public HtmlResponse update(final EditForm form) {
+        verifyCrudMode(form.crudMode, CrudMode.EDIT);
+        validate(form, messages -> {}, () -> asEditHtml());
+        verifyToken(() -> asEditHtml());
+        getAccessToken(form).ifPresent(
+                entity -> {
+                    try {
+                        accessTokenService.store(entity);
+                        saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+                    } catch (final Exception e) {
+                        throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
+                                () -> asEditHtml());
+                    }
+                }).orElse(() -> {
+            throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
+        });
+        return redirect(getClass());
+    }
+
     @Execute
     public HtmlResponse delete(final EditForm form) {
         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
@@ -208,12 +285,19 @@ public class AdminAccesstokenAction extends FessAdminAction {
     protected OptionalEntity<AccessToken> getAccessToken(final CreateForm form) {
         final String username = systemHelper.getUsername();
         final long currentTime = systemHelper.getCurrentTimeAsLong();
-        return getEntity(form, username, currentTime).map(entity -> {
-            entity.setUpdatedBy(username);
-            entity.setUpdatedTime(currentTime);
-            copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
-            return entity;
-        });
+        return getEntity(form, username, currentTime).map(
+                entity -> {
+                    entity.setUpdatedBy(username);
+                    entity.setUpdatedTime(currentTime);
+                    copyBeanToBean(form, entity,
+                            op -> op.exclude(Constants.COMMON_CONVERSION_RULE).exclude(TOKEN, Constants.PERMISSIONS, EXPIRED_TIME)
+                                    .dateConverter(Constants.DEFAULT_DATETIME_FORMAT, EXPIRES));
+                    final PermissionHelper permissionHelper = ComponentUtil.getPermissionHelper();
+                    entity.setPermissions(split(form.permissions, "\n").get(
+                            stream -> stream.map(s -> permissionHelper.encode(s)).filter(StringUtil::isNotBlank).distinct()
+                                    .toArray(n -> new String[n])));
+                    return entity;
+                });
     }
 
     // ===================================================================================

+ 11 - 1
src/main/java/org/codelibs/fess/app/web/admin/accesstoken/CreateForm.java

@@ -15,6 +15,7 @@
  */
 package org.codelibs.fess.app.web.admin.accesstoken;
 
+import javax.validation.constraints.Pattern;
 import javax.validation.constraints.Size;
 
 import org.codelibs.fess.app.web.CrudMode;
@@ -28,12 +29,21 @@ public class CreateForm {
     public Integer crudMode;
 
     @Required
-    @Size(max = 10000)
+    @Size(max = 1000)
     public String name;
 
     @Size(max = 10000)
     public String token;
 
+    @Size(max = 4000)
+    public String permissions;
+
+    @Size(max = 10000)
+    public String parameterName;
+
+    @Pattern(regexp = "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]")
+    public String expires;
+
     @Required
     @Size(max = 1000)
     public String createdBy;

+ 1 - 0
src/main/java/org/codelibs/fess/es/config/bsbhv/BsAccessTokenBhv.java

@@ -77,6 +77,7 @@ public abstract class BsAccessTokenBhv extends EsAbstractBehavior<AccessToken, A
             result.setToken(DfTypeUtil.toString(source.get("token")));
             result.setPermissions(toStringArray(source.get("permissions")));
             result.setParameterName(DfTypeUtil.toString(source.get("parameter_name")));
+            result.setExpiredTime(DfTypeUtil.toLong(source.get("expiredTime")));
             result.setCreatedBy(DfTypeUtil.toString(source.get("createdBy")));
             result.setCreatedTime(DfTypeUtil.toLong(source.get("createdTime")));
             result.setUpdatedBy(DfTypeUtil.toString(source.get("updatedBy")));

+ 17 - 0
src/main/java/org/codelibs/fess/es/config/bsentity/BsAccessToken.java

@@ -49,6 +49,9 @@ public class BsAccessToken extends EsAbstractEntity {
     /** parameter_name */
     protected String parameterName;
 
+    /** expiredTime */
+    protected Long expiredTime;
+
     /** createdBy */
     protected String createdBy;
 
@@ -94,6 +97,9 @@ public class BsAccessToken extends EsAbstractEntity {
         if (parameterName != null) {
             sourceMap.put("parameter_name", parameterName);
         }
+        if (expiredTime != null) {
+            sourceMap.put("expiredTime", expiredTime);
+        }
         if (createdBy != null) {
             sourceMap.put("createdBy", createdBy);
         }
@@ -119,6 +125,7 @@ public class BsAccessToken extends EsAbstractEntity {
         sb.append(dm).append(token);
         sb.append(dm).append(permissions);
         sb.append(dm).append(parameterName);
+        sb.append(dm).append(expiredTime);
         sb.append(dm).append(createdBy);
         sb.append(dm).append(createdTime);
         sb.append(dm).append(updatedBy);
@@ -173,6 +180,16 @@ public class BsAccessToken extends EsAbstractEntity {
         this.parameterName = value;
     }
 
+    public Long getExpiredTime() {
+        checkSpecifiedProperty("expiredTime");
+        return expiredTime;
+    }
+
+    public void setExpiredTime(Long value) {
+        registerModifiedProperty("expiredTime");
+        this.expiredTime = value;
+    }
+
     public String getCreatedBy() {
         checkSpecifiedProperty("createdBy");
         return convertEmptyToNull(createdBy);

+ 9 - 0
src/main/java/org/codelibs/fess/es/config/bsentity/dbmeta/AccessTokenDbm.java

@@ -85,6 +85,8 @@ public class AccessTokenDbm extends AbstractDBMeta {
                 "permissions");
         setupEpg(_epgMap, et -> ((AccessToken) et).getParameterName(),
                 (et, vl) -> ((AccessToken) et).setParameterName(DfTypeUtil.toString(vl)), "parameterName");
+        setupEpg(_epgMap, et -> ((AccessToken) et).getExpiredTime(), (et, vl) -> ((AccessToken) et).setExpiredTime(DfTypeUtil.toLong(vl)),
+                "expiredTime");
         setupEpg(_epgMap, et -> ((AccessToken) et).getCreatedBy(), (et, vl) -> ((AccessToken) et).setCreatedBy(DfTypeUtil.toString(vl)),
                 "createdBy");
         setupEpg(_epgMap, et -> ((AccessToken) et).getCreatedTime(), (et, vl) -> ((AccessToken) et).setCreatedTime(DfTypeUtil.toLong(vl)),
@@ -137,6 +139,8 @@ public class AccessTokenDbm extends AbstractDBMeta {
             false, false, false, "String", 0, 0, null, false, null, null, null, null, null, false);
     protected final ColumnInfo _columnParameterName = cci("parameter_name", "parameter_name", null, null, String.class, "parameterName",
             null, false, false, false, "String", 0, 0, null, false, null, null, null, null, null, false);
+    protected final ColumnInfo _columnExpiredTime = cci("expiredTime", "expiredTime", null, null, Long.class, "expiredTime", null, false,
+            false, false, "Long", 0, 0, null, false, null, null, null, null, null, false);
     protected final ColumnInfo _columnCreatedBy = cci("createdBy", "createdBy", null, null, String.class, "createdBy", null, false, false,
             false, "String", 0, 0, null, false, null, null, null, null, null, false);
     protected final ColumnInfo _columnCreatedTime = cci("createdTime", "createdTime", null, null, Long.class, "createdTime", null, false,
@@ -162,6 +166,10 @@ public class AccessTokenDbm extends AbstractDBMeta {
         return _columnParameterName;
     }
 
+    public ColumnInfo columnExpiredTime() {
+        return _columnExpiredTime;
+    }
+
     public ColumnInfo columnCreatedBy() {
         return _columnCreatedBy;
     }
@@ -184,6 +192,7 @@ public class AccessTokenDbm extends AbstractDBMeta {
         ls.add(columnToken());
         ls.add(columnPermissions());
         ls.add(columnParameterName());
+        ls.add(columnExpiredTime());
         ls.add(columnCreatedBy());
         ls.add(columnCreatedTime());
         ls.add(columnUpdatedBy());

+ 4 - 0
src/main/java/org/codelibs/fess/es/config/cbean/bs/BsAccessTokenCB.java

@@ -163,6 +163,10 @@ public class BsAccessTokenCB extends EsAbstractConditionBean {
             doColumn("parameter_name");
         }
 
+        public void columnExpiredTime() {
+            doColumn("expiredTime");
+        }
+
         public void columnCreatedBy() {
             doColumn("createdBy");
         }

+ 174 - 0
src/main/java/org/codelibs/fess/es/config/cbean/cq/bs/BsAccessTokenCQ.java

@@ -990,6 +990,180 @@ public abstract class BsAccessTokenCQ extends EsAbstractConditionQuery {
         return this;
     }
 
+    public void setExpiredTime_Equal(Long expiredTime) {
+        setExpiredTime_Term(expiredTime, null);
+    }
+
+    public void setExpiredTime_Equal(Long expiredTime, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        setExpiredTime_Term(expiredTime, opLambda);
+    }
+
+    public void setExpiredTime_Term(Long expiredTime) {
+        setExpiredTime_Term(expiredTime, null);
+    }
+
+    public void setExpiredTime_Term(Long expiredTime, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        TermQueryBuilder builder = regTermQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_NotEqual(Long expiredTime) {
+        setExpiredTime_NotTerm(expiredTime, null);
+    }
+
+    public void setExpiredTime_NotTerm(Long expiredTime) {
+        setExpiredTime_NotTerm(expiredTime, null);
+    }
+
+    public void setExpiredTime_NotEqual(Long expiredTime, ConditionOptionCall<BoolQueryBuilder> opLambda) {
+        setExpiredTime_NotTerm(expiredTime, opLambda);
+    }
+
+    public void setExpiredTime_NotTerm(Long expiredTime, ConditionOptionCall<BoolQueryBuilder> opLambda) {
+        not(not -> not.setExpiredTime_Term(expiredTime), opLambda);
+    }
+
+    public void setExpiredTime_Terms(Collection<Long> expiredTimeList) {
+        setExpiredTime_Terms(expiredTimeList, null);
+    }
+
+    public void setExpiredTime_Terms(Collection<Long> expiredTimeList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        TermsQueryBuilder builder = regTermsQ("expiredTime", expiredTimeList);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_InScope(Collection<Long> expiredTimeList) {
+        setExpiredTime_Terms(expiredTimeList, null);
+    }
+
+    public void setExpiredTime_InScope(Collection<Long> expiredTimeList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        setExpiredTime_Terms(expiredTimeList, opLambda);
+    }
+
+    public void setExpiredTime_Match(Long expiredTime) {
+        setExpiredTime_Match(expiredTime, null);
+    }
+
+    public void setExpiredTime_Match(Long expiredTime, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_MatchPhrase(Long expiredTime) {
+        setExpiredTime_MatchPhrase(expiredTime, null);
+    }
+
+    public void setExpiredTime_MatchPhrase(Long expiredTime, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhraseQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_MatchPhrasePrefix(Long expiredTime) {
+        setExpiredTime_MatchPhrasePrefix(expiredTime, null);
+    }
+
+    public void setExpiredTime_MatchPhrasePrefix(Long expiredTime, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhrasePrefixQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_Fuzzy(Long expiredTime) {
+        setExpiredTime_Fuzzy(expiredTime, null);
+    }
+
+    public void setExpiredTime_Fuzzy(Long expiredTime, ConditionOptionCall<FuzzyQueryBuilder> opLambda) {
+        FuzzyQueryBuilder builder = regFuzzyQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_GreaterThan(Long expiredTime) {
+        setExpiredTime_GreaterThan(expiredTime, null);
+    }
+
+    public void setExpiredTime_GreaterThan(Long expiredTime, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("expiredTime", ConditionKey.CK_GREATER_THAN, expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_LessThan(Long expiredTime) {
+        setExpiredTime_LessThan(expiredTime, null);
+    }
+
+    public void setExpiredTime_LessThan(Long expiredTime, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("expiredTime", ConditionKey.CK_LESS_THAN, expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_GreaterEqual(Long expiredTime) {
+        setExpiredTime_GreaterEqual(expiredTime, null);
+    }
+
+    public void setExpiredTime_GreaterEqual(Long expiredTime, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("expiredTime", ConditionKey.CK_GREATER_EQUAL, expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_LessEqual(Long expiredTime) {
+        setExpiredTime_LessEqual(expiredTime, null);
+    }
+
+    public void setExpiredTime_LessEqual(Long expiredTime, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("expiredTime", ConditionKey.CK_LESS_EQUAL, expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_Exists() {
+        setExpiredTime_Exists(null);
+    }
+
+    public void setExpiredTime_Exists(ConditionOptionCall<ExistsQueryBuilder> opLambda) {
+        ExistsQueryBuilder builder = regExistsQ("expiredTime");
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public void setExpiredTime_CommonTerms(Long expiredTime) {
+        setExpiredTime_CommonTerms(expiredTime, null);
+    }
+
+    public void setExpiredTime_CommonTerms(Long expiredTime, ConditionOptionCall<CommonTermsQueryBuilder> opLambda) {
+        CommonTermsQueryBuilder builder = regCommonTermsQ("expiredTime", expiredTime);
+        if (opLambda != null) {
+            opLambda.callback(builder);
+        }
+    }
+
+    public BsAccessTokenCQ addOrderBy_ExpiredTime_Asc() {
+        regOBA("expiredTime");
+        return this;
+    }
+
+    public BsAccessTokenCQ addOrderBy_ExpiredTime_Desc() {
+        regOBD("expiredTime");
+        return this;
+    }
+
     public void setCreatedBy_Equal(String createdBy) {
         setCreatedBy_Term(createdBy, null);
     }

+ 12 - 0
src/main/java/org/codelibs/fess/es/config/exentity/AccessToken.java

@@ -16,6 +16,7 @@
 package org.codelibs.fess.es.config.exentity;
 
 import java.util.Arrays;
+import java.util.Date;
 
 import org.codelibs.fess.es.config.bsentity.BsAccessToken;
 
@@ -42,6 +43,17 @@ public class AccessToken extends BsAccessToken {
         asDocMeta().version(version);
     }
 
+    public Date getExpires() {
+        if (getExpiredTime() == null) {
+            return null;
+        }
+        return new Date(getExpiredTime().longValue());
+    }
+
+    public void setExpires(Date date) {
+        setExpiredTime(date != null ? date.getTime() : null);
+    }
+
     @Override
     public String toString() {
         return "AccessToken [name=" + name + ", token=" + token + ", permissions=" + Arrays.toString(permissions) + ", parameterName="

+ 18 - 7
src/main/java/org/codelibs/fess/helper/RoleQueryHelper.java

@@ -35,6 +35,7 @@ import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
 import org.codelibs.fess.exception.InvalidAccessTokenException;
 import org.codelibs.fess.mylasta.action.FessUserBean;
 import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.taglib.FessFunctions;
 import org.codelibs.fess.util.ComponentUtil;
 import org.lastaflute.web.servlet.request.RequestManager;
 import org.lastaflute.web.util.LaRequestUtil;
@@ -156,13 +157,23 @@ public class RoleQueryHelper {
         final String token = request.getHeader("Authorization");
         if (StringUtil.isNotBlank(token)) {
             final AccessTokenService accessTokenService = ComponentUtil.getComponent(AccessTokenService.class);
-            accessTokenService.getAccessTokenByToken(token).ifPresent(accessToken -> {
-                stream(accessToken.getPermissions()).of(stream -> stream.forEach(roleSet::add));
-                final String name = accessToken.getParameterName();
-                stream(request.getParameterValues(name)).of(stream -> stream.filter(StringUtil::isNotBlank).forEach(roleSet::add));
-            }).orElse(() -> {
-                throw new InvalidAccessTokenException("invalid_token", "Invalid token: " + token);
-            });
+            accessTokenService
+                    .getAccessTokenByToken(token)
+                    .ifPresent(
+                            accessToken -> {
+                                final Long expiredTime = accessToken.getExpiredTime();
+                                if (expiredTime != null && expiredTime.longValue() > 0
+                                        && expiredTime.longValue() < ComponentUtil.getSystemHelper().getCurrentTimeAsLong()) {
+                                    throw new InvalidAccessTokenException("invalid_token", "The token is expired("
+                                            + FessFunctions.formatDate(FessFunctions.date(expiredTime)) + ").");
+                                }
+                                stream(accessToken.getPermissions()).of(stream -> stream.forEach(roleSet::add));
+                                final String name = accessToken.getParameterName();
+                                stream(request.getParameterValues(name)).of(
+                                        stream -> stream.filter(StringUtil::isNotBlank).forEach(roleSet::add));
+                            }).orElse(() -> {
+                        throw new InvalidAccessTokenException("invalid_token", "Invalid token: " + token);
+                    });
         }
 
     }

+ 9 - 0
src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java

@@ -197,6 +197,9 @@ public class FessLabels extends UserMessages {
     /** The key of the message: Expired */
     public static final String LABELS_EXPIRED_TIME = "{labels.expiredTime}";
 
+    /** The key of the message: Expired */
+    public static final String LABELS_EXPIRES = "{labels.expires}";
+
     /** The key of the message: Failure Count */
     public static final String LABELS_FAILURE_COUNT_THRESHOLD = "{labels.failureCountThreshold}";
 
@@ -2097,6 +2100,12 @@ public class FessLabels extends UserMessages {
     /** The key of the message: Token */
     public static final String LABELS_access_token_token = "{labels.access_token_token}";
 
+    /** The key of the message: Expired */
+    public static final String LABELS_access_token_expires = "{labels.access_token_expires}";
+
+    /** The key of the message: Parameter Name */
+    public static final String LABELS_access_token_parameter_name = "{labels.access_token_parameter_name}";
+
     /** The key of the message: Created */
     public static final String LABELS_access_token_updated_time = "{labels.access_token_updated_time}";
 

+ 3 - 0
src/main/resources/fess_indices/.fess_config/access_token.json

@@ -23,6 +23,9 @@
         "type": "string",
         "index": "not_analyzed"
       },
+      "expiredTime": {
+        "type": "long"
+      },
       "createdBy": {
         "type": "string",
         "index": "not_analyzed"

+ 3 - 0
src/main/resources/fess_label.properties

@@ -55,6 +55,7 @@ labels.errorCount=Error Count
 labels.errorLog=Error Log
 labels.errorName=Error Name
 labels.expiredTime=Expired
+labels.expires=Expired
 labels.failureCountThreshold=Failure Count
 labels.fileConfigName=File System Config Name
 labels.fileName=File name
@@ -689,6 +690,8 @@ labels.access_token_title_details=Access Token
 labels.access_token_list_name=Name
 labels.access_token_name=Name
 labels.access_token_token=Token
+labels.access_token_expires=Expired
+labels.access_token_parameter_name=Parameter Name
 labels.access_token_updated_time=Created
 labels.elevate_word_configuration=Additional Word
 labels.elevate_word_title_details=Additional Word

+ 3 - 0
src/main/resources/fess_label_en.properties

@@ -55,6 +55,7 @@ labels.errorCount=Error Count
 labels.errorLog=Error Log
 labels.errorName=Error Name
 labels.expiredTime=Expired
+labels.expires=Expired
 labels.failureCountThreshold=Failure Count
 labels.fileConfigName=File System Config Name
 labels.fileName=File name
@@ -689,6 +690,8 @@ labels.access_token_title_details=Access Token
 labels.access_token_list_name=Name
 labels.access_token_name=Name
 labels.access_token_token=Token
+labels.access_token_expires=Expired
+labels.access_token_parameter_name=Parameter Name
 labels.access_token_updated_time=Created
 labels.elevate_word_configuration=Additional Word
 labels.elevate_word_title_details=Additional Word

+ 3 - 0
src/main/resources/fess_label_ja.properties

@@ -55,6 +55,7 @@ labels.errorCount=\u30a8\u30e9\u30fc\u56de\u6570
 labels.errorLog=\u30a8\u30e9\u30fc\u30ed\u30b0
 labels.errorName=\u30a8\u30e9\u30fc\u540d
 labels.expiredTime=\u6709\u52b9\u671f\u9650
+labels.expires=\u6709\u52b9\u671f\u9650
 labels.failureCountThreshold=\u969c\u5bb3\u6570
 labels.fileConfigName=\u30d5\u30a1\u30a4\u30eb\u30af\u30ed\u30fc\u30eb\u8a2d\u5b9a\u540d
 labels.fileName=\u30d5\u30a1\u30a4\u30eb\u540d
@@ -687,6 +688,8 @@ labels.access_token_title_details=\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30
 labels.access_token_list_name=\u540d\u524d
 labels.access_token_name=\u540d\u524d
 labels.access_token_token=\u30c8\u30fc\u30af\u30f3
+labels.access_token_expires=\u6709\u52b9\u671f\u9650
+labels.access_token_parameter_name=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u540d
 labels.access_token_updated_time=\u4f5c\u6210\u65e5
 labels.elevate_word_configuration=\u8ffd\u52a0\u306e\u5358\u8a9e
 labels.elevate_word_title_details=\u8ffd\u52a0\u306e\u5358\u8a9e

+ 17 - 51
src/main/webapp/WEB-INF/view/admin/accesstoken/admin_accesstoken_details.jsp

@@ -59,66 +59,32 @@
 														key="labels.access_token_token" /></th>
 												<td>${f:h(token)}</td>
 											</tr>
+											<tr>
+												<th><la:message key="labels.permissions" /></th>
+												<td>${f:br(f:h(permissions))}<la:hidden
+														property="permissions" /></td>
+											</tr>
+											<tr>
+												<th><la:message
+														key="labels.access_token_parameter_name" /></th>
+												<td>${f:h(parameterName)}</td>
+											</tr>
+											<tr>
+												<th><la:message
+														key="labels.access_token_expires" /></th>
+												<td>${f:h(expires)}<la:hidden property="expires" /></td>
+											</tr>
 											<tr>
 												<th><la:message
 														key="labels.access_token_updated_time" /></th>
-												<td>${fe:date(updatedTime)}</td>
+												<td><fmt:formatDate value="${fe:date(updatedTime)}" pattern="yyyy-MM-dd'T'HH:mm:ss" /></td>
 											</tr>
 										</tbody>
 									</table>
 								</div>
 								<!-- /.box-body -->
 								<div class="box-footer">
-									<button type="submit" class="btn btn-default" name="list" value="back">
-										<i class="fa fa-arrow-circle-left"></i>
-										<la:message key="labels.crud_button_back" />
-									</button>
-									<%--
-									<button type="submit" class="btn btn-warning" name="edit"
-										value="<la:message key="labels.crud_button_edit" />">
-										<i class="fa fa-pencil"></i>
-										<la:message key="labels.crud_button_edit" />
-									</button>
-									 --%>
-									<button type="button" class="btn btn-danger" name="delete"
-										data-toggle="modal" data-target="#confirmToDelete"
-										value="<la:message key="labels.crud_button_delete" />">
-										<i class="fa fa-trash"></i>
-										<la:message key="labels.crud_button_delete" />
-									</button>
-									<div class="modal modal-danger fade" id="confirmToDelete" tabindex="-1"
-										role="dialog">
-										<div class="modal-dialog">
-											<div class="modal-content">
-												<div class="modal-header">
-													<button type="button" class="close" data-dismiss="modal"
-														aria-label="Close">
-														<span aria-hidden="true">×</span>
-													</button>
-													<h4 class="modal-title">
-														<la:message key="labels.crud_title_delete" />
-													</h4>
-												</div>
-												<div class="modal-body">
-													<p>
-														<la:message key="labels.crud_delete_confirmation" />
-													</p>
-												</div>
-												<div class="modal-footer">
-													<button type="button" class="btn btn-outline pull-left"
-														data-dismiss="modal">
-														<la:message key="labels.crud_button_cancel" />
-													</button>
-													<button type="submit" class="btn btn-outline btn-danger"
-														name="delete"
-														value="<la:message key="labels.crud_button_delete" />">
-														<i class="fa fa-trash"></i>
-														<la:message key="labels.crud_button_delete" />
-													</button>
-												</div>
-											</div>
-										</div>
-									</div>
+									<jsp:include page="/WEB-INF/view/common/admin/crud/buttons.jsp"></jsp:include>
 								</div>
 								<!-- /.box-footer -->
 							</div>

+ 34 - 0
src/main/webapp/WEB-INF/view/admin/accesstoken/admin_accesstoken_edit.jsp

@@ -52,6 +52,40 @@
 											<la:text property="name" styleClass="form-control"/>
 										</div>
 									</div>
+									<c:if test="${crudMode==2}">
+									<div class="form-group">
+										<label for="token" class="col-sm-3 control-label"><la:message
+												key="labels.access_token_token" /></label>
+										<div class="col-sm-9">
+											${f:h(token)}
+										</div>
+									</div>
+									</c:if>
+									<div class="form-group">
+										<label for="permissions" class="col-sm-3 control-label"><la:message
+												key="labels.permissions" /></label>
+										<div class="col-sm-9">
+											<la:errors property="permissions" />
+											<la:textarea property="permissions" styleClass="form-control"
+												rows="5" />
+										</div>
+									</div>
+									<div class="form-group">
+										<label for="name" class="col-sm-3 control-label"><la:message
+												key="labels.access_token_parameter_name" /></label>
+										<div class="col-sm-9">
+											<la:errors property="parameterName" />
+											<la:text property="parameterName" styleClass="form-control"/>
+										</div>
+									</div>
+									<div class="form-group">
+										<label for="name" class="col-sm-3 control-label"><la:message
+												key="labels.access_token_expires" /></label>
+										<div class="col-sm-9">
+											<la:errors property="expires" />
+											<la:text property="expires" styleClass="form-control"/>
+										</div>
+									</div>
 								</div>
 								<!-- /.box-body -->
 								<div class="box-footer">