View Javadoc
1   /**
2    * Waffle (https://github.com/dblock/waffle)
3    *
4    * Copyright (c) 2010 - 2015 Application Security, Inc.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   *
11   * Contributors:
12   *     Application Security, Inc.
13   */
14  package waffle.apache;
15  
16  import java.io.IOException;
17  import java.security.Principal;
18  import java.util.Arrays;
19  import java.util.LinkedHashSet;
20  import java.util.Locale;
21  import java.util.Set;
22  
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.apache.catalina.authenticator.AuthenticatorBase;
27  import org.apache.catalina.connector.Request;
28  import org.slf4j.Logger;
29  
30  import waffle.windows.auth.IWindowsAuthProvider;
31  import waffle.windows.auth.IWindowsIdentity;
32  import waffle.windows.auth.PrincipalFormat;
33  import waffle.windows.auth.impl.WindowsAuthProviderImpl;
34  
35  /**
36   * The Class WaffleAuthenticatorBase.
37   *
38   * @author dblock[at]dblock[dot]org
39   */
40  abstract class WaffleAuthenticatorBase extends AuthenticatorBase {
41  
42      /** The Constant SUPPORTED_PROTOCOLS. */
43      private static final Set<String> SUPPORTED_PROTOCOLS = new LinkedHashSet<String>(Arrays.asList("Negotiate", "NTLM"));
44  
45      /** The info. */
46      protected String                 info;
47      
48      /** The log. */
49      protected Logger                 log;
50      
51      /** The principal format. */
52      protected PrincipalFormat        principalFormat     = PrincipalFormat.FQN;
53      
54      /** The role format. */
55      protected PrincipalFormat        roleFormat          = PrincipalFormat.FQN;
56      
57      /** The allow guest login. */
58      protected boolean                allowGuestLogin     = true;
59      
60      /** The protocols. */
61      protected Set<String>            protocols           = WaffleAuthenticatorBase.SUPPORTED_PROTOCOLS;
62  
63      /** The auth. */
64      protected IWindowsAuthProvider   auth                = new WindowsAuthProviderImpl();
65  
66      /**
67       * Windows authentication provider.
68       * 
69       * @return IWindowsAuthProvider.
70       */
71      public IWindowsAuthProvider getAuth() {
72          return this.auth;
73      }
74  
75      /**
76       * Set Windows auth provider.
77       * 
78       * @param provider
79       *            Class implements IWindowsAuthProvider.
80       */
81      public void setAuth(final IWindowsAuthProvider provider) {
82          this.auth = provider;
83      }
84  
85      /* (non-Javadoc)
86       * @see org.apache.catalina.authenticator.AuthenticatorBase#getInfo()
87       */
88      @Override
89      public String getInfo() {
90          return this.info;
91      }
92  
93      /**
94       * Set the principal format.
95       * 
96       * @param format
97       *            Principal format.
98       */
99      public void setPrincipalFormat(final String format) {
100         this.principalFormat = PrincipalFormat.valueOf(format.toUpperCase(Locale.ENGLISH));
101         this.log.debug("principal format: {}", this.principalFormat);
102     }
103 
104     /**
105      * Principal format.
106      * 
107      * @return Principal format.
108      */
109     public PrincipalFormat getPrincipalFormat() {
110         return this.principalFormat;
111     }
112 
113     /**
114      * Set the principal format.
115      * 
116      * @param format
117      *            Role format.
118      */
119     public void setRoleFormat(final String format) {
120         this.roleFormat = PrincipalFormat.valueOf(format.toUpperCase(Locale.ENGLISH));
121         this.log.debug("role format: {}", this.roleFormat);
122     }
123 
124     /**
125      * Principal format.
126      * 
127      * @return Role format.
128      */
129     public PrincipalFormat getRoleFormat() {
130         return this.roleFormat;
131     }
132 
133     /**
134      * True if Guest login permitted.
135      * 
136      * @return True if Guest login permitted, false otherwise.
137      */
138     public boolean isAllowGuestLogin() {
139         return this.allowGuestLogin;
140     }
141 
142     /**
143      * Set whether Guest login is permitted. Default is true, if the Guest account is enabled, an invalid
144      * username/password results in a Guest login.
145      * 
146      * @param value
147      *            True or false.
148      */
149     public void setAllowGuestLogin(final boolean value) {
150         this.allowGuestLogin = value;
151     }
152 
153     /**
154      * Set the authentication protocols. Default is "Negotiate, NTLM".
155      * 
156      * @param value
157      *            Authentication protocols
158      */
159     public void setProtocols(final String value) {
160         this.protocols = new LinkedHashSet<String>();
161         final String[] protocolNames = value.split(",");
162         for (String protocolName : protocolNames) {
163             protocolName = protocolName.trim();
164             if (!protocolName.isEmpty()) {
165                 this.log.debug("init protocol: {}", protocolName);
166                 if (WaffleAuthenticatorBase.SUPPORTED_PROTOCOLS.contains(protocolName)) {
167                     this.protocols.add(protocolName);
168                 } else {
169                     this.log.error("unsupported protocol: {}", protocolName);
170                     throw new RuntimeException("Unsupported protocol: " + protocolName);
171                 }
172             }
173         }
174     }
175 
176     /**
177      * Send a 401 Unauthorized along with protocol authentication headers.
178      * 
179      * @param response
180      *            HTTP Response
181      */
182     protected void sendUnauthorized(final HttpServletResponse response) {
183         try {
184             for (final String protocol : this.protocols) {
185                 response.addHeader("WWW-Authenticate", protocol);
186             }
187             response.setHeader("Connection", "close");
188             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
189             response.flushBuffer();
190         } catch (final IOException e) {
191             throw new RuntimeException(e);
192         }
193     }
194 
195     /**
196      * Send an error code.
197      * 
198      * @param response
199      *            HTTP Response
200      * @param code
201      *            Error Code
202      */
203     protected void sendError(final HttpServletResponse response, final int code) {
204         try {
205             response.sendError(code);
206         } catch (final IOException e) {
207             this.log.error(e.getMessage());
208             this.log.trace("{}", e);
209             throw new RuntimeException(e);
210         }
211     }
212 
213     /* (non-Javadoc)
214      * @see org.apache.catalina.authenticator.AuthenticatorBase#getAuthMethod()
215      */
216     @Override
217     protected String getAuthMethod() {
218         return null;
219     }
220 
221     /* (non-Javadoc)
222      * @see org.apache.catalina.authenticator.AuthenticatorBase#doLogin(org.apache.catalina.connector.Request, java.lang.String, java.lang.String)
223      */
224     @Override
225     protected Principal doLogin(final Request request, final String username, final String password)
226             throws ServletException {
227         this.log.debug("logging in: {}", username);
228         IWindowsIdentity windowsIdentity;
229         try {
230             windowsIdentity = this.auth.logonUser(username, password);
231         } catch (final Exception e) {
232             this.log.error(e.getMessage());
233             this.log.trace("{}", e);
234             return super.doLogin(request, username, password);
235         }
236         // disable guest login
237         if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
238             this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
239             return super.doLogin(request, username, password);
240         }
241         try {
242             this.log.debug("successfully logged in {} ({})", username, windowsIdentity.getSidString());
243             final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
244                     this.principalFormat, this.roleFormat);
245             this.log.debug("roles: {}", windowsPrincipal.getRolesString());
246             return windowsPrincipal;
247         } finally {
248             windowsIdentity.dispose();
249         }
250     }
251 
252 }