James Moger
2011-10-31 17820f3a1153250a325fed23dfc2da59ce6ba777
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()) {