Merge branch 'rcaa-master' into master.
diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java
index 6d7230f..025b1d8 100644
--- a/src/main/java/com/gitblit/ConfigUserService.java
+++ b/src/main/java/com/gitblit/ConfigUserService.java
@@ -898,7 +898,7 @@
 					user.countryCode = config.getString(USER, username, COUNTRYCODE);

 					user.cookie = config.getString(USER, username, COOKIE);

 					if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) {

-						user.cookie = StringUtils.getSHA1(user.username + user.password);

+						user.cookie = user.createCookie();

 					}

 

 					// preferences

diff --git a/src/main/java/com/gitblit/auth/AuthenticationProvider.java b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
index 0bfe235..e359fd7 100644
--- a/src/main/java/com/gitblit/auth/AuthenticationProvider.java
+++ b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
@@ -78,10 +78,10 @@
 
 	public abstract AuthenticationType getAuthenticationType();
 
-	protected void setCookie(UserModel user, char [] password) {
+	protected void setCookie(UserModel user) {
 		// create a user cookie
-		if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
-			user.cookie = StringUtils.getSHA1(user.username + new String(password));
+		if (StringUtils.isEmpty(user.cookie)) {
+			user.cookie = user.createCookie();
 		}
 	}
 
diff --git a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
index 2cdabf6..3a6cb8e 100644
--- a/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
@@ -196,7 +196,7 @@
                 }
 
                 // create a user cookie
-                setCookie(user, password);
+                setCookie(user);
 
                 // Set user attributes, hide password from backing user service.
                 user.password = Constants.EXTERNAL_ACCOUNT;
diff --git a/src/main/java/com/gitblit/auth/LdapAuthProvider.java b/src/main/java/com/gitblit/auth/LdapAuthProvider.java
index 19fd463..c31694b 100644
--- a/src/main/java/com/gitblit/auth/LdapAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/LdapAuthProvider.java
@@ -307,7 +307,7 @@
 							}
 
 							// create a user cookie
-							setCookie(user, password);
+							setCookie(user);
 
 							if (!supportsTeamMembershipChanges()) {
 								getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
diff --git a/src/main/java/com/gitblit/auth/PAMAuthProvider.java b/src/main/java/com/gitblit/auth/PAMAuthProvider.java
index 46f4dd6..b38d49d 100644
--- a/src/main/java/com/gitblit/auth/PAMAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/PAMAuthProvider.java
@@ -122,7 +122,7 @@
         }
 
         // create a user cookie
-        setCookie(user, password);
+        setCookie(user);
 
         // update user attributes from UnixUser
         user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
index 27cece2..364aff0 100644
--- a/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/RedmineAuthProvider.java
@@ -139,7 +139,7 @@
         }
 
         // create a user cookie
-        setCookie(user, password);
+        setCookie(user);
 
         // update user attributes from Redmine
         user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
index df033c2..79c3a0c 100644
--- a/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
@@ -66,7 +66,7 @@
 					user = new UserModel(simpleUsername);
 				}
 
-				setCookie(user, password);
+				setCookie(user);
 				setUserAttributes(user, info);
 
 				updateUser(user);
diff --git a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
index aee5100..4c31fb1 100644
--- a/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
+++ b/src/main/java/com/gitblit/auth/WindowsAuthProvider.java
@@ -153,7 +153,7 @@
         }
 
         // create a user cookie
-        setCookie(user, password);
+        setCookie(user);
 
         // update user attributes from Windows identity
         user.accountType = getAccountType();
diff --git a/src/main/java/com/gitblit/client/EditUserDialog.java b/src/main/java/com/gitblit/client/EditUserDialog.java
index 676916b..4b01ff0 100644
--- a/src/main/java/com/gitblit/client/EditUserDialog.java
+++ b/src/main/java/com/gitblit/client/EditUserDialog.java
@@ -330,7 +330,7 @@
 			}

 

 			// change the cookie

-			user.cookie = StringUtils.getSHA1(user.username + password);

