improve security

This commit is contained in:
Sebastian Sdorra
2010-10-15 17:58:16 +02:00
parent e891d762fb
commit d0825b25c8
9 changed files with 469 additions and 101 deletions

View File

@@ -10,11 +10,12 @@ package sonia.scm.web.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.User;
import sonia.scm.util.Util;
import sonia.scm.web.security.Authenticator;
import sonia.scm.web.security.SecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -56,20 +57,6 @@ public class BasicAuthenticationFilter extends HttpFilter
/** Field description */
public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param authenticator
*/
@Inject
public BasicAuthenticationFilter(Authenticator authenticator)
{
this.authenticator = authenticator;
}
//~--- methods --------------------------------------------------------------
/**
@@ -88,32 +75,40 @@ public class BasicAuthenticationFilter extends HttpFilter
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
User user = authenticator.getUser(request);
SecurityContext securityContext = securityContextProvider.get();
User user = null;
if (user == null)
if (securityContext != null)
{
String authentication = request.getHeader(HEADER_AUTHORIZATION);
if (Util.isEmpty(authentication))
if (!securityContext.isAuthenticated())
{
sendUnauthorized(response);
String authentication = request.getHeader(HEADER_AUTHORIZATION);
if (Util.isEmpty(authentication))
{
sendUnauthorized(response);
}
else
{
if (!authentication.toUpperCase().startsWith(
AUTHORIZATION_BASIC_PREFIX))
{
throw new ServletException("wrong basic header");
}
String token = authentication.substring(6);
token = new String(Base64.decode(token.getBytes()));
String[] credentials = token.split(CREDENTIAL_SEPARATOR);
user = securityContext.authenticate(request, response,
credentials[0], credentials[1]);
}
}
else
{
if (!authentication.toUpperCase().startsWith(
AUTHORIZATION_BASIC_PREFIX))
{
throw new ServletException("wrong basic header");
}
String token = authentication.substring(6);
token = new String(Base64.decode(token.getBytes()));
String[] credentials = token.split(CREDENTIAL_SEPARATOR);
user = authenticator.authenticate(request, credentials[0],
credentials[1]);
user = securityContext.getUser();
}
}
@@ -145,5 +140,6 @@ public class BasicAuthenticationFilter extends HttpFilter
//~--- fields ---------------------------------------------------------------
/** Field description */
private Authenticator authenticator;
@Inject
private Provider<SecurityContext> securityContextProvider;
}

View File

@@ -14,6 +14,7 @@ import sonia.scm.User;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
@@ -27,23 +28,13 @@ public interface Authenticator
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
public User authenticate(HttpServletRequest request, String username,
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password);
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param request
*
* @return
*/
public User getUser(HttpServletRequest request);
}

View File

