James Moger
2011-04-14 fc948cacef9c9b8c0a1e84cbc082ca67cd5f68d9
Authenticate the webapp against the same realm as the git servlet.

Right now the implementation is hard-coded to pass the realm into a
singleton. This won't work for a WAR distribution so I will
need to figure out how to properly authenticate the webapp using form
authentication and the git servlet using basic authentication - host
against the same realm.
3 files added
12 files modified
406 ■■■■■ changed files
gitblit.properties 19 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 127 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlitServer.java 47 ●●●● patch | view | raw | blame | history
src/com/gitblit/ILoginService.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/JettyLoginService.java 32 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.java 93 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebSession.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/LoginPage.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/RepositoryPage.java 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/User.java 27 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/PatchPage.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RawPage.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RepositoriesPage.java 17 ●●●● patch | view | raw | blame | history
users.properties 3 ●●●● patch | view | raw | blame | history
gitblit.properties
@@ -32,22 +32,13 @@
# Simple user realm file to authenticate users for push/pull
realmFile = users.properties
# User roles for push/pull git repository access
# (* is the wildcard for any role)
gitRoles = *
# User roles for administrative features such
# as create repository, edit repository description,
# and set repository owner.
# (* is the wildcard for any role)
adminRoles = *
#
# Server Settings
#
debug = true
debugMode = true
tempFolder = temp
log4jPattern = %-5p %d{MM-dd HH:mm:ss.SSS}  %-20.20c{1}  %m%n
# Aggressive heap management will run the garbage collector on every generated
# page.  This slows down page generation but improves heap consumption. 
aggressiveHeapManagement = true
@@ -56,7 +47,13 @@
# Git:Blit UI Settings
#
siteName =
# If authenticateWebUI=true, users with "admin" role can create repositories,
# create users, and edit repository metadata (owner, description, etc)
#
# If authenticateWebUI=false, any user can execute the aforementioned functions.
allowAdministration = true
repositoriesMessage = Welcome to Git:Blit!<br>A quick and easy way to host your own Git repositories.<br>Built with <a href="http://eclipse.org/jgit">JGit</a>, <a href="http://wicket.apache.org">Wicket</a>, <a href="http://code.google.com/p/google-code-prettify/">google-code-prettify</a>, <a href="http://eclipse.org/jetty">Jetty</a>, <a href="http://www.slf4j.org">SLF4J</a>, <a href="http://logging.apache.org/log4j">Log4j</a>, and <a href="http://jcommander.org">JCommander</a>.
# Use the client timezone when formatting dates.
src/com/gitblit/Constants.java
@@ -5,6 +5,12 @@
    public final static String NAME = "Git:Blit";
    public final static String VERSION = "0.0.1";
    public final static String ADMIN_ROLE = "admin";
    public final static String PULL_ROLE = "pull";
    public final static String PUSH_ROLE = "push";
    public static String getGitBlitVersion() {
        return NAME + " v" + VERSION;
src/com/gitblit/GitBlit.java
New file
@@ -0,0 +1,127 @@
package com.gitblit;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Request;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.http.server.resolver.FileResolver;
import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.User;
import com.gitblit.wicket.models.RepositoryModel;
public class GitBlit {
    private static GitBlit gitblit;
    private final Logger logger = LoggerFactory.getLogger(GitBlit.class);
    private final boolean debugMode;
    private final FileResolver repositoryResolver;
    private final File repositories;
    private final boolean exportAll;
    private ILoginService loginService;
    public static GitBlit self() {
        if (gitblit == null) {
            gitblit = new GitBlit();
        }
        return gitblit;
    }
    private GitBlit() {
        repositories = new File(StoredSettings.getString("repositoriesFolder", "repos"));
        exportAll = StoredSettings.getBoolean("exportAll", true);
        repositoryResolver = new FileResolver(repositories, exportAll);
        debugMode = StoredSettings.getBoolean("debugMode", false);
    }
    public boolean isDebugMode() {
        return debugMode;
    }
    public void setLoginService(ILoginService loginService) {
        this.loginService = loginService;
    }
    public User authenticate(String username, char[] password) {
        if (loginService == null) {
            return null;
        }
        return loginService.authenticate(username, password);
    }
    public User authenticate(Cookie[] cookies) {
        if (loginService == null) {
            return null;
        }
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(Constants.NAME)) {
                    String value = cookie.getValue();
                    return loginService.authenticate(value.toCharArray());
                }
            }
        }
        return null;
    }
    public void setCookie(WebResponse response, User user) {
        Cookie userCookie = new Cookie(Constants.NAME, user.getCookie());
        userCookie.setMaxAge(Integer.MAX_VALUE);
        userCookie.setPath("/");
        response.addCookie(userCookie);
    }
    public List<String> getRepositoryList() {
        return JGitUtils.getRepositoryList(repositories, exportAll, StoredSettings.getBoolean("nestedRepositories", true));
    }
    public List<RepositoryModel> getRepositories(Request request) {
        List<String> list = getRepositoryList();
        ServletWebRequest servletWebRequest = (ServletWebRequest) request;
        HttpServletRequest req = servletWebRequest.getHttpServletRequest();
        List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
        for (String repo : list) {
            Repository r = getRepository(req, repo);
            String description = JGitUtils.getRepositoryDescription(r);
            String owner = JGitUtils.getRepositoryOwner(r);
            Date lastchange = JGitUtils.getLastChange(r);
            r.close();
            repositories.add(new RepositoryModel(repo, description, owner, lastchange));
        }
        return repositories;
    }
    public Repository getRepository(HttpServletRequest req, String repositoryName) {
        Repository r = null;
        try {
            r = repositoryResolver.open(req, repositoryName);
        } catch (RepositoryNotFoundException e) {
            r = null;
            logger.error("Failed to find repository " + repositoryName);
            e.printStackTrace();
        } catch (ServiceNotEnabledException e) {
            r = null;
            e.printStackTrace();
        }
        return r;
    }
}
src/com/gitblit/GitBlitServer.java
@@ -21,7 +21,7 @@
import org.eclipse.jetty.http.security.Constraint;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
@@ -50,11 +50,6 @@
    private final static Logger logger = Log.getLogger(GitBlitServer.class.getSimpleName());
    private final static String border_star = "***********************************************************";
    private static boolean debugMode = false;
    public static boolean isDebugMode() {
        return debugMode;
    }
    public static void main(String[] args) {
        Params params = new Params();
@@ -111,6 +106,9 @@
     * Start Server.
     */
    private static void start(Params params) {
        // instantiate GitBlit
        GitBlit.self();
        PatternLayout layout = new PatternLayout(StoredSettings.getString("log4jPattern", "%-5p %d{MM-dd HH:mm:ss.SSS}  %-20.20c{1}  %m%n"));
        org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
        rootLogger.addAppender(new ConsoleAppender(layout));
@@ -123,7 +121,7 @@
        String osversion = System.getProperty("os.version");
        logger.info("Running on " + osname + " (" + osversion + ")");
        if (params.debug) {
        if (StoredSettings.getBoolean("debugMode", false)) {
            logger.warn("DEBUG Mode");
        }
@@ -173,9 +171,8 @@
        FilterHolder wicketFilter = new FilterHolder(WicketFilter.class);
        wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM, GitBlitWebApp.class.getName());
        wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);
        wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");
        rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);
        Handler handler;
        // Git Servlet
        ServletHolder gitServlet = null;
