| /* |
| * Copyright 2013 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.manager; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| 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.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.StringUtils; |
| import com.google.inject.Inject; |
| import com.google.inject.Singleton; |
| |
| /** |
| * The user manager manages persistence and retrieval of users and teams. |
| * |
| * @author James Moger |
| * |
| */ |
| @Singleton |
| public class UserManager implements IUserManager { |
| |
| private final Logger logger = LoggerFactory.getLogger(getClass()); |
| |
| private final IStoredSettings settings; |
| |
| private final IRuntimeManager runtimeManager; |
| |
| private final IPluginManager pluginManager; |
| |
| private final Map<String, String> legacyBackingServices; |
| |
| private IUserService userService; |
| |
| @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 all users and all teams. |
| * |
| * @param userService |
| */ |
| public void setUserService(IUserService userService) { |
| this.userService = userService; |
| this.userService.setup(runtimeManager); |
| logger.info(userService.toString()); |
| } |
| |
| @Override |
| public void setup(IRuntimeManager runtimeManager) { |
| // NOOP |
| } |
| |
| @Override |
| public UserManager start() { |
| if (this.userService == null) { |
| String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf"); |
| IUserService service = null; |
| 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 {}: {}. Trying once again with IRuntimeManager constructor", realm, e.getMessage()); |
| //try once again with IRuntimeManager constructor. This adds support for subclasses of ConfigUserService and other custom IUserServices |
| service = createIRuntimeManagerAwareUserService(realm); |
| } |
| } |
| setUserService(service); |
| } |
| return this; |
| } |
| |
| /** |
| * Tries to create an {@link IUserService} with {@link #runtimeManager} as a constructor parameter |
| * |
| * @param realm the class name of the {@link IUserService} to be instantiated |
| * @return the {@link IUserService} or {@code null} if instantiation fails |
| */ |
| private IUserService createIRuntimeManagerAwareUserService(String realm) { |
| try { |
| Constructor<?> constructor = Class.forName(realm).getConstructor(IRuntimeManager.class); |
| return (IUserService) constructor.newInstance(runtimeManager); |
| } catch (NoSuchMethodException | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| logger.error("failed to instantiate user service {}: {}", realm, e.getMessage()); |
| return null; |
| } |
| } |
| |
| protected IUserService createUserService(File realmFile) { |
| IUserService service = null; |
| if (realmFile.getName().toLowerCase().endsWith(".conf")) { |
| // config-based realm file |
| service = new ConfigUserService(realmFile); |
| } |
| |
| assert service != null; |
| |
| if (!realmFile.exists()) { |
| // Create the Administrator account for a new realm file |
| try { |
| realmFile.createNewFile(); |
| } catch (IOException x) { |
| logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmFile), x); |
| } |
| UserModel admin = new UserModel("admin"); |
| admin.password = "admin"; |
| admin.canAdmin = true; |
| admin.excludeFromFederation = true; |
| service.updateUserModel(admin); |
| } |
| |
| return service; |
| } |
| |
| @Override |
| public UserManager stop() { |
| return this; |
| } |
| |
| /** |
| * Returns true if the username represents an internal account |
| * |
| * @param username |
| * @return true if the specified username represents an internal account |
| */ |
| @Override |
| public boolean isInternalAccount(String username) { |
| return !StringUtils.isEmpty(username) |
| && (username.equalsIgnoreCase(Constants.FEDERATION_USER) |
| || username.equalsIgnoreCase(UserModel.ANONYMOUS.username)); |
| } |
| |
| /** |
| * Returns the cookie value for the specified user. |
| * |
| * @param model |
| * @return cookie value |
| */ |
| @Override |
| public String getCookie(UserModel model) { |
| return userService.getCookie(model); |
| } |
| |
| /** |
| * Retrieve the user object for the specified cookie. |
| * |
| * @param cookie |
| * @return a user object or null |
| */ |
| @Override |
| public UserModel getUserModel(char[] cookie) { |
| UserModel user = userService.getUserModel(cookie); |
| return user; |
| } |
| |
| /** |
| * Retrieve the user object for the specified username. |
| * |
| * @param username |
| * @return a user object or null |
| */ |
| @Override |
| public UserModel getUserModel(String username) { |
| if (StringUtils.isEmpty(username)) { |
| return null; |
| } |
| String usernameDecoded = StringUtils.decodeUsername(username); |
| UserModel user = userService.getUserModel(usernameDecoded); |
| return user; |
| } |
| |
| /** |
| * Updates/writes a complete user object. |
| * |
| * @param model |
| * @return true if update is successful |
| */ |
| @Override |
| public boolean updateUserModel(UserModel model) { |
| final boolean isCreate = null == userService.getUserModel(model.username); |
| if (userService.updateUserModel(model)) { |
| if (isCreate) { |
| callCreateUserListeners(model); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Updates/writes all specified user objects. |
| * |
| * @param models a list of user models |
| * @return true if update is successful |
| * @since 1.2.0 |
| */ |
| @Override |
| public boolean updateUserModels(Collection<UserModel> models) { |
| return userService.updateUserModels(models); |
| } |
| |
| /** |
| * Adds/updates a user object keyed by username. This method allows for |
| * renaming a user. |
| * |
| * @param username |
| * the old username |
| * @param model |
| * the user object to use for username |
| * @return true if update is successful |
| */ |
| @Override |
| public boolean updateUserModel(String username, UserModel model) { |
| final boolean isCreate = null == userService.getUserModel(username); |
| if (userService.updateUserModel(username, model)) { |
| if (isCreate) { |
| callCreateUserListeners(model); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Deletes the user object from the user service. |
| * |
| * @param model |
| * @return true if successful |
| */ |
| @Override |
| public boolean deleteUserModel(UserModel model) { |
| if (userService.deleteUserModel(model)) { |
| callDeleteUserListeners(model); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Delete the user object with the specified username |
| * |
| * @param username |
| * @return true if successful |
| */ |
| @Override |
| public boolean deleteUser(String username) { |
| if (StringUtils.isEmpty(username)) { |
| return false; |
| } |
| String usernameDecoded = StringUtils.decodeUsername(username); |
| UserModel user = getUserModel(usernameDecoded); |
| if (userService.deleteUser(usernameDecoded)) { |
| callDeleteUserListeners(user); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the list of all users available to the login service. |
| * |
| * @return list of all usernames |
| */ |
| @Override |
| public List<String> getAllUsernames() { |
| List<String> names = new ArrayList<String>(userService.getAllUsernames()); |
| return names; |
| } |
| |
| /** |
| * Returns the list of all users available to the login service. |
| * |
| * @return list of all users |
| * @since 0.8.0 |
| */ |
| @Override |
| public List<UserModel> getAllUsers() { |
| List<UserModel> users = userService.getAllUsers(); |
| return users; |
| } |
| |
| /** |
| * 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> teams = userService.getAllTeamNames(); |
| return teams; |
| } |
| |
| /** |
| * 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> teams = userService.getAllTeams(); |
| return teams; |
| } |
| |
| /** |
| * 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 teams that can bypass the access restriction |
| * @since 0.8.0 |
| */ |
| @Override |
| public List<String> getTeamNamesForRepositoryRole(String role) { |
| List<String> teams = userService.getTeamNamesForRepositoryRole(role); |
| return teams; |
| } |
| |
| /** |
| * 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) { |
| TeamModel team = userService.getTeamModel(teamname); |
| 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) { |
| final boolean isCreate = null == userService.getTeamModel(model.name); |
| if (userService.updateTeamModel(model)) { |
| if (isCreate) { |
| callCreateTeamListeners(model); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Updates/writes all specified team objects. |
| * |
| * @param models a list of team models |
| * @return true if update is successful |
| * @since 1.2.0 |
| */ |
| @Override |
| public boolean updateTeamModels(Collection<TeamModel> models) { |
| return userService.updateTeamModels(models); |
| } |
| |
| /** |
| * 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) { |
| final boolean isCreate = null == userService.getTeamModel(teamname); |
| if (userService.updateTeamModel(teamname, model)) { |
| if (isCreate) { |
| callCreateTeamListeners(model); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Deletes the team object from the user service. |
| * |
| * @param model |
| * @return true if successful |
| * @since 0.8.0 |
| */ |
| @Override |
| public boolean deleteTeamModel(TeamModel model) { |
| if (userService.deleteTeamModel(model)) { |
| callDeleteTeamListeners(model); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Delete the team object with the specified teamname |
| * |
| * @param teamname |
| * @return true if successful |
| * @since 0.8.0 |
| */ |
| @Override |
| public boolean deleteTeam(String teamname) { |
| TeamModel team = userService.getTeamModel(teamname); |
| if (userService.deleteTeam(teamname)) { |
| callDeleteTeamListeners(team); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the list of all users who are allowed to bypass the access |
| * restriction placed on the specified repository. |
| * |
| * @param role |
| * the repository name |
| * @return list of all usernames that can bypass the access restriction |
| * @since 0.8.0 |
| */ |
| @Override |
| public List<String> getUsernamesForRepositoryRole(String role) { |
| return userService.getUsernamesForRepositoryRole(role); |
| } |
| |
| /** |
| * Renames a repository role. |
| * |
| * @param oldRole |
| * @param newRole |
| * @return true if successful |
| */ |
| @Override |
| public boolean renameRepositoryRole(String oldRole, String newRole) { |
| return userService.renameRepositoryRole(oldRole, newRole); |
| } |
| |
| /** |
| * Removes a repository role from all users. |
| * |
| * @param role |
| * @return true if successful |
| */ |
| @Override |
| public boolean deleteRepositoryRole(String role) { |
| return userService.deleteRepositoryRole(role); |
| } |
| |
| 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); |
| } |
| } |
| } |
| } |