From 17820f3a1153250a325fed23dfc2da59ce6ba777 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Mon, 31 Oct 2011 22:47:21 -0400
Subject: [PATCH] More feeds work in Manager

---
 src/com/gitblit/FileUserService.java |  162 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 150 insertions(+), 12 deletions(-)

diff --git a/src/com/gitblit/FileUserService.java b/src/com/gitblit/FileUserService.java
index 01a50be..3c8914d 100644
--- a/src/com/gitblit/FileUserService.java
+++ b/src/com/gitblit/FileUserService.java
@@ -33,6 +33,15 @@
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
 
+/**
+ * FileUserService is Gitblit's default user service implementation.
+ * 
+ * Users and their repository memberships are stored in a simple properties file
+ * which is cached and dynamically reloaded when modified.
+ * 
+ * @author James Moger
+ * 
+ */
 public class FileUserService extends FileSettings implements IUserService {
 
 	private final Logger logger = LoggerFactory.getLogger(FileUserService.class);
@@ -43,11 +52,32 @@
 		super(realmFile.getAbsolutePath());
 	}
 
+	/**
+	 * Setup the user service.
+	 * 
+	 * @param settings
+	 * @since 0.6.1
+	 */
+	@Override
+	public void setup(IStoredSettings settings) {
+	}
+
+	/**
+	 * Does the user service support cookie authentication?
+	 * 
+	 * @return true or false
+	 */
 	@Override
 	public boolean supportsCookies() {
 		return true;
 	}
 
+	/**
+	 * Returns the cookie value for the specified user.
+	 * 
+	 * @param model
+	 * @return cookie value
+	 */
 	@Override
 	public char[] getCookie(UserModel model) {
 		Properties allUsers = super.read();
@@ -58,6 +88,12 @@
 		return cookie.toCharArray();
 	}
 
+	/**
+	 * Authenticate a user based on their cookie.
+	 * 
+	 * @param cookie
+	 * @return a user object or null
+	 */
 	@Override
 	public UserModel authenticate(char[] cookie) {
 		String hash = new String(cookie);
@@ -73,6 +109,13 @@
 		return model;
 	}
 
+	/**
+	 * Authenticate a user based on a username and password.
+	 * 
+	 * @param username
+	 * @param password
+	 * @return a user object or null
+	 */
 	@Override
 	public UserModel authenticate(String username, char[] password) {
 		Properties allUsers = read();
@@ -83,16 +126,31 @@
 		UserModel returnedUser = null;
 		UserModel user = getUserModel(username);
 		if (user.password.startsWith(StringUtils.MD5_TYPE)) {
+			// password digest
 			String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));
 			if (user.password.equalsIgnoreCase(md5)) {
 				returnedUser = user;
 			}
+		} else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) {
+			// username+password digest
+			String md5 = StringUtils.COMBINED_MD5_TYPE
+					+ StringUtils.getMD5(username.toLowerCase() + new String(password));
+			if (user.password.equalsIgnoreCase(md5)) {
+				returnedUser = user;
+			}
 		} else if (user.password.equals(new String(password))) {
+			// plain-text password
 			returnedUser = user;
 		}
 		return returnedUser;
 	}
 
+	/**
+	 * Retrieve the user object for the specified username.
+	 * 
+	 * @param username
+	 * @return a user object or null
+	 */
 	@Override
 	public UserModel getUserModel(String username) {
 		Properties allUsers = read();
@@ -110,6 +168,8 @@
 				// Permissions
 				if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) {
 					model.canAdmin = true;
+				} else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) {
+					model.excludeFromFederation = true;
 				}
 				break;
 			default:
@@ -119,11 +179,27 @@
 		return model;
 	}
 
+	/**
+	 * Updates/writes a complete user object.
+	 * 
+	 * @param model
+	 * @return true if update is successful
+	 */
 	@Override
 	public boolean updateUserModel(UserModel model) {
 		return updateUserModel(model.username, model);
 	}
 
