Selaa lähdekoodia

#361 send user/role/group operation event to ldap server

Shinsuke Sugaya 9 vuotta sitten
vanhempi
commit
0fc3bc51f9

+ 3 - 0
src/main/java/org/codelibs/fess/app/service/GroupService.java

@@ -27,6 +27,7 @@ import org.codelibs.fess.es.user.cbean.GroupCB;
 import org.codelibs.fess.es.user.exbhv.GroupBhv;
 import org.codelibs.fess.es.user.exentity.Group;
 import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.util.ComponentUtil;
 import org.dbflute.cbean.result.PagingResultBean;
 import org.dbflute.optional.OptionalEntity;
 
@@ -61,6 +62,7 @@ public class GroupService implements Serializable {
     }
 
     public void store(final Group group) {
+        ComponentUtil.getLdapManager().insert(group);
 
         groupBhv.insertOrUpdate(group, op -> {
             op.setRefresh(true);
@@ -69,6 +71,7 @@ public class GroupService implements Serializable {
     }
 
     public void delete(final Group group) {
+        ComponentUtil.getLdapManager().delete(group);
 
         groupBhv.delete(group, op -> {
             op.setRefresh(true);

+ 3 - 0
src/main/java/org/codelibs/fess/app/service/RoleService.java

@@ -27,6 +27,7 @@ import org.codelibs.fess.es.user.cbean.RoleCB;
 import org.codelibs.fess.es.user.exbhv.RoleBhv;
 import org.codelibs.fess.es.user.exentity.Role;
 import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.util.ComponentUtil;
 import org.dbflute.cbean.result.PagingResultBean;
 import org.dbflute.optional.OptionalEntity;
 
@@ -61,6 +62,7 @@ public class RoleService implements Serializable {
     }
 
     public void store(final Role role) {
+        ComponentUtil.getLdapManager().insert(role);
 
         roleBhv.insertOrUpdate(role, op -> {
             op.setRefresh(true);
@@ -69,6 +71,7 @@ public class RoleService implements Serializable {
     }
 
     public void delete(final Role role) {
+        ComponentUtil.getLdapManager().delete(role);
 
         roleBhv.delete(role, op -> {
             op.setRefresh(true);

+ 3 - 0
src/main/java/org/codelibs/fess/app/service/UserService.java

@@ -26,6 +26,7 @@ import org.codelibs.fess.app.pager.UserPager;
 import org.codelibs.fess.es.user.cbean.UserCB;
 import org.codelibs.fess.es.user.exbhv.UserBhv;
 import org.codelibs.fess.es.user.exentity.User;
+import org.codelibs.fess.util.ComponentUtil;
 import org.dbflute.cbean.result.PagingResultBean;
 import org.dbflute.optional.OptionalEntity;
 
@@ -57,6 +58,7 @@ public class UserService implements Serializable {
     }
 
     public void store(final User user) {
+        ComponentUtil.getLdapManager().insert(user);
 
         userBhv.insertOrUpdate(user, op -> {
             op.setRefresh(true);
@@ -65,6 +67,7 @@ public class UserService implements Serializable {
     }
 
     public void delete(final User user) {
+        ComponentUtil.getLdapManager().delete(user);
 
         userBhv.delete(user, op -> {
             op.setRefresh(true);

+ 25 - 6
src/main/java/org/codelibs/fess/app/web/admin/group/AdminGroupAction.java

@@ -32,6 +32,8 @@ import org.lastaflute.web.Execute;
 import org.lastaflute.web.response.HtmlResponse;
 import org.lastaflute.web.response.render.RenderData;
 import org.lastaflute.web.ruts.process.ActionRuntime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author shinsuke
@@ -39,6 +41,8 @@ import org.lastaflute.web.ruts.process.ActionRuntime;
  */
 public class AdminGroupAction extends FessAdminAction {
 
+    private static final Logger logger = LoggerFactory.getLogger(AdminGroupAction.class);
+
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
@@ -166,8 +170,13 @@ public class AdminGroupAction extends FessAdminAction {
         validate(form, messages -> {}, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getGroup(form).ifPresent(entity -> {
-            groupService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            try {
+                groupService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to add " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
         });
@@ -180,8 +189,13 @@ public class AdminGroupAction extends FessAdminAction {
         validate(form, messages -> {}, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getGroup(form).ifPresent(entity -> {
-            groupService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            try {
+                groupService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to update " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
         });
@@ -195,8 +209,13 @@ public class AdminGroupAction extends FessAdminAction {
         verifyToken(() -> asDetailsHtml());
         final String id = form.id;
         groupService.getGroup(id).ifPresent(entity -> {
-            groupService.delete(entity);
-            saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            try {
+                groupService.delete(entity);
+                saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to delete " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
         });

+ 25 - 6
src/main/java/org/codelibs/fess/app/web/admin/role/AdminRoleAction.java

@@ -32,6 +32,8 @@ import org.lastaflute.web.Execute;
 import org.lastaflute.web.response.HtmlResponse;
 import org.lastaflute.web.response.render.RenderData;
 import org.lastaflute.web.ruts.process.ActionRuntime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author shinsuke
@@ -39,6 +41,8 @@ import org.lastaflute.web.ruts.process.ActionRuntime;
  */
 public class AdminRoleAction extends FessAdminAction {
 
+    private static final Logger logger = LoggerFactory.getLogger(AdminRoleAction.class);
+
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
@@ -166,8 +170,13 @@ public class AdminRoleAction extends FessAdminAction {
         validate(form, messages -> {}, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getRole(form).ifPresent(entity -> {
-            roleService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            try {
+                roleService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to add " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
         });
@@ -180,8 +189,13 @@ public class AdminRoleAction extends FessAdminAction {
         validate(form, messages -> {}, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getRole(form).ifPresent(entity -> {
-            roleService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            try {
+                roleService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to update " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
         });
@@ -195,8 +209,13 @@ public class AdminRoleAction extends FessAdminAction {
         verifyToken(() -> asDetailsHtml());
         final String id = form.id;
         roleService.getRole(id).ifPresent(entity -> {
-            roleService.delete(entity);
-            saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            try {
+                roleService.delete(entity);
+                saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to delete " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
         });

+ 25 - 6
src/main/java/org/codelibs/fess/app/web/admin/user/AdminUserAction.java

@@ -39,6 +39,8 @@ import org.lastaflute.web.response.HtmlResponse;
 import org.lastaflute.web.response.render.RenderData;
 import org.lastaflute.web.ruts.process.ActionRuntime;
 import org.lastaflute.web.validation.VaErrorHook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author shinsuke
@@ -46,6 +48,8 @@ import org.lastaflute.web.validation.VaErrorHook;
  */
 public class AdminUserAction extends FessAdminAction {
 
+    private static final Logger logger = LoggerFactory.getLogger(AdminUserAction.class);
+
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
@@ -188,8 +192,13 @@ public class AdminUserAction extends FessAdminAction {
         verifyPassword(form, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getUser(form).ifPresent(entity -> {
-            userService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            try {
+                userService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to add " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL), () -> asEditHtml());
         });
@@ -203,8 +212,13 @@ public class AdminUserAction extends FessAdminAction {
         verifyPassword(form, () -> asEditHtml());
         verifyToken(() -> asEditHtml());
         getUser(form).ifPresent(entity -> {
-            userService.store(entity);
-            saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            try {
+                userService.store(entity);
+                saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to update " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), () -> asEditHtml());
         });
@@ -217,8 +231,13 @@ public class AdminUserAction extends FessAdminAction {
         validate(form, messages -> {}, () -> asDetailsHtml());
         final String id = form.id;
         userService.getUser(id).ifPresent(entity -> {
-            userService.delete(entity);
-            saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            try {
+                userService.delete(entity);
+                saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+            } catch (Exception e) {
+                logger.error("Failed to delete " + entity, e);
+                throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
+            }
         }).orElse(() -> {
             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml());
         });

+ 1 - 1
src/main/java/org/codelibs/fess/crawler/transformer/AbstractFessFileTransformer.java

@@ -208,7 +208,7 @@ public abstract class AbstractFessFileTransformer extends AbstractTransformer im
             putResultDataBody(dataMap, fessConfig.getIndexFieldContent(), StringUtil.EMPTY);
         }
         if ((Constants.TRUE.equalsIgnoreCase(fieldConfigMap.get(fessConfig.getIndexFieldCache())) || fessConfig
-                .isCrawlerDocumentCacheEnable()) && fessConfig.isSupportedDocumentCacheMimetypes(mimeType)) {
+                .isCrawlerDocumentCacheEnabled()) && fessConfig.isSupportedDocumentCacheMimetypes(mimeType)) {
             if (responseData.getContentLength() > 0
                     && responseData.getContentLength() <= fessConfig.getCrawlerDocumentCacheMaxSizeAsInteger().longValue()) {
 

+ 1 - 1
src/main/java/org/codelibs/fess/crawler/transformer/FessXpathTransformer.java

@@ -210,7 +210,7 @@ public class FessXpathTransformer extends XpathTransformer implements FessTransf
         // content
         putResultDataBody(dataMap, fessConfig.getIndexFieldContent(), getDocumentContent(responseData, document));
         if ((Constants.TRUE.equalsIgnoreCase(fieldConfigMap.get(fessConfig.getIndexFieldCache())) || fessConfig
-                .isCrawlerDocumentCacheEnable()) && fessConfig.isSupportedDocumentCacheMimetypes(mimeType)) {
+                .isCrawlerDocumentCacheEnabled()) && fessConfig.isSupportedDocumentCacheMimetypes(mimeType)) {
             if (responseData.getContentLength() > 0
                     && responseData.getContentLength() <= fessConfig.getCrawlerDocumentCacheMaxSizeAsInteger().longValue()) {
                 String charSet = responseData.getCharSet();

+ 5 - 0
src/main/java/org/codelibs/fess/es/user/exentity/Group.java

@@ -40,4 +40,9 @@ public class Group extends BsGroup {
         asDocMeta().id(id);
     }
 
+    @Override
+    public String toString() {
+        return "Group [name=" + name + "]";
+    }
+
 }

+ 5 - 0
src/main/java/org/codelibs/fess/es/user/exentity/Role.java

@@ -39,4 +39,9 @@ public class Role extends BsRole {
     public void setId(final String id) {
         asDocMeta().id(id);
     }
+
+    @Override
+    public String toString() {
+        return "Role [name=" + name + "]";
+    }
 }

+ 6 - 0
src/main/java/org/codelibs/fess/es/user/exentity/User.java

@@ -15,6 +15,7 @@
  */
 package org.codelibs.fess.es.user.exentity;
 
+import java.util.Arrays;
 import java.util.Base64;
 
 import org.codelibs.fess.Constants;
@@ -57,4 +58,9 @@ public class User extends BsUser implements FessUser {
                 .toArray(n -> new String[n]);
     }
 
+    @Override
+    public String toString() {
+        return "User [name=" + name + ", roles=" + Arrays.toString(roles) + ", groups=" + Arrays.toString(groups) + "]";
+    }
+
 }

+ 30 - 0
src/main/java/org/codelibs/fess/exception/LdapOperationException.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012-2016 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.exception;
+
+public class LdapOperationException extends FessSystemException {
+
+    private static final long serialVersionUID = 1L;
+
+    public LdapOperationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public LdapOperationException(String message) {
+        super(message);
+    }
+
+}

+ 192 - 8
src/main/java/org/codelibs/fess/ldap/LdapManager.java

@@ -24,6 +24,8 @@ import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
 import javax.naming.directory.SearchControls;
@@ -31,10 +33,15 @@ import javax.naming.directory.SearchResult;
 
 import org.codelibs.core.lang.StringUtil;
 import org.codelibs.fess.entity.FessUser;
+import org.codelibs.fess.es.user.exentity.Group;
+import org.codelibs.fess.es.user.exentity.Role;
+import org.codelibs.fess.es.user.exentity.User;
+import org.codelibs.fess.exception.LdapOperationException;
 import org.codelibs.fess.helper.SambaHelper;
 import org.codelibs.fess.mylasta.direction.FessConfig;
 import org.codelibs.fess.util.ComponentUtil;
 import org.dbflute.optional.OptionalEntity;
+import org.lastaflute.core.security.PrimaryCipher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,20 +50,16 @@ public class LdapManager {
 
     public OptionalEntity<FessUser> login(final String username, final String password) {
         final FessConfig fessConfig = ComponentUtil.getFessConfig();
-        final String providerUrl = fessConfig.getLdapProviderUrl();
 
-        if (StringUtil.isBlank(providerUrl)) {
+        if (StringUtil.isBlank(fessConfig.getLdapProviderUrl())) {
             return OptionalEntity.empty();
         }
 
         DirContext ctx = null;
         try {
-            final Hashtable<String, String> env = new Hashtable<>();
-            env.put(Context.INITIAL_CONTEXT_FACTORY, fessConfig.getLdapInitialContextFactory());
-            env.put(Context.SECURITY_AUTHENTICATION, fessConfig.getLdapSecurityAuthentication());
-            env.put(Context.PROVIDER_URL, providerUrl);
-            env.put(Context.SECURITY_PRINCIPAL, fessConfig.getLdapSecurityPrincipal(username));
-            env.put(Context.SECURITY_CREDENTIALS, password);
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapInitialContextFactory(), fessConfig.getLdapSecurityAuthentication(),
+                            fessConfig.getLdapProviderUrl(), fessConfig.getLdapSecurityPrincipal(username), password);
             ctx = new InitialDirContext(env);
             if (logger.isDebugEnabled()) {
                 logger.debug("Logged in.", ctx);
@@ -76,6 +79,17 @@ public class LdapManager {
         return OptionalEntity.empty();
     }
 
+    protected Hashtable<String, String> createEnvironment(final String initialContextFactory, final String securityAuthentication,
+            String providerUrl, String principal, String credntials) {
+        final Hashtable<String, String> env = new Hashtable<>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
+        env.put(Context.SECURITY_AUTHENTICATION, securityAuthentication);
+        env.put(Context.PROVIDER_URL, providerUrl);
+        env.put(Context.SECURITY_PRINCIPAL, principal);
+        env.put(Context.SECURITY_CREDENTIALS, credntials);
+        return env;
+    }
+
     protected LdapUser createLdapUser(final String username, final Hashtable<String, String> env) {
         return new LdapUser(env, username);
     }
@@ -149,4 +163,174 @@ public class LdapManager {
 
         return roleList.toArray(new String[roleList.size()]);
     }
+
+    public void insert(User user) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            BasicAttributes entry = new BasicAttributes();
+
+            String entryDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName());
+
+            addUserAttributes(entry, user, fessConfig);
+
+            Attribute oc = fessConfig.getLdapAdminUserObjectClassAttribute();
+            entry.put(oc);
+
+            // TODO role and group
+
+            ctx.createSubcontext(entryDN, entry);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to add " + user, e);
+        }
+    }
+
+    protected void addUserAttributes(final BasicAttributes entry, final User user, final FessConfig fessConfig) {
+        final PrimaryCipher cipher = ComponentUtil.getComponent(PrimaryCipher.class);
+        entry.put(new BasicAttribute("cn", user.getName()));
+        entry.put(new BasicAttribute("sn", user.getName()));
+        entry.put(new BasicAttribute("userPassword", fessConfig.getLdapAdminDigestAlgorismPrefix() + cipher.oneway(user.getPassword())));
+    }
+
+    public void delete(User user) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            String entryDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName());
+
+            ctx.destroySubcontext(entryDN);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to delete " + user, e);
+        }
+    }
+
+    public void insert(Role role) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            BasicAttributes entry = new BasicAttributes();
+
+            String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName());
+
+            addRoleAttributes(entry, role, fessConfig);
+
+            Attribute oc = fessConfig.getLdapAdminRoleObjectClassAttribute();
+            entry.put(oc);
+
+            ctx.createSubcontext(entryDN, entry);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to add " + role, e);
+        }
+    }
+
+    protected void addRoleAttributes(final BasicAttributes entry, final Role user, final FessConfig fessConfig) {
+        // nothing
+    }
+
+    public void delete(Role role) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName());
+
+            ctx.destroySubcontext(entryDN);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to delete " + role, e);
+        }
+    }
+
+    public void insert(Group group) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            BasicAttributes entry = new BasicAttributes();
+
+            String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName());
+
+            addGroupAttributes(entry, group, fessConfig);
+
+            Attribute oc = fessConfig.getLdapAdminGroupObjectClassAttribute();
+            entry.put(oc);
+
+            ctx.createSubcontext(entryDN, entry);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to add " + group, e);
+        }
+    }
+
+    protected void addGroupAttributes(final BasicAttributes entry, final Group group, final FessConfig fessConfig) {
+        // nothing
+    }
+
+    public void delete(Group group) {
+        final FessConfig fessConfig = ComponentUtil.getFessConfig();
+        if (!fessConfig.isLdapAdminEnabled()) {
+            return;
+        }
+
+        DirContext ctx = null;
+        try {
+            final Hashtable<String, String> env =
+                    createEnvironment(fessConfig.getLdapAdminInitialContextFactory(), fessConfig.getLdapAdminSecurityAuthentication(),
+                            fessConfig.getLdapAdminProviderUrl(), fessConfig.getLdapAdminSecurityPrincipal(),
+                            fessConfig.getLdapAdminSecurityCredentials());
+            ctx = new InitialDirContext(env);
+
+            String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName());
+
+            ctx.destroySubcontext(entryDN);
+        } catch (NamingException e) {
+            throw new LdapOperationException("Failed to delete " + group, e);
+        }
+    }
 }

+ 204 - 9
src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java

@@ -180,7 +180,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     String CRAWLER_DOCUMENT_FILE_DEFAULT_LANG = "crawler.document.file.default.lang";
 
     /** The key of the configuration. e.g. true */
-    String CRAWLER_DOCUMENT_CACHE_ENABLE = "crawler.document.cache.enable";
+    String CRAWLER_DOCUMENT_CACHE_ENABLED = "crawler.document.cache.enabled";
 
     /** The key of the configuration. e.g. 2621440 */
     String CRAWLER_DOCUMENT_CACHE_MAX_SIZE = "crawler.document.cache.max.size";
@@ -614,6 +614,45 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     /** The key of the configuration. e.g. guest */
     String SUGGEST_ROLE_FILTERS = "suggest.role.filters";
 
+    /** The key of the configuration. e.g. true */
+    String LDAP_ADMIN_ENABLED = "ldap.admin.enabled";
+
+    /** The key of the configuration. e.g. com.sun.jndi.ldap.LdapCtxFactory */
+    String LDAP_ADMIN_INITIAL_CONTEXT_FACTORY = "ldap.admin.initial.context.factory";
+
+    /** The key of the configuration. e.g. simple */
+    String LDAP_ADMIN_SECURITY_AUTHENTICATION = "ldap.admin.security.authentication";
+
+    /** The key of the configuration. e.g. ldap://localhost:1389 */
+    String LDAP_ADMIN_PROVIDER_URL = "ldap.admin.provider.url";
+
+    /** The key of the configuration. e.g. cn=Directory Manager */
+    String LDAP_ADMIN_SECURITY_PRINCIPAL = "ldap.admin.security.principal";
+
+    /** The key of the configuration. e.g. password */
+    String LDAP_ADMIN_SECURITY_CREDENTIALS = "ldap.admin.security.credentials";
+
+    /** The key of the configuration. e.g. uid=%s,ou=People,dc=fess,dc=codelibs,dc=org */
+    String LDAP_ADMIN_USER_SECURITY_PRINCIPAL = "ldap.admin.user.security.principal";
+
+    /** The key of the configuration. e.g. organizationalPerson,top,person,inetOrgPerson */
+    String LDAP_ADMIN_USER_OBJECT_CLASSES = "ldap.admin.user.object.classes";
+
+    /** The key of the configuration. e.g. cn=%s,ou=Role,dc=fess,dc=codelibs,dc=org */
+    String LDAP_ADMIN_ROLE_SECURITY_PRINCIPAL = "ldap.admin.role.security.principal";
+
+    /** The key of the configuration. e.g. groupOfNames */
+    String LDAP_ADMIN_ROLE_OBJECT_CLASSES = "ldap.admin.role.object.classes";
+
+    /** The key of the configuration. e.g. cn=%s,ou=Group,dc=fess,dc=codelibs,dc=org */
+    String LDAP_ADMIN_GROUP_SECURITY_PRINCIPAL = "ldap.admin.group.security.principal";
+
+    /** The key of the configuration. e.g. groupOfNames */
+    String LDAP_ADMIN_GROUP_OBJECT_CLASSES = "ldap.admin.group.object.classes";
+
+    /** The key of the configuration. e.g. {SHA256} */
+    String LDAP_ADMIN_DIGEST_ALGORISM_PREFIX = "ldap.admin.digest.algorism.prefix";
+
     /**
      * Get the value of property as {@link String}.
      * @param propertyKey The key of the property. (NotNull)
@@ -1084,20 +1123,20 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     Integer getCrawlerDocumentFileDefaultLangAsInteger();
 
     /**
-     * Get the value for the key 'crawler.document.cache.enable'. <br>
+     * Get the value for the key 'crawler.document.cache.enabled'. <br>
      * The value is, e.g. true <br>
      * comment: cache
      * @return The value of found property. (NotNull: if not found, exception but basically no way)
      */
-    String getCrawlerDocumentCacheEnable();
+    String getCrawlerDocumentCacheEnabled();
 
     /**
-     * Is the property for the key 'crawler.document.cache.enable' true? <br>
+     * Is the property for the key 'crawler.document.cache.enabled' true? <br>
      * The value is, e.g. true <br>
      * comment: cache
      * @return The determination, true or false. (if not found, exception but basically no way)
      */
-    boolean isCrawlerDocumentCacheEnable();
+    boolean isCrawlerDocumentCacheEnabled();
 
     /**
      * Get the value for the key 'crawler.document.cache.max.size'. <br>
@@ -2454,6 +2493,106 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
      */
     String getSuggestRoleFilters();
 
+    /**
+     * Get the value for the key 'ldap.admin.enabled'. <br>
+     * The value is, e.g. true <br>
+     * comment: ------
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminEnabled();
+
+    /**
+     * Is the property for the key 'ldap.admin.enabled' true? <br>
+     * The value is, e.g. true <br>
+     * comment: ------
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isLdapAdminEnabled();
+
+    /**
+     * Get the value for the key 'ldap.admin.initial.context.factory'. <br>
+     * The value is, e.g. com.sun.jndi.ldap.LdapCtxFactory <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminInitialContextFactory();
+
+    /**
+     * Get the value for the key 'ldap.admin.security.authentication'. <br>
+     * The value is, e.g. simple <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminSecurityAuthentication();
+
+    /**
+     * Get the value for the key 'ldap.admin.provider.url'. <br>
+     * The value is, e.g. ldap://localhost:1389 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminProviderUrl();
+
+    /**
+     * Get the value for the key 'ldap.admin.security.principal'. <br>
+     * The value is, e.g. cn=Directory Manager <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminSecurityPrincipal();
+
+    /**
+     * Get the value for the key 'ldap.admin.security.credentials'. <br>
+     * The value is, e.g. password <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminSecurityCredentials();
+
+    /**
+     * Get the value for the key 'ldap.admin.user.security.principal'. <br>
+     * The value is, e.g. uid=%s,ou=People,dc=fess,dc=codelibs,dc=org <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminUserSecurityPrincipal();
+
+    /**
+     * Get the value for the key 'ldap.admin.user.object.classes'. <br>
+     * The value is, e.g. organizationalPerson,top,person,inetOrgPerson <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminUserObjectClasses();
+
+    /**
+     * Get the value for the key 'ldap.admin.role.security.principal'. <br>
+     * The value is, e.g. cn=%s,ou=Role,dc=fess,dc=codelibs,dc=org <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminRoleSecurityPrincipal();
+
+    /**
+     * Get the value for the key 'ldap.admin.role.object.classes'. <br>
+     * The value is, e.g. groupOfNames <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminRoleObjectClasses();
+
+    /**
+     * Get the value for the key 'ldap.admin.group.security.principal'. <br>
+     * The value is, e.g. cn=%s,ou=Group,dc=fess,dc=codelibs,dc=org <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminGroupSecurityPrincipal();
+
+    /**
+     * Get the value for the key 'ldap.admin.group.object.classes'. <br>
+     * The value is, e.g. groupOfNames <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminGroupObjectClasses();
+
+    /**
+     * Get the value for the key 'ldap.admin.digest.algorism.prefix'. <br>
+     * The value is, e.g. {SHA256} <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getLdapAdminDigestAlgorismPrefix();
+
     /**
      * The simple implementation for configuration.
      * @author FreeGen
@@ -2699,12 +2838,12 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
             return getAsInteger(FessConfig.CRAWLER_DOCUMENT_FILE_DEFAULT_LANG);
         }
 
-        public String getCrawlerDocumentCacheEnable() {
-            return get(FessConfig.CRAWLER_DOCUMENT_CACHE_ENABLE);
+        public String getCrawlerDocumentCacheEnabled() {
+            return get(FessConfig.CRAWLER_DOCUMENT_CACHE_ENABLED);
         }
 
-        public boolean isCrawlerDocumentCacheEnable() {
-            return is(FessConfig.CRAWLER_DOCUMENT_CACHE_ENABLE);
+        public boolean isCrawlerDocumentCacheEnabled() {
+            return is(FessConfig.CRAWLER_DOCUMENT_CACHE_ENABLED);
         }
 
         public String getCrawlerDocumentCacheMaxSize() {
@@ -3410,5 +3549,61 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
         public String getSuggestRoleFilters() {
             return get(FessConfig.SUGGEST_ROLE_FILTERS);
         }
+
+        public String getLdapAdminEnabled() {
+            return get(FessConfig.LDAP_ADMIN_ENABLED);
+        }
+
+        public boolean isLdapAdminEnabled() {
+            return is(FessConfig.LDAP_ADMIN_ENABLED);
+        }
+
+        public String getLdapAdminInitialContextFactory() {
+            return get(FessConfig.LDAP_ADMIN_INITIAL_CONTEXT_FACTORY);
+        }
+
+        public String getLdapAdminSecurityAuthentication() {
+            return get(FessConfig.LDAP_ADMIN_SECURITY_AUTHENTICATION);
+        }
+
+        public String getLdapAdminProviderUrl() {
+            return get(FessConfig.LDAP_ADMIN_PROVIDER_URL);
+        }
+
+        public String getLdapAdminSecurityPrincipal() {
+            return get(FessConfig.LDAP_ADMIN_SECURITY_PRINCIPAL);
+        }
+
+        public String getLdapAdminSecurityCredentials() {
+            return get(FessConfig.LDAP_ADMIN_SECURITY_CREDENTIALS);
+        }
+
+        public String getLdapAdminUserSecurityPrincipal() {
+            return get(FessConfig.LDAP_ADMIN_USER_SECURITY_PRINCIPAL);
+        }
+
+        public String getLdapAdminUserObjectClasses() {
+            return get(FessConfig.LDAP_ADMIN_USER_OBJECT_CLASSES);
+        }
+
+        public String getLdapAdminRoleSecurityPrincipal() {
+            return get(FessConfig.LDAP_ADMIN_ROLE_SECURITY_PRINCIPAL);
+        }
+
+        public String getLdapAdminRoleObjectClasses() {
+            return get(FessConfig.LDAP_ADMIN_ROLE_OBJECT_CLASSES);
+        }
+
+        public String getLdapAdminGroupSecurityPrincipal() {
+            return get(FessConfig.LDAP_ADMIN_GROUP_SECURITY_PRINCIPAL);
+        }
+
+        public String getLdapAdminGroupObjectClasses() {
+            return get(FessConfig.LDAP_ADMIN_GROUP_OBJECT_CLASSES);
+        }
+
+        public String getLdapAdminDigestAlgorismPrefix() {
+            return get(FessConfig.LDAP_ADMIN_DIGEST_ALGORISM_PREFIX);
+        }
     }
 }

+ 44 - 0
src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java

@@ -21,6 +21,9 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+
 import org.codelibs.core.exception.ClassNotFoundRuntimeException;
 import org.codelibs.core.lang.StringUtil;
 import org.codelibs.core.misc.Pair;
@@ -419,4 +422,45 @@ public interface FessProp {
                 .anyMatch(s -> s.equals(name));
     }
 
+    String getLdapAdminUserObjectClasses();
+
+    public default Attribute getLdapAdminUserObjectClassAttribute() {
+        final Attribute oc = new BasicAttribute("objectClass");
+        StreamUtil.of(getLdapAdminUserObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        return oc;
+    }
+
+    String getLdapAdminUserSecurityPrincipal();
+
+    public default String getLdapAdminUserSecurityPrincipal(final String name) {
+        return String.format(getLdapAdminUserSecurityPrincipal(), name);
+    }
+
+    String getLdapAdminRoleObjectClasses();
+
+    public default Attribute getLdapAdminRoleObjectClassAttribute() {
+        final Attribute oc = new BasicAttribute("objectClass");
+        StreamUtil.of(getLdapAdminRoleObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        return oc;
+    }
+
+    String getLdapAdminRoleSecurityPrincipal();
+
+    public default String getLdapAdminRoleSecurityPrincipal(final String name) {
+        return String.format(getLdapAdminRoleSecurityPrincipal(), name);
+    }
+
+    String getLdapAdminGroupObjectClasses();
+
+    public default Attribute getLdapAdminGroupObjectClassAttribute() {
+        final Attribute oc = new BasicAttribute("objectClass");
+        StreamUtil.of(getLdapAdminGroupObjectClasses().split(",")).filter(s -> StringUtil.isNotBlank(s)).forEach(s -> oc.add(s.trim()));
+        return oc;
+    }
+
+    String getLdapAdminGroupSecurityPrincipal();
+
+    public default String getLdapAdminGroupSecurityPrincipal(final String name) {
+        return String.format(getLdapAdminGroupSecurityPrincipal(), name);
+    }
 }

+ 23 - 5
src/main/resources/fess_config.properties

@@ -99,7 +99,7 @@ crawler.document.file.append.body.content=true
 crawler.document.file.default.lang=
 
 # cache
-crawler.document.cache.enable=true
+crawler.document.cache.enabled=true
 crawler.document.cache.max.size=2621440
 crawler.document.cache.supported.mimetypes=text/html
 #,text/plain,application/xml,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation
@@ -271,21 +271,21 @@ paging.search.page.size=20
 paging.search.page.max.size=100
 
 # ----------------------------------------------------------
-#                                                      Mail
+#                                                       Mail
 #                                                     ------
 # From
 mail.from.name = Administrator
 mail.from.address = root@localhost
 
 # ----------------------------------------------------------
-#                                                 Scheduler
+#                                                  Scheduler
 #                                                     ------
 scheduler.target.name=
 scheduler.job.class=org.codelibs.fess.app.job.ScriptExecutorJob
 scheduler.concurrent.exec.mode=QUIT
 
 # ----------------------------------------------------------
-#                                                OnlineHelp
+#                                                 OnlineHelp
 #                                                     ------
 online.help.base.link=http://fess.codelibs.org/{lang}/{version}/admin/
 online.help.name.failureurl=failureurl
@@ -324,7 +324,7 @@ online.help.name.backup=backup
 online.help.supported.langs=
 
 # ----------------------------------------------------------
-#                                                   Suggest
+#                                                    Suggest
 #                                                     ------
 
 suggest.popular.word.seed=0
@@ -346,3 +346,21 @@ suggest.role.filters=\
 guest
 
 
+# ----------------------------------------------------------
+#                                                      LDAP
+#                                                     ------
+
+ldap.admin.enabled=false
+ldap.admin.initial.context.factory=com.sun.jndi.ldap.LdapCtxFactory
+ldap.admin.security.authentication=simple
+ldap.admin.provider.url=ldap\://localhost\:1389
+ldap.admin.security.principal=cn\=Directory Manager
+ldap.admin.security.credentials=password
+ldap.admin.user.security.principal=uid\=%s,ou\=People,dc\=fess,dc\=codelibs,dc\=org
+ldap.admin.user.object.classes=organizationalPerson,top,person,inetOrgPerson
+ldap.admin.role.security.principal=cn\=%s,ou\=Role,dc\=fess,dc\=codelibs,dc\=org
+ldap.admin.role.object.classes=groupOfNames
+ldap.admin.group.security.principal=cn\=%s,ou\=Group,dc\=fess,dc\=codelibs,dc\=org
+ldap.admin.group.object.classes=groupOfNames
+ldap.admin.digest.algorism.prefix={SHA256}
+