@@ -184,20 +181,25 @@
            gitServlet = rootContext.addServlet(GitServlet.class, gitServletPathSpec);
            gitServlet.setInitParameter("base-path", params.repositoriesFolder);
            gitServlet.setInitParameter("export-all", params.exportAll ? "1" : "0");
            String realmUsers = params.realmFile;
            if (realmUsers != null && new File(realmUsers).exists() && params.authenticatePushPull) {
        }
        // Login Service
        LoginService loginService = null;
        String realmUsers = params.realmFile;
        if (realmUsers != null && new File(realmUsers).exists()) {
            logger.info("Setting up login service from " + realmUsers);
            JettyLoginService jettyLoginService = new JettyLoginService(realmUsers);
            GitBlit.self().setLoginService(jettyLoginService);
            loginService = jettyLoginService;
        }
        // Determine what handler to use
        Handler handler;
        if (gitServlet != null) {
            if (loginService != null && params.authenticatePushPull) {
                // Authenticate Pull/Push
                List<String> list = StoredSettings.getStrings("gitRoles");
                String[] roles;
                if (list.size() == 0) {
                    roles = new String[] { "*" };
                } else {
                    roles = list.toArray(new String[list.size()]);
                }
                String[] roles = new String[] { Constants.PULL_ROLE, Constants.PUSH_ROLE };
                logger.info("Authentication required for git servlet pull/push access");
                logger.info("Setting up realm from " + realmUsers);
                HashLoginService loginService = new HashLoginService(Constants.NAME, realmUsers);
                Constraint constraint = new Constraint();
                constraint.setName("auth");
@@ -355,9 +357,6 @@
        @Parameter(names = { "--temp" }, description = "Server temp folder")
        public String temp = StoredSettings.getString("tempFolder", "temp");
        @Parameter(names = { "--debug" }, description = "Run server in DEBUG mode")
        public Boolean debug = StoredSettings.getBoolean("debug", false);
        /*
         * GIT Servlet Parameters
src/com/gitblit/ILoginService.java
New file
@@ -0,0 +1,10 @@
package com.gitblit;
import com.gitblit.wicket.User;
public interface ILoginService {
    User authenticate(String username, char [] password);
    User authenticate(char [] cookie);
}
src/com/gitblit/JettyLoginService.java
New file
@@ -0,0 +1,32 @@
package com.gitblit;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.UserIdentity;
import com.gitblit.wicket.User;
public class JettyLoginService extends HashLoginService implements ILoginService {
    public JettyLoginService(String realmFile) {
        super(Constants.NAME, realmFile);
    }
    @Override
    public User authenticate(String username, char[] password) {
        UserIdentity identity = login(username, new String(password));
        if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) {
            return null;
        }
        User user = new User(username, password);
        user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null));
        user.canClone(identity.isUserInRole(Constants.PULL_ROLE, null));
        user.canPush(identity.isUserInRole(Constants.PUSH_ROLE, null));
        return user;
    }
    @Override
    public User authenticate(char [] cookie) {
        // TODO cookie login
        return null;
    }
}
src/com/gitblit/wicket/GitBlitWebApp.java
@@ -1,36 +1,17 @@
package com.gitblit.wicket;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Application;
import org.apache.wicket.Page;
import org.apache.wicket.Request;
import org.apache.wicket.Response;
import org.apache.wicket.Session;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.request.urlcompressing.UrlCompressingWebRequestProcessor;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.IRequestCycleProcessor;
import org.apache.wicket.request.target.coding.MixedParamUrlCodingStrategy;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.http.server.resolver.FileResolver;
import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants;
import com.gitblit.GitBlitServer;
import com.gitblit.GitBlit;
import com.gitblit.StoredSettings;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.models.RepositoryModel;
import com.gitblit.wicket.pages.BlobDiffPage;
import com.gitblit.wicket.pages.BlobPage;
import com.gitblit.wicket.pages.BranchesPage;
@@ -48,14 +29,6 @@
import com.gitblit.wicket.pages.TreePage;
public class GitBlitWebApp extends WebApplication {
    Logger logger = LoggerFactory.getLogger(GitBlitWebApp.class);
    FileResolver repositoryResolver;
    private File repositories;
    private boolean exportAll;
    @Override
    public void init() {
@@ -92,10 +65,6 @@
        mount(new MixedParamUrlCodingStrategy("/ticgittkt", TicGitTicketPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/login", LoginPage.class, new String[] {}));
        repositories = new File(StoredSettings.getString("repositoriesFolder", "repos"));
        exportAll = StoredSettings.getBoolean("exportAll", true);
        repositoryResolver = new FileResolver(repositories, exportAll);
    }
    @Override
@@ -115,67 +84,9 @@
    @Override
    public final String getConfigurationType() {
        if (GitBlitServer.isDebugMode())
        if (GitBlit.self().isDebugMode())
            return Application.DEVELOPMENT;
        return Application.DEPLOYMENT;
    }
    public User authenticate(String username, char [] password) {
        return new User(username, password);
    }
    public User authenticate(Cookie[] cookies) {
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie:cookies) {
                if (cookie.getName().equals(Constants.NAME)) {
                    String value = cookie.getValue();
                }
            }
        }
        return null;
    }
    public void setCookie(WebResponse response, User user) {
        Cookie userCookie = new Cookie(Constants.NAME, user.getCookie());
        userCookie.setMaxAge(Integer.MAX_VALUE);
        userCookie.setPath("/");
        response.addCookie(userCookie);
    }
    public List<String> getRepositoryList() {
        return JGitUtils.getRepositoryList(repositories, exportAll, StoredSettings.getBoolean("nestedRepositories", true));
    }
    public List<RepositoryModel> getRepositories(Request request) {
        List<String> list = getRepositoryList();
        ServletWebRequest servletWebRequest = (ServletWebRequest) request;
        HttpServletRequest req = servletWebRequest.getHttpServletRequest();
        List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
        for (String repo : list) {
            Repository r = getRepository(req, repo);
            String description = JGitUtils.getRepositoryDescription(r);
            String owner = JGitUtils.getRepositoryOwner(r);
            Date lastchange = JGitUtils.getLastChange(r);
            r.close();
            repositories.add(new RepositoryModel(repo, description, owner, lastchange));
        }
        return repositories;
    }
    public Repository getRepository(HttpServletRequest req, String repositoryName) {
        Repository r = null;
        try {
            r = repositoryResolver.open(req, repositoryName);
        } catch (RepositoryNotFoundException e) {
            r = null;
            logger.error("Failed to find repository " + repositoryName);
            e.printStackTrace();
        } catch (ServiceNotEnabledException e) {
            r = null;
            e.printStackTrace();
        }
        return r;
    }
    public String getCloneUrl(String repositoryName) {
src/com/gitblit/wicket/GitBlitWebSession.java
@@ -28,6 +28,13 @@
        return user != null;
    }
    
    public boolean canAdmin() {
        if (user == null) {
            return false;
        }
        return user.canAdmin();
    }
    public User getUser() {
        return user;
    }
src/com/gitblit/wicket/LoginPage.java
@@ -18,6 +18,7 @@
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import com.gitblit.Constants;
import com.gitblit.GitBlit;
public class LoginPage extends WebPage {
@@ -58,7 +59,7 @@
            String username = LoginPage.this.username.getObject();
            char [] password = LoginPage.this.password.getObject().toCharArray();
            User user = GitBlitWebApp.get().authenticate(username, password);
            User user = GitBlit.self().authenticate(username, password);
            if (user == null)
                error("Invalid username or password!");
            else
@@ -72,7 +73,7 @@
        // Grab cookie from Browser Session
        Cookie[] cookies = ((WebRequest) getRequestCycle().getRequest()).getCookies();
        if (cookies != null && cookies.length > 0) {
            user = GitBlitWebApp.get().authenticate(cookies);
            user = GitBlit.self().authenticate(cookies);
        }
        // Login the user
@@ -85,7 +86,7 @@
            // Set Cookie
            WebResponse response = (WebResponse) getRequestCycle().getResponse();
            GitBlitWebApp.get().setCookie(response, user);
            GitBlit.self().setCookie(response, user);
            
            // track user object so that we do not have to continue
            // re-authenticating on each request.
src/com/gitblit/wicket/RepositoryPage.java
@@ -12,6 +12,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit;
import com.gitblit.StoredSettings;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.pages.RepositoriesPage;
@@ -51,7 +52,7 @@
            HttpServletRequest req = servletWebRequest.getHttpServletRequest();
            req.getServerName();
            Repository r = GitBlitWebApp.get().getRepository(req, repositoryName);
            Repository r = GitBlit.self().getRepository(req, repositoryName);
            if (r == null) {
                error("Can not load repository " + repositoryName);
                redirectToInterceptPage(new RepositoriesPage());
src/com/gitblit/wicket/User.java
@@ -7,12 +7,39 @@
    
    private String username;
    private char [] password;
    private boolean canAdmin = false;
    private boolean canClone = false;
    private boolean canPush = false;
    
    public User(String username, char [] password) {
        this.username = username;
        this.password = password;
    }
    
    public void canAdmin(boolean value) {
        canAdmin = value;
    }
    public boolean canAdmin() {
        return canAdmin;
    }
    public void canClone(boolean value) {
        canClone = value;
    }
    public boolean canClone() {
        return canClone;
    }
    public void canPush(boolean value) {
        canPush = value;
    }
    public boolean canPush() {
        return canPush;
    }
    public String getCookie() {
        return Build.getSHA1((Constants.NAME + username + new String(password)).getBytes());
    }
src/com/gitblit/wicket/pages/PatchPage.java
@@ -9,8 +9,8 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.WicketUtils;
@@ -31,7 +31,7 @@
        HttpServletRequest req = servletWebRequest.getHttpServletRequest();
        req.getServerName();
        Repository r = GitBlitWebApp.get().getRepository(req, repositoryName);
        Repository r = GitBlit.self().getRepository(req, repositoryName);
        if (r == null) {
            error("Can not load repository " + repositoryName);
            redirectToInterceptPage(new RepositoriesPage());
src/com/gitblit/wicket/pages/RawPage.java
@@ -13,9 +13,9 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.GitBlit;
import com.gitblit.StoredSettings;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.WicketUtils;
@@ -36,7 +36,7 @@
        HttpServletRequest req = servletWebRequest.getHttpServletRequest();
        req.getServerName();
        Repository r = GitBlitWebApp.get().getRepository(req, repositoryName);
        Repository r = GitBlit.self().getRepository(req, repositoryName);
        if (r == null) {
            error("Can not load repository " + repositoryName);
            redirectToInterceptPage(new RepositoriesPage());
src/com/gitblit/wicket/pages/RepositoriesPage.java
@@ -17,10 +17,11 @@
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import com.gitblit.GitBlit;
import com.gitblit.StoredSettings;
import com.gitblit.utils.Utils;
import com.gitblit.wicket.BasePage;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.LinkPanel;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.models.RepositoryModel;
@@ -32,14 +33,22 @@
        super();
        setupPage("", "");
        
        boolean showAdmin = false;
        if (StoredSettings.getBoolean("authenticateWebUI", true)) {
            boolean allowAdmin = StoredSettings.getBoolean("allowAdministration", false);
            showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
        } else {
            showAdmin = StoredSettings.getBoolean("allowAdministration", false);
        }
        Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
        adminLinks.add(new BookmarkablePageLink<Void>("newRepository", RepositoriesPage.class));
        adminLinks.add(new BookmarkablePageLink<Void>("newUser", RepositoriesPage.class));
        add(adminLinks.setVisible(StoredSettings.getBoolean("allowAdministration", false)));
        adminLinks.add(new BookmarkablePageLink<Void>("newUser", RepositoriesPage.class));
        add(adminLinks.setVisible(showAdmin));
        
        add(new Label("repositoriesMessage", StoredSettings.getString("repositoriesMessage", "")).setEscapeModelStrings(false));
        List<RepositoryModel> rows = GitBlitWebApp.get().getRepositories(getRequest());
        List<RepositoryModel> rows = GitBlit.self().getRepositories(getRequest());
        DataProvider dp = new DataProvider(rows);
        DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repository", dp) {
            private static final long serialVersionUID = 1L;
users.properties
@@ -1 +1,2 @@
test: test
test: test,pull
admin: admin,pull,push,admin