Start of webapp authentication.
diff --git a/gitblit.properties b/gitblit.properties
index b06adc2..a2e9ab6 100644
--- a/gitblit.properties
+++ b/gitblit.properties
@@ -26,6 +26,9 @@
 # Require authentication for http/https push/pull access of git repositories

 authenticatePushPull = true

 

+# Require authentication to see the web ui

+authenticateWebUI = true

+

 # Simple user realm file to authenticate users for push/pull

 realmFile = users.properties

 

diff --git a/src/com/gitblit/Build.java b/src/com/gitblit/Build.java
index 08202ba..0af7390 100644
--- a/src/com/gitblit/Build.java
+++ b/src/com/gitblit/Build.java
@@ -100,7 +100,7 @@
 	 *            the byte array

 	 * @return the SHA1 checksum

 	 */

-	private static String getSHA1(byte[] data) {

+	public static String getSHA1(byte[] data) {

 		MessageDigest md;

 		try {

 			md = MessageDigest.getInstance("SHA-1");

diff --git a/src/com/gitblit/wicket/SecuredPage.java b/src/com/gitblit/wicket/AdminPage.java
similarity index 88%
rename from src/com/gitblit/wicket/SecuredPage.java
rename to src/com/gitblit/wicket/AdminPage.java
index c3153a6..2f8345b 100644
--- a/src/com/gitblit/wicket/SecuredPage.java
+++ b/src/com/gitblit/wicket/AdminPage.java
@@ -7,5 +7,5 @@
 

 @Retention(RetentionPolicy.RUNTIME)

 @Target(ElementType.TYPE)

-public @interface SecuredPage {

+public @interface AdminPage {

 }

diff --git a/src/com/gitblit/wicket/AuthorizationStrategy.java b/src/com/gitblit/wicket/AuthorizationStrategy.java
new file mode 100644
index 0000000..b99ad6d
--- /dev/null
+++ b/src/com/gitblit/wicket/AuthorizationStrategy.java
@@ -0,0 +1,45 @@
+package com.gitblit.wicket;

+

+import org.apache.wicket.Component;

+import org.apache.wicket.RestartResponseAtInterceptPageException;

+import org.apache.wicket.authorization.IUnauthorizedComponentInstantiationListener;

+import org.apache.wicket.authorization.strategies.page.AbstractPageAuthorizationStrategy;

+

+import com.gitblit.wicket.pages.RepositoriesPage;

+

+public class AuthorizationStrategy extends AbstractPageAuthorizationStrategy implements IUnauthorizedComponentInstantiationListener {

+

+	public AuthorizationStrategy() {

+	}

+

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	@Override

+	protected boolean isPageAuthorized(Class pageClass) {

+		if (BasePage.class.isAssignableFrom(pageClass))

+			return isAuthorized(pageClass);

+		// Return contruction by default

+		return true;

+	}

+

+	@Override

+	public void onUnauthorizedInstantiation(Component component) {

+		if (component instanceof BasePage) {			

+			GitBlitWebSession session = GitBlitWebSession.get();			

+			if (!session.isLoggedIn())

+				throw new RestartResponseAtInterceptPageException(LoginPage.class);

+			else

+				throw new RestartResponseAtInterceptPageException(RepositoriesPage.class);

+		}

+	}

+

+	protected boolean isAuthorized(Class<? extends BasePage> pageClass) {

+		GitBlitWebSession session = GitBlitWebSession.get();

+		if (!session.isLoggedIn())

+			return false;

+		User user = session.getUser();

+		if (pageClass.isAnnotationPresent(AdminPage.class)) {

+			

+		}

+		return true;

+	}

+}

diff --git a/src/com/gitblit/wicket/BasePage.html b/src/com/gitblit/wicket/BasePage.html
index dabdb56..9ca9f13 100644
--- a/src/com/gitblit/wicket/BasePage.html
+++ b/src/com/gitblit/wicket/BasePage.html
@@ -32,7 +32,7 @@
 			<div style="float:right">

 				<a href="http://gitblit.com"><span wicket:id="gbVersion"></span></a> 

 			</div>

-			<div wicket:id="footerText">[footer text]</div>

+			<div wicket:id="userText">[user text]</div>

 		</div>

 	</body>

 </html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/BasePage.java b/src/com/gitblit/wicket/BasePage.java
index 3121804..8084b33 100644
--- a/src/com/gitblit/wicket/BasePage.java
+++ b/src/com/gitblit/wicket/BasePage.java
@@ -26,7 +26,7 @@
 	public BasePage(PageParameters params) {

 		super(params);

 	}

-		

+	

 	protected void setupPage(String repositoryName, String pageName) {

 		if (repositoryName != null && repositoryName.trim().length() > 0) {

 			add(new Label("title", getServerName() + " - " + repositoryName));

@@ -43,7 +43,13 @@
 		add(new Label("pageName", pageName));

 		

 		// footer

-		add(new Label("footerText", ""));

+		User user = null;

+		if (StoredSettings.getBoolean("authenticateWebUI", true)) {

+			user = GitBlitWebSession.get().getUser();

+			add(new Label("userText", "Logout " + user.toString()));

+		} else {

+			add(new Label("userText", ""));

+		}

 		add(new Label("gbVersion", "v" + Constants.VERSION));

 		if (StoredSettings.getBoolean("aggressiveHeapManagement", false)) {

 			System.gc();

diff --git a/src/com/gitblit/wicket/GitBlitWebApp.java b/src/com/gitblit/wicket/GitBlitWebApp.java
index a1de7d6..2bd3179 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/com/gitblit/wicket/GitBlitWebApp.java
@@ -5,6 +5,7 @@
 import java.util.Date;

 import java.util.List;

 

+import javax.servlet.http.Cookie;

 import javax.servlet.http.HttpServletRequest;

 

 import org.apache.wicket.Application;

@@ -13,6 +14,7 @@
 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;

@@ -24,6 +26,7 @@
 import org.slf4j.Logger;

 import org.slf4j.LoggerFactory;

 

+import com.gitblit.Constants;

 import com.gitblit.GitBlitServer;

 import com.gitblit.StoredSettings;

 import com.gitblit.utils.JGitUtils;

@@ -31,8 +34,8 @@
 import com.gitblit.wicket.pages.BlobDiffPage;

 import com.gitblit.wicket.pages.BlobPage;

 import com.gitblit.wicket.pages.BranchesPage;

-import com.gitblit.wicket.pages.CommitPage;

 import com.gitblit.wicket.pages.CommitDiffPage;

+import com.gitblit.wicket.pages.CommitPage;

 import com.gitblit.wicket.pages.LogPage;

 import com.gitblit.wicket.pages.PatchPage;

 import com.gitblit.wicket.pages.RawPage;

@@ -44,11 +47,8 @@
 import com.gitblit.wicket.pages.TicGitTicketPage;

 import com.gitblit.wicket.pages.TreePage;

 

-

 public class GitBlitWebApp extends WebApplication {

 

-	public static int PAGING_ITEM_COUNT = 50;

-

 	Logger logger = LoggerFactory.getLogger(GitBlitWebApp.class);

 

 	FileResolver repositoryResolver;

@@ -61,8 +61,17 @@
 	public void init() {

 		super.init();

 

+		// Setup page authorization mechanism

+		if (StoredSettings.getBoolean("authenticateWebUI", false)) {

+			AuthorizationStrategy authStrategy = new AuthorizationStrategy();

+			getSecuritySettings().setAuthorizationStrategy(authStrategy);

+			getSecuritySettings().setUnauthorizedComponentInstantiationListener(authStrategy);

+		}

+

 		// Grab Browser info (like timezone, etc)

-		getRequestCycleSettings().setGatherExtendedBrowserInfo(true);

+		if (StoredSettings.getBoolean("useClientTimezone", false)) {

+			getRequestCycleSettings().setGatherExtendedBrowserInfo(true);

+		}

 

 		// setup the standard gitweb-ish urls

 		mount(new MixedParamUrlCodingStrategy("/summary", SummaryPage.class, new String[] { "r" }));

@@ -77,11 +86,13 @@
 		mount(new MixedParamUrlCodingStrategy("/blobdiff", BlobDiffPage.class, new String[] { "r", "h", "f" }));

 		mount(new MixedParamUrlCodingStrategy("/commitdiff", CommitDiffPage.class, new String[] { "r", "h" }));

 		mount(new MixedParamUrlCodingStrategy("/patch", PatchPage.class, new String[] { "r", "h", "f" }));

-		

+

 		// setup extended urls

 		mount(new MixedParamUrlCodingStrategy("/ticgit", TicGitPage.class, new String[] { "r" }));

 		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);

@@ -109,6 +120,28 @@
 		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));

 	}

diff --git a/src/com/gitblit/wicket/GitBlitWebSession.java b/src/com/gitblit/wicket/GitBlitWebSession.java
index 038118e..b2106e7 100644
--- a/src/com/gitblit/wicket/GitBlitWebSession.java
+++ b/src/com/gitblit/wicket/GitBlitWebSession.java
@@ -12,6 +12,8 @@
 	private static final long serialVersionUID = 1L;

 

 	protected TimeZone timezone = null;

+	

+	private User user = null;

 

 	public GitBlitWebSession(Request request) {

 		super(request);

@@ -19,6 +21,19 @@
 

 	public void invalidate() {

 		super.invalidate();

+		user = null;

+	}

+	

+	public boolean isLoggedIn() {

+		return user != null;

+	}

+	

+	public User getUser() {

+		return user;

+	}

+	

+	public void setUser(User user) {

+		this.user = user;

 	}

 

 	public TimeZone getTimezone() {

diff --git a/src/com/gitblit/wicket/LoginPage.html b/src/com/gitblit/wicket/LoginPage.html
new file mode 100644
index 0000000..adbe64f
--- /dev/null
+++ b/src/com/gitblit/wicket/LoginPage.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

+<html xmlns="http://www.w3.org/1999/xhtml"  

+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"  

+      xml:lang="en"  

+      lang="en"> 

+      

+	<!-- Head with Wicket-controlled resources in this package -->

+	<wicket:head>

+   		<title wicket:id="title">[page title]</title>

+   		<wicket:link>

+   			<link rel="stylesheet" type="text/css" href="resources/gitblit.css"/>

+   			<link rel="shortcut icon" href="resources/gitblt-favicon.png" type="image/png" />

+   		</wicket:link>

+	</wicket:head>

+	

+

+	<body onload="document.getElementById('username').focus();">

+	<div>

+		<center>

+			<img wicket:id="logo" /><br/>

+			<span style="font-weight:bold;" wicket:id="name">[name]</span><br/>

+

+			<div>

+				<form style="text-align:center;" wicket:id="loginForm">

+					<p/>

+					Username <input type="text" id="username" wicket:id="username" value=""/>

+					<p/>

+					Password <input type="password"  wicket:id="password" value=""/>

+					<p/>

+					<input type="submit" value="Login" />

+					<div style="background-color:#c7c7c7" wicket:id="feedback"></div>

+				</form>

+			</div>

+		</center>

+	</div>

+	</body>

+</html>
\ No newline at end of file
diff --git a/src/com/gitblit/wicket/LoginPage.java b/src/com/gitblit/wicket/LoginPage.java
new file mode 100644
index 0000000..39b4285
--- /dev/null
+++ b/src/com/gitblit/wicket/LoginPage.java
@@ -0,0 +1,101 @@
+package com.gitblit.wicket;

+

+import javax.servlet.http.Cookie;

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.wicket.PageParameters;

+import org.apache.wicket.markup.html.WebPage;

+import org.apache.wicket.markup.html.basic.Label;

+import org.apache.wicket.markup.html.form.Form;

+import org.apache.wicket.markup.html.form.PasswordTextField;

+import org.apache.wicket.markup.html.form.TextField;

+import org.apache.wicket.markup.html.image.ContextImage;

+import org.apache.wicket.markup.html.panel.FeedbackPanel;

+import org.apache.wicket.model.IModel;

+import org.apache.wicket.model.Model;

+import org.apache.wicket.protocol.http.WebRequest;

+import org.apache.wicket.protocol.http.WebResponse;

+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;

+

+import com.gitblit.Constants;

+

+public class LoginPage extends WebPage {

+

+	IModel<String> username = new Model<String>("");

+	IModel<String> password = new Model<String>("");

+

+	public LoginPage(PageParameters params) {

+		super(params);

+

+		tryAutomaticLogin();

+

+		add(new Label("title", getServerName()));

+		add(new ContextImage("logo", "gitblt2.png"));

+		add(new Label("name", Constants.NAME));

+

+		Form<Void> loginForm = new LoginForm("loginForm");

+		loginForm.add(new TextField<String>("username", username));

+		loginForm.add(new PasswordTextField("password", password));

+		loginForm.add(new FeedbackPanel("feedback"));

+		add(loginForm);

+	}

+	

+	protected String getServerName() {

+		ServletWebRequest servletWebRequest = (ServletWebRequest) getRequest();

+		HttpServletRequest req = servletWebRequest.getHttpServletRequest();

+		return req.getServerName();

+	}

+

+	class LoginForm extends Form<Void> {

+		private static final long serialVersionUID = 1L;

+

+		public LoginForm(String id) {

+			super(id);

+		}

+

+		@Override

+		public void onSubmit() {

+			String username = LoginPage.this.username.getObject();

+			char [] password = LoginPage.this.password.getObject().toCharArray();

+

+			User user = GitBlitWebApp.get().authenticate(username, password);

+			if (user == null)

+				error("Invalid username or password!");

+			else

+				loginUser(user);

+		}

+	}

+

+	private void tryAutomaticLogin() {

+		User user = null;

+

+		// Grab cookie from Browser Session

+		Cookie[] cookies = ((WebRequest) getRequestCycle().getRequest()).getCookies();

+		if (cookies != null && cookies.length > 0) {

+			user = GitBlitWebApp.get().authenticate(cookies);

+		}

+

+		// Login the user

+		loginUser(user);

+	}

+

+	private void loginUser(User user) {

+		if (user != null) {

+			GitBlitWebSession session = GitBlitWebSession.get();

+

+			// Set Cookie

+			WebResponse response = (WebResponse) getRequestCycle().getResponse();

+			GitBlitWebApp.get().setCookie(response, user);

+			

+			// track user object so that we do not have to continue

+			// re-authenticating on each request.

+			session.setUser(user);

+

+			// Redirect to original page OR to first available tab

+			if (!continueToOriginalDestination()) {

+				// Redirect to home page

+				setResponsePage(session.getApplication().getHomePage());

+			}

+		}

+	}

+}

diff --git a/src/com/gitblit/wicket/User.java b/src/com/gitblit/wicket/User.java
new file mode 100644
index 0000000..fb49b40
--- /dev/null
+++ b/src/com/gitblit/wicket/User.java
@@ -0,0 +1,23 @@
+package com.gitblit.wicket;

+

+import com.gitblit.Build;

+import com.gitblit.Constants;

+

+public class User {

+	

+	private String username;

+	private char [] password;

+	

+	public User(String username, char [] password) {

+		this.username = username;

+		this.password = password;

+	}

+	

+	public String getCookie() {

+		return Build.getSHA1((Constants.NAME + username + new String(password)).getBytes());

+	}

+	

+	public String toString() {

+		return username;

+	}

+}