James Moger
2012-11-28 0bbdd9f9adf12ad9082a4c49ae1c9a0778b00bb4
commit | author | age
ca9d0f 1 /*
JM 2  * Copyright 2011 gitblit.com.
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, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit;
17
18 import java.io.IOException;
19 import java.nio.charset.Charset;
20 import java.security.Principal;
21 import java.text.MessageFormat;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import javax.servlet.Filter;
27 import javax.servlet.FilterChain;
28 import javax.servlet.FilterConfig;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import javax.servlet.http.HttpSession;
35
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.gitblit.models.UserModel;
773bb6 40 import com.gitblit.utils.Base64;
ca9d0f 41 import com.gitblit.utils.StringUtils;
JM 42
43 /**
44  * The AuthenticationFilter is a servlet filter that preprocesses requests that
45  * match its url pattern definition in the web.xml file.
46  * 
47  * http://en.wikipedia.org/wiki/Basic_access_authentication
48  * 
49  * @author James Moger
50  * 
51  */
52 public abstract class AuthenticationFilter implements Filter {
53
54     protected static final String BASIC = "Basic";
55
56     protected static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";
57
58     protected static final String SESSION_SECURED = "com.gitblit.secured";
59
60     protected transient Logger logger = LoggerFactory.getLogger(getClass());
61
62     /**
63      * doFilter does the actual work of preprocessing the request to ensure that
64      * the user may proceed.
65      * 
66      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
67      *      javax.servlet.ServletResponse, javax.servlet.FilterChain)
68      */
69     @Override
70     public abstract void doFilter(final ServletRequest request, final ServletResponse response,
71             final FilterChain chain) throws IOException, ServletException;
3983a6 72     
JM 73     /**
74      * Allow the filter to require a client certificate to continue processing.
75      * 
76      * @return true, if a client certificate is required
77      */
78     protected boolean requiresClientCertificate() {
79         return false;
80     }
ca9d0f 81
JM 82     /**
83      * Returns the full relative url of the request.
84      * 
85      * @param httpRequest
86      * @return url
87      */
88     protected String getFullUrl(HttpServletRequest httpRequest) {
89         String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();
90         String url = httpRequest.getRequestURI().substring(servletUrl.length());
91         String params = httpRequest.getQueryString();
92         if (url.length() > 0 && url.charAt(0) == '/') {
93             url = url.substring(1);
94         }
95         String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));
96         return fullUrl;
97     }
98
99     /**
100      * Returns the user making the request, if the user has authenticated.
101      * 
102      * @param httpRequest
103      * @return user
104      */
105     protected UserModel getUser(HttpServletRequest httpRequest) {
106         UserModel user = null;
3983a6 107         // try request authentication
JM 108         user = GitBlit.self().authenticate(httpRequest);
109         if (user != null) {
110             return user;
111         } else if (requiresClientCertificate()) {
112             // http request does not have a valid certificate
113             // and the filter requires one
114             return null;
115         }
116         
ca9d0f 117         // look for client authorization credentials in header
JM 118         final String authorization = httpRequest.getHeader("Authorization");
119         if (authorization != null && authorization.startsWith(BASIC)) {
120             // Authorization: Basic base64credentials
121             String base64Credentials = authorization.substring(BASIC.length()).trim();
122             String credentials = new String(Base64.decode(base64Credentials),
123                     Charset.forName("UTF-8"));
124             // credentials = username:password
2551e8 125             final String[] values = credentials.split(":",2);
ca9d0f 126
JM 127             if (values.length == 2) {
128                 String username = values[0];
129                 char[] password = values[1].toCharArray();
130                 user = GitBlit.self().authenticate(username, password);
131                 if (user != null) {
132                     return user;
133                 }
134             }
135             if (GitBlit.isDebugMode()) {
136                 logger.info(MessageFormat.format("AUTH: invalid credentials ({0})", credentials));
137             }
138         }
139         return null;
140     }
141
142     /**
143      * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()
144      */
145     @SuppressWarnings("unchecked")
146     protected void newSession(HttpServletRequest request, HttpServletResponse response) {
147         HttpSession oldSession = request.getSession(false);
148         if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {
149             synchronized (this) {
150                 Map<String, Object> attributes = new HashMap<String, Object>();
151                 Enumeration<String> e = oldSession.getAttributeNames();
152                 while (e.hasMoreElements()) {
153                     String name = e.nextElement();
154                     attributes.put(name, oldSession.getAttribute(name));
155                     oldSession.removeAttribute(name);
156                 }
157                 oldSession.invalidate();
158
159                 HttpSession newSession = request.getSession(true);
160                 newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);
161                 for (Map.Entry<String, Object> entry : attributes.entrySet()) {
162                     newSession.setAttribute(entry.getKey(), entry.getValue());
163                 }
164             }
165         }
166     }
167
168     /**
169      * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
170      */
171     @Override
172     public void init(final FilterConfig config) throws ServletException {
173     }
174
175     /**
176      * @see javax.servlet.Filter#destroy()
177      */
178     @Override
179     public void destroy() {
180     }
181
182     /**
183      * Wraps a standard HttpServletRequest and overrides user principal methods.
184      */
185     public static class AuthenticatedRequest extends ServletRequestWrapper {
186
187         private UserModel user;
188
189         public AuthenticatedRequest(HttpServletRequest req) {
190             super(req);
191             user = new UserModel("anonymous");
6adf56 192             user.isAuthenticated = false;
ca9d0f 193         }
efe8ec 194
ca9d0f 195         UserModel getUser() {
JM 196             return user;
197         }
198
199         void setUser(UserModel user) {
200             this.user = user;
201         }
202
203         @Override
204         public String getRemoteUser() {
205             return user.username;
206         }
207
208         @Override
209         public boolean isUserInRole(String role) {
210             if (role.equals(Constants.ADMIN_ROLE)) {
7f7051 211                 return user.canAdmin();
ca9d0f 212             }
efe8ec 213             // Gitblit does not currently use actual roles in the traditional
JM 214             // servlet container sense.  That is the reason this is marked
215             // deprecated, but I may want to revisit this.
ca9d0f 216             return user.canAccessRepository(role);
JM 217         }
218
219         @Override
220         public Principal getUserPrincipal() {
221             return user;
222         }
223     }
224 }