From f988253399ee475aa4f4e60adb95a220f8f88d21 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 12 May 2011 17:33:31 -0400 Subject: [PATCH] Moved distribution files. Revised build script. Security revisions. --- src/com/gitblit/JettyLoginService.java | 313 +++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 273 insertions(+), 40 deletions(-) diff --git a/src/com/gitblit/JettyLoginService.java b/src/com/gitblit/JettyLoginService.java index 4b43964..fb510ee 100644 --- a/src/com/gitblit/JettyLoginService.java +++ b/src/com/gitblit/JettyLoginService.java @@ -5,9 +5,13 @@ import java.io.FileWriter; import java.io.IOException; import java.security.Principal; +import java.text.MessageFormat; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import javax.security.auth.Subject; @@ -16,11 +20,15 @@ import org.eclipse.jetty.security.MappedLoginService; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.log.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.models.UserModel; public class JettyLoginService extends MappedLoginService implements ILoginService { + + private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class); private final File realmFile; @@ -44,8 +52,9 @@ for (Principal principal : identity.getSubject().getPrincipals()) { if (principal instanceof RolePrincipal) { RolePrincipal role = (RolePrincipal) principal; - if (role.getName().charAt(0) != '#') { - user.addRepository(role.getName().substring(1)); + String roleName = role.getName(); + if (roleName.charAt(0) != '#') { + user.addRepository(roleName); } } } @@ -75,9 +84,20 @@ } break; default: - model.addRepository(name.substring(1)); + model.addRepository(name); } } + } + // Retrieve the password from the realm file. + // Stupid, I know, but the password is buried within protected inner + // classes in private variables. Too much work to reflectively retrieve. + try { + Properties allUsers = readRealmFile(); + String value = allUsers.getProperty(username); + String password = value.split(",")[0]; + model.setPassword(password); + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t); } return model; } @@ -85,15 +105,8 @@ @Override public boolean updateUserModel(UserModel model) { try { - Properties properties = new Properties(); - FileReader reader = new FileReader(realmFile); - properties.load(reader); - reader.close(); - - ArrayList<String> roles = new ArrayList<String>(); - - // Repositories - roles.addAll(model.getRepositories()); + Properties allUsers = readRealmFile(); + ArrayList<String> roles = new ArrayList<String>(model.getRepositories()); // Permissions if (model.canAdmin()) { @@ -109,21 +122,15 @@ } // trim trailing comma sb.setLength(sb.length() - 1); + allUsers.put(model.getUsername(), sb.toString()); - // Update realm file - File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp"); - FileWriter writer = new FileWriter(realmFileCopy); - properties.put(model.getUsername(), sb.toString()); - properties.store(writer, null); - writer.close(); - realmFile.delete(); - realmFileCopy.renameTo(realmFile); + writeRealmFile(allUsers); // Update login service putUser(model.getUsername(), Credential.getCredential(model.getPassword()), roles.toArray(new String[0])); return true; } catch (Throwable t) { - t.printStackTrace(); + logger.error(MessageFormat.format("Failed to update user model {0}!", model.getUsername()), t); } return false; } @@ -132,27 +139,256 @@ public boolean deleteUserModel(UserModel model) { try { // Read realm file - Properties properties = new Properties(); - FileReader reader = new FileReader(realmFile); - properties.load(reader); - reader.close(); - properties.remove(model.getUsername()); - - // Update realm file - File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp"); - FileWriter writer = new FileWriter(realmFileCopy); - properties.store(writer, null); - writer.close(); - realmFile.delete(); - realmFileCopy.renameTo(realmFile); + Properties allUsers = readRealmFile(); + allUsers.remove(model.getUsername()); + writeRealmFile(allUsers); // Drop user from map _users.remove(model.getUsername()); return true; } catch (Throwable t) { - t.printStackTrace(); + logger.error(MessageFormat.format("Failed to delete user model {0}!", model.getUsername()), t); } return false; + } + + @Override + public List<String> getAllUsernames() { + List<String> list = new ArrayList<String>(); + list.addAll(_users.keySet()); + return list; + } + + @Override + public List<String> getUsernamesForRole(String role) { + List<String> list = new ArrayList<String>(); + try { + Properties allUsers = readRealmFile(); + for (String username : allUsers.stringPropertyNames()) { + String value = allUsers.getProperty(username); + String[] values = value.split(","); + // skip first value (password) + for (int i = 1; i < values.length; i++) { + String r = values[i]; + if (r.equalsIgnoreCase(role)) { + list.add(username); + break; + } + } + } + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t); + } + return list; + } + + @Override + public boolean setUsernamesForRole(String role, List<String> usernames) { + try { + Set<String> specifiedUsers = new HashSet<String>(usernames); + Set<String> needsAddRole = new HashSet<String>(specifiedUsers); + Set<String> needsRemoveRole = new HashSet<String>(); + + // identify users which require add and remove role + Properties allUsers = readRealmFile(); + for (String username : allUsers.stringPropertyNames()) { + String value = allUsers.getProperty(username); + String[] values = value.split(","); + // skip first value (password) + for (int i = 1; i < values.length; i++) { + String r = values[i]; + if (r.equalsIgnoreCase(role)) { + // user has role, check against revised user list + if (specifiedUsers.contains(username)) { + needsAddRole.remove(username); + } else { + // remove role from user + needsRemoveRole.add(username); + } + break; + } + } + } + + // add roles to users + for (String user : needsAddRole) { + String userValues = allUsers.getProperty(user); + userValues += ("," + role); + allUsers.put(user, userValues); + String[] values = userValues.split(","); + String password = values[0]; + String[] roles = new String[values.length - 1]; + System.arraycopy(values, 1, roles, 0, values.length - 1); + putUser(user, Credential.getCredential(password), roles); + } + + // remove role from user + for (String user : needsRemoveRole) { + String[] values = allUsers.getProperty(user).split(","); + String password = values[0]; + 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(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t); + } + return false; + } + + @Override + public boolean renameRole(String oldRole, String newRole) { + try { + Properties allUsers = readRealmFile(); + Set<String> needsRenameRole = new HashSet<String>(); + + // identify users which require role rename + for (String username : allUsers.stringPropertyNames()) { + String value = allUsers.getProperty(username); + String[] roles = value.split(","); + // skip first value (password) + for (int i = 1; i < roles.length; i++) { + String r = roles[i]; + if (r.equalsIgnoreCase(oldRole)) { + needsRenameRole.remove(username); + break; + } + } + } + + // rename role for identified users + for (String user : needsRenameRole) { + String userValues = allUsers.getProperty(user); + String[] values = userValues.split(","); + String password = values[0]; + StringBuilder sb = new StringBuilder(); + sb.append(password); + sb.append(','); + List<String> revisedRoles = new ArrayList<String>(); + revisedRoles.add(newRole); + // 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(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t); + } + return false; + } + + @Override + public boolean deleteRole(String role) { + try { + Properties allUsers = readRealmFile(); + Set<String> needsDeleteRole = new HashSet<String>(); + + // identify users which require role rename + for (String username : allUsers.stringPropertyNames()) { + String value = allUsers.getProperty(username); + String[] roles = value.split(","); + // skip first value (password) + for (int i = 1; i < roles.length; i++) { + String r = roles[i]; + if (r.equalsIgnoreCase(role)) { + needsDeleteRole.remove(username); + break; + } + } + } + + // delete role for identified users + for (String user : needsDeleteRole) { + String userValues = allUsers.getProperty(user); + String[] values = userValues.split(","); + String password = values[0]; + 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(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to delete role {0}!", role), t); + } + return false; + } + + private Properties readRealmFile() throws IOException { + Properties allUsers = new Properties(); + FileReader reader = new FileReader(realmFile); + allUsers.load(reader); + reader.close(); + return allUsers; + } + + private void writeRealmFile(Properties properties) throws IOException { + // Update realm file + File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp"); + FileWriter writer = new FileWriter(realmFileCopy); + properties.store(writer, "# Git:Blit realm file format: username=password,\\#permission,repository1,repository2..."); + writer.close(); + if (realmFileCopy.exists() && realmFileCopy.length() > 0) { + realmFile.delete(); + realmFileCopy.renameTo(realmFile); + } else { + throw new IOException("Failed to save realmfile!"); + } } /* ------------------------------------------------------------ */ @@ -163,13 +399,10 @@ if (Log.isDebugEnabled()) Log.debug("Load " + this + " from " + realmFile); - Properties properties = new Properties(); - FileReader reader = new FileReader(realmFile); - properties.load(reader); - reader.close(); + Properties allUsers = readRealmFile(); // Map Users - for (Map.Entry<Object, Object> entry : properties.entrySet()) { + for (Map.Entry<Object, Object> entry : allUsers.entrySet()) { String username = ((String) entry.getKey()).trim(); String credentials = ((String) entry.getValue()).trim(); String roles = null; -- Gitblit v1.9.1