blob: d7d6c14f1664f048f0a3a26fb5a4406ffbc95487 [file] [log] [blame]
/*
* 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() + ")";
}
}