Send git error messages to git client. Disable session creation for git

operation.
This commit is contained in:
robin shine 2013-10-02 16:13:56 +08:00
parent ff4dd0b7cf
commit 4e10bbc1b6
6 changed files with 104 additions and 91 deletions

View File

@ -12,6 +12,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.authz.UnauthorizedException;
@ -73,31 +75,30 @@ public class BasicAuthenticationFilter extends PathMatchingFilter {
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
throws ServletException, IOException { throws ServletException, IOException {
boolean sendChallenge = false; HttpServletResponse httpResponse = WebUtils.toHttp(response);
if (existing != null) { if (existing != null) {
if (ExceptionUtils.find(existing, UnauthenticatedException.class) != null) { if (ExceptionUtils.find(existing, UnauthenticatedException.class) != null) {
sendChallenge = true; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String authcHeader = HttpServletRequest.BASIC_AUTH + " realm=\"" + appName + "\"";
httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
existing = null;
} else if (ExceptionUtils.find(existing, UnauthorizedException.class) != null) { } else if (ExceptionUtils.find(existing, UnauthorizedException.class) != null) {
if (!SecurityUtils.getSubject().isAuthenticated()) { if (!SecurityUtils.getSubject().isAuthenticated()) {
sendChallenge = true; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String authcHeader = HttpServletRequest.BASIC_AUTH + " realm=\"" + appName + "\"";
httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
existing = null;
} }
} else if (ExceptionUtils.find(existing, IncorrectCredentialsException.class) != null) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Incorrect credentials.");
existing = null;
} else if (ExceptionUtils.find(existing, UnknownAccountException.class) != null) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unknown user name.");
existing = null;
} }
} }
if (sendChallenge) {
existing = null;
sendChallenge(request, response);
}
super.cleanup(request, response, existing); super.cleanup(request, response, existing);
} }
protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String authcHeader = HttpServletRequest.BASIC_AUTH + " realm=\"" + appName + "\"";
httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
return false;
}
} }

View File

@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.NamingStrategy;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -14,6 +15,7 @@ import com.pmease.commons.jetty.ServletConfigurator;
import com.pmease.commons.loader.AbstractPlugin; import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule; import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.shiro.AbstractRealm; import com.pmease.commons.shiro.AbstractRealm;
import com.pmease.commons.shiro.FilterChainConfigurator;
import com.pmease.commons.util.ClassUtils; import com.pmease.commons.util.ClassUtils;
import com.pmease.gitop.core.model.ModelLocator; import com.pmease.gitop.core.model.ModelLocator;
import com.pmease.gitop.core.permission.UserRealm; import com.pmease.gitop.core.permission.UserRealm;
@ -69,6 +71,18 @@ public class CoreModule extends AbstractPluginModule {
return Sets.newHashSet(); return Sets.newHashSet();
} }
}); });
contribute(FilterChainConfigurator.class, new FilterChainConfigurator() {
@Override
public void configure(FilterChainManager filterChainManager) {
filterChainManager.createChain("/**/info/refs", "noSessionCreation, authcBasic");
filterChainManager.createChain("/**/git-upload-pack", "noSessionCreation, authcBasic");
filterChainManager.createChain("/**/git-receive-pack", "noSessionCreation, authcBasic");
}
});
} }
@Override @Override

View File

@ -48,6 +48,13 @@ public class CoreServletConfigurator implements ServletConfigurator {
filterHolder = new FilterHolder(gitFilter); filterHolder = new FilterHolder(gitFilter);
context.addFilter(filterHolder, "/*", EnumSet.allOf(DispatcherType.class)); context.addFilter(filterHolder, "/*", EnumSet.allOf(DispatcherType.class));
/*
ServletHolder servletHolder = new ServletHolder(new GitServlet());
servletHolder.setInitParameter("export-all", "1");
servletHolder.setInitParameter("base-path", "w:\\temp\\storage\\1");
context.addServlet(servletHolder, "/git/*");
*/
} }
} }

View File

