From 793f76563d4bb3f58fa62ff53985e20561c6e330 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Wed, 01 Jun 2011 21:01:51 -0400
Subject: [PATCH] Refactored some unit tests and utils.
---
src/com/gitblit/JettyLoginService.java | 393 +++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 334 insertions(+), 59 deletions(-)
diff --git a/src/com/gitblit/JettyLoginService.java b/src/com/gitblit/JettyLoginService.java
index 4b43964..63a9861 100644
--- a/src/com/gitblit/JettyLoginService.java
+++ b/src/com/gitblit/JettyLoginService.java
@@ -1,3 +1,18 @@
+/*
+ * 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;
@@ -5,9 +20,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 +35,14 @@
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;
+import com.gitblit.models.UserModel;
public class JettyLoginService extends MappedLoginService implements ILoginService {
+
+ private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class);
private final File realmFile;
@@ -37,15 +59,15 @@
return null;
}
UserModel user = new UserModel(username);
- user.setCookie(StringUtils.getSHA1((Constants.NAME + username + new String(password))));
- user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null));
+ user.canAdmin = identity.isUserInRole(Constants.ADMIN_ROLE, null);
// Add repositories
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);
}
}
}
@@ -53,15 +75,12 @@
}
@Override
- public UserModel authenticate(char[] cookie) {
- // TODO cookie login
- return null;
- }
-
- @Override
public UserModel getUserModel(String username) {
- UserModel model = new UserModel(username);
UserIdentity identity = _users.get(username);
+ if (identity == null) {
+ return null;
+ }
+ UserModel model = new UserModel(username);
Subject subject = identity.getSubject();
for (Principal principal : subject.getPrincipals()) {
if (principal instanceof RolePrincipal) {
@@ -71,37 +90,46 @@
case '#':
// Permissions
if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {
- model.canAdmin(true);
+ model.canAdmin = true;
}
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.password = password;
+ } catch (Throwable t) {
+ logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t);
}
return model;
}
@Override
public boolean updateUserModel(UserModel model) {
+ return updateUserModel(model.username, model);
+ }
+
+ @Override
+ public boolean updateUserModel(String username, 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.repositories);
// Permissions
- if (model.canAdmin()) {
+ if (model.canAdmin) {
roles.add(Constants.ADMIN_ROLE);
}
StringBuilder sb = new StringBuilder();
- sb.append(model.getPassword());
+ sb.append(model.password);
sb.append(',');
for (String role : roles) {
sb.append(role);
@@ -109,67 +137,313 @@
}
// trim trailing comma
sb.setLength(sb.length() - 1);
+ allUsers.remove(username);
+ allUsers.put(model.username, 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]));
+ removeUser(username);
+ putUser(model.username, Credential.getCredential(model.password),
+ roles.toArray(new String[0]));
return true;
} catch (Throwable t) {
- t.printStackTrace();
+ logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),
+ t);
}
return false;
}
@Override
public boolean deleteUserModel(UserModel model) {
+ return deleteUser(model.username);
+ }
+
+ @Override
+ public boolean deleteUser(String username) {
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(username);
+ writeRealmFile(allUsers);
// Drop user from map
- _users.remove(model.getUsername());
+ removeUser(username);
return true;
} catch (Throwable t) {
- t.printStackTrace();
+ logger.error(MessageFormat.format("Failed to delete user {0}!", username), 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);
+ return true;
+ } 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) {
+ if (realmFile.delete()) {
+ 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 delete (0)!",
+ realmFile.getAbsolutePath()));
+ }
+ } else {
+ throw new IOException(MessageFormat.format("Failed to save {0}!",
+ realmFileCopy.getAbsolutePath()));
+ }
}
/* ------------------------------------------------------------ */
@Override
public void loadUsers() throws IOException {
- if (realmFile == null)
+ if (realmFile == null) {
return;
+ }
- if (Log.isDebugEnabled())
+ 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;
@@ -179,7 +453,8 @@
credentials = credentials.substring(0, c).trim();
}
- if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0) {
+ if (username != null && username.length() > 0 && credentials != null
+ && credentials.length() > 0) {
String[] roleArray = IdentityService.NO_ROLES;
if (roles != null && roles.length() > 0) {
roleArray = roles.split(",");
--
Gitblit v1.9.1