浏览代码

#571 add SsoAuthenticator

Shinsuke Sugaya 9 年之前
父节点
当前提交
3eeb4957c9

+ 36 - 0
src/main/java/org/codelibs/fess/app/web/base/login/EmptyLoginCredential.java

@@ -0,0 +1,36 @@
+/*
+ * 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.app.web.base.login;
+
+import java.util.Collections;
+
+public class EmptyLoginCredential implements LoginCredential {
+
+    @Override
+    public void validate() {
+    }
+
+    @Override
+    public String getId() {
+        return "empty";
+    }
+
+    @Override
+    public Object getResource() {
+        return Collections.emptyMap();
+    }
+
+}

+ 2 - 2
src/main/java/org/codelibs/fess/app/web/base/login/FessLoginAssist.java

@@ -175,8 +175,8 @@ public class FessLoginAssist extends TypicalLoginAssist<String, FessUserBean, Fe
                 }
             }
             return doFindLoginUser(username, encryptPassword(password));
-        } else if (credential instanceof SSOLoginCredential) {
-            final String username = ((SSOLoginCredential) credential).getUsername();
+        } else if (credential instanceof SsoLoginCredential) {
+            final String username = ((SsoLoginCredential) credential).getUsername();
             if (!fessConfig.isAdminUser(username)) {
                 return ComponentUtil.getLdapManager().login(username);
             }

+ 2 - 2
src/main/java/org/codelibs/fess/app/web/base/login/SSOLoginCredential.java → src/main/java/org/codelibs/fess/app/web/base/login/SsoLoginCredential.java

@@ -17,12 +17,12 @@ package org.codelibs.fess.app.web.base.login;
 
 import org.dbflute.util.DfCollectionUtil;
 
-public class SSOLoginCredential implements LoginCredential {
+public class SsoLoginCredential implements LoginCredential {
     private final String username;
 
     // private Principal principal;
 
-    public SSOLoginCredential(final String username) {
+    public SsoLoginCredential(final String username) {
         this.username = username;
     }
 

+ 13 - 8
src/main/java/org/codelibs/fess/app/web/sso/SsoAction.java

@@ -16,12 +16,14 @@
 package org.codelibs.fess.app.web.sso;
 
 import org.codelibs.fess.app.web.base.FessLoginAction;
-import org.codelibs.fess.app.web.base.login.SSOLoginCredential;
+import org.codelibs.fess.app.web.base.login.EmptyLoginCredential;
+import org.codelibs.fess.app.web.base.login.LoginCredential;
 import org.codelibs.fess.app.web.login.LoginAction;
-import org.jsoup.helper.StringUtil;
+import org.codelibs.fess.sso.SsoAuthenticator;
+import org.codelibs.fess.util.ComponentUtil;
 import org.lastaflute.web.Execute;
 import org.lastaflute.web.login.exception.LoginFailureException;
-import org.lastaflute.web.response.HtmlResponse;
+import org.lastaflute.web.response.ActionResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,17 +38,20 @@ public class SsoAction extends FessLoginAction {
     //                                                                      ==============
 
     @Execute
-    public HtmlResponse index() {
-        final String user = request.getRemoteUser();
-        if (StringUtil.isBlank(user)) {
+    public ActionResponse index() {
+        final SsoAuthenticator authenticator = ComponentUtil.getSsoAuthenticator();
+        LoginCredential loginCredential = authenticator.getLoginCredential();
+        if (loginCredential == null) {
             if (logger.isDebugEnabled()) {
-                logger.debug("No remote user in SSO request.");
+                logger.debug("No user in SSO request.");
             }
             saveError(messages -> messages.addErrorsSsoLoginError(GLOBAL));
             return redirect(LoginAction.class);
+        } else if (loginCredential instanceof EmptyLoginCredential) {
+            return null;
         }
         try {
-            return fessLoginAssist.loginRedirect(new SSOLoginCredential(user), op -> {}, () -> {
+            return fessLoginAssist.loginRedirect(loginCredential, op -> {}, () -> {
                 activityHelper.login(getUserBean());
                 return getHtmlResponse();
             });

+ 30 - 0
src/main/java/org/codelibs/fess/exception/SsoLoginException.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 SsoLoginException extends FessSystemException {
+
+    private static final long serialVersionUID = 1L;
+
+    public SsoLoginException(String message) {
+        super(message);
+    }
+
+    public SsoLoginException(String message, Exception e) {
+        super(message, e);
+    }
+
+}

+ 0 - 172
src/main/java/org/codelibs/fess/filter/SpnegoFilter.java

@@ -1,172 +0,0 @@
-/*
- * 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.filter;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Enumeration;
-
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.codelibs.core.exception.IORuntimeException;
-import org.codelibs.core.io.ResourceUtil;
-import org.codelibs.core.lang.StringUtil;
-import org.codelibs.fess.exception.FessSystemException;
-import org.codelibs.fess.exception.ServletRuntimeException;
-import org.codelibs.fess.mylasta.action.FessUserBean;
-import org.codelibs.fess.util.ComponentUtil;
-import org.codelibs.spnego.SpnegoHttpFilter;
-import org.codelibs.spnego.SpnegoHttpServletRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * SpnegoFilter supports Integrated Windows Authentication(SSO).
- * 
- * @author shinsuke
- */
-public class SpnegoFilter extends SpnegoHttpFilter {
-
-    private static final Logger logger = LoggerFactory.getLogger(SpnegoFilter.class);
-
-    @Override
-    public void init(final FilterConfig filterConfig) throws ServletException {
-        ComponentUtil.processAfterContainerInit(() -> {
-            if (ComponentUtil.getFessConfig().isSsoEnabled()) {
-                initAuthenticator(filterConfig);
-            }
-        });
-    }
-
-    private void initAuthenticator(final FilterConfig filterConfig) {
-        try {
-            super.init(new FilterConfig() {
-
-                @Override
-                public ServletContext getServletContext() {
-                    return filterConfig.getServletContext();
-                }
-
-                @Override
-                public Enumeration<String> getInitParameterNames() {
-                    return filterConfig.getInitParameterNames();
-                }
-
-                @Override
-                public String getInitParameter(final String name) {
-                    if (Constants.KRB5_CONF.equals(name)) {
-                        final String krb5Conf = ComponentUtil.getFessConfig().getSpnegoKrb5Conf();
-                        if (StringUtil.isNotBlank(krb5Conf)) {
-                            final File file = ResourceUtil.getResourceAsFileNoException(krb5Conf);
-                            if (file != null) {
-                                return file.getAbsolutePath();
-                            }
-                        }
-                    } else if (Constants.LOGIN_CONF.equals(name)) {
-                        final String loginConf = ComponentUtil.getFessConfig().getSpnegoLoginConf();
-                        final File file = ResourceUtil.getResourceAsFileNoException(loginConf);
-                        if (file != null) {
-                            return file.getAbsolutePath();
-                        }
-                    } else if (Constants.PREAUTH_USERNAME.equals(name)) {
-                        final String username = ComponentUtil.getFessConfig().getSpnegoPreauthUsername();
-                        if (StringUtil.isNotBlank(username)) {
-                            return username;
-                        }
-                    } else if (Constants.PREAUTH_PASSWORD.equals(name)) {
-                        final String password = ComponentUtil.getFessConfig().getSpnegoPreauthPassword();
-                        if (StringUtil.isNotBlank(password)) {
-                            return password;
-                        }
-                    }
-                    return filterConfig.getInitParameter(name);
-                }
-
-                @Override
-                public String getFilterName() {
-                    return filterConfig.getFilterName();
-                }
-            });
-        } catch (final ServletException e) {
-            throw new FessSystemException("Initialization failed.", e);
-        }
-    }
-
-    @Override
-    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException,
-            ServletException {
-        try {
-            ComponentUtil.getRequestManager().findUserBean(FessUserBean.class).ifPresent(u -> {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("process your request as " + u.getUserId());
-                }
-                doFilter(() -> chain.doFilter(request, response));
-            }).orElse(() -> {
-                if (ComponentUtil.getFessConfig().isSsoEnabled()) {
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("process authentication");
-                    }
-                    doFilter(() -> SpnegoFilter.super.doFilter(request, response, chain));
-                } else {
-                    doFilter(() -> chain.doFilter(request, response));
-                }
-            });
-        } catch (final IORuntimeException e) {
-            throw (IOException) e.getCause();
-        } catch (final ServletRuntimeException e) {
-            throw (ServletException) e.getCause();
-        }
-    }
-
-    @Override
-    protected void processRequest(final SpnegoHttpServletRequest request, final ServletResponse response, final FilterChain chain)
-            throws IOException, ServletException {
-        if (StringUtil.isNotBlank(request.getRemoteUser())) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("log in as " + request.getRemoteUser());
-            }
-            // TODO save path and parameters into session
-            final RequestDispatcher dispatcher = request.getRequestDispatcher(ComponentUtil.getFessConfig().getSsoLoginPath());
-            dispatcher.forward(request, response);
-        } else {
-            if (logger.isDebugEnabled()) {
-                logger.debug("this request is not logged in.");
-            }
-            chain.doFilter(request, response);
-        }
-    }
-
-    protected void doFilter(final DoFilterCallback callback) {
-        try {
-            callback.run();
-        } catch (final IOException e) {
-            throw new IORuntimeException(e);
-        } catch (final ServletException e) {
-            throw new ServletRuntimeException(e);
-        }
-    }
-
-    interface DoFilterCallback {
-
-        void run() throws IOException, ServletException;
-    }
-}