+			user.cookie = user.createCookie();

 

 			String type = settings.get(Keys.realm.passwordStorage).getString("md5");

 			if (type.equalsIgnoreCase("md5")) {

diff --git a/src/main/java/com/gitblit/models/UserModel.java b/src/main/java/com/gitblit/models/UserModel.java
index e152274..f8f7ed6 100644
--- a/src/main/java/com/gitblit/models/UserModel.java
+++ b/src/main/java/com/gitblit/models/UserModel.java
@@ -17,6 +17,7 @@
 

 import java.io.Serializable;

 import java.security.Principal;

+import java.security.SecureRandom;

 import java.util.ArrayList;

 import java.util.Collections;

 import java.util.HashSet;

@@ -36,6 +37,7 @@
 import com.gitblit.Constants.RegistrantType;

 import com.gitblit.utils.ArrayUtils;

 import com.gitblit.utils.ModelUtils;

+import com.gitblit.utils.SecureRandom;

 import com.gitblit.utils.StringUtils;

 

 /**

@@ -52,6 +54,8 @@
 

 	public static final UserModel ANONYMOUS = new UserModel();

 

+	private static final SecureRandom RANDOM = new SecureRandom();

+

 	// field names are reflectively mapped in EditUser page

 	public String username;

 	public String password;

@@ -660,4 +664,8 @@
 		String projectPath = StringUtils.getFirstPathElement(repository);

 		return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase(getPersonalPath());

 	}

+

+	public String createCookie() {

+		return StringUtils.getSHA1(RANDOM.randomBytes(32));

+	}

 }

diff --git a/src/main/java/com/gitblit/utils/SecureRandom.java b/src/main/java/com/gitblit/utils/SecureRandom.java
new file mode 100644
index 0000000..119533d
--- /dev/null
+++ b/src/main/java/com/gitblit/utils/SecureRandom.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 gitblit.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.utils;
+
+/**
+ * Wrapper class for java.security.SecureRandom, which will periodically reseed
+ * the PRNG in case an instance of the class has been running for a long time.
+ *
+ * @author Florian Zschocke
+ */
+public class SecureRandom {
+
+	/** Period (in ms) after which a new SecureRandom will be created in order to get a fresh random seed. */
+	private static final long RESEED_PERIOD = 24 * 60 * 60 * 1000; /* 24 hours */
+
+
+	private long last;
+	private java.security.SecureRandom random;
+
+
+
+	public SecureRandom() {
+		// Make sure the SecureRandom is seeded right from the start.
+		// This also lets any blocks during seeding occur at creation
+		// and prevents it from happening when getting next random bytes.
+		seed();
+	}
+
+
+
+	public byte[] randomBytes(int num) {
+		byte[] bytes = new byte[num];
+		nextBytes(bytes);
+		return bytes;
+	}
+
+
+	public void nextBytes(byte[] bytes) {
+		random.nextBytes(bytes);
+		reseed(false);
+	}
+
+
+	void reseed(boolean forced) {
+		long ts = System.currentTimeMillis();
+		if (forced || (ts - last) > RESEED_PERIOD) {
+			last = ts;
+			runReseed();
+		}
+	}
+
+
+
+	private void seed() {
+		random = new java.security.SecureRandom();
+		random.nextBytes(new byte[0]);
+		last = System.currentTimeMillis();
+	}
+
+
+	private void runReseed() {
+		// Have some other thread hit the penalty potentially incurred by reseeding,
+		// so that we can immediately return and not block the operation in progress.
+		new Thread() {
+			public void run() {
+				seed();
+			}
+		}.start();
+	}
+}
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
index 220bee3..72dee6b 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -156,7 +156,7 @@
 						}

 

 						// change the cookie

-						userModel.cookie = StringUtils.getSHA1(userModel.username + password);

+						userModel.cookie = userModel.createCookie();

 

 						// Optionally store the password MD5 digest.

 						String type = app().settings().getString(Keys.realm.passwordStorage, "md5");

diff --git a/src/test/java/com/gitblit/utils/SecureRandomTest.java b/src/test/java/com/gitblit/utils/SecureRandomTest.java
new file mode 100644
index 0000000..c4098c2
--- /dev/null
+++ b/src/test/java/com/gitblit/utils/SecureRandomTest.java
@@ -0,0 +1,33 @@
+package com.gitblit.utils;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+public class SecureRandomTest {
+
+	@Test
+	public void testRandomBytes() {
+		SecureRandom sr = new SecureRandom();
+		byte[] bytes1 = sr.randomBytes(10);
+		assertEquals(10, bytes1.length);
+		byte[] bytes2 = sr.randomBytes(10);
+		assertEquals(10, bytes2.length);
+		assertFalse(Arrays.equals(bytes1, bytes2));
+
+		assertEquals(0, sr.randomBytes(0).length);
+		assertEquals(200, sr.randomBytes(200).length);
+	}
+
+	@Test
+	public void testNextBytes() {
+		SecureRandom sr = new SecureRandom();
+		byte[] bytes1 = new byte[32];
+		sr.nextBytes(bytes1);
+		byte[] bytes2 = new byte[32];
+		sr.nextBytes(bytes2);
+		assertFalse(Arrays.equals(bytes1, bytes2));
+	}
+}