Package com.gitblit

Source Code of com.gitblit.ConfigUserService

/*
* Copyright 2011 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;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccountType;
import com.gitblit.Constants.Transport;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.models.UserRepositoryPreferences;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.StringUtils;

/**
* ConfigUserService is Gitblit's default user service implementation since
* version 0.8.0.
*
* Users and their repository memberships are stored in a git-style config file
* which is cached and dynamically reloaded when modified. This file is
* plain-text, human-readable, and may be edited with a text editor.
*
* Additionally, this format allows for expansion of the user model without
* bringing in the complexity of a database.
*
* @author James Moger
*
*/
public class ConfigUserService implements IUserService {

  private static final String TEAM = "team";

  private static final String USER = "user";

  private static final String PASSWORD = "password";

  private static final String DISPLAYNAME = "displayName";

  private static final String EMAILADDRESS = "emailAddress";

  private static final String ORGANIZATIONALUNIT = "organizationalUnit";

  private static final String ORGANIZATION = "organization";

  private static final String LOCALITY = "locality";

  private static final String STATEPROVINCE = "stateProvince";

  private static final String COUNTRYCODE = "countryCode";

  private static final String COOKIE = "cookie";

  private static final String REPOSITORY = "repository";

  private static final String ROLE = "role";

  private static final String MAILINGLIST = "mailingList";

  private static final String PRERECEIVE = "preReceiveScript";

  private static final String POSTRECEIVE = "postReceiveScript";

  private static final String STARRED = "starred";

  private static final String LOCALE = "locale";

  private static final String EMAILONMYTICKETCHANGES = "emailMeOnMyTicketChanges";

  private static final String TRANSPORT = "transport";

  private static final String ACCOUNTTYPE = "accountType";

  private static final String DISABLED = "disabled";

  private final File realmFile;