+ 172 - 7
src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java

@@ -931,8 +931,8 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     /** The key of the configuration. e.g. false */
     String SSO_ENABLED = "sso.enabled";
 
-    /** The key of the configuration. e.g. /sso/ */
-    String SSO_LOGIN_PATH = "sso.login.path";
+    /** The key of the configuration. e.g. 0 */
+    String SPNEGO_LOGGER_LEVEL = "spnego.logger.level";
 
     /** The key of the configuration. e.g. krb5.conf */
     String SPNEGO_KRB5_CONF = "spnego.krb5.conf";
@@ -946,6 +946,27 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     /** The key of the configuration. e.g. password */
     String SPNEGO_PREAUTH_PASSWORD = "spnego.preauth.password";
 
+    /** The key of the configuration. e.g. spnego-client */
+    String SPNEGO_LOGIN_CLIENT_MODULE = "spnego.login.client.module";
+
+    /** The key of the configuration. e.g. spnego-server */
+    String SPNEGO_LOGIN_SERVER_MODULE = "spnego.login.server.module";
+
+    /** The key of the configuration. e.g. true */
+    String SPNEGO_ALLOW_BASIC = "spnego.allow.basic";
+
+    /** The key of the configuration. e.g. true */
+    String SPNEGO_ALLOW_UNSECURE_BASIC = "spnego.allow.unsecure.basic";
+
+    /** The key of the configuration. e.g. true */
+    String SPNEGO_PROMPT_NTLM = "spnego.prompt.ntlm";
+
+    /** The key of the configuration. e.g. true */
+    String SPNEGO_ALLOW_LOCALHOST = "spnego.allow.localhost";
+
+    /** The key of the configuration. e.g. false */
+    String SPNEGO_ALLOW_DELEGATION = "spnego.allow.delegation";
+
     /**
      * Get the value of property as {@link String}.
      * @param propertyKey The key of the property. (NotNull)
@@ -3757,11 +3778,19 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
     boolean isSsoEnabled();
 
     /**
-     * Get the value for the key 'sso.login.path'. <br>
-     * The value is, e.g. /sso/ <br>
+     * Get the value for the key 'spnego.logger.level'. <br>
+     * The value is, e.g. 0 <br>
      * @return The value of found property. (NotNull: if not found, exception but basically no way)
      */