+	/**
+	 * Updates/writes and replaces a complete user object keyed by username.
+	 * This method allows for renaming a user.
+	 * 
+	 * @param username
+	 *            the old username
+	 * @param model
+	 *            the user object to use for username
+	 * @return true if update is successful
+	 */
 	@Override
 	public boolean updateUserModel(String username, UserModel model) {
 		try {
@@ -133,6 +209,9 @@
 			// Permissions
 			if (model.canAdmin) {
 				roles.add(Constants.ADMIN_ROLE);
+			}
+			if (model.excludeFromFederation) {
+				roles.add(Constants.NOT_FEDERATED_ROLE);
 			}
 
 			StringBuilder sb = new StringBuilder();
@@ -156,11 +235,23 @@
 		return false;
 	}
 
+	/**
+	 * Deletes the user object from the user service.
+	 * 
+	 * @param model
+	 * @return true if successful
+	 */
 	@Override
 	public boolean deleteUserModel(UserModel model) {
 		return deleteUser(model.username);
 	}
 
+	/**
+	 * Delete the user object with the specified username
+	 * 
+	 * @param username
+	 * @return true if successful
+	 */
 	@Override
 	public boolean deleteUser(String username) {
 		try {
@@ -175,6 +266,11 @@
 		return false;
 	}
 
+	/**
+	 * Returns the list of all users available to the login service.
+	 * 
+	 * @return list of all usernames
+	 */
 	@Override
 	public List<String> getAllUsernames() {
 		Properties allUsers = read();
@@ -182,8 +278,16 @@
 		return list;
 	}
 
+	/**
+	 * Returns the list of all users who are allowed to bypass the access
+	 * restriction placed on the specified repository.
+	 * 
+	 * @param role
+	 *            the repository name
+	 * @return list of all usernames that can bypass the access restriction
+	 */
 	@Override
-	public List<String> getUsernamesForRepository(String role) {
+	public List<String> getUsernamesForRepositoryRole(String role) {
 		List<String> list = new ArrayList<String>();
 		try {
 			Properties allUsers = read();
@@ -205,8 +309,17 @@
 		return list;
 	}
 
+	/**
+	 * Sets the list of all uses who are allowed to bypass the access
+	 * restriction placed on the specified repository.
+	 * 
+	 * @param role
+	 *            the repository name
+	 * @param usernames
+	 * @return true if successful
+	 */
 	@Override
-	public boolean setUsernamesForRepository(String role, List<String> usernames) {
+	public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {
 		try {
 			Set<String> specifiedUsers = new HashSet<String>(usernames);
 			Set<String> needsAddRole = new HashSet<String>(specifiedUsers);
@@ -272,6 +385,13 @@
 		return false;
 	}
 
+	/**
+	 * Renames a repository role.
+	 * 
+	 * @param oldRole
+	 * @param newRole
+	 * @return true if successful
+	 */
 	@Override
 	public boolean renameRepositoryRole(String oldRole, String newRole) {
 		try {
@@ -327,6 +447,12 @@
 		return false;
 	}
 
+	/**
+	 * Removes a repository role from all users.
+	 * 
+	 * @param role
+	 * @return true if successful
+	 */
 	@Override
 	public boolean deleteRepositoryRole(String role) {
 		try {
@@ -380,23 +506,32 @@
 		return false;
 	}
 
+	/**
+	 * Writes the properties file.
+	 * 
+	 * @param properties
+	 * @throws IOException
+	 */
 	private void write(Properties properties) throws IOException {
-		// Update realm file
+		// Write a temporary copy of the users file
 		File realmFileCopy = new File(propertiesFile.getAbsolutePath() + ".tmp");
 		FileWriter writer = new FileWriter(realmFileCopy);
 		properties
 				.store(writer,
 						"# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");
 		writer.close();
+		// If the write is successful, delete the current file and rename
+		// the temporary copy to the original filename.
 		if (realmFileCopy.exists() && realmFileCopy.length() > 0) {
-			if (propertiesFile.delete()) {
-				if (!realmFileCopy.renameTo(propertiesFile)) {
-					throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
-							realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));
+			if (propertiesFile.exists()) {
+				if (!propertiesFile.delete()) {
+					throw new IOException(MessageFormat.format("Failed to delete {0}!",
+							propertiesFile.getAbsolutePath()));
 				}
-			} else {
-				throw new IOException(MessageFormat.format("Failed to delete (0)!",
-						propertiesFile.getAbsolutePath()));
+			}
+			if (!realmFileCopy.renameTo(propertiesFile)) {
+				throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
+						realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));
 			}
 		} else {
 			throw new IOException(MessageFormat.format("Failed to save {0}!",
@@ -404,11 +539,14 @@
 		}
 	}
 
+	/**
+	 * Reads the properties file and rebuilds the in-memory cookie lookup table.
+	 */
 	@Override
 	protected synchronized Properties read() {
-		long lastRead = lastRead();
+		long lastRead = lastModified();
 		Properties allUsers = super.read();
-		if (lastRead != lastRead()) {
+		if (lastRead != lastModified()) {
 			// reload hash cache
 			cookies.clear();
 			for (String username : allUsers.stringPropertyNames()) {

--
Gitblit v1.9.1