  private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class);

  private final Map<String, UserModel> users = new ConcurrentHashMap<String, UserModel>();

  private final Map<String, UserModel> cookies = new ConcurrentHashMap<String, UserModel>();

  private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>();

  private volatile long lastModified;

  private volatile boolean forceReload;

  public ConfigUserService(File realmFile) {
    this.realmFile = realmFile;
  }

  /**
   * Setup the user service.
   *
   * @param runtimeManager
   * @since 1.4.0
   */
  @Override
  public void setup(IRuntimeManager runtimeManager) {
  }

  /**
   * Returns the cookie value for the specified user.
   *
   * @param model
   * @return cookie value
   */
  @Override
  public synchronized String getCookie(UserModel model) {
    if (!StringUtils.isEmpty(model.cookie)) {
      return model.cookie;
    }
    UserModel storedModel = getUserModel(model.username);
    if (storedModel == null) {
      return null;
    }
    return storedModel.cookie;
  }

  /**
   * Gets the user object for the specified cookie.
   *
   * @param cookie
   * @return a user object or null
   */
  @Override
  public synchronized UserModel getUserModel(char[] cookie) {
    String hash = new String(cookie);
    if (StringUtils.isEmpty(hash)) {
      return null;
    }
    read();
    UserModel model = null;
    if (cookies.containsKey(hash)) {
      model = cookies.get(hash);
    }

    if (model != null) {
      // clone the model, otherwise all changes to this object are
      // live and unpersisted
      model = DeepCopier.copy(model);
    }
    return model;
  }

  /**
   * Retrieve the user object for the specified username.
   *
   * @param username
   * @return a user object or null
   */
  @Override
  public synchronized UserModel getUserModel(String username) {
    read();
    UserModel model = users.get(username.toLowerCase());
    if (model != null) {
      // clone the model, otherwise all changes to this object are
      // live and unpersisted
      model = DeepCopier.copy(model);
    }
    return model;
  }

  /**
   * Updates/writes a complete user object.
   *
   * @param model
   * @return true if update is successful
   */
  @Override
  public synchronized boolean updateUserModel(UserModel model) {
    return updateUserModel(model.username, model);
  }

  /**
   * Updates/writes all specified user objects.
   *
   * @param models a list of user models
   * @return true if update is successful
   * @since 1.2.0
   */
  @Override
  public synchronized boolean updateUserModels(Collection<UserModel> models) {
    try {
      read();
      for (UserModel model : models) {
        UserModel originalUser = users.remove(model.username.toLowerCase());
        users.put(model.username.toLowerCase(), model);
        // null check on "final" teams because JSON-sourced UserModel
        // can have a null teams object
        if (model.teams != null) {
          Set<TeamModel> userTeams = new HashSet<TeamModel>();
          for (TeamModel team : model.teams) {
            TeamModel t = teams.get(team.name.toLowerCase());
            if (t == null) {
              // new team
              t = team;
              teams.put(team.name.toLowerCase(), t);
            }
            // do not clobber existing team definition
            // maybe because this is a federated user
            t.addUser(model.username);
            userTeams.add(t);
          }
          // replace Team-Models in users by new ones.
          model.teams.clear();
          model.teams.addAll(userTeams);

          // check for implicit team removal
          if (originalUser != null) {
            for (TeamModel team : originalUser.teams) {
              if (!model.isTeamMember(team.name)) {
                team.removeUser(model.username);
              }
            }
          }
        }
      }
      write();
      return true;
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to update user {0} models!", models.size()),
          t);
    }
    return false;
  }

  /**
   * 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 synchronized boolean updateUserModel(String username, UserModel model) {
    UserModel originalUser = null;
    try {
      if (!model.isLocalAccount()) {
        // do not persist password
        model.password = Constants.EXTERNAL_ACCOUNT;
      }
      read();
      originalUser = users.remove(username.toLowerCase());
      if (originalUser != null) {
        cookies.remove(originalUser.cookie);
      }
      users.put(model.username.toLowerCase(), model);
      // null check on "final" teams because JSON-sourced UserModel
      // can have a null teams object
      if (model.teams != null) {
        for (TeamModel team : model.teams) {
          TeamModel t = teams.get(team.name.toLowerCase());
          if (t == null) {
            // new team
            team.addUser(username);
            teams.put(team.name.toLowerCase(), team);
          } else {
            // do not clobber existing team definition
            // maybe because this is a federated user
            t.removeUser(username);
            t.addUser(model.username);
          }
        }

        // check for implicit team removal
        if (originalUser != null) {
          for (TeamModel team : originalUser.teams) {
            if (!model.isTeamMember(team.name)) {
              team.removeUser(username);
            }
          }
        }
      }
      write();
      return true;
    } catch (Throwable t) {
      if (originalUser != null) {
        // restore original user
        users.put(originalUser.username.toLowerCase(), originalUser);
      } else {
        // drop attempted add
        users.remove(model.username.toLowerCase());
      }
      logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),
          t);
    }
    return false;
  }

  /**
   * Deletes the user object from the user service.
   *
   * @param model
   * @return true if successful
   */
  @Override
  public synchronized boolean deleteUserModel(UserModel model) {
    return deleteUser(model.username);
  }

  /**
   * Delete the user object with the specified username
   *
   * @param username
   * @return true if successful
   */
  @Override
  public synchronized boolean deleteUser(String username) {
    try {
      // Read realm file
      read();
      UserModel model = users.remove(username.toLowerCase());
      if (model == null) {
        // user does not exist
        return false;
      }
      // remove user from team
      for (TeamModel team : model.teams) {
        TeamModel t = teams.get(team.name);
        if (t == null) {
          // new team
          team.removeUser(username);
          teams.put(team.name.toLowerCase(), team);
        } else {
          // existing team
          t.removeUser(username);
        }
      }
      write();
      return true;
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);
    }
    return false;
  }

  /**
   * Returns the list of all teams available to the login service.
   *
   * @return list of all teams
   * @since 0.8.0
   */
  @Override
  public synchronized List<String> getAllTeamNames() {
    read();
    List<String> list = new ArrayList<String>(teams.keySet());
    Collections.sort(list);
    return list;
  }

  /**
   * Returns the list of all teams available to the login service.
   *
   * @return list of all teams
   * @since 0.8.0
   */
  @Override
  public synchronized List<TeamModel> getAllTeams() {
    read();
    List<TeamModel> list = new ArrayList<TeamModel>(teams.values());
    list = DeepCopier.copy(list);
    Collections.sort(list);
    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 synchronized List<String> getTeamNamesForRepositoryRole(String role) {
    List<String> list = new ArrayList<String>();
    try {
      read();
      for (Map.Entry<String, TeamModel> entry : teams.entrySet()) {
        TeamModel model = entry.getValue();
        if (model.hasRepositoryPermission(role)) {
          list.add(model.name);
        }
      }
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to get teamnames for role {0}!", role), t);
    }
    Collections.sort(list);
    return list;
  }

  /**
   * Retrieve the team object for the specified team name.
   *
   * @param teamname
   * @return a team object or null
   * @since 0.8.0
   */
  @Override
  public synchronized TeamModel getTeamModel(String teamname) {
    read();
    TeamModel model = teams.get(teamname.toLowerCase());
    if (model != null) {
      // clone the model, otherwise all changes to this object are
      // live and unpersisted
      model = DeepCopier.copy(model);
    }
    return model;
  }

  /**
   * Updates/writes a complete team object.
   *
   * @param model
   * @return true if update is successful
   * @since 0.8.0
   */
  @Override
  public synchronized boolean updateTeamModel(TeamModel model) {
    return updateTeamModel(model.name, model);
  }

  /**
   * Updates/writes all specified team objects.
   *
   * @param models a list of team models
   * @return true if update is successful
   * @since 1.2.0
   */
  @Override
  public synchronized boolean updateTeamModels(Collection<TeamModel> models) {
    try {
      read();
      for (TeamModel team : models) {
        teams.put(team.name.toLowerCase(), team);
      }
      write();
      return true;
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to update team {0} models!", models.size()), t);
    }
    return false;
  }

  /**
   * Updates/writes and replaces a complete team object keyed by teamname.
   * This method allows for renaming a team.
   *
   * @param teamname
   *            the old teamname
   * @param model
   *            the team object to use for teamname
   * @return true if update is successful
   * @since 0.8.0
   */
  @Override
  public synchronized boolean updateTeamModel(String teamname, TeamModel model) {
    TeamModel original = null;
    try {
      read();
      original = teams.remove(teamname.toLowerCase());
      teams.put(model.name.toLowerCase(), model);
      write();
      return true;
    } catch (Throwable t) {
      if (original != null) {
        // restore original team
        teams.put(original.name.toLowerCase(), original);
      } else {
        // drop attempted add
        teams.remove(model.name.toLowerCase());
      }
      logger.error(MessageFormat.format("Failed to update team model {0}!", model.name), t);
    }
    return false;
  }

  /**
   * Deletes the team object from the user service.
   *
   * @param model
   * @return true if successful
   * @since 0.8.0
   */
  @Override
  public synchronized boolean deleteTeamModel(TeamModel model) {
    return deleteTeam(model.name);
  }

  /**
   * Delete the team object with the specified teamname
   *
   * @param teamname
   * @return true if successful
   * @since 0.8.0
   */
  @Override
  public synchronized boolean deleteTeam(String teamname) {
    try {
      // Read realm file
      read();
      teams.remove(teamname.toLowerCase());
      write();
      return true;
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to delete team {0}!", teamname), t);
    }
    return false;
  }

  /**
   * Returns the list of all users available to the login service.
   *
   * @return list of all usernames
   */
  @Override
  public synchronized List<String> getAllUsernames() {
    read();
    List<String> list = new ArrayList<String>(users.keySet());
    Collections.sort(list);
    return list;
  }

  /**
   * Returns the list of all users available to the login service.
   *
   * @return list of all usernames
   */
  @Override
  public synchronized List<UserModel> getAllUsers() {
    read();
    List<UserModel> list = new ArrayList<UserModel>(users.values());
    list = DeepCopier.copy(list);
    Collections.sort(list);
    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 synchronized List<String> getUsernamesForRepositoryRole(String role) {
    List<String> list = new ArrayList<String>();
    try {
      read();
      for (Map.Entry<String, UserModel> entry : users.entrySet()) {
        UserModel model = entry.getValue();
        if (model.hasRepositoryPermission(role)) {
          list.add(model.username);
        }
      }
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);
    }
    Collections.sort(list);
    return list;
  }

  /**
   * Renames a repository role.
   *
   * @param oldRole
   * @param newRole
   * @return true if successful
   */
  @Override
  public synchronized boolean renameRepositoryRole(String oldRole, String newRole) {
    try {
      read();
      // identify users which require role rename
      for (UserModel model : users.values()) {
        if (model.hasRepositoryPermission(oldRole)) {
          AccessPermission permission = model.removeRepositoryPermission(oldRole);
          model.setRepositoryPermission(newRole, permission);
        }
      }

      // identify teams which require role rename
      for (TeamModel model : teams.values()) {
        if (model.hasRepositoryPermission(oldRole)) {
          AccessPermission permission = model.removeRepositoryPermission(oldRole);
          model.setRepositoryPermission(newRole, permission);
        }
      }
      // persist changes
      write();
      return true;
    } catch (Throwable t) {
      logger.error(
          MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);
    }
    return false;
  }

  /**
   * Removes a repository role from all users.
   *
   * @param role
   * @return true if successful
   */
  @Override
  public synchronized boolean deleteRepositoryRole(String role) {
    try {
      read();

      // identify users which require role rename
      for (UserModel user : users.values()) {
        user.removeRepositoryPermission(role);
      }

      // identify teams which require role rename
      for (TeamModel team : teams.values()) {
        team.removeRepositoryPermission(role);
      }

      // persist changes
      write();
      return true;
    } catch (Throwable t) {
      logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);
    }
    return false;
  }

  /**
   * Writes the properties file.
   *
   * @throws IOException
   */
  private synchronized void write() throws IOException {
    // Write a temporary copy of the users file
    File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");

    StoredConfig config = new FileBasedConfig(realmFileCopy, FS.detect());

    // write users
    for (UserModel model : users.values()) {
      if (!StringUtils.isEmpty(model.password)) {
        config.setString(USER, model.username, PASSWORD, model.password);
      }
      if (!StringUtils.isEmpty(model.cookie)) {
        config.setString(USER, model.username, COOKIE, model.cookie);
      }
      if (!StringUtils.isEmpty(model.displayName)) {
        config.setString(USER, model.username, DISPLAYNAME, model.displayName);
      }
      if (!StringUtils.isEmpty(model.emailAddress)) {
        config.setString(USER, model.username, EMAILADDRESS, model.emailAddress);
      }
      if (model.accountType != null) {
        config.setString(USER, model.username, ACCOUNTTYPE, model.accountType.name());
      }
      if (!StringUtils.isEmpty(model.organizationalUnit)) {
        config.setString(USER, model.username, ORGANIZATIONALUNIT, model.organizationalUnit);
      }
      if (!StringUtils.isEmpty(model.organization)) {
        config.setString(USER, model.username, ORGANIZATION, model.organization);
      }
      if (!StringUtils.isEmpty(model.locality)) {
        config.setString(USER, model.username, LOCALITY, model.locality);
      }
      if (!StringUtils.isEmpty(model.stateProvince)) {
        config.setString(USER, model.username, STATEPROVINCE, model.stateProvince);
      }
      if (!StringUtils.isEmpty(model.countryCode)) {
        config.setString(USER, model.username, COUNTRYCODE, model.countryCode);
      }
      if (model.disabled) {
        config.setBoolean(USER, model.username, DISABLED, true);
      }
      if (model.getPreferences() != null) {
        Locale locale = model.getPreferences().getLocale();
        if (locale != null) {
          String val;
          if (StringUtils.isEmpty(locale.getCountry())) {
            val = locale.getLanguage();
          } else {
            val = locale.getLanguage() + "_" + locale.getCountry();
          }
          config.setString(USER, model.username, LOCALE, val);
        }

        config.setBoolean(USER, model.username, EMAILONMYTICKETCHANGES, model.getPreferences().isEmailMeOnMyTicketChanges());

        if (model.getPreferences().getTransport() != null) {
          config.setString(USER, model.username, TRANSPORT, model.getPreferences().getTransport().name());
        }
      }

      // user roles
      List<String> roles = new ArrayList<String>();
      if (model.canAdmin) {
        roles.add(Constants.ADMIN_ROLE);
      }
      if (model.canFork) {
        roles.add(Constants.FORK_ROLE);
      }
      if (model.canCreate) {
        roles.add(Constants.CREATE_ROLE);
      }
      if (model.excludeFromFederation) {
        roles.add(Constants.NOT_FEDERATED_ROLE);
      }
      if (roles.size() == 0) {
        // we do this to ensure that user record with no password
        // is written.  otherwise, StoredConfig optimizes that account
        // away. :(
        roles.add(Constants.NO_ROLE);
      }
      config.setStringList(USER, model.username, ROLE, roles);

      // discrete repository permissions
      if (model.permissions != null && !model.canAdmin) {
        List<String> permissions = new ArrayList<String>();
        for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {
          if (entry.getValue().exceeds(AccessPermission.NONE)) {
            permissions.add(entry.getValue().asRole(entry.getKey()));
          }
        }
        config.setStringList(USER, model.username, REPOSITORY, permissions);
      }

      // user preferences
      if (model.getPreferences() != null) {
        List<String> starred =  model.getPreferences().getStarredRepositories();
        if (starred.size() > 0) {
          config.setStringList(USER, model.username, STARRED, starred);
        }
      }
    }

    // write teams
    for (TeamModel model : teams.values()) {
      // team roles
      List<String> roles = new ArrayList<String>();
      if (model.canAdmin) {
        roles.add(Constants.ADMIN_ROLE);
      }
      if (model.canFork) {
        roles.add(Constants.FORK_ROLE);
      }
      if (model.canCreate) {
        roles.add(Constants.CREATE_ROLE);
      }
      if (roles.size() == 0) {
        // we do this to ensure that team record is written.
        // Otherwise, StoredConfig might optimizes that record away.
        roles.add(Constants.NO_ROLE);
      }
      config.setStringList(TEAM, model.name, ROLE, roles);
      if (model.accountType != null) {
        config.setString(TEAM, model.name, ACCOUNTTYPE, model.accountType.name());
      }

      if (!model.canAdmin) {
        // write team permission for non-admin teams
        if (model.permissions == null) {
          // null check on "final" repositories because JSON-sourced TeamModel
          // can have a null repositories object
          if (!ArrayUtils.isEmpty(model.repositories)) {
            config.setStringList(TEAM, model.name, REPOSITORY, new ArrayList<String>(
                model.repositories));
          }
        } else {
          // discrete repository permissions
          List<String> permissions = new ArrayList<String>();
          for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) {
            if (entry.getValue().exceeds(AccessPermission.NONE)) {
              // code:repository (e.g. RW+:~james/myrepo.git
              permissions.add(entry.getValue().asRole(entry.getKey()));
            }
          }
          config.setStringList(TEAM, model.name, REPOSITORY, permissions);
        }
      }

      // null check on "final" users because JSON-sourced TeamModel
      // can have a null users object
      if (!ArrayUtils.isEmpty(model.users)) {
        config.setStringList(TEAM, model.name, USER, new ArrayList<String>(model.users));
      }

      // null check on "final" mailing lists because JSON-sourced
      // TeamModel can have a null users object
      if (!ArrayUtils.isEmpty(model.mailingLists)) {
        config.setStringList(TEAM, model.name, MAILINGLIST, new ArrayList<String>(
            model.mailingLists));
      }

      // null check on "final" preReceiveScripts because JSON-sourced
      // TeamModel can have a null preReceiveScripts object
      if (!ArrayUtils.isEmpty(model.preReceiveScripts)) {
        config.setStringList(TEAM, model.name, PRERECEIVE, model.preReceiveScripts);
      }

      // null check on "final" postReceiveScripts because JSON-sourced
      // TeamModel can have a null postReceiveScripts object
      if (!ArrayUtils.isEmpty(model.postReceiveScripts)) {
        config.setStringList(TEAM, model.name, POSTRECEIVE, model.postReceiveScripts);
      }
    }

    config.save();
    // manually set the forceReload flag because not all JVMs support real
    // millisecond resolution of lastModified. (issue-55)
    forceReload = true;

    // 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 (realmFile.exists()) {
        if (!realmFile.delete()) {
          throw new IOException(MessageFormat.format("Failed to delete {0}!",
              realmFile.getAbsolutePath()));
        }
      }
      if (!realmFileCopy.renameTo(realmFile)) {
        throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
            realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath()));
      }
    } else {
      throw new IOException(MessageFormat.format("Failed to save {0}!",
          realmFileCopy.getAbsolutePath()));
    }
  }

  /**
   * Reads the realm file and rebuilds the in-memory lookup tables.
   */
  protected synchronized void read() {
    if (realmFile.exists() && (forceReload || (realmFile.lastModified() != lastModified))) {
      forceReload = false;
      lastModified = realmFile.lastModified();
      users.clear();
      cookies.clear();
      teams.clear();

      try {
        StoredConfig config = new FileBasedConfig(realmFile, FS.detect());
        config.load();
        Set<String> usernames = config.getSubsections(USER);
        for (String username : usernames) {
          UserModel user = new UserModel(username.toLowerCase());
          user.password = config.getString(USER, username, PASSWORD);
          user.displayName = config.getString(USER, username, DISPLAYNAME);
          user.emailAddress = config.getString(USER, username, EMAILADDRESS);
          user.accountType = AccountType.fromString(config.getString(USER, username, ACCOUNTTYPE));
          if (Constants.EXTERNAL_ACCOUNT.equals(user.password) && user.accountType.isLocal()) {
            user.accountType = AccountType.EXTERNAL;
          }
          user.disabled = config.getBoolean(USER, username, DISABLED, false);
          user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT);
          user.organization = config.getString(USER, username, ORGANIZATION);
          user.locality = config.getString(USER, username, LOCALITY);
          user.stateProvince = config.getString(USER, username, STATEPROVINCE);
          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);
          }

          // preferences
          user.getPreferences().setLocale(config.getString(USER, username, LOCALE));
          user.getPreferences().setEmailMeOnMyTicketChanges(config.getBoolean(USER, username, EMAILONMYTICKETCHANGES, true));
          user.getPreferences().setTransport(Transport.fromString(config.getString(USER, username, TRANSPORT)));

          // user roles
          Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
              USER, username, ROLE)));
          user.canAdmin = roles.contains(Constants.ADMIN_ROLE);
          user.canFork = roles.contains(Constants.FORK_ROLE);
          user.canCreate = roles.contains(Constants.CREATE_ROLE);
          user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);

          // repository memberships
          if (!user.canAdmin) {
            // non-admin, read permissions
            Set<String> repositories = new HashSet<String>(Arrays.asList(config
                .getStringList(USER, username, REPOSITORY)));
            for (String repository : repositories) {
              user.addRepositoryPermission(repository);
            }
          }

          // starred repositories
          Set<String> starred = new HashSet<String>(Arrays.asList(config
              .getStringList(USER, username, STARRED)));
          for (String repository : starred) {
            UserRepositoryPreferences prefs = user.getPreferences().getRepositoryPreferences(repository);
            prefs.starred = true;
          }

          // update cache
          users.put(user.username, user);
          if (!StringUtils.isEmpty(user.cookie)) {
            cookies.put(user.cookie, user);
          }
        }

        // load the teams
        Set<String> teamnames = config.getSubsections(TEAM);
        for (String teamname : teamnames) {
          TeamModel team = new TeamModel(teamname);
          Set<String> roles = new HashSet<String>(Arrays.asList(config.getStringList(
              TEAM, teamname, ROLE)));
          team.canAdmin = roles.contains(Constants.ADMIN_ROLE);
          team.canFork = roles.contains(Constants.FORK_ROLE);
          team.canCreate = roles.contains(Constants.CREATE_ROLE);
          team.accountType = AccountType.fromString(config.getString(TEAM, teamname, ACCOUNTTYPE));

          if (!team.canAdmin) {
            // non-admin team, read permissions
            team.addRepositoryPermissions(Arrays.asList(config.getStringList(TEAM, teamname,
                REPOSITORY)));
          }
          team.addUsers(Arrays.asList(config.getStringList(TEAM, teamname, USER)));
          team.addMailingLists(Arrays.asList(config.getStringList(TEAM, teamname,
              MAILINGLIST)));
          team.preReceiveScripts.addAll(Arrays.asList(config.getStringList(TEAM,
              teamname, PRERECEIVE)));
          team.postReceiveScripts.addAll(Arrays.asList(config.getStringList(TEAM,
              teamname, POSTRECEIVE)));

          teams.put(team.name.toLowerCase(), team);

          // set the teams on the users
          for (String user : team.users) {
            UserModel model = users.get(user);
            if (model != null) {
              model.teams.add(team);
            }
          }
        }
      } catch (Exception e) {
        logger.error(MessageFormat.format("Failed to read {0}", realmFile), e);
      }
    }
  }

  protected long lastModified() {
    return lastModified;
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + "(" + realmFile.getAbsolutePath() + ")";
  }
}
TOP

Related Classes of com.gitblit.ConfigUserService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.