-    String getSsoLoginPath();
+    String getSpnegoLoggerLevel();
+
+    /**
+     * Get the value for the key 'spnego.logger.level' as {@link Integer}. <br>
+     * The value is, e.g. 0 <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getSpnegoLoggerLevelAsInteger();
 
     /**
      * Get the value for the key 'spnego.krb5.conf'. <br>
@@ -3791,6 +3820,90 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
      */
     String getSpnegoPreauthPassword();
 
+    /**
+     * Get the value for the key 'spnego.login.client.module'. <br>
+     * The value is, e.g. spnego-client <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoLoginClientModule();
+
+    /**
+     * Get the value for the key 'spnego.login.server.module'. <br>
+     * The value is, e.g. spnego-server <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoLoginServerModule();
+
+    /**
+     * Get the value for the key 'spnego.allow.basic'. <br>
+     * The value is, e.g. true <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoAllowBasic();
+
+    /**
+     * Is the property for the key 'spnego.allow.basic' true? <br>
+     * The value is, e.g. true <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isSpnegoAllowBasic();
+
+    /**
+     * Get the value for the key 'spnego.allow.unsecure.basic'. <br>
+     * The value is, e.g. true <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoAllowUnsecureBasic();
+
+    /**
+     * Is the property for the key 'spnego.allow.unsecure.basic' true? <br>
+     * The value is, e.g. true <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isSpnegoAllowUnsecureBasic();
+
+    /**
+     * Get the value for the key 'spnego.prompt.ntlm'. <br>
+     * The value is, e.g. true <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoPromptNtlm();
+
+    /**
+     * Is the property for the key 'spnego.prompt.ntlm' true? <br>
+     * The value is, e.g. true <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isSpnegoPromptNtlm();
+
+    /**
+     * Get the value for the key 'spnego.allow.localhost'. <br>
+     * The value is, e.g. true <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoAllowLocalhost();
+
+    /**
+     * Is the property for the key 'spnego.allow.localhost' true? <br>
+     * The value is, e.g. true <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isSpnegoAllowLocalhost();
+
+    /**
+     * Get the value for the key 'spnego.allow.delegation'. <br>
+     * The value is, e.g. false <br>
+     * @return The value of found property. (NotNull: if not found, exception but basically no way)
+     */
+    String getSpnegoAllowDelegation();
+
+    /**
+     * Is the property for the key 'spnego.allow.delegation' true? <br>
+     * The value is, e.g. false <br>
+     * @return The determination, true or false. (if not found, exception but basically no way)
+     */
+    boolean isSpnegoAllowDelegation();
+
     /**
      * The simple implementation for configuration.
      * @author FreeGen
@@ -5280,8 +5393,12 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
             return is(FessConfig.SSO_ENABLED);
         }
 
-        public String getSsoLoginPath() {
-            return get(FessConfig.SSO_LOGIN_PATH);
+        public String getSpnegoLoggerLevel() {
+            return get(FessConfig.SPNEGO_LOGGER_LEVEL);
+        }
+
+        public Integer getSpnegoLoggerLevelAsInteger() {
+            return getAsInteger(FessConfig.SPNEGO_LOGGER_LEVEL);
         }
 
         public String getSpnegoKrb5Conf() {
@@ -5299,5 +5416,53 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
         public String getSpnegoPreauthPassword() {
             return get(FessConfig.SPNEGO_PREAUTH_PASSWORD);
         }
+
+        public String getSpnegoLoginClientModule() {
+            return get(FessConfig.SPNEGO_LOGIN_CLIENT_MODULE);
+        }
+
+        public String getSpnegoLoginServerModule() {
+            return get(FessConfig.SPNEGO_LOGIN_SERVER_MODULE);
+        }
+
+        public String getSpnegoAllowBasic() {
+            return get(FessConfig.SPNEGO_ALLOW_BASIC);
+        }
+
+        public boolean isSpnegoAllowBasic() {
+            return is(FessConfig.SPNEGO_ALLOW_BASIC);
+        }
+
+        public String getSpnegoAllowUnsecureBasic() {
+            return get(FessConfig.SPNEGO_ALLOW_UNSECURE_BASIC);
+        }
+
+        public boolean isSpnegoAllowUnsecureBasic() {
+            return is(FessConfig.SPNEGO_ALLOW_UNSECURE_BASIC);
+        }
+
+        public String getSpnegoPromptNtlm() {
+            return get(FessConfig.SPNEGO_PROMPT_NTLM);
+        }
+
+        public boolean isSpnegoPromptNtlm() {
+            return is(FessConfig.SPNEGO_PROMPT_NTLM);
+        }
+
+        public String getSpnegoAllowLocalhost() {
+            return get(FessConfig.SPNEGO_ALLOW_LOCALHOST);
+        }
+
+        public boolean isSpnegoAllowLocalhost() {
+            return is(FessConfig.SPNEGO_ALLOW_LOCALHOST);
+        }
+
+        public String getSpnegoAllowDelegation() {
+            return get(FessConfig.SPNEGO_ALLOW_DELEGATION);
+        }
+
+        public boolean isSpnegoAllowDelegation() {
+            return is(FessConfig.SPNEGO_ALLOW_DELEGATION);
+        }
     }
 }

+ 24 - 0
src/main/java/org/codelibs/fess/sso/SsoAuthenticator.java

@@ -0,0 +1,24 @@
+/*
+ * 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.sso;
+
+import org.codelibs.fess.app.web.base.login.LoginCredential;
+
+public interface SsoAuthenticator {
+
+    LoginCredential getLoginCredential();
+
+}

+ 162 - 0
src/main/java/org/codelibs/fess/sso/spnego/SpnegoAuthenticator.java

@@ -0,0 +1,162 @@
+/*
+ * 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.sso.spnego;
+
+import java.util.Enumeration;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletResponse;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.app.web.base.login.EmptyLoginCredential;
+import org.codelibs.fess.app.web.base.login.LoginCredential;
+import org.codelibs.fess.app.web.base.login.SsoLoginCredential;
+import org.codelibs.fess.exception.FessSystemException;
+import org.codelibs.fess.exception.SsoLoginException;
+import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.sso.SsoAuthenticator;
+import org.codelibs.fess.util.ComponentUtil;
+import org.codelibs.spnego.SpnegoFilterConfig;
+import org.codelibs.spnego.SpnegoHttpFilter;
+import org.codelibs.spnego.SpnegoHttpFilter.Constants;
+import org.codelibs.spnego.SpnegoHttpServletResponse;
+import org.codelibs.spnego.SpnegoPrincipal;
+import org.lastaflute.web.util.LaRequestUtil;
+import org.lastaflute.web.util.LaResponseUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SpnegoAuthenticator implements SsoAuthenticator {
+    private static final Logger logger = LoggerFactory.getLogger(SpnegoAuthenticator.class);
+
+    protected org.codelibs.spnego.SpnegoAuthenticator authenticator = null;
+
+    @PostConstruct
+    public void init() {
+        if (ComponentUtil.getFessConfig().isSsoEnabled()) {
+            try {
+                // set some System properties
+                final SpnegoFilterConfig config = SpnegoFilterConfig.getInstance(new SpengoConfig());
+
+                // pre-authenticate
+                authenticator = new org.codelibs.spnego.SpnegoAuthenticator(config);
+            } catch (Exception e) {
+                throw new FessSystemException("Failed to initialize SPNEGO.", e);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.codelibs.fess.sso.spnego.SsoAuthenticator#getLoginCredential()
+     */
+    @Override
+    public LoginCredential getLoginCredential() {
+        if (!ComponentUtil.getFessConfig().isSsoEnabled()) {
+            return null;
+        }
+
+        return LaRequestUtil.getOptionalRequest().map(request -> {
+            final HttpServletResponse response = LaResponseUtil.getResponse();
+            final SpnegoHttpServletResponse spnegoResponse = new SpnegoHttpServletResponse(response);
+
+            // client/caller principal
+                final SpnegoPrincipal principal;
+                try {
+                    principal = authenticator.authenticate(request, spnegoResponse);
+                } catch (Exception e) {
+                    final String msg = "HTTP Authorization Header=" + request.getHeader(Constants.AUTHZ_HEADER);
+                    logger.error(msg);
+                    throw new SsoLoginException(msg, e);
+                }
+
+                // context/auth loop not yet complete
+                if (spnegoResponse.isStatusSet()) {
+                    return new EmptyLoginCredential();
+                }
+
+                // assert
+                if (null == principal) {
+                    String msg = "Principal was null.";
+                    logger.error(msg);
+                    throw new SsoLoginException(msg);
+                }
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("principal=" + principal);
+                }
+
+                final String username = LaRequestUtil.getOptionalRequest().map(r -> r.getRemoteUser()).orElseGet(() -> null);
+                if (StringUtil.isBlank(username)) {
+                    return null;
+                }
+                return new SsoLoginCredential(username);
+            }).orElseGet(() -> null);
+
+    }
+
+    protected class SpengoConfig implements FilterConfig {
+
+        protected FessConfig fessConfig = ComponentUtil.getFessConfig();
+
+        @Override
+        public String getFilterName() {
+            return SpnegoAuthenticator.class.getName();
+        }
+
+        @Override
+        public ServletContext getServletContext() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getInitParameter(String name) {
+            if (SpnegoHttpFilter.Constants.LOGGER_LEVEL.equals(name)) {
+                return fessConfig.getSpnegoLoggerLevel();
+            } else if (SpnegoHttpFilter.Constants.LOGIN_CONF.equals(name)) {
+                return fessConfig.getSpnegoLoginConf();
+            } else if (SpnegoHttpFilter.Constants.KRB5_CONF.equals(name)) {
+                return fessConfig.getSpnegoKrb5Conf();
+            } else if (SpnegoHttpFilter.Constants.CLIENT_MODULE.equals(name)) {
+                return fessConfig.getSpnegoLoginClientModule();
+            } else if (SpnegoHttpFilter.Constants.SERVER_MODULE.equals(name)) {
+                return fessConfig.getSpnegoLoginServerModule();
+            } else if (SpnegoHttpFilter.Constants.PREAUTH_USERNAME.equals(name)) {
+                return fessConfig.getSpnegoPreauthUsername();
+            } else if (SpnegoHttpFilter.Constants.PREAUTH_PASSWORD.equals(name)) {
+                return fessConfig.getSpnegoPreauthPassword();
+            } else if (SpnegoHttpFilter.Constants.ALLOW_BASIC.equals(name)) {
+                return fessConfig.getSpnegoAllowBasic();
+            } else if (SpnegoHttpFilter.Constants.ALLOW_UNSEC_BASIC.equals(name)) {
+                return fessConfig.getSpnegoAllowUnsecureBasic();
+            } else if (SpnegoHttpFilter.Constants.PROMPT_NTLM.equals(name)) {
+                return fessConfig.getSpnegoPromptNtlm();
+            } else if (SpnegoHttpFilter.Constants.ALLOW_LOCALHOST.equals(name)) {
+                return fessConfig.getSpnegoAllowLocalhost();
+            } else if (SpnegoHttpFilter.Constants.ALLOW_DELEGATION.equals(name)) {
+                return fessConfig.getSpnegoAllowDelegation();
+            }
+            return null;
+        }
+
+        @Override
+        public Enumeration<String> getInitParameterNames() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+}

