From a502d96a860456ec5e8c96761db70f7cabb74751 Mon Sep 17 00:00:00 2001
From: Paul Martin <paul@paulsputer.com>
Date: Sat, 30 Apr 2016 04:19:14 -0400
Subject: [PATCH] Merge pull request #1073 from gitblit/1062-DocEditorUpdates

---
 src/main/java/com/gitblit/manager/UserManager.java |  337 +++++++++++++++++++++++++++++---------------------------
 1 files changed, 175 insertions(+), 162 deletions(-)

diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java
index f781c4c..e88ac93 100644
--- a/src/main/java/com/gitblit/manager/UserManager.java
+++ b/src/main/java/com/gitblit/manager/UserManager.java
@@ -20,21 +20,24 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.ConfigUserService;
 import com.gitblit.Constants;
-import com.gitblit.Constants.AccountType;
 import com.gitblit.IStoredSettings;
 import com.gitblit.IUserService;
 import com.gitblit.Keys;
+import com.gitblit.extensions.UserTeamLifeCycleListener;
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
-import com.gitblit.utils.DeepCopier;
 import com.gitblit.utils.StringUtils;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 /**
  * The user manager manages persistence and retrieval of users and teams.
@@ -42,6 +45,7 @@
  * @author James Moger
  *
  */
