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