From 0e44acbb2fec928a1606dc60f427a148fff405c9 Mon Sep 17 00:00:00 2001
From: Mohamed Ragab <moragab@gmail.com>
Date: Wed, 02 May 2012 11:15:01 -0400
Subject: [PATCH] Added a script to facilitate setting the proxy host and port and no proxy hosts, and then it concatenates all the java system properties for setting the java proxy configurations and puts the resulting string in an environment variable JAVA_PROXY_CONFIG, modified the scirpts gitblit, gitblit-ubuntu, and gitblit-centos to source the java-proxy-config.sh script and then include the resulting java proxy configuration in the java command
---
src/com/gitblit/FileUserService.java | 499 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 476 insertions(+), 23 deletions(-)
diff --git a/src/com/gitblit/FileUserService.java b/src/com/gitblit/FileUserService.java
index cae0d79..fcb8eae 100644
--- a/src/com/gitblit/FileUserService.java
+++ b/src/com/gitblit/FileUserService.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -30,23 +31,33 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.StringUtils;
/**
- * FileUserService is Gitblit's default user service implementation.
+ * FileUserService is Gitblit's original default user service implementation.
*
* Users and their repository memberships are stored in a simple properties file
* which is cached and dynamically reloaded when modified.
*
+ * This class was deprecated in Gitblit 0.8.0 in favor of ConfigUserService
+ * which is still a human-readable, editable, plain-text file but it is more
+ * flexible for storing additional fields.
+ *
* @author James Moger
*
*/
+@Deprecated
public class FileUserService extends FileSettings implements IUserService {
private final Logger logger = LoggerFactory.getLogger(FileUserService.class);
private final Map<String, String> cookies = new ConcurrentHashMap<String, String>();
+
+ private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>();
public FileUserService(File realmFile) {
super(realmFile.getAbsolutePath());
@@ -56,10 +67,53 @@
* Setup the user service.
*
* @param settings
- * @since 0.6.1
+ * @since 0.7.0
*/
@Override
public void setup(IStoredSettings settings) {
+ }
+
+ /**
+ * Does the user service support changes to credentials?
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsCredentialChanges() {
+ return true;
+ }
+
+ /**
+ * Does the user service support changes to user display name?
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ /**
+ * Does the user service support changes to user email address?
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return false;
+ }
+
+ /**
+ * Does the user service support changes to team memberships?
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ public boolean supportsTeamMembershipChanges() {
+ return true;
}
/**
@@ -126,14 +180,32 @@
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;
+ }
+
+ /**
+ * Logout a user.
+ *
+ * @param user
+ */
+ @Override
+ public void logout(UserModel user) {
}
/**
@@ -145,11 +217,11 @@
@Override
public UserModel getUserModel(String username) {
Properties allUsers = read();
- String userInfo = allUsers.getProperty(username);
+ String userInfo = allUsers.getProperty(username.toLowerCase());
if (userInfo == null) {
return null;
}
- UserModel model = new UserModel(username);
+ UserModel model = new UserModel(username.toLowerCase());
String[] userValues = userInfo.split(",");
model.password = userValues[0];
for (int i = 1; i < userValues.length; i++) {
@@ -165,6 +237,12 @@
break;
default:
model.addRepository(role);
+ }
+ }
+ // set the teams for the user
+ for (TeamModel team : teams.values()) {
+ if (team.hasUser(username)) {
+ model.teams.add(DeepCopier.copy(team));
}
}
return model;
@@ -193,8 +271,9 @@
*/
@Override
public boolean updateUserModel(String username, UserModel model) {
- try {
+ try {
Properties allUsers = read();
+ UserModel oldUser = getUserModel(username);
ArrayList<String> roles = new ArrayList<String>(model.repositories);
// Permissions
@@ -206,7 +285,9 @@
}
StringBuilder sb = new StringBuilder();
- sb.append(model.password);
+ if (!StringUtils.isEmpty(model.password)) {
+ sb.append(model.password);
+ }
sb.append(',');
for (String role : roles) {
sb.append(role);
@@ -214,8 +295,34 @@
}
// trim trailing comma
sb.setLength(sb.length() - 1);
- allUsers.remove(username);
- allUsers.put(model.username, sb.toString());
+ allUsers.remove(username.toLowerCase());
+ allUsers.put(model.username.toLowerCase(), sb.toString());
+
+ // null check on "final" teams because JSON-sourced UserModel
+ // can have a null teams object
+ if (model.teams != null) {
+ // update team cache
+ for (TeamModel team : model.teams) {
+ TeamModel t = getTeamModel(team.name);
+ if (t == null) {
+ // new team
+ t = team;
+ }
+ t.removeUser(username);
+ t.addUser(model.username);
+ updateTeamCache(allUsers, t.name, t);
+ }
+
+ // check for implicit team removal
+ if (oldUser != null) {
+ for (TeamModel team : oldUser.teams) {
+ if (!model.isTeamMember(team.name)) {
+ team.removeUser(username);
+ updateTeamCache(allUsers, team.name, team);
+ }
+ }
+ }
+ }
write(allUsers);
return true;
@@ -248,7 +355,17 @@
try {
// Read realm file
Properties allUsers = read();
+ UserModel user = getUserModel(username);
allUsers.remove(username);
+ for (TeamModel team : user.teams) {
+ TeamModel t = getTeamModel(team.name);
+ if (t == null) {
+ // new team
+ t = team;
+ }
+ t.removeUser(username);
+ updateTeamCache(allUsers, t.name, t);
+ }
write(allUsers);
return true;
} catch (Throwable t) {
@@ -265,7 +382,31 @@
@Override
public List<String> getAllUsernames() {
Properties allUsers = read();
- List<String> list = new ArrayList<String>(allUsers.stringPropertyNames());
+ List<String> list = new ArrayList<String>();
+ for (String user : allUsers.stringPropertyNames()) {
+ if (user.charAt(0) == '@') {
+ // skip team user definitions
+ continue;
+ }
+ list.add(user);
+ }
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * Returns the list of all users available to the login service.
+ *
+ * @return list of all usernames
+ */
+ @Override
+ public List<UserModel> getAllUsers() {
+ read();
+ List<UserModel> list = new ArrayList<UserModel>();
+ for (String username : getAllUsernames()) {
+ list.add(getUserModel(username));
+ }
+ Collections.sort(list);
return list;
}
@@ -283,6 +424,9 @@
try {
Properties allUsers = read();
for (String username : allUsers.stringPropertyNames()) {
+ if (username.charAt(0) == '@') {
+ continue;
+ }
String value = allUsers.getProperty(username);
String[] values = value.split(",");
// skip first value (password)
@@ -297,11 +441,12 @@
} catch (Throwable t) {
logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);
}
+ Collections.sort(list);
return list;
}
/**
- * Sets the list of all uses who are allowed to bypass the access
+ * Sets the list of all users who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
@@ -351,12 +496,11 @@
StringBuilder sb = new StringBuilder();
sb.append(password);
sb.append(',');
- List<String> revisedRoles = new ArrayList<String>();
+
// skip first value (password)
for (int i = 1; i < values.length; i++) {
String value = values[i];
if (!value.equalsIgnoreCase(role)) {
- revisedRoles.add(value);
sb.append(value);
sb.append(',');
}
@@ -397,7 +541,7 @@
for (int i = 1; i < roles.length; i++) {
String r = roles[i];
if (r.equalsIgnoreCase(oldRole)) {
- needsRenameRole.remove(username);
+ needsRenameRole.add(username);
break;
}
}
@@ -411,13 +555,13 @@
StringBuilder sb = new StringBuilder();
sb.append(password);
sb.append(',');
- List<String> revisedRoles = new ArrayList<String>();
- revisedRoles.add(newRole);
+ sb.append(newRole);
+ sb.append(',');
+
// skip first value (password)
for (int i = 1; i < values.length; i++) {
String value = values[i];
if (!value.equalsIgnoreCase(oldRole)) {
- revisedRoles.add(value);
sb.append(value);
sb.append(',');
}
@@ -458,7 +602,7 @@
for (int i = 1; i < roles.length; i++) {
String r = roles[i];
if (r.equalsIgnoreCase(role)) {
- needsDeleteRole.remove(username);
+ needsDeleteRole.add(username);
break;
}
}
@@ -472,12 +616,10 @@
StringBuilder sb = new StringBuilder();
sb.append(password);
sb.append(',');
- List<String> revisedRoles = new ArrayList<String>();
// skip first value (password)
for (int i = 1; i < values.length; i++) {
String value = values[i];
if (!value.equalsIgnoreCase(role)) {
- revisedRoles.add(value);
sb.append(value);
sb.append(',');
}
@@ -509,7 +651,7 @@
FileWriter writer = new FileWriter(realmFileCopy);
properties
.store(writer,
- "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");
+ " Gitblit realm file format:\n username=password,\\#permission,repository1,repository2...\n @teamname=!username1,!username2,!username3,repository1,repository2...");
writer.close();
// If the write is successful, delete the current file and rename
// the temporary copy to the original filename.
@@ -536,17 +678,328 @@
@Override
protected synchronized Properties read() {
long lastRead = lastModified();
+ boolean reload = forceReload();
Properties allUsers = super.read();
- if (lastRead != lastModified()) {
+ if (reload || (lastRead != lastModified())) {
// reload hash cache
cookies.clear();
+ teams.clear();
+
for (String username : allUsers.stringPropertyNames()) {
String value = allUsers.getProperty(username);
String[] roles = value.split(",");
- String password = roles[0];
- cookies.put(StringUtils.getSHA1(username + password), username);
+ if (username.charAt(0) == '@') {
+ // team definition
+ TeamModel team = new TeamModel(username.substring(1));
+ List<String> repositories = new ArrayList<String>();
+ List<String> users = new ArrayList<String>();
+ List<String> mailingLists = new ArrayList<String>();
+ List<String> preReceive = new ArrayList<String>();
+ List<String> postReceive = new ArrayList<String>();
+ for (String role : roles) {
+ if (role.charAt(0) == '!') {
+ users.add(role.substring(1));
+ } else if (role.charAt(0) == '&') {
+ mailingLists.add(role.substring(1));
+ } else if (role.charAt(0) == '^') {
+ preReceive.add(role.substring(1));
+ } else if (role.charAt(0) == '%') {
+ postReceive.add(role.substring(1));
+ } else {
+ repositories.add(role);
+ }
+ }
+ team.addRepositories(repositories);
+ team.addUsers(users);
+ team.addMailingLists(mailingLists);
+ team.preReceiveScripts.addAll(preReceive);
+ team.postReceiveScripts.addAll(postReceive);
+ teams.put(team.name.toLowerCase(), team);
+ } else {
+ // user definition
+ String password = roles[0];
+ cookies.put(StringUtils.getSHA1(username.toLowerCase() + password), username.toLowerCase());
+ }
}
}
return allUsers;
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + propertiesFile.getAbsolutePath() + ")";
+ }
+
+ /**
+ * Returns the list of all teams available to the login service.
+ *
+ * @return list of all teams
+ * @since 0.8.0
+ */
+ @Override
+ public List<String> getAllTeamNames() {
+ 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 List<TeamModel> getAllTeams() {
+ List<TeamModel> list = new ArrayList<TeamModel>(teams.values());
+ list = DeepCopier.copy(list);
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * Returns the list of all teams who are allowed to bypass the access
+ * restriction placed on the specified repository.
+ *
+ * @param role
+ * the repository name
+ * @return list of all teamnames that can bypass the access restriction
+ */
+ @Override
+ public List<String> getTeamnamesForRepositoryRole(String role) {
+ List<String> list = new ArrayList<String>();
+ try {
+ Properties allUsers = read();
+ for (String team : allUsers.stringPropertyNames()) {
+ if (team.charAt(0) != '@') {
+ // skip users
+ continue;
+ }
+ String value = allUsers.getProperty(team);
+ String[] values = value.split(",");
+ for (int i = 0; i < values.length; i++) {
+ String r = values[i];
+ if (r.equalsIgnoreCase(role)) {
+ // strip leading @
+ list.add(team.substring(1));
+ break;
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format("Failed to get teamnames for role {0}!", role), t);
+ }
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * Sets the list of all teams who are allowed to bypass the access
+ * restriction placed on the specified repository.
+ *
+ * @param role
+ * the repository name
+ * @param teamnames
+ * @return true if successful
+ */
+ @Override
+ public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {
+ try {
+ Set<String> specifiedTeams = new HashSet<String>(teamnames);
+ Set<String> needsAddRole = new HashSet<String>(specifiedTeams);
+ Set<String> needsRemoveRole = new HashSet<String>();
+
+ // identify teams which require add and remove role
+ Properties allUsers = read();
+ for (String team : allUsers.stringPropertyNames()) {
+ if (team.charAt(0) != '@') {
+ // skip users
+ continue;
+ }
+ String name = team.substring(1);
+ String value = allUsers.getProperty(team);
+ String[] values = value.split(",");
+ for (int i = 0; i < values.length; i++) {
+ String r = values[i];
+ if (r.equalsIgnoreCase(role)) {
+ // team has role, check against revised team list
+ if (specifiedTeams.contains(name)) {
+ needsAddRole.remove(name);
+ } else {
+ // remove role from team
+ needsRemoveRole.add(name);
+ }
+ break;
+ }
+ }
+ }
+
+ // add roles to teams
+ for (String name : needsAddRole) {
+ String team = "@" + name;
+ String teamValues = allUsers.getProperty(team);
+ teamValues += "," + role;
+ allUsers.put(team, teamValues);
+ }
+
+ // remove role from team
+ for (String name : needsRemoveRole) {
+ String team = "@" + name;
+ String[] values = allUsers.getProperty(team).split(",");
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < values.length; i++) {
+ String value = values[i];
+ if (!value.equalsIgnoreCase(role)) {
+ sb.append(value);
+ sb.append(',');
+ }
+ }
+ sb.setLength(sb.length() - 1);
+
+ // update properties
+ allUsers.put(team, sb.toString());
+ }
+
+ // persist changes
+ write(allUsers);
+ return true;
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format("Failed to set teamnames for role {0}!", role), t);
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the team object for the specified team name.
+ *
+ * @param teamname
+ * @return a team object or null
+ * @since 0.8.0
+ */
+ @Override
+ public TeamModel getTeamModel(String teamname) {
+ read();
+ TeamModel team = teams.get(teamname.toLowerCase());
+ if (team != null) {
+ // clone the model, otherwise all changes to this object are
+ // live and unpersisted
+ team = DeepCopier.copy(team);
+ }
+ return team;
+ }
+
+ /**
+ * Updates/writes a complete team object.
+ *
+ * @param model
+ * @return true if update is successful
+ * @since 0.8.0
+ */
+ @Override
+ public boolean updateTeamModel(TeamModel model) {
+ return updateTeamModel(model.name, model);
+ }
+
+ /**
+ * 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 boolean updateTeamModel(String teamname, TeamModel model) {
+ try {
+ Properties allUsers = read();
+ updateTeamCache(allUsers, teamname, model);
+ write(allUsers);
+ return true;
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format("Failed to update team model {0}!", model.name), t);
+ }
+ return false;
+ }
+
+ private void updateTeamCache(Properties allUsers, String teamname, TeamModel model) {
+ StringBuilder sb = new StringBuilder();
+ if (!ArrayUtils.isEmpty(model.repositories)) {
+ for (String repository : model.repositories) {
+ sb.append(repository);
+ sb.append(',');
+ }
+ }
+ if (!ArrayUtils.isEmpty(model.users)) {
+ for (String user : model.users) {
+ sb.append('!');
+ sb.append(user);
+ sb.append(',');
+ }
+ }
+ if (!ArrayUtils.isEmpty(model.mailingLists)) {
+ for (String address : model.mailingLists) {
+ sb.append('&');
+ sb.append(address);
+ sb.append(',');
+ }
+ }
+ if (!ArrayUtils.isEmpty(model.preReceiveScripts)) {
+ for (String script : model.preReceiveScripts) {
+ sb.append('^');
+ sb.append(script);
+ sb.append(',');
+ }
+ }
+ if (!ArrayUtils.isEmpty(model.postReceiveScripts)) {
+ for (String script : model.postReceiveScripts) {
+ sb.append('%');
+ sb.append(script);
+ sb.append(',');
+ }
+ }
+ // trim trailing comma
+ sb.setLength(sb.length() - 1);
+ allUsers.remove("@" + teamname);
+ allUsers.put("@" + model.name, sb.toString());
+
+ // update team cache
+ teams.remove(teamname.toLowerCase());
+ teams.put(model.name.toLowerCase(), model);
+ }
+
+ /**
+ * Deletes the team object from the user service.
+ *
+ * @param model
+ * @return true if successful
+ * @since 0.8.0
+ */
+ @Override
+ public 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 boolean deleteTeam(String teamname) {
+ Properties allUsers = read();
+ teams.remove(teamname.toLowerCase());
+ allUsers.remove("@" + teamname);
+ try {
+ write(allUsers);
+ return true;
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format("Failed to delete team {0}!", teamname), t);
+ }
+ return false;
+ }
}
--
Gitblit v1.9.1