View Javadoc
1   /*
2    * Copyright 2012-2017 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.sso.spnego;
17  
18  import java.io.File;
19  import java.util.Enumeration;
20  
21  import javax.annotation.PostConstruct;
22  import javax.servlet.FilterConfig;
23  import javax.servlet.ServletContext;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.codelibs.core.io.ResourceUtil;
27  import org.codelibs.fess.app.web.base.login.ActionResponseCredential;
28  import org.codelibs.fess.app.web.base.login.SpnegoCredential;
29  import org.codelibs.fess.exception.FessSystemException;
30  import org.codelibs.fess.exception.SsoLoginException;
31  import org.codelibs.fess.mylasta.direction.FessConfig;
32  import org.codelibs.fess.sso.SsoAuthenticator;
33  import org.codelibs.fess.util.ComponentUtil;
34  import org.codelibs.spnego.SpnegoFilterConfig;
35  import org.codelibs.spnego.SpnegoHttpFilter;
36  import org.codelibs.spnego.SpnegoHttpFilter.Constants;
37  import org.codelibs.spnego.SpnegoHttpServletResponse;
38  import org.codelibs.spnego.SpnegoPrincipal;
39  import org.lastaflute.web.login.credential.LoginCredential;
40  import org.lastaflute.web.servlet.filter.RequestLoggingFilter;
41  import org.lastaflute.web.util.LaRequestUtil;
42  import org.lastaflute.web.util.LaResponseUtil;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  public class SpnegoAuthenticator implements SsoAuthenticator {
47      private static final Logger logger = LoggerFactory.getLogger(SpnegoAuthenticator.class);
48  
49      protected org.codelibs.spnego.SpnegoAuthenticator authenticator = null;
50  
51      @PostConstruct
52      public void init() {
53          if ("spnego".equals(ComponentUtil.getFessConfig().getSsoType())) {
54              try {
55                  // set some System properties
56                  final SpnegoFilterConfig config = SpnegoFilterConfig.getInstance(new SpengoConfig());
57  
58                  // pre-authenticate
59                  authenticator = new org.codelibs.spnego.SpnegoAuthenticator(config);
60              } catch (final Exception e) {
61                  throw new FessSystemException("Failed to initialize SPNEGO.", e);
62              }
63          }
64      }
65  
66      /* (non-Javadoc)
67       * @see org.codelibs.fess.sso.spnego.SsoAuthenticator#getLoginCredential()
68       */
69      @Override
70      public LoginCredential getLoginCredential() {
71          return LaRequestUtil
72                  .getOptionalRequest()
73                  .map(request -> {
74                      final HttpServletResponse response = LaResponseUtil.getResponse();
75                      final SpnegoHttpServletResponse spnegoResponse = new SpnegoHttpServletResponse(response);
76  
77                      // client/caller principal
78                      final SpnegoPrincipal principal;
79                      try {
80                          principal = authenticator.authenticate(request, spnegoResponse);
81                      } catch (final Exception e) {
82                          final String msg = "HTTP Authorization Header=" + request.getHeader(Constants.AUTHZ_HEADER);
83                          logger.error(msg);
84                          throw new SsoLoginException(msg, e);
85                      }
86  
87                      // context/auth loop not yet complete
88                      if (spnegoResponse.isStatusSet()) {
89                          return new ActionResponseCredential(() -> {
90                              throw new RequestLoggingFilter.RequestClientErrorException("Your request is not authorized.",
91                                      "401 Unauthorized", HttpServletResponse.SC_UNAUTHORIZED);
92                          });
93                      }
94  
95                      // assert
96                      if (null == principal) {
97                          final String msg = "Principal was null.";
98                          logger.error(msg);
99                          throw new SsoLoginException(msg);
100                     }
101 
102                     if (logger.isDebugEnabled()) {
103                         logger.debug("principal=" + principal);
104                     }
105 
106                     final String[] username = principal.getName().split("@", 2);
107                     return new SpnegoCredential(username[0]);
108                 }).orElseGet(() -> null);
109 
110     }
111 
112     protected class SpengoConfig implements FilterConfig {
113 
114         protected FessConfig fessConfig = ComponentUtil.getFessConfig();
115 
116         @Override
117         public String getFilterName() {
118             return SpnegoAuthenticator.class.getName();
119         }
120 
121         @Override
122         public ServletContext getServletContext() {
123             throw new UnsupportedOperationException();
124         }
125 
126         @Override
127         public String getInitParameter(final String name) {
128             if (SpnegoHttpFilter.Constants.LOGGER_LEVEL.equals(name)) {
129                 return fessConfig.getSpnegoLoggerLevel();
130             } else if (SpnegoHttpFilter.Constants.LOGIN_CONF.equals(name)) {
131                 return getResourcePath(fessConfig.getSpnegoLoginConf());
132             } else if (SpnegoHttpFilter.Constants.KRB5_CONF.equals(name)) {
133                 return getResourcePath(fessConfig.getSpnegoKrb5Conf());
134             } else if (SpnegoHttpFilter.Constants.CLIENT_MODULE.equals(name)) {
135                 return fessConfig.getSpnegoLoginClientModule();
136             } else if (SpnegoHttpFilter.Constants.SERVER_MODULE.equals(name)) {
137                 return fessConfig.getSpnegoLoginServerModule();
138             } else if (SpnegoHttpFilter.Constants.PREAUTH_USERNAME.equals(name)) {
139                 return fessConfig.getSpnegoPreauthUsername();
140             } else if (SpnegoHttpFilter.Constants.PREAUTH_PASSWORD.equals(name)) {
141                 return fessConfig.getSpnegoPreauthPassword();
142             } else if (SpnegoHttpFilter.Constants.ALLOW_BASIC.equals(name)) {
143                 return fessConfig.getSpnegoAllowBasic();
144             } else if (SpnegoHttpFilter.Constants.ALLOW_UNSEC_BASIC.equals(name)) {
145                 return fessConfig.getSpnegoAllowUnsecureBasic();
146             } else if (SpnegoHttpFilter.Constants.PROMPT_NTLM.equals(name)) {
147                 return fessConfig.getSpnegoPromptNtlm();
148             } else if (SpnegoHttpFilter.Constants.ALLOW_LOCALHOST.equals(name)) {
149                 return fessConfig.getSpnegoAllowLocalhost();
150             } else if (SpnegoHttpFilter.Constants.ALLOW_DELEGATION.equals(name)) {
151                 return fessConfig.getSpnegoAllowDelegation();
152             }
153             return null;
154         }
155 
156         protected String getResourcePath(final String path) {
157             final File file = ResourceUtil.getResourceAsFileNoException(path);
158             if (file != null) {
159                 return file.getAbsolutePath();
160             }
161             return null;
162         }
163 
164         @Override
165         public Enumeration<String> getInitParameterNames() {
166             throw new UnsupportedOperationException();
167         }
168 
169     }
170 }