From: James Moger Date: Thu, 21 Nov 2013 01:52:11 +0000 (-0500) Subject: Extract UserManager from GitBlit singleton X-Git-Tag: v1.4.0~180 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8f1c9fd7e0f7ea3d7d0b87788eb92ba2f0f09d59;p=gitblit.git Extract UserManager from GitBlit singleton Change-Id: I4885255ed63aa6c4e000c3e5501675440dca3958 --- diff --git a/src/main/java/com/gitblit/ConfigUserService.java b/src/main/java/com/gitblit/ConfigUserService.java index 79cbdba7..39374e88 100644 --- a/src/main/java/com/gitblit/ConfigUserService.java +++ b/src/main/java/com/gitblit/ConfigUserService.java @@ -35,6 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.AccountType; import com.gitblit.manager.IRuntimeManager; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; @@ -486,7 +487,7 @@ public class ConfigUserService implements IUserService { * @return list of all usernames that can bypass the access restriction */ @Override - public synchronized List getTeamnamesForRepositoryRole(String role) { + public synchronized List getTeamNamesForRepositoryRole(String role) { List list = new ArrayList(); try { read(); @@ -1111,4 +1112,9 @@ public class ConfigUserService implements IUserService { public String toString() { return getClass().getSimpleName() + "(" + realmFile.getAbsolutePath() + ")"; } + + @Override + public AccountType getAccountType() { + return AccountType.LOCAL; + } } diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java index 2f060c0b..7f12fca3 100644 --- a/src/main/java/com/gitblit/DaggerModule.java +++ b/src/main/java/com/gitblit/DaggerModule.java @@ -30,6 +30,7 @@ import com.gitblit.manager.ISessionManager; import com.gitblit.manager.IUserManager; import com.gitblit.manager.NotificationManager; import com.gitblit.manager.RuntimeManager; +import com.gitblit.manager.UserManager; import com.gitblit.wicket.GitBlitWebApp; import com.gitblit.wicket.GitblitWicketFilter; @@ -100,8 +101,8 @@ public class DaggerModule { return new NotificationManager(settings); } - @Provides @Singleton IUserManager provideUserManager() { - return gitblit; + @Provides @Singleton IUserManager provideUserManager(IRuntimeManager runtimeManager) { + return new UserManager(runtimeManager); } @Provides @Singleton ISessionManager provideSessionManager() { diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index 4e779748..f7e76273 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -80,7 +80,6 @@ import org.slf4j.Logger; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; -import com.gitblit.Constants.AccountType; import com.gitblit.Constants.AuthenticationType; import com.gitblit.Constants.AuthorizationControl; import com.gitblit.Constants.CommitMessageRenderer; @@ -162,8 +161,7 @@ import dagger.ObjectGraph; */ @WebListener public class GitBlit extends DaggerContextListener - implements IUserManager, - ISessionManager, + implements ISessionManager, IRepositoryManager, IProjectManager, IFederationManager, @@ -202,8 +200,6 @@ public class GitBlit extends DaggerContextListener private File repositoriesFolder; - private IUserService userService; - private IStoredSettings settings; private LuceneExecutor luceneExecutor; @@ -223,13 +219,6 @@ public class GitBlit extends DaggerContextListener this.goBaseFolder = null; } - protected GitBlit(final IUserService userService) { - this.goSettings = null; - this.goBaseFolder = null; - this.userService = userService; - gitblit = this; - } - public GitBlit(IStoredSettings settings, File baseFolder) { this.goSettings = settings; this.goBaseFolder = baseFolder; @@ -335,7 +324,7 @@ public class GitBlit extends DaggerContextListener if (user == null) { user = UserModel.ANONYMOUS; } - String username = encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); + String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); List list = new ArrayList(); // http/https url @@ -485,75 +474,6 @@ public class GitBlit extends DaggerContextListener return null; } - /** - * Set the user service. The user service authenticates all users and is - * responsible for managing user permissions. - * - * @param userService - */ - public void setUserService(IUserService userService) { - logger.info("Setting up user service " + userService.toString()); - this.userService = userService; - this.userService.setup(getManager(IRuntimeManager.class)); - } - - @Override - public boolean supportsAddUser() { - return supportsCredentialChanges(new UserModel("")); - } - - /** - * Returns true if the user's credentials can be changed. - * - * @param user - * @return true if the user service supports credential changes - */ - @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(); - } - /** * Returns true if the username represents an internal account * @@ -580,7 +500,7 @@ public class GitBlit extends DaggerContextListener // can not authenticate empty username return null; } - String usernameDecoded = decodeUsername(username); + String usernameDecoded = StringUtils.decodeUsername(username); String pw = new String(password); if (StringUtils.isEmpty(pw)) { // can not authenticate empty password @@ -598,10 +518,7 @@ public class GitBlit extends DaggerContextListener } // delegate authentication to the user service - if (userService == null) { - return null; - } - return userService.authenticate(usernameDecoded, password); + return getManager(IUserManager.class).authenticate(usernameDecoded, password); } /** @@ -611,15 +528,12 @@ public class GitBlit extends DaggerContextListener * @return a user object or null */ protected UserModel authenticate(Cookie[] cookies) { - if (userService == null) { - return null; - } - if (userService.supportsCookies()) { + if (getManager(IUserManager.class).supportsCookies()) { if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { if (cookie.getName().equals(Constants.NAME)) { String value = cookie.getValue(); - return userService.authenticate(value.toCharArray()); + return getManager(IUserManager.class).authenticate(value.toCharArray()); } } } @@ -658,7 +572,7 @@ public class GitBlit extends DaggerContextListener UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids); if (model != null) { // grab real user model and preserve certificate serial number - UserModel user = getUserModel(model.username); + UserModel user = getManager(IUserManager.class).getUserModel(model.username); X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); if (user != null) { flagWicketSession(AuthenticationType.CERTIFICATE); @@ -682,7 +596,7 @@ public class GitBlit extends DaggerContextListener String username = principal.getName(); if (!StringUtils.isEmpty(username)) { boolean internalAccount = isInternalAccount(username); - UserModel user = getUserModel(username); + UserModel user = getManager(IUserManager.class).getUserModel(username); if (user != null) { // existing user flagWicketSession(AuthenticationType.CONTAINER); @@ -695,7 +609,7 @@ public class GitBlit extends DaggerContextListener user = new UserModel(username.toLowerCase()); user.displayName = username; user.password = Constants.EXTERNAL_ACCOUNT; - userService.updateUserModel(user); + getManager(IUserManager.class).updateUserModel(user); flagWicketSession(AuthenticationType.CONTAINER); logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", user.username, httpRequest.getRemoteAddr())); @@ -708,7 +622,7 @@ public class GitBlit extends DaggerContextListener } // try to authenticate by cookie - if (supportsCookies()) { + if (getManager(IUserManager.class).supportsCookies()) { UserModel user = authenticate(httpRequest.getCookies()); if (user != null) { flagWicketSession(AuthenticationType.COOKIE); @@ -726,7 +640,7 @@ public class GitBlit extends DaggerContextListener String credentials = new String(Base64.decode(base64Credentials), Charset.forName("UTF-8")); // credentials = username:password - final String[] values = credentials.split(":",2); + final String[] values = credentials.split(":", 2); if (values.length == 2) { String username = values[0]; @@ -774,20 +688,17 @@ public class GitBlit extends DaggerContextListener */ @Override public void setCookie(HttpServletResponse response, UserModel user) { - if (userService == null) { - return; - } GitBlitWebSession session = GitBlitWebSession.get(); boolean standardLogin = session.authenticationType.isStandard(); - if (userService.supportsCookies() && standardLogin) { + if (getManager(IUserManager.class).supportsCookies() && standardLogin) { Cookie userCookie; if (user == null) { // clear cookie for logout userCookie = new Cookie(Constants.NAME, ""); } else { // set cookie for login - String cookie = userService.getCookie(user); + String cookie = getManager(IUserManager.class).getCookie(user); if (StringUtils.isEmpty(cookie)) { // create empty cookie userCookie = new Cookie(Constants.NAME, ""); @@ -802,79 +713,6 @@ public class GitBlit extends DaggerContextListener } } - /** - * Logout a user. - * - * @param user - */ - @Override - public void logout(UserModel user) { - if (userService == null) { - return; - } - userService.logout(user); - } - - /** - * Encode the username for user in an url. - * - * @param name - * @return the encoded name - */ - protected String encodeUsername(String name) { - return name.replace("@", "%40").replace(" ", "%20").replace("\\", "%5C"); - } - - /** - * Decode a username from an encoded url. - * - * @param name - * @return the decoded name - */ - protected String decodeUsername(String name) { - return name.replace("%40", "@").replace("%20", " ").replace("%5C", "\\"); - } - - /** - * Returns the list of all users available to the login service. - * - * @see IUserService.getAllUsernames() - * @return list of all usernames - */ - @Override - public List getAllUsernames() { - List names = new ArrayList(userService.getAllUsernames()); - return names; - } - - /** - * Returns the list of all users available to the login service. - * - * @see IUserService.getAllUsernames() - * @return list of all usernames - */ - @Override - public List getAllUsers() { - List users = userService.getAllUsers(); - return users; - } - - /** - * Delete the user object with the specified username - * - * @see IUserService.deleteUser(String) - * @param username - * @return true if successful - */ - @Override - public boolean deleteUser(String username) { - if (StringUtils.isEmpty(username)) { - return false; - } - String usernameDecoded = decodeUsername(username); - return userService.deleteUser(usernameDecoded); - } - @Override public UserModel getFederationUser() { // the federation user is an administrator @@ -883,23 +721,6 @@ public class GitBlit extends DaggerContextListener return federationUser; } - /** - * Retrieve the user object for the specified username. - * - * @see IUserService.getUserModel(String) - * @param username - * @return a user object or null - */ - @Override - public UserModel getUserModel(String username) { - if (StringUtils.isEmpty(username)) { - return null; - } - String usernameDecoded = decodeUsername(username); - UserModel user = userService.getUserModel(usernameDecoded); - return user; - } - /** * Returns the effective list of permissions for this user, taking into account * team memberships, ownerships. @@ -965,7 +786,7 @@ public class GitBlit extends DaggerContextListener return list; } // NAMED users and teams - for (UserModel user : userService.getAllUsers()) { + for (UserModel user : getManager(IUserManager.class).getAllUsers()) { RegistrantAccessPermission ap = user.getRepositoryPermission(repository); if (ap.permission.exceeds(AccessPermission.NONE)) { list.add(ap); @@ -987,12 +808,12 @@ public class GitBlit extends DaggerContextListener for (RegistrantAccessPermission up : permissions) { if (up.mutable) { // only set editable defined permissions - UserModel user = userService.getUserModel(up.registrant); + UserModel user = getManager(IUserManager.class).getUserModel(up.registrant); user.setRepositoryPermission(repository.name, up.permission); users.add(user); } } - return userService.updateUserModels(users); + return getManager(IUserManager.class).updateUserModels(users); } /** @@ -1005,7 +826,7 @@ public class GitBlit extends DaggerContextListener */ @Override public List getRepositoryUsers(RepositoryModel repository) { - return userService.getUsernamesForRepositoryRole(repository.name); + return getManager(IUserManager.class).getUsernamesForRepositoryRole(repository.name); } /** @@ -1038,7 +859,7 @@ public class GitBlit extends DaggerContextListener public void updateUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException { if (!username.equalsIgnoreCase(user.username)) { - if (userService.getUserModel(user.username) != null) { + if (getManager(IUserManager.class).getUserModel(user.username) != null) { throw new GitBlitException(MessageFormat.format( "Failed to rename ''{0}'' because ''{1}'' already exists.", username, user.username)); @@ -1060,45 +881,11 @@ public class GitBlit extends DaggerContextListener } } } - if (!userService.updateUserModel(username, user)) { + if (!getManager(IUserManager.class).updateUserModel(username, user)) { throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); } } - /** - * Returns the list of available teams that a user or repository may be - * assigned to. - * - * @return the list of teams - */ - public List getAllTeamnames() { - List teams = new ArrayList(userService.getAllTeamNames()); - return teams; - } - - /** - * Returns the list of available teams that a user or repository may be - * assigned to. - * - * @return the list of teams - */ - @Override - public List getAllTeams() { - List teams = userService.getAllTeams(); - return teams; - } - - /** - * Returns the TeamModel object for the specified name. - * - * @param teamname - * @return a TeamModel object or null - */ - @Override - public TeamModel getTeamModel(String teamname) { - return userService.getTeamModel(teamname); - } - /** * Returns the list of teams and their access permissions for the specified * repository including the source of the permission such as the admin flag @@ -1110,7 +897,7 @@ public class GitBlit extends DaggerContextListener @Override public List getTeamAccessPermissions(RepositoryModel repository) { List list = new ArrayList(); - for (TeamModel team : userService.getAllTeams()) { + for (TeamModel team : getManager(IUserManager.class).getAllTeams()) { RegistrantAccessPermission ap = team.getRepositoryPermission(repository); if (ap.permission.exceeds(AccessPermission.NONE)) { list.add(ap); @@ -1133,12 +920,12 @@ public class GitBlit extends DaggerContextListener for (RegistrantAccessPermission tp : permissions) { if (tp.mutable) { // only set explicitly defined access permissions - TeamModel team = userService.getTeamModel(tp.registrant); + TeamModel team = getManager(IUserManager.class).getTeamModel(tp.registrant); team.setRepositoryPermission(repository.name, tp.permission); teams.add(team); } } - return userService.updateTeamModels(teams); + return getManager(IUserManager.class).updateTeamModels(teams); } /** @@ -1151,7 +938,7 @@ public class GitBlit extends DaggerContextListener */ @Override public List getRepositoryTeams(RepositoryModel repository) { - return userService.getTeamnamesForRepositoryRole(repository.name); + return getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name); } /** @@ -1181,29 +968,17 @@ public class GitBlit extends DaggerContextListener public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) throws GitBlitException { if (!teamname.equalsIgnoreCase(team.name)) { - if (userService.getTeamModel(team.name) != null) { + if (getManager(IUserManager.class).getTeamModel(team.name) != null) { throw new GitBlitException(MessageFormat.format( "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname, team.name)); } } - if (!userService.updateTeamModel(teamname, team)) { + if (!getManager(IUserManager.class).updateTeamModel(teamname, team)) { throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!"); } } - /** - * Delete the team object with the specified teamname - * - * @see IUserService.deleteTeam(String) - * @param teamname - * @return true if successful - */ - @Override - public boolean deleteTeam(String teamname) { - return userService.deleteTeam(teamname); - } - /** * Adds the repository to the list of cached repositories if Gitblit is * configured to cache the repository list. @@ -1519,7 +1294,7 @@ public class GitBlit extends DaggerContextListener @Override public long getStarCount(RepositoryModel repository) { long count = 0; - for (UserModel user : getAllUsers()) { + for (UserModel user : getManager(IUserManager.class).getAllUsers()) { if (user.getPreferences().isStarredRepository(repository.name)) { count++; } @@ -1680,7 +1455,7 @@ public class GitBlit extends DaggerContextListener if (project == null) { project = new ProjectModel(name); if (ModelUtils.isPersonalRepository(name)) { - UserModel user = getUserModel(ModelUtils.getUserNameFromRepoPath(name)); + UserModel user = getManager(IUserManager.class).getUserModel(ModelUtils.getUserNameFromRepoPath(name)); if (user != null) { project.title = user.getDisplayName(); project.description = "personal repositories"; @@ -2297,7 +2072,7 @@ public class GitBlit extends DaggerContextListener repository.name)); } // rename the roles - if (!userService.renameRepositoryRole(repositoryName, repository.name)) { + if (!getManager(IUserManager.class).renameRepositoryRole(repositoryName, repository.name)) { throw new GitBlitException(MessageFormat.format( "Failed to rename repository permissions ''{0}'' to ''{1}''.", repositoryName, repository.name)); @@ -2514,7 +2289,7 @@ public class GitBlit extends DaggerContextListener File folder = new File(repositoriesFolder, repositoryName); if (folder.exists() && folder.isDirectory()) { FileUtils.delete(folder, FileUtils.RECURSIVE | FileUtils.RETRY); - if (userService.deleteRepositoryRole(repositoryName)) { + if (getManager(IUserManager.class).deleteRepositoryRole(repositoryName)) { logger.info(MessageFormat.format("Repository \"{0}\" deleted", repositoryName)); return true; } @@ -3046,8 +2821,8 @@ public class GitBlit extends DaggerContextListener // Team Scripts if (repository != null) { - for (String teamname : userService.getTeamnamesForRepositoryRole(repository.name)) { - TeamModel team = userService.getTeamModel(teamname); + for (String teamname : getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name)) { + TeamModel team = getManager(IUserManager.class).getTeamModel(teamname); if (!ArrayUtils.isEmpty(team.preReceiveScripts)) { scripts.addAll(team.preReceiveScripts); } @@ -3100,8 +2875,8 @@ public class GitBlit extends DaggerContextListener } // Team Scripts if (repository != null) { - for (String teamname : userService.getTeamnamesForRepositoryRole(repository.name)) { - TeamModel team = userService.getTeamModel(teamname); + for (String teamname : getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name)) { + TeamModel team = getManager(IUserManager.class).getTeamModel(teamname); if (!ArrayUtils.isEmpty(team.postReceiveScripts)) { scripts.addAll(team.postReceiveScripts); } @@ -3156,10 +2931,14 @@ public class GitBlit extends DaggerContextListener * @return Map */ private ServerSettings loadSettingModels(ServerSettings settingsModel) { - settingsModel.supportsCredentialChanges = userService.supportsCredentialChanges(); - settingsModel.supportsDisplayNameChanges = userService.supportsDisplayNameChanges(); - settingsModel.supportsEmailAddressChanges = userService.supportsEmailAddressChanges(); - settingsModel.supportsTeamMembershipChanges = userService.supportsTeamMembershipChanges(); + // this entire "supports" concept will go away with user service refactoring + UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT); + externalUser.password = Constants.EXTERNAL_ACCOUNT; + IUserManager userManager = getManager(IUserManager.class); + settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser); + settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser); + settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser); + settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser); try { // Read bundled Gitblit properties to extract setting descriptions. // This copy is pristine and only used for populating the setting @@ -3321,7 +3100,7 @@ public class GitBlit extends DaggerContextListener Gitblit gitblit = new Gitblit( getManager(IRuntimeManager.class), getManager(INotificationManager.class), - this, + getManager(IUserManager.class), this, this, this, @@ -3430,6 +3209,7 @@ public class GitBlit extends DaggerContextListener runtime.getStatus().servletContainer = context.getServerInfo(); startManager(injector, INotificationManager.class); + startManager(injector, IUserManager.class); repositoriesFolder = getRepositoriesFolder(); @@ -3454,19 +3234,6 @@ public class GitBlit extends DaggerContextListener getRepositoryList(); } - if (this.userService == null) { - String realm = runtimeSettings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); - IUserService loginService = null; - try { - // check to see if this "file" is a login service class - Class realmClass = Class.forName(realm); - loginService = (IUserService) realmClass.newInstance(); - } catch (Throwable t) { - loginService = new GitblitUserService(); - } - setUserService(loginService); - } - loadSettingModels(runtime.getSettingsModel()); // load and cache the project metadata @@ -3747,7 +3514,7 @@ public class GitBlit extends DaggerContextListener // add the owner of the source repository to the clone's access list if (!ArrayUtils.isEmpty(repository.owners)) { for (String owner : repository.owners) { - UserModel originOwner = getUserModel(owner); + UserModel originOwner = getManager(IUserManager.class).getUserModel(owner); if (originOwner != null) { originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE); updateUserModel(originOwner.username, originOwner, false); @@ -3760,7 +3527,7 @@ public class GitBlit extends DaggerContextListener List cloneUsers = new ArrayList(); for (String name : users) { if (!name.equalsIgnoreCase(user.username)) { - UserModel cloneUser = getUserModel(name); + UserModel cloneUser = getManager(IUserManager.class).getUserModel(name); if (cloneUser.canClone(repository)) { // origin user can clone origin, grant clone access to fork cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE); @@ -3768,116 +3535,30 @@ public class GitBlit extends DaggerContextListener cloneUsers.add(cloneUser); } } - userService.updateUserModels(cloneUsers); + getManager(IUserManager.class).updateUserModels(cloneUsers); // grant origin's team list clone permission to fork List teams = getRepositoryTeams(repository); List cloneTeams = new ArrayList(); for (String name : teams) { - TeamModel cloneTeam = getTeamModel(name); + TeamModel cloneTeam = getManager(IUserManager.class).getTeamModel(name); if (cloneTeam.canClone(repository)) { // origin team can clone origin, grant clone access to fork cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE); } cloneTeams.add(cloneTeam); } - userService.updateTeamModels(cloneTeams); + getManager(IUserManager.class).updateTeamModels(cloneTeams); // add this clone to the cached model addToCachedRepositoryList(cloneModel); return cloneModel; } - /** - * 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(); - } - - @Override - public String getCookie(UserModel model) { - return userService.getCookie(model); - } - - @Override - public UserModel authenticate(char[] cookie) { - return userService.authenticate(cookie); - } - - @Override - public boolean updateUserModel(UserModel model) { - return userService.updateUserModel(model); - } - - @Override - public boolean updateUserModels(Collection models) { - return userService.updateUserModels(models); - } - - @Override - public boolean updateUserModel(String username, UserModel model) { - return userService.updateUserModel(username, model); - } - - @Override - public boolean deleteUserModel(UserModel model) { - return userService.deleteUserModel(model); - } - - @Override - public List getAllTeamNames() { - return userService.getAllTeamNames(); - } - - @Override - public List getTeamnamesForRepositoryRole(String role) { - return userService.getTeamnamesForRepositoryRole(role); - } - - @Override - public boolean updateTeamModel(TeamModel model) { - return userService.updateTeamModel(model); - } - - @Override - public boolean updateTeamModels(Collection models) { - return userService.updateTeamModels(models); - } - - @Override - public boolean updateTeamModel(String teamname, TeamModel model) { - return userService.updateTeamModel(teamname, model); - } - - @Override - public boolean deleteTeamModel(TeamModel model) { - return userService.deleteTeamModel(model); - } - - @Override - public List getUsernamesForRepositoryRole(String role) { - return userService.getUsernamesForRepositoryRole(role); - } - - @Override - public boolean renameRepositoryRole(String oldRole, String newRole) { - return userService.renameRepositoryRole(oldRole, newRole); - } - - @Override - public boolean deleteRepositoryRole(String role) { - return userService.deleteRepositoryRole(role); - } - @Override public void logout(HttpServletResponse response, UserModel user) { setCookie(response, null); - userService.logout(user); + getManager(IUserManager.class).logout(user); } @Override diff --git a/src/main/java/com/gitblit/Gitblit.java b/src/main/java/com/gitblit/Gitblit.java index 687e4e20..7a5c73ea 100644 --- a/src/main/java/com/gitblit/Gitblit.java +++ b/src/main/java/com/gitblit/Gitblit.java @@ -348,8 +348,8 @@ public class Gitblit implements IRuntimeManager, } @Override - public List getTeamnamesForRepositoryRole(String role) { - return userManager.getTeamnamesForRepositoryRole(role); + public List getTeamNamesForRepositoryRole(String role) { + return userManager.getTeamNamesForRepositoryRole(role); } @Override diff --git a/src/main/java/com/gitblit/GitblitUserService.java b/src/main/java/com/gitblit/GitblitUserService.java index 7278b222..715aed9d 100644 --- a/src/main/java/com/gitblit/GitblitUserService.java +++ b/src/main/java/com/gitblit/GitblitUserService.java @@ -126,6 +126,12 @@ public class GitblitUserService implements IUserService { return serviceImpl.getCookie(model); } + /** + * Authenticate a user based on their cookie. + * + * @param cookie + * @return a user object or null + */ @Override public UserModel authenticate(char[] cookie) { UserModel user = serviceImpl.authenticate(cookie); @@ -226,8 +232,8 @@ public class GitblitUserService implements IUserService { } @Override - public List getTeamnamesForRepositoryRole(String role) { - return serviceImpl.getTeamnamesForRepositoryRole(role); + public List getTeamNamesForRepositoryRole(String role) { + return serviceImpl.getTeamNamesForRepositoryRole(role); } @Override @@ -312,7 +318,8 @@ public class GitblitUserService implements IUserService { } } - protected AccountType getAccountType() { + @Override + public AccountType getAccountType() { return AccountType.LOCAL; } } diff --git a/src/main/java/com/gitblit/HtpasswdUserService.java b/src/main/java/com/gitblit/HtpasswdUserService.java index 3b7120f0..ca5295c9 100644 --- a/src/main/java/com/gitblit/HtpasswdUserService.java +++ b/src/main/java/com/gitblit/HtpasswdUserService.java @@ -275,7 +275,7 @@ public class HtpasswdUserService extends GitblitUserService * @return AccountType.HTPASSWD */ @Override - protected AccountType getAccountType() + public AccountType getAccountType() { return AccountType.HTPASSWD; } diff --git a/src/main/java/com/gitblit/IUserService.java b/src/main/java/com/gitblit/IUserService.java index 33f519f0..316e4a55 100644 --- a/src/main/java/com/gitblit/IUserService.java +++ b/src/main/java/com/gitblit/IUserService.java @@ -18,6 +18,7 @@ package com.gitblit; import java.util.Collection; import java.util.List; +import com.gitblit.Constants.AccountType; import com.gitblit.manager.IRuntimeManager; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; @@ -205,7 +206,7 @@ public interface IUserService { * @return list of all usernames that can bypass the access restriction * @since 0.8.0 */ - List getTeamnamesForRepositoryRole(String role); + List getTeamNamesForRepositoryRole(String role); /** * Sets the list of all teams who are allowed to bypass the access @@ -318,6 +319,14 @@ public interface IUserService { */ boolean deleteRepositoryRole(String role); + /** + * Returns the account type for the user models. + * + * @return the account type + * @since 1.4.0 + */ + AccountType getAccountType(); + /** * @See java.lang.Object.toString(); * @return string representation of the login service diff --git a/src/main/java/com/gitblit/LdapUserService.java b/src/main/java/com/gitblit/LdapUserService.java index 5619dadd..c075afca 100644 --- a/src/main/java/com/gitblit/LdapUserService.java +++ b/src/main/java/com/gitblit/LdapUserService.java @@ -262,7 +262,7 @@ public class LdapUserService extends GitblitUserService { } @Override - protected AccountType getAccountType() { + public AccountType getAccountType() { return AccountType.LDAP; } diff --git a/src/main/java/com/gitblit/PAMUserService.java b/src/main/java/com/gitblit/PAMUserService.java index b348e64c..db569fbf 100644 --- a/src/main/java/com/gitblit/PAMUserService.java +++ b/src/main/java/com/gitblit/PAMUserService.java @@ -92,7 +92,7 @@ public class PAMUserService extends GitblitUserService { } @Override - protected AccountType getAccountType() { + public AccountType getAccountType() { return AccountType.PAM; } diff --git a/src/main/java/com/gitblit/RedmineUserService.java b/src/main/java/com/gitblit/RedmineUserService.java index 62322ebc..7c38ef2f 100644 --- a/src/main/java/com/gitblit/RedmineUserService.java +++ b/src/main/java/com/gitblit/RedmineUserService.java @@ -91,7 +91,7 @@ public class RedmineUserService extends GitblitUserService { } @Override - protected AccountType getAccountType() { + public AccountType getAccountType() { return AccountType.REDMINE; } diff --git a/src/main/java/com/gitblit/SalesforceUserService.java b/src/main/java/com/gitblit/SalesforceUserService.java index 0eca6c9c..6161ba93 100644 --- a/src/main/java/com/gitblit/SalesforceUserService.java +++ b/src/main/java/com/gitblit/SalesforceUserService.java @@ -22,7 +22,7 @@ public class SalesforceUserService extends GitblitUserService { private IStoredSettings settings; @Override - protected AccountType getAccountType() { + public AccountType getAccountType() { return AccountType.SALESFORCE; } diff --git a/src/main/java/com/gitblit/WindowsUserService.java b/src/main/java/com/gitblit/WindowsUserService.java index 9b25efbe..99077c67 100644 --- a/src/main/java/com/gitblit/WindowsUserService.java +++ b/src/main/java/com/gitblit/WindowsUserService.java @@ -104,7 +104,7 @@ public class WindowsUserService extends GitblitUserService { } @Override - protected AccountType getAccountType() { + public AccountType getAccountType() { return AccountType.WINDOWS; } diff --git a/src/main/java/com/gitblit/manager/IUserManager.java b/src/main/java/com/gitblit/manager/IUserManager.java index 3ce1e744..387a7208 100644 --- a/src/main/java/com/gitblit/manager/IUserManager.java +++ b/src/main/java/com/gitblit/manager/IUserManager.java @@ -21,7 +21,7 @@ import java.util.List; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; -public interface IUserManager { +public interface IUserManager extends IManager { boolean supportsAddUser(); @@ -189,7 +189,7 @@ public interface IUserManager { * @return list of all usernames that can bypass the access restriction * @since 0.8.0 */ - List getTeamnamesForRepositoryRole(String role); + List getTeamNamesForRepositoryRole(String role); /** * Retrieve the team object for the specified team name. diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java new file mode 100644 index 00000000..f781c4c7 --- /dev/null +++ b/src/main/java/com/gitblit/manager/UserManager.java @@ -0,0 +1,542 @@ +/* + * 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.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +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.models.TeamModel; +import com.gitblit.models.UserModel; +import com.gitblit.utils.DeepCopier; +import com.gitblit.utils.StringUtils; + +/** + * The user manager manages persistence and retrieval of users and teams. + * + * @author James Moger + * + */ +public class UserManager implements IUserManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final IStoredSettings settings; + + private final IRuntimeManager runtimeManager; + + private IUserService userService; + + public UserManager(IRuntimeManager runtimeManager) { + this.settings = runtimeManager.getSettings(); + this.runtimeManager = runtimeManager; + } + + /** + * Set the user service. The user service authenticates local users and is + * responsible for persisting and retrieving users and teams. + * + * @param userService + */ + public void setUserService(IUserService userService) { + logger.info("Setting up user service " + userService.toString()); + this.userService = userService; + this.userService.setup(runtimeManager); + } + + @Override + public IManager setup() { + if (this.userService == null) { + String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); + 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"); + service = createUserService(realmFile); + } + setUserService(service); + } + return this; + } + + protected IUserService createUserService(File realmFile) { + IUserService service = null; + if (realmFile.getName().toLowerCase().endsWith(".conf")) { + // v0.8.0+ 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 IManager stop() { + return this; + } + + @Override + public boolean supportsAddUser() { + return supportsCredentialChanges(new UserModel("")); + } + + /** + * Returns true if the user's credentials can be changed. + * + * @param user + * @return true if the user service supports credential changes + */ + @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(); + } + + /** + * Returns the cookie value for the specified user. + * + * @param model + * @return cookie value + */ + @Override + public String getCookie(UserModel model) { + return userService.getCookie(model); + } + + /** + * 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. + * + * @param cookie + * @return a user object or null + */ + @Override + public UserModel authenticate(char[] cookie) { + UserModel user = userService.authenticate(cookie); + setAccountType(user); + return user; + } + + /** + * Logout a user. + * + * @param user + */ + @Override + public void logout(UserModel user) { + if (userService == null) { + return; + } + userService.logout(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); + setAccountType(user); + return user; + } + + /** + * Updates/writes a complete user object. + * + * @param model + * @return true if update is successful + */ + @Override + public boolean updateUserModel(UserModel model) { + return userService.updateUserModel(model); + } + + /** + * 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 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) { + 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); + } + return userService.updateUserModel(username, model); + } + 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; + } + + /** + * Deletes the user object from the user service. + * + * @param model + * @return true if successful + */ + @Override + public boolean deleteUserModel(UserModel model) { + return userService.deleteUserModel(model); + } + + /** + * 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); + return userService.deleteUser(usernameDecoded); + } + + /** + * Returns the list of all users available to the login service. + * + * @return list of all usernames + */ + @Override + public List getAllUsernames() { + List names = new ArrayList(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 getAllUsers() { + List users = userService.getAllUsers(); + for (UserModel user : users) { + setAccountType(user); + } + 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 getAllTeamNames() { + return userService.getAllTeamNames(); + } + + /** + * Returns the list of all teams available to the login service. + * + * @return list of all teams + * @since 0.8.0 + */ + @Override + public List getAllTeams() { + List 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 getTeamNamesForRepositoryRole(String role) { + return userService.getTeamNamesForRepositoryRole(role); + } + + /** + * 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) { + return userService.getTeamModel(teamname); + } + + /** + * Updates/writes a complete team object. + * + * @param model + * @return true if update is successful + * @since 0.8.0 + */ + @Override + public boolean updateTeamModel(TeamModel model) { + return userService.updateTeamModel(model); + } + + /** + * 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 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) { + 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); + } + return userService.updateTeamModel(teamname, model); + } + + /** + * Deletes the team object from the user service. + * + * @param model + * @return true if successful + * @since 0.8.0 + */ + @Override + public boolean deleteTeamModel(TeamModel model) { + return userService.deleteTeamModel(model); + } + + /** + * Delete the team object with the specified teamname + * + * @param teamname + * @return true if successful + * @since 0.8.0 + */ + @Override + public boolean deleteTeam(String teamname) { + return userService.deleteTeam(teamname); + } + + /** + * 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 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 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(); + } + } + } +} diff --git a/src/main/java/com/gitblit/utils/StringUtils.java b/src/main/java/com/gitblit/utils/StringUtils.java index e18bdc4f..ed10eb6c 100644 --- a/src/main/java/com/gitblit/utils/StringUtils.java +++ b/src/main/java/com/gitblit/utils/StringUtils.java @@ -747,4 +747,25 @@ public class StringUtils { } return input.replace('\n',' ').replace('\r', ' ').trim(); } + + + /** + * Encode the username for user in an url. + * + * @param name + * @return the encoded name + */ + public static String encodeUsername(String name) { + return name.replace("@", "%40").replace(" ", "%20").replace("\\", "%5C"); + } + + /** + * Decode a username from an encoded url. + * + * @param name + * @return the decoded name + */ + public static String decodeUsername(String name) { + return name.replace("%40", "@").replace("%20", " ").replace("%5C", "\\"); + } } \ No newline at end of file diff --git a/src/test/java/de/akquinet/devops/GitBlit4UITests.java b/src/test/java/de/akquinet/devops/GitBlit4UITests.java index d4e32222..9c559f88 100644 --- a/src/test/java/de/akquinet/devops/GitBlit4UITests.java +++ b/src/test/java/de/akquinet/devops/GitBlit4UITests.java @@ -9,7 +9,7 @@ public class GitBlit4UITests extends GitBlit { private boolean luceneIndexingEnabled; public GitBlit4UITests(boolean luceneIndexingEnabled) { - super(null); + super(); this.luceneIndexingEnabled = luceneIndexingEnabled; }