@@ -0,0 +1,85 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import sonia.scm.User;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@SessionScoped
public class BasicSecurityContext implements SecurityContext
{
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
{
user = authenticator.authenticate(request, response, username, password);
return user;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public User getUser()
{
return user;
}
/**
* Method description
*
*
* @return
*/
@Override
public boolean isAuthenticated()
{
return user != null;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@Inject
private Authenticator authenticator;
/** Field description */
private User user;
}

View File

@@ -0,0 +1,58 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.User;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
public interface SecurityContext
{
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password);
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public User getUser();
/**
* Method description
*
*
* @return
*/
public boolean isAuthenticated();
}

View File

@@ -10,21 +10,24 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import com.google.inject.servlet.ServletModule;
import sonia.scm.api.rest.UriExtensionsConfig;
import sonia.scm.cache.CacheManager;
import sonia.scm.cache.CacheRepositoryManagerDecorator;
import sonia.scm.cache.EhCacheManager;
import sonia.scm.filter.SecurityFilter;
import sonia.scm.plugin.SCMPluginManager;
import sonia.scm.plugin.ScriptResourceServlet;
import sonia.scm.repository.BasicRepositoryManager;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.util.DebugServlet;
import sonia.scm.web.ScmWebPluginContext;
import sonia.scm.web.security.Authenticator;
import sonia.scm.web.security.BasicSecurityContext;
import sonia.scm.web.security.DemoAuthenticator;
import sonia.scm.web.security.SecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -47,6 +50,9 @@ import java.util.logging.Logger;
public class ScmServletModule extends ServletModule
{
/** Field description */
public static final String PATTERN_DEBUG = "/debug.html";
/** Field description */
public static final String PATTERN_PAGE = "*.html";
@@ -100,6 +106,8 @@ public class ScmServletModule extends ServletModule
SCMContextProvider context = SCMContext.getContext();
bind(SCMContextProvider.class).toInstance(context);
bind(Authenticator.class).to(DemoAuthenticator.class);
bind(SecurityContext.class).to(BasicSecurityContext.class);
Multibinder<RepositoryHandler> repositoryHandlerBinder =
Multibinder.newSetBinder(binder(), RepositoryHandler.class);
@@ -123,7 +131,6 @@ public class ScmServletModule extends ServletModule
}
bind(CacheManager.class).to(EhCacheManager.class);
bind(Authenticator.class).to(DemoAuthenticator.class);
bind(RepositoryManager.class).annotatedWith(Undecorated.class).to(
BasicRepositoryManager.class);
bind(RepositoryManager.class).to(CacheRepositoryManagerDecorator.class);
@@ -135,6 +142,10 @@ public class ScmServletModule extends ServletModule
* filter(PATTERN_PAGE, PATTERN_COMPRESSABLE).through(GZipFilter.class);
* filter(PATTERN_RESTAPI).through(SecurityFilter.class);
*/
filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(SecurityFilter.class);
// debug servlet
serve(PATTERN_DEBUG).with(DebugServlet.class);
// plugin resources
serve(PATTERN_PLUGIN_SCRIPT).with(ScriptResourceServlet.class);

View File

@@ -16,11 +16,12 @@ import sonia.scm.ScmState;
import sonia.scm.User;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryType;
import sonia.scm.web.security.Authenticator;
import sonia.scm.web.security.SecurityContext;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
@@ -47,6 +48,7 @@ public class AuthenticationResource
*
*
* @param request
* @param response
* @param username
* @param password
*
@@ -54,11 +56,13 @@ public class AuthenticationResource
*/
@POST
public ScmState getState(@Context HttpServletRequest request,
@Context HttpServletResponse response,
@FormParam("username") String username,
@FormParam("password") String password)
{
ScmState state = null;
User user = authenticator.authenticate(request, username, password);
User user = securityContext.authenticate(request, response, username,
password);
if (user != null)
{
@@ -84,7 +88,7 @@ public class AuthenticationResource
public ScmState getState(@Context HttpServletRequest request)
{
ScmState state = null;
User user = authenticator.getUser(request);
User user = securityContext.getUser();
if (user != null)
{
@@ -122,9 +126,9 @@ public class AuthenticationResource
/** Field description */
@Inject
private Authenticator authenticator;
private RepositoryManager repositoryManger;
/** Field description */
@Inject
private RepositoryManager repositoryManger;
private SecurityContext securityContext;
}

View File

@@ -10,23 +10,20 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.User;
import sonia.scm.web.filter.HttpFilter;
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
import sonia.scm.web.security.Authenticator;
import sonia.scm.web.security.SecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.security.Principal;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
/**
@@ -58,26 +55,33 @@ public class SecurityFilter extends HttpFilter
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
String uri =
request.getRequestURI().substring(request.getContextPath().length());
SecurityContext securityContext = securityContextProvider.get();
if (!uri.startsWith(URL_AUTHENTICATION))
if (securityContext != null)
{
User user = authenticator.getUser(request);
String uri =
request.getRequestURI().substring(request.getContextPath().length());
if (user != null)
if (!uri.startsWith(URL_AUTHENTICATION))
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
response);
if (securityContext.isAuthenticated())
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
securityContext.getUser()), response);
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
chain.doFilter(request, response);
}
}
else
{
chain.doFilter(request, response);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
@@ -85,5 +89,5 @@ public class SecurityFilter extends HttpFilter
/** Field description */
@Inject
private Authenticator authenticator;
private Provider<SecurityContext> securityContextProvider;
}

View File

@@ -0,0 +1,245 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sonia.scm.util;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Singleton;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class DebugServlet extends HttpServlet
{
/** Field description */
private static final long serialVersionUID = -1918351712617918728L;
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
* @throws ServletException
*/
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}
/**
* Method description
*
*
* @param writer
* @param context
*/
private void appendAttributes(PrintWriter writer, AttributeContext context)
{
writer.append("<ul>");
Enumeration<?> enm = context.getNames();
while (enm.hasMoreElements())
{
String key = (String) enm.nextElement();
writer.append("<li>").append(key).append(" = ");
Object value = context.getValue(key);
writer.append("(").append(value.getClass().toString()).append(") ");
writer.append(value.toString()).append("</li>");
}
writer.append("</ul>");
}
/**
* Method description
*
*
* @param writer
*/
private void appendContextAttributes(PrintWriter writer)
{
writer.append("<h2>ServletContext Attributes</h2>");
final ServletContext context = getServletContext();
appendAttributes(writer, new AttributeContext()
{
@Override
public Enumeration<String> getNames()
{
return context.getAttributeNames();
}
@Override
public Object getValue(String name)
{
return context.getAttribute(name);
}
});
}
/**
* Method description
*
*
* @param writer
* @param session
*/
private void appendSessionAttributes(PrintWriter writer,
final HttpSession session)
{
writer.append("<h2>Session Attributes</h2>");
appendAttributes(writer, new AttributeContext()
{
@Override
public Enumeration<String> getNames()
{
return session.getAttributeNames();
}
@Override
public Object getValue(String name)
{
return session.getAttribute(name);
}
});
}
/**
* Method description
*
*
* @param writer
*/
private void printFooter(PrintWriter writer)
{
writer.append("</body></html>");
}
/**
* Method description
*
*
* @param writer
*/
private void printHeader(PrintWriter writer)
{
writer.append("<html>");
writer.append("<head><title>SCM Manaer :: Debugging</title></head>");
writer.append("<body><h1>SCM Manaer :: Debugging</h1>");
}
/**
* Method description
*
*
* @param request
* @param response
*
* @throws IOException
* @throws ServletException
*/
private void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter writer = null;
try
{
response.setContentType("text/html");
writer = response.getWriter();
printHeader(writer);
appendContextAttributes(writer);
HttpSession session = request.getSession();
appendSessionAttributes(writer, session);
printFooter(writer);
}
finally
{
writer.close();
}
}
//~--- inner interfaces -----------------------------------------------------
/**
* Interface description
*
*
* @version Enter version here..., 10/10/15
* @author Enter your name here...
*/
private interface AttributeContext
{
/**
* Method description
*
*
* @return
*/
public Enumeration<String> getNames();
/**
* Method description
*
*
* @param name
*
* @return
*/
public Object getValue(String name);
}
}

View File

@@ -14,7 +14,7 @@ import sonia.scm.User;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
/**
*
@@ -42,13 +42,15 @@ public class DemoAuthenticator implements Authenticator
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public User authenticate(HttpServletRequest request, String username,
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
{
User user = null;
@@ -56,34 +58,6 @@ public class DemoAuthenticator implements Authenticator
if (DEMO_USERNAME.equals(username) && DEMO_PASSWORD.equals(password))
{
user = new User(username, DEMO_DISPLAYNAME, DEMO_MAIL);
HttpSession session = request.getSession(true);
session.setAttribute(DemoAuthenticator.class.getName(), user);
}
return user;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param request
*
* @return
*/
@Override
public User getUser(HttpServletRequest request)
{
User user = null;
HttpSession session = request.getSession();
if (session != null)
{
user = (User) session.getAttribute(DemoAuthenticator.class.getName());
}
return user;