@ -16,12 +16,14 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.authz.UnauthorizedException;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
import org.eclipse.jgit.http.server.ServletUtils; import org.eclipse.jgit.http.server.ServletUtils;
import org.eclipse.jgit.transport.PacketLineOut; import org.eclipse.jgit.transport.PacketLineOut;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.pmease.commons.git.Git; import com.pmease.commons.git.Git;
import com.pmease.commons.util.GeneralException;
import com.pmease.gitop.core.manager.ProjectManager; import com.pmease.gitop.core.manager.ProjectManager;
import com.pmease.gitop.core.model.Project; import com.pmease.gitop.core.model.Project;
import com.pmease.gitop.core.permission.ObjectPermission; import com.pmease.gitop.core.permission.ObjectPermission;
@ -40,9 +42,13 @@ public class GitFilter implements Filter {
this.projectManager = projectManager; this.projectManager = projectManager;
} }
private Project getProject(HttpServletRequest request, HttpServletResponse response, String pathInfo, String repoInfo) private String getPathInfo(HttpServletRequest request) {
String pathInfo = request.getRequestURI().substring(request.getContextPath().length());
return StringUtils.stripStart(pathInfo, "/");
}
private Project getProject(HttpServletRequest request, HttpServletResponse response, String repoInfo)
throws IOException { throws IOException {
repoInfo = StringUtils.stripStart(StringUtils.stripEnd(repoInfo, "/"), "/"); repoInfo = StringUtils.stripStart(StringUtils.stripEnd(repoInfo, "/"), "/");
String ownerName = StringUtils.substringBefore(repoInfo, "/"); String ownerName = StringUtils.substringBefore(repoInfo, "/");
@ -50,11 +56,8 @@ public class GitFilter implements Filter {
if (StringUtils.isBlank(ownerName) || StringUtils.isBlank(projectName)) { if (StringUtils.isBlank(ownerName) || StringUtils.isBlank(projectName)) {
String url = request.getRequestURL().toString(); String url = request.getRequestURL().toString();
String urlRoot = url.substring(0, url.length()-pathInfo.length()); String urlRoot = url.substring(0, url.length()-getPathInfo(request).length());
String message = "Expecting url of format " + urlRoot + "<owner name>/<project name>"; throw new GeneralException("Expecting url of format %s<owner name>/<project name>", urlRoot);
logger.error("Error serving git request: " + message);
response.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
return null;
} }
if (projectName.endsWith(".git")) if (projectName.endsWith(".git"))
@ -62,10 +65,7 @@ public class GitFilter implements Filter {
Project project = projectManager.find(ownerName, projectName); Project project = projectManager.find(ownerName, projectName);
if (project == null) { if (project == null) {
String message = "Unable to find project '" + projectName + "' owned by '" + ownerName + "'."; throw new GeneralException("Unable to find project %s owned by %s.", projectName, ownerName);
logger.error("Error serving git request: " + message);
response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
return null;
} }
return project; return project;
@ -77,77 +77,64 @@ public class GitFilter implements Filter {
response.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate"); response.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
} }
protected void processPacks(HttpServletRequest req, HttpServletResponse resp, String pathInfo) throws ServletException, IOException { protected void processPacks(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = getPathInfo(request);
String service = StringUtils.substringAfterLast(pathInfo, "/"); String service = StringUtils.substringAfterLast(pathInfo, "/");
String repoInfo = StringUtils.substringBeforeLast(pathInfo, "/"); String repoInfo = StringUtils.substringBeforeLast(pathInfo, "/");
Project project = getProject(req, resp, pathInfo, repoInfo); Project project = getProject(request, response, repoInfo);
if (project != null) { doNotCache(response);
doNotCache(resp); response.setHeader("Content-Type", "application/x-" + service + "-result");
resp.setHeader("Content-Type", "application/x-" + service + "-result");
Git git = new Git(projectManager.locateStorage(project).ofCode()); Git git = new Git(projectManager.locateStorage(project).ofCode());
if (service.contains("upload")) { if (GitSmartHttpTools.isUploadPack(request)) {
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(project))) { if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(project))) {
throw new UnauthorizedException("You do not have permission to pull from this project."); throw new UnauthorizedException("You do not have permission to pull from this project.");
}
git.upload().input(ServletUtils.getInputStream(req)).output(resp.getOutputStream()).call();
} else if (service.contains("receive")) {
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectWrite(project))) {
throw new UnauthorizedException("You do not have permission to push to this project.");
}
git.receive().input(ServletUtils.getInputStream(req)).output(resp.getOutputStream()).call();
} else {
String message = "Invalid service name '" + service + "'.";
logger.error("Error serving git request: " + message);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
} }
git.upload().input(ServletUtils.getInputStream(request)).output(response.getOutputStream()).call();
} else {
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectWrite(project))) {
throw new UnauthorizedException("You do not have permission to push to this project.");
}
git.receive().input(ServletUtils.getInputStream(request)).output(response.getOutputStream()).call();
} }
} }
private void writeInitial(HttpServletResponse resp, String service) throws IOException { private void writeInitial(HttpServletResponse response, String service) throws IOException {
doNotCache(resp); doNotCache(response);
resp.setHeader("Content-Type", "application/x-" + service + "-advertisement"); response.setHeader("Content-Type", "application/x-" + service + "-advertisement");
PacketLineOut pack = new PacketLineOut(resp.getOutputStream()); PacketLineOut pack = new PacketLineOut(response.getOutputStream());
pack.setFlushOnEnd(false); pack.setFlushOnEnd(false);
pack.writeString("# service=" + service + "\n"); pack.writeString("# service=" + service + "\n");
pack.end(); pack.end();
} }
protected void processRefs(HttpServletRequest req, HttpServletResponse resp, String pathInfo) throws ServletException, IOException { protected void processRefs(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (!pathInfo.endsWith(INFO_REFS)) { String pathInfo = request.getRequestURI().substring(request.getContextPath().length());
String message = "Invalid refs request url: " + req.getRequestURL(); pathInfo = StringUtils.stripStart(pathInfo, "/");
logger.error("Error serving git request: " + message);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
return;
}
String repoInfo = pathInfo.substring(0, pathInfo.length() - INFO_REFS.length());
Project project = getProject(req, resp, pathInfo, repoInfo);
if (project != null) {
String service = req.getParameter("service");
Git git = new Git(projectManager.locateStorage(project).ofCode());
if (service.contains("upload")) { String repoInfo = pathInfo.substring(0, pathInfo.length() - INFO_REFS.length());
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(project))) { Project project = getProject(request, response, repoInfo);
throw new UnauthorizedException("You do not have permission to pull from this project."); String service = request.getParameter("service");
}
writeInitial(resp, service); Git git = new Git(projectManager.locateStorage(project).ofCode());
git.advertiseUploadRefs().output(resp.getOutputStream()).call();
} else if (service.contains("receive")) { if (service.contains("upload")) {
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectWrite(project))) { if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectRead(project))) {
throw new UnauthorizedException("You do not have permission to push to this project."); throw new UnauthorizedException("You do not have permission to pull from this project.");
}
writeInitial(resp, service);
git.advertiseReceiveRefs().output(resp.getOutputStream()).call();
} else {
String message = "Invalid service name '" + service + "'.";
logger.error("Error serving git request: " + message);
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
} }
writeInitial(response, service);
git.advertiseUploadRefs().output(response.getOutputStream()).call();
} else {
if (!SecurityUtils.getSubject().isPermitted(ObjectPermission.ofProjectWrite(project))) {
throw new UnauthorizedException("You do not have permission to push to this project.");
}
writeInitial(response, service);
git.advertiseReceiveRefs().output(response.getOutputStream()).call();
} }
} }
@ -160,15 +147,17 @@ public class GitFilter implements Filter {
FilterChain chain) throws IOException, ServletException { FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletResponse httpResponse = (HttpServletResponse) response;
String pathInfo = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length()); try {
pathInfo = StringUtils.stripStart(pathInfo, "/"); if (GitSmartHttpTools.isInfoRefs(httpRequest)) {
processRefs(httpRequest, httpResponse);
if (pathInfo.endsWith(INFO_REFS)) { } else if (GitSmartHttpTools.isReceivePack(httpRequest) || GitSmartHttpTools.isUploadPack(httpRequest)) {
processRefs(httpRequest, httpResponse, pathInfo); processPacks(httpRequest, httpResponse);
} else if (pathInfo.endsWith("git-receive-pack") || pathInfo.endsWith("git-upload-pack")) { } else {
processPacks(httpRequest, httpResponse, pathInfo); chain.doFilter(request, response);
} else { }
chain.doFilter(request, response); } catch (GeneralException e) {
logger.error("Error serving git request", e);
GitSmartHttpTools.sendError(httpRequest, httpResponse, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
} }
} }

View File

@ -60,7 +60,8 @@ public class AccountHomePage extends AbstractLayoutPage {
@Override @Override
public void detachModels() { public void detachModels() {
accountModel.detach(); if (accountModel != null)
accountModel.detach();
super.detachModels(); super.detachModels();
} }

View File

@ -54,7 +54,8 @@ public class ProjectHomePage extends AbstractLayoutPage {
@Override @Override
public void detachModels() { public void detachModels() {
projectModel.detach(); if (projectModel != null)
projectModel.detach();
super.detachModels(); super.detachModels();
} }