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      /**
86       * Gets the info.
87       *
88       * @return the info
89       */
90      public String getInfo() {
91          return this.info;
92      }
93  
94      /**
95       * Set the principal format.
96       * 
97       * @param format
98       *            Principal format.
99       */
100     public void setPrincipalFormat(final String format) {
101         this.principalFormat = PrincipalFormat.valueOf(format.toUpperCase(Locale.ENGLISH));
102         this.log.debug("principal format: {}", this.principalFormat);
103     }
104 
105     /**
106      * Principal format.
107      * 
108      * @return Principal format.
109      */
110     public PrincipalFormat getPrincipalFormat() {
111         return this.principalFormat;
112     }
113 
114     /**
115      * Set the principal format.
116      * 
117      * @param format
118      *            Role format.
119      */
120     public void setRoleFormat(final String format) {
121         this.roleFormat = PrincipalFormat.valueOf(format.toUpperCase(Locale.ENGLISH));
122         this.log.debug("role format: {}", this.roleFormat);
123     }
124 
125     /**
126      * Principal format.
127      * 
128      * @return Role format.
129      */
130     public PrincipalFormat getRoleFormat() {
131         return this.roleFormat;
132     }
133 
134     /**
135      * True if Guest login permitted.
136      * 
137      * @return True if Guest login permitted, false otherwise.
138      */
139     public boolean isAllowGuestLogin() {
140         return this.allowGuestLogin;
141     }
142 
143     /**
144      * Set whether Guest login is permitted. Default is true, if the Guest account is enabled, an invalid
145      * username/password results in a Guest login.
146      * 
147      * @param value
148      *            True or false.
149      */
150     public void setAllowGuestLogin(final boolean value) {
151         this.allowGuestLogin = value;
152     }
153 
154     /**
155      * Set the authentication protocols. Default is "Negotiate, NTLM".
156      * 
157      * @param value
158      *            Authentication protocols
159      */
160     public void setProtocols(final String value) {
161         this.protocols = new LinkedHashSet<String>();
162         final String[] protocolNames = value.split(",");
163         for (String protocolName : protocolNames) {
164             protocolName = protocolName.trim();
165             if (!protocolName.isEmpty()) {
166                 this.log.debug("init protocol: {}", protocolName);
167                 if (WaffleAuthenticatorBase.SUPPORTED_PROTOCOLS.contains(protocolName)) {
168                     this.protocols.add(protocolName);
169                 } else {
170                     this.log.error("unsupported protocol: {}", protocolName);
171                     throw new RuntimeException("Unsupported protocol: " + protocolName);
172                 }
173             }
174         }
175     }
176 
177     /**
178      * Send a 401 Unauthorized along with protocol authentication headers.
179      * 
180      * @param response
181      *            HTTP Response
182      */
183     protected void sendUnauthorized(final HttpServletResponse response) {
184         try {
185             for (final String protocol : this.protocols) {
186                 response.addHeader("WWW-Authenticate", protocol);
187             }
188             response.setHeader("Connection", "close");
189             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
190             response.flushBuffer();
191         } catch (final IOException e) {
192             throw new RuntimeException(e);
193         }
194     }
195 
196     /**
197      * Send an error code.
198      * 
199      * @param response
200      *            HTTP Response
201      * @param code
202      *            Error Code
203      */
204     protected void sendError(final HttpServletResponse response, final int code) {
205         try {
206             response.sendError(code);
207         } catch (final IOException e) {
208             this.log.error(e.getMessage());
209             this.log.trace("{}", e);
210             throw new RuntimeException(e);
211         }
212     }
213 
214     /* (non-Javadoc)
215      * @see org.apache.catalina.authenticator.AuthenticatorBase#getAuthMethod()
216      */
217     @Override
218     protected String getAuthMethod() {
219         return null;
220     }
221 
222     /* (non-Javadoc)
223      * @see org.apache.catalina.authenticator.AuthenticatorBase#doLogin(org.apache.catalina.connector.Request, java.lang.String, java.lang.String)
224      */
225     @Override
226     protected Principal doLogin(final Request request, final String username, final String password)
227             throws ServletException {
228         this.log.debug("logging in: {}", username);
229         IWindowsIdentity windowsIdentity;
230         try {
231             windowsIdentity = this.auth.logonUser(username, password);
232         } catch (final Exception e) {
233             this.log.error(e.getMessage());
234             this.log.trace("{}", e);
235             return super.doLogin(request, username, password);
236         }
237         // disable guest login
238         if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
239             this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
240             return super.doLogin(request, username, password);
241         }
242         try {
243             this.log.debug("successfully logged in {} ({})", username, windowsIdentity.getSidString());
244             final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
245                     this.principalFormat, this.roleFormat);
246             this.log.debug("roles: {}", windowsPrincipal.getRolesString());
247             return windowsPrincipal;
248         } finally {
249             windowsIdentity.dispose();
250         }
251     }
252 
253 }