+ 8 - 0
src/main/java/org/codelibs/fess/util/ComponentUtil.java

@@ -59,6 +59,7 @@ import org.codelibs.fess.indexer.IndexUpdater;
 import org.codelibs.fess.job.JobExecutor;
 import org.codelibs.fess.ldap.LdapManager;
 import org.codelibs.fess.mylasta.direction.FessConfig;
+import org.codelibs.fess.sso.SsoAuthenticator;
 import org.lastaflute.core.message.MessageManager;
 import org.lastaflute.di.core.SingletonLaContainer;
 import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
@@ -69,8 +70,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public final class ComponentUtil {
+
     private static final Logger logger = LoggerFactory.getLogger(ComponentUtil.class);
 
+    private static final String SSO_AUTHENTICATOR = "ssoAuthenticator";
+
     private static final String PERMISSION_HELPER = "permissionHelper";
 
     private static final String QUERY_PARSER = "queryParser";
@@ -356,6 +360,10 @@ public final class ComponentUtil {
         return getComponent(PERMISSION_HELPER);
     }
 
+    public static SsoAuthenticator getSsoAuthenticator() {
+        return getComponent(SSO_AUTHENTICATOR);
+    }
+
     public static CrawlerClientFactory getCrawlerClientFactory() {
         return getComponent(CrawlerClientFactory.class);
     }

+ 1 - 0
src/main/resources/app.xml

@@ -12,6 +12,7 @@
 	<include path="fess_dict.xml"/>
 	<include path="fess_job.xml"/>
 	<include path="fess_screenshot.xml"/>
+	<include path="fess_sso.xml"/>
 
 	<include path="crawler/client.xml" />
 	<include path="crawler/mimetype.xml" />

+ 9 - 1
src/main/resources/fess_config.properties

@@ -473,9 +473,17 @@ ldap.attr.homeDirectory=homeDirectory
 #                                                      SSO
 #                                                     ------
 sso.enabled=false
-sso.login.path=/sso/
+spnego.logger.level=0
 spnego.krb5.conf=krb5.conf
 spnego.login.conf=auth_login.conf
 spnego.preauth.username=username
 spnego.preauth.password=password
+spnego.login.client.module=spnego-client
+spnego.login.server.module=spnego-server
+spnego.allow.basic=true
+spnego.allow.unsecure.basic=true
+spnego.prompt.ntlm=true
+spnego.allow.localhost=true
+spnego.allow.delegation=false
+
 

+ 8 - 0
src/main/resources/fess_sso.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE components PUBLIC "-//DBFLUTE//DTD LastaDi 1.0//EN"
+	"http://dbflute.org/meta/lastadi10.dtd">
+<components>
+	<component name="ssoAuthenticator" class="org.codelibs.fess.sso.spnego.SpnegoAuthenticator">
+	</component>
+
+</components>

+ 0 - 39
src/main/webapp/WEB-INF/web.xml

@@ -55,39 +55,6 @@
     <filter-class>org.lastaflute.web.servlet.filter.LastaToActionFilter</filter-class>
   </filter>
 
-  <filter>
-    <filter-name>spnegoFilter</filter-name>
-    <filter-class>org.codelibs.fess.filter.SpnegoFilter</filter-class>
-    <init-param>
-      <param-name>spnego.allow.basic</param-name>
-      <param-value>true</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.allow.localhost</param-name>
-      <param-value>true</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.allow.unsecure.basic</param-name>
-      <param-value>true</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.login.client.module</param-name>
-      <param-value>spnego-client</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.login.server.module</param-name>
-      <param-value>spnego-server</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.prompt.ntlm</param-name>
-      <param-value>true</param-value>
-    </init-param>
-    <init-param>
-      <param-name>spnego.logger.level</param-name>
-      <param-value>0</param-value>
-    </init-param>
-  </filter>
-
   <!-- ================================================================================= -->
   <!--                                                                    Filter Mapping -->
   <!--                                                                    ============== -->
@@ -106,12 +73,6 @@
     <dispatcher>INCLUDE</dispatcher>
   </filter-mapping>
 
-  <filter-mapping>
-    <filter-name>spnegoFilter</filter-name>
-    <url-pattern>/*</url-pattern>
-    <dispatcher>REQUEST</dispatcher>
-  </filter-mapping>
-
   <filter-mapping>
     <filter-name>webApiFilter</filter-name>
     <url-pattern>/*</url-pattern>