+@Singleton
 public class UserManager implements IUserManager {
 
 	private final Logger logger = LoggerFactory.getLogger(getClass());
@@ -50,37 +54,74 @@
 
 	private final IRuntimeManager runtimeManager;
 
+	private final IPluginManager pluginManager;
+
+	private final Map<String, String> legacyBackingServices;
+
 	private IUserService userService;
 
-	public UserManager(IRuntimeManager runtimeManager) {
+	@Inject
+	public UserManager(IRuntimeManager runtimeManager, IPluginManager pluginManager) {
 		this.settings = runtimeManager.getSettings();
 		this.runtimeManager = runtimeManager;
+		this.pluginManager = pluginManager;
+
+		// map of legacy realm backing user services
+		legacyBackingServices = new HashMap<String, String>();
+		legacyBackingServices.put("com.gitblit.HtpasswdUserService", "realm.htpasswd.backingUserService");
+		legacyBackingServices.put("com.gitblit.LdapUserService", "realm.ldap.backingUserService");
+		legacyBackingServices.put("com.gitblit.PAMUserService", "realm.pam.backingUserService");
+		legacyBackingServices.put("com.gitblit.RedmineUserService", "realm.redmine.backingUserService");
+		legacyBackingServices.put("com.gitblit.SalesforceUserService", "realm.salesforce.backingUserService");
+		legacyBackingServices.put("com.gitblit.WindowsUserService", "realm.windows.backingUserService");
 	}
 
 	/**
-	 * Set the user service. The user service authenticates local users and is
-	 * responsible for persisting and retrieving users and teams.
+	 * Set the user service. The user service authenticates *local* users and is
+	 * responsible for persisting and retrieving all users and all teams.
 	 *
 	 * @param userService
 	 */
 	public void setUserService(IUserService userService) {
-		logger.info("Setting up user service " + userService.toString());
 		this.userService = userService;
 		this.userService.setup(runtimeManager);
+		logger.info(userService.toString());
 	}
 
 	@Override
-	public IManager setup() {
+	public void setup(IRuntimeManager runtimeManager) {
+		// NOOP
+	}
+
+	@Override
+	public UserManager start() {
 		if (this.userService == null) {
-			String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties");
+			String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf");
 			IUserService service = null;
-			try {
-				// check to see if this "file" is a login service class
-				Class<?> realmClass = Class.forName(realm);
-				service = (IUserService) realmClass.newInstance();
-			} catch (Throwable t) {
-				File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
+			if (legacyBackingServices.containsKey(realm)) {
+				// create the user service from the legacy config
+				String realmKey = legacyBackingServices.get(realm);
+				logger.warn("");
+				logger.warn(Constants.BORDER2);
+				logger.warn(" Key '{}' is obsolete!", realmKey);
+				logger.warn(" Please set '{}={}'", Keys.realm.userService, settings.getString(realmKey, "${baseFolder}/users.conf"));
+				logger.warn(Constants.BORDER2);
+				logger.warn("");
+				File realmFile = runtimeManager.getFileOrFolder(realmKey, "${baseFolder}/users.conf");
 				service = createUserService(realmFile);
+			} else {
+				// either a file path OR a custom user service
+				try {
+					// check to see if this "file" is a custom user service class
+					Class<?> realmClass = Class.forName(realm);
+					service = (IUserService) realmClass.newInstance();
+				} catch (ClassNotFoundException t) {
+					// typical file path configuration
+					File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
+					service = createUserService(realmFile);
+				} catch (InstantiationException | IllegalAccessException  e) {
+					logger.error("failed to instantiate user service {}: {}", realm, e.getMessage());
+				}
 			}
 			setUserService(service);
 		}
@@ -90,7 +131,7 @@
 	protected IUserService createUserService(File realmFile) {
 		IUserService service = null;
 		if (realmFile.getName().toLowerCase().endsWith(".conf")) {
-			// v0.8.0+ config-based realm file
+			// config-based realm file
 			service = new ConfigUserService(realmFile);
 		}
 
@@ -114,76 +155,21 @@
 	}
 
 	@Override
-	public IManager stop() {
+	public UserManager stop() {
 		return this;
 	}
 
-	@Override
-	public boolean supportsAddUser() {
-		return supportsCredentialChanges(new UserModel(""));
-	}
-
 	/**
-	 * Returns true if the user's credentials can be changed.
+	 * Returns true if the username represents an internal account
 	 *
-	 * @param user
-	 * @return true if the user service supports credential changes
+	 * @param username
+	 * @return true if the specified username represents an internal account
 	 */
 	@Override
-	public boolean supportsCredentialChanges(UserModel user) {
-		if (user == null) {
-			return false;
-		} else if (AccountType.LOCAL.equals(user.accountType)) {
-			// local account, we can change credentials
-			return true;
-		} else {
-			// external account, ask user service
-			return userService.supportsCredentialChanges();
-		}
-	}
-
-	/**
-	 * Returns true if the user's display name can be changed.
-	 *
-	 * @param user
-	 * @return true if the user service supports display name changes
-	 */
-	@Override
-	public boolean supportsDisplayNameChanges(UserModel user) {
-		return (user != null && user.isLocalAccount()) || userService.supportsDisplayNameChanges();
-	}
-
-	/**
-	 * Returns true if the user's email address can be changed.
-	 *
-	 * @param user
-	 * @return true if the user service supports email address changes
-	 */
-	@Override
-	public boolean supportsEmailAddressChanges(UserModel user) {
-		return (user != null && user.isLocalAccount()) || userService.supportsEmailAddressChanges();
-	}
-
-	/**
-	 * Returns true if the user's team memberships can be changed.
-	 *
-	 * @param user
-	 * @return true if the user service supports team membership changes
-	 */
-	@Override
-	public boolean supportsTeamMembershipChanges(UserModel user) {
-		return (user != null && user.isLocalAccount()) || userService.supportsTeamMembershipChanges();
-	}
-
-	/**
-	 * Allow to understand if GitBlit supports and is configured to allow
-	 * cookie-based authentication.
-	 *
-	 * @return status of Cookie authentication enablement.
-	 */
-	@Override
-	public boolean supportsCookies() {
-		return settings.getBoolean(Keys.web.allowCookieAuthentication, true) && userService.supportsCookies();
+	public boolean isInternalAccount(String username) {
+		return !StringUtils.isEmpty(username)
+				&& (username.equalsIgnoreCase(Constants.FEDERATION_USER)
+						|| username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
 	}
 
 	/**
@@ -198,44 +184,15 @@
 	}
 
 	/**
-	 * Authenticate a user based on a username and password.
-	 *
-	 * @param username
-	 * @param password
-	 * @return a user object or null
-	 */
-
-	@Override
-	public UserModel authenticate(String username, char[] password) {
-		UserModel user = userService.authenticate(username, password);
-		setAccountType(user);
-		return user;
-	}
-
-	/**
-	 * Authenticate a user based on their cookie.
+	 * Retrieve the user object for the specified cookie.
 	 *
 	 * @param cookie
 	 * @return a user object or null
 	 */
 	@Override
-	public UserModel authenticate(char[] cookie) {
-		UserModel user = userService.authenticate(cookie);
-		setAccountType(user);
+	public UserModel getUserModel(char[] cookie) {
+		UserModel user = userService.getUserModel(cookie);
 		return user;
-	}
-
-	/**
-	 * Logout a user.
-	 *
-	 * @param user
-	 */
-	@Override
-	public void logout(UserModel user) {
-		if (userService == null) {
-			return;
-		}
-		userService.logout(user);
 	}
 
 	/**
@@ -251,7 +208,6 @@
 		}
 		String usernameDecoded = StringUtils.decodeUsername(username);
 		UserModel user = userService.getUserModel(usernameDecoded);
-		setAccountType(user);
 		return user;
 	}
 
@@ -263,7 +219,14 @@
 	 */
 	@Override
 	public boolean updateUserModel(UserModel model) {
-		return userService.updateUserModel(model);
+		final boolean isCreate = null == userService.getUserModel(model.username);
+		if (userService.updateUserModel(model)) {
+			if (isCreate) {
+				callCreateUserListeners(model);
+			}
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -290,31 +253,13 @@
 	 */
 	@Override
 	public boolean updateUserModel(String username, UserModel model) {
-		if (model.isLocalAccount() || userService.supportsCredentialChanges()) {
-			if (!model.isLocalAccount() && !userService.supportsTeamMembershipChanges()) {
-				//  teams are externally controlled - copy from original model
-				UserModel existingModel = getUserModel(username);
-
-				model = DeepCopier.copy(model);
-				model.teams.clear();
-				model.teams.addAll(existingModel.teams);
+		final boolean isCreate = null == userService.getUserModel(username);
+		if (userService.updateUserModel(username, model)) {
+			if (isCreate) {
+				callCreateUserListeners(model);
 			}
-			return userService.updateUserModel(username, model);
+			return true;
 		}
-		if (model.username.equals(username)) {
-			// passwords are not persisted by the backing user service
-			model.password = null;
-			if (!model.isLocalAccount() && !userService.supportsTeamMembershipChanges()) {
-				//  teams are externally controlled- copy from original model
-				UserModel existingModel = getUserModel(username);
-
-				model = DeepCopier.copy(model);
-				model.teams.clear();
-				model.teams.addAll(existingModel.teams);
-			}
-			return userService.updateUserModel(username, model);
-		}
-		logger.error("Users can not be renamed!");
 		return false;
 	}
 
@@ -326,7 +271,11 @@
 	 */
 	@Override
 	public boolean deleteUserModel(UserModel model) {
-		return userService.deleteUserModel(model);
+		if (userService.deleteUserModel(model)) {
+			callDeleteUserListeners(model);
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -341,7 +290,12 @@
 			return false;
 		}
 		String usernameDecoded = StringUtils.decodeUsername(username);
-		return userService.deleteUser(usernameDecoded);
+		UserModel user = getUserModel(usernameDecoded);
+		if (userService.deleteUser(usernameDecoded)) {
+			callDeleteUserListeners(user);
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -364,9 +318,6 @@
 	@Override
 	public List<UserModel> getAllUsers() {
 		List<UserModel> users = userService.getAllUsers();
-    	for (UserModel user : users) {
-    		setAccountType(user);
-    	}
 		return users;
 	}
 
@@ -378,7 +329,8 @@
 	 */
 	@Override
 	public List<String> getAllTeamNames() {
-		return userService.getAllTeamNames();
+		List<String> teams = userService.getAllTeamNames();
+		return teams;
 	}
 
 	/**
@@ -404,7 +356,8 @@
 	 */
 	@Override
 	public List<String> getTeamNamesForRepositoryRole(String role) {
-		return userService.getTeamNamesForRepositoryRole(role);
+		List<String> teams = userService.getTeamNamesForRepositoryRole(role);
+		return teams;
 	}
 
 	/**
@@ -416,7 +369,8 @@
 	 */
 	@Override
 	public TeamModel getTeamModel(String teamname) {
-		return userService.getTeamModel(teamname);
+		TeamModel team = userService.getTeamModel(teamname);
+		return team;
 	}
 
 	/**
@@ -428,7 +382,14 @@
 	 */
 	@Override
 	public boolean updateTeamModel(TeamModel model) {
-		return userService.updateTeamModel(model);
+		final boolean isCreate = null == userService.getTeamModel(model.name);
+		if (userService.updateTeamModel(model)) {
+			if (isCreate) {
+				callCreateTeamListeners(model);
+			}
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -456,15 +417,14 @@
 	 */
 	@Override
 	public boolean updateTeamModel(String teamname, TeamModel model) {
-		if (!userService.supportsTeamMembershipChanges()) {
-			// teams are externally controlled - copy from original model
-			TeamModel existingModel = getTeamModel(teamname);
-
-			model = DeepCopier.copy(model);
-			model.users.clear();
-			model.users.addAll(existingModel.users);
+		final boolean isCreate = null == userService.getTeamModel(teamname);
+		if (userService.updateTeamModel(teamname, model)) {
+			if (isCreate) {
+				callCreateTeamListeners(model);
+			}
+			return true;
 		}
-		return userService.updateTeamModel(teamname, model);
+		return false;
 	}
 
 	/**
@@ -476,7 +436,11 @@
 	 */
 	@Override
 	public boolean deleteTeamModel(TeamModel model) {
-		return userService.deleteTeamModel(model);
+		if (userService.deleteTeamModel(model)) {
+			callDeleteTeamListeners(model);
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -488,7 +452,12 @@
 	 */
 	@Override
 	public boolean deleteTeam(String teamname) {
-		return userService.deleteTeam(teamname);
+		TeamModel team = userService.getTeamModel(teamname);
+		if (userService.deleteTeam(teamname)) {
+			callDeleteTeamListeners(team);
+			return true;
+		}
+		return false;
 	}
 
 	/**
@@ -528,14 +497,58 @@
 		return userService.deleteRepositoryRole(role);
 	}
 
-	protected void setAccountType(UserModel user) {
-		if (user != null) {
-			if (!StringUtils.isEmpty(user.password)
-					&& !Constants.EXTERNAL_ACCOUNT.equalsIgnoreCase(user.password)
-					&& !"StoredInLDAP".equalsIgnoreCase(user.password)) {
-				user.accountType = AccountType.LOCAL;
-			} else {
-				user.accountType = userService.getAccountType();
+	protected void callCreateUserListeners(UserModel user) {
+		if (pluginManager == null || user == null) {
+			return;
+		}
+
+		for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
+			try {
+				listener.onCreation(user);
+			} catch (Throwable t) {
+				logger.error(String.format("failed to call plugin.onCreation%s", user.username), t);
+			}
+		}
+	}
+
+	protected void callCreateTeamListeners(TeamModel team) {
+		if (pluginManager == null || team == null) {
+			return;
+		}
+
+		for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
+			try {
+				listener.onCreation(team);
+			} catch (Throwable t) {
+				logger.error(String.format("failed to call plugin.onCreation %s", team.name), t);
+			}
+		}
+	}
+
+	protected void callDeleteUserListeners(UserModel user) {
+		if (pluginManager == null || user == null) {
+			return;
+		}
+
+		for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
+			try {
+				listener.onDeletion(user);
+			} catch (Throwable t) {
+				logger.error(String.format("failed to call plugin.onDeletion %s", user.username), t);
+			}
+		}
+	}
+
+	protected void callDeleteTeamListeners(TeamModel team) {
+		if (pluginManager == null || team == null) {
+			return;
+		}
+
+		for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
+			try {
+				listener.onDeletion(team);
+			} catch (Throwable t) {
+				logger.error(String.format("failed to call plugin.onDeletion %s", team.name), t);
 			}
 		}
 	}

--
Gitblit v1.9.1