diff options
Diffstat (limited to 'src/main/java/com/gitblit/manager')
-rw-r--r-- | src/main/java/com/gitblit/manager/AuthenticationManager.java | 511 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/GitblitManager.java | 7 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/IAuthenticationManager.java (renamed from src/main/java/com/gitblit/manager/ISessionManager.java) | 43 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/IUserManager.java | 261 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/SessionManager.java | 340 | ||||
-rw-r--r-- | src/main/java/com/gitblit/manager/UserManager.java | 221 |
6 files changed, 609 insertions, 774 deletions
diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java new file mode 100644 index 00000000..6e541c45 --- /dev/null +++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java @@ -0,0 +1,511 @@ +/* + * 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.nio.charset.Charset; +import java.security.Principal; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.wicket.RequestCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants; +import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.AuthenticationType; +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.auth.AuthenticationProvider; +import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; +import com.gitblit.auth.HtpasswdAuthProvider; +import com.gitblit.auth.LdapAuthProvider; +import com.gitblit.auth.PAMAuthProvider; +import com.gitblit.auth.RedmineAuthProvider; +import com.gitblit.auth.SalesforceAuthProvider; +import com.gitblit.auth.WindowsAuthProvider; +import com.gitblit.models.TeamModel; +import com.gitblit.models.UserModel; +import com.gitblit.utils.Base64; +import com.gitblit.utils.HttpUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.X509Utils.X509Metadata; +import com.gitblit.wicket.GitBlitWebSession; + +/** + * The authentication manager handles user login & logout. + * + * @author James Moger + * + */ +public class AuthenticationManager implements IAuthenticationManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final IStoredSettings settings; + + private final IRuntimeManager runtimeManager; + + private final IUserManager userManager; + + private final List<AuthenticationProvider> authenticationProviders; + + private final Map<String, Class<? extends AuthenticationProvider>> providerNames; + + private final Map<String, String> legacyRedirects; + + public AuthenticationManager( + IRuntimeManager runtimeManager, + IUserManager userManager) { + + this.settings = runtimeManager.getSettings(); + this.runtimeManager = runtimeManager; + this.userManager = userManager; + this.authenticationProviders = new ArrayList<AuthenticationProvider>(); + + // map of shortcut provider names + providerNames = new HashMap<String, Class<? extends AuthenticationProvider>>(); + providerNames.put("htpasswd", HtpasswdAuthProvider.class); + providerNames.put("ldap", LdapAuthProvider.class); + providerNames.put("pam", PAMAuthProvider.class); + providerNames.put("redmine", RedmineAuthProvider.class); + providerNames.put("salesforce", SalesforceAuthProvider.class); + providerNames.put("windows", WindowsAuthProvider.class); + + // map of legacy external user services + legacyRedirects = new HashMap<String, String>(); + legacyRedirects.put("com.gitblit.HtpasswdUserService", "htpasswd"); + legacyRedirects.put("com.gitblit.LdapUserService", "ldap"); + legacyRedirects.put("com.gitblit.PAMUserService", "pam"); + legacyRedirects.put("com.gitblit.RedmineUserService", "redmine"); + legacyRedirects.put("com.gitblit.SalesforceUserService", "salesforce"); + legacyRedirects.put("com.gitblit.WindowsUserService", "windows"); + } + + @Override + public AuthenticationManager start() { + // automatically adjust legacy configurations + String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf"); + if (legacyRedirects.containsKey(realm)) { + logger.warn(""); + logger.warn("#################################################################"); + logger.warn(" IUserService '{}' is obsolete!", realm); + logger.warn(" Please set '{}={}'", "realm.authenticationProviders", legacyRedirects.get(realm)); + logger.warn("#################################################################"); + logger.warn(""); + + // conditionally override specified authentication providers + if (StringUtils.isEmpty(settings.getString(Keys.realm.authenticationProviders, null))) { + settings.overrideSetting(Keys.realm.authenticationProviders, legacyRedirects.get(realm)); + } + } + + // instantiate and setup specified authentication providers + List<String> providers = settings.getStrings(Keys.realm.authenticationProviders); + if (providers.isEmpty()) { + logger.info("External authentication disabled."); + } else { + for (String provider : providers) { + try { + Class<?> authClass; + if (providerNames.containsKey(provider)) { + // map the name -> class + authClass = providerNames.get(provider); + } else { + // reflective lookup + authClass = Class.forName(provider); + } + logger.info("setting up {}", authClass.getName()); + AuthenticationProvider authImpl = (AuthenticationProvider) authClass.newInstance(); + authImpl.setup(runtimeManager, userManager); + authenticationProviders.add(authImpl); + } catch (Exception e) { + logger.error("", e); + } + } + } + return this; + } + + @Override + public AuthenticationManager stop() { + return this; + } + + /** + * Authenticate a user based on HTTP request parameters. + * + * Authentication by X509Certificate is tried first and then by cookie. + * + * @param httpRequest + * @return a user object or null + */ + @Override + public UserModel authenticate(HttpServletRequest httpRequest) { + return authenticate(httpRequest, false); + } + + /** + * Authenticate a user based on HTTP request parameters. + * + * Authentication by servlet container principal, X509Certificate, cookie, + * and finally BASIC header. + * + * @param httpRequest + * @param requiresCertificate + * @return a user object or null + */ + @Override + public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { + // try to authenticate by servlet container principal + if (!requiresCertificate) { + Principal principal = httpRequest.getUserPrincipal(); + if (principal != null) { + String username = principal.getName(); + if (!StringUtils.isEmpty(username)) { + boolean internalAccount = isInternalAccount(username); + UserModel user = userManager.getUserModel(username); + if (user != null) { + // existing user + flagWicketSession(AuthenticationType.CONTAINER); + logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false) + && !internalAccount) { + // auto-create user from an authenticated container principal + user = new UserModel(username.toLowerCase()); + user.displayName = username; + user.password = Constants.EXTERNAL_ACCOUNT; + user.accountType = AccountType.CONTAINER; + userManager.updateUserModel(user); + flagWicketSession(AuthenticationType.CONTAINER); + logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } else if (!internalAccount) { + logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", + principal.getName(), httpRequest.getRemoteAddr())); + } + } + } + } + + // try to authenticate by certificate + boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true); + String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]); + UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids); + if (model != null) { + // grab real user model and preserve certificate serial number + UserModel user = userManager.getUserModel(model.username); + X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); + if (user != null) { + flagWicketSession(AuthenticationType.CERTIFICATE); + logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}", + user.username, metadata.serialNumber, httpRequest.getRemoteAddr())); + return user; + } else { + logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}", + model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); + } + } + + if (requiresCertificate) { + // caller requires client certificate authentication (e.g. git servlet) + return null; + } + + // try to authenticate by cookie + UserModel user = authenticate(httpRequest.getCookies()); + if (user != null) { + flagWicketSession(AuthenticationType.COOKIE); + logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } + + // try to authenticate by BASIC + final String authorization = httpRequest.getHeader("Authorization"); + if (authorization != null && authorization.startsWith("Basic")) { + // Authorization: Basic base64credentials + String base64Credentials = authorization.substring("Basic".length()).trim(); + String credentials = new String(Base64.decode(base64Credentials), + Charset.forName("UTF-8")); + // credentials = username:password + final String[] values = credentials.split(":", 2); + + if (values.length == 2) { + String username = values[0]; + char[] password = values[1].toCharArray(); + user = authenticate(username, password); + if (user != null) { + flagWicketSession(AuthenticationType.CREDENTIALS); + logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}", + user.username, httpRequest.getRemoteAddr())); + return user; + } else { + logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", + username, httpRequest.getRemoteAddr())); + } + } + } + return null; + } + + /** + * Authenticate a user based on their cookie. + * + * @param cookies + * @return a user object or null + */ + protected UserModel authenticate(Cookie[] cookies) { + if (settings.getBoolean(Keys.web.allowCookieAuthentication, true)) { + if (cookies != null && cookies.length > 0) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(Constants.NAME)) { + String value = cookie.getValue(); + return userManager.getUserModel(value.toCharArray()); + } + } + } + } + return null; + } + + protected void flagWicketSession(AuthenticationType authenticationType) { + RequestCycle requestCycle = RequestCycle.get(); + if (requestCycle != null) { + // flag the Wicket session, if this is a Wicket request + GitBlitWebSession session = GitBlitWebSession.get(); + session.authenticationType = authenticationType; + } + } + + /** + * Authenticate a user based on a username and password. + * + * @see IUserService.authenticate(String, char[]) + * @param username + * @param password + * @return a user object or null + */ + @Override + public UserModel authenticate(String username, char[] password) { + if (StringUtils.isEmpty(username)) { + // can not authenticate empty username + return null; + } + + String usernameDecoded = StringUtils.decodeUsername(username); + String pw = new String(password); + if (StringUtils.isEmpty(pw)) { + // can not authenticate empty password + return null; + } + // check to see if this is the federation user +// if (canFederate()) { +// if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) { +// List<String> tokens = getFederationTokens(); +// if (tokens.contains(pw)) { +// return getFederationUser(); +// } +// } +// } + + // try local authentication + UserModel user = userManager.getUserModel(usernameDecoded); + if (user != null) { + UserModel returnedUser = null; + if (user.password.startsWith(StringUtils.MD5_TYPE)) { + // password digest + String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password)); + if (user.password.equalsIgnoreCase(md5)) { + returnedUser = user; + } + } else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) { + // username+password digest + String md5 = StringUtils.COMBINED_MD5_TYPE + + StringUtils.getMD5(username.toLowerCase() + new String(password)); + if (user.password.equalsIgnoreCase(md5)) { + returnedUser = user; + } + } else if (user.password.equals(new String(password))) { + // plain-text password + returnedUser = user; + } + return returnedUser; + } + + // try registered external authentication providers + if (user == null) { + for (AuthenticationProvider provider : authenticationProviders) { + if (provider instanceof UsernamePasswordAuthenticationProvider) { + user = provider.authenticate(usernameDecoded, password); + if (user != null) { + // user authenticated + user.accountType = provider.getAccountType(); + return user; + } + } + } + } + return user; + } + + /** + * Sets a cookie for the specified user. + * + * @param response + * @param user + */ + @Override + public void setCookie(HttpServletResponse response, UserModel user) { + if (settings.getBoolean(Keys.web.allowCookieAuthentication, true)) { + GitBlitWebSession session = GitBlitWebSession.get(); + boolean standardLogin = session.authenticationType.isStandard(); + + if (standardLogin) { + Cookie userCookie; + if (user == null) { + // clear cookie for logout + userCookie = new Cookie(Constants.NAME, ""); + } else { + // set cookie for login + String cookie = userManager.getCookie(user); + if (StringUtils.isEmpty(cookie)) { + // create empty cookie + userCookie = new Cookie(Constants.NAME, ""); + } else { + // create real cookie + userCookie = new Cookie(Constants.NAME, cookie); + userCookie.setMaxAge(Integer.MAX_VALUE); + } + } + userCookie.setPath("/"); + response.addCookie(userCookie); + } + } + } + + /** + * Logout a user. + * + * @param user + */ + @Override + public void logout(HttpServletResponse response, UserModel user) { + setCookie(response, null); + } + + /** + * 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) { + return (user != null && user.isLocalAccount()) || findProvider(user).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()) || findProvider(user).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()) || findProvider(user).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()) || findProvider(user).supportsTeamMembershipChanges(); + } + + /** + * Returns true if the team memberships can be changed. + * + * @param user + * @return true if the team membership can be changed + */ + @Override + public boolean supportsTeamMembershipChanges(TeamModel team) { + return (team != null && team.isLocalTeam()) || findProvider(team).supportsTeamMembershipChanges(); + } + + protected AuthenticationProvider findProvider(UserModel user) { + for (AuthenticationProvider provider : authenticationProviders) { + if (provider.getAccountType().equals(user.accountType)) { + return provider; + } + } + return AuthenticationProvider.NULL_PROVIDER; + } + + protected AuthenticationProvider findProvider(TeamModel team) { + for (AuthenticationProvider provider : authenticationProviders) { + if (provider.getAccountType().equals(team.accountType)) { + return provider; + } + } + return AuthenticationProvider.NULL_PROVIDER; + } + + /** + * Returns true if the username represents an internal account + * + * @param username + * @return true if the specified username represents an internal account + */ + protected boolean isInternalAccount(String username) { + return !StringUtils.isEmpty(username) + && (username.equalsIgnoreCase(Constants.FEDERATION_USER) + || username.equalsIgnoreCase(UserModel.ANONYMOUS.username)); + } + +// protected UserModel getFederationUser() { +// // the federation user is an administrator +// UserModel federationUser = new UserModel(Constants.FEDERATION_USER); +// federationUser.canAdmin = true; +// return federationUser; +// } +} diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 2e6a33e3..9947382a 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -101,13 +101,6 @@ public class GitblitManager implements IGitblitManager { * @return Map<String, SettingModel> */ private void loadSettingModels(ServerSettings settingsModel) { - // this entire "supports" concept will go away with user service refactoring - UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT); - externalUser.password = Constants.EXTERNAL_ACCOUNT; - 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 diff --git a/src/main/java/com/gitblit/manager/ISessionManager.java b/src/main/java/com/gitblit/manager/IAuthenticationManager.java index 29f17096..093f44d9 100644 --- a/src/main/java/com/gitblit/manager/ISessionManager.java +++ b/src/main/java/com/gitblit/manager/IAuthenticationManager.java @@ -18,9 +18,10 @@ package com.gitblit.manager; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; -public interface ISessionManager extends IManager { +public interface IAuthenticationManager extends IManager { /** * Authenticate a user based on HTTP request parameters. @@ -69,4 +70,44 @@ public interface ISessionManager extends IManager { */ void logout(HttpServletResponse response, UserModel user); + /** + * Does the user service support changes to credentials? + * + * @return true or false + * @since 1.0.0 + */ + boolean supportsCredentialChanges(UserModel user); + + /** + * Returns true if the user's display name can be changed. + * + * @param user + * @return true if the user service supports display name changes + */ + boolean supportsDisplayNameChanges(UserModel user); + + /** + * Returns true if the user's email address can be changed. + * + * @param user + * @return true if the user service supports email address changes + */ + boolean supportsEmailAddressChanges(UserModel user); + + /** + * Returns true if the user's team memberships can be changed. + * + * @param user + * @return true if the user service supports team membership changes + */ + boolean supportsTeamMembershipChanges(UserModel user); + + /** + * Returns true if the team memberships can be changed. + * + * @param user + * @return true if the team memberships can be changed + */ + boolean supportsTeamMembershipChanges(TeamModel team); + }
\ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/IUserManager.java b/src/main/java/com/gitblit/manager/IUserManager.java index 387a7208..5815bcaf 100644 --- a/src/main/java/com/gitblit/manager/IUserManager.java +++ b/src/main/java/com/gitblit/manager/IUserManager.java @@ -15,266 +15,9 @@ */ package com.gitblit.manager; -import java.util.Collection; -import java.util.List; +import com.gitblit.IUserService; -import com.gitblit.models.TeamModel; -import com.gitblit.models.UserModel; +public interface IUserManager extends IManager, IUserService { -public interface IUserManager extends IManager { - - boolean supportsAddUser(); - - /** - * Does the user service support changes to credentials? - * - * @return true or false - * @since 1.0.0 - */ - boolean supportsCredentialChanges(UserModel user); - - /** - * Returns true if the user's display name can be changed. - * - * @param user - * @return true if the user service supports display name changes - */ - boolean supportsDisplayNameChanges(UserModel user); - - /** - * Returns true if the user's email address can be changed. - * - * @param user - * @return true if the user service supports email address changes - */ - boolean supportsEmailAddressChanges(UserModel user); - - /** - * Returns true if the user's team memberships can be changed. - * - * @param user - * @return true if the user service supports team membership changes - */ - boolean supportsTeamMembershipChanges(UserModel user); - - /** - * Does the user service support cookie authentication? - * - * @return true or false - */ - boolean supportsCookies(); - - /** - * Returns the cookie value for the specified user. - * - * @param model - * @return cookie value - */ - String getCookie(UserModel model); - - /** - * Authenticate a user based on their cookie. - * - * @param cookie - * @return a user object or null - */ - UserModel authenticate(char[] cookie); - - /** - * Authenticate a user based on a username and password. - * - * @param username - * @param password - * @return a user object or null - */ - UserModel authenticate(String username, char[] password); - - /** - * Logout a user. - * - * @param user - */ - void logout(UserModel user); - - /** - * Retrieve the user object for the specified username. - * - * @param username - * @return a user object or null - */ - UserModel getUserModel(String username); - - /** - * Updates/writes a complete user object. - * - * @param model - * @return true if update is successful - */ - boolean updateUserModel(UserModel model); - - /** - * Updates/writes all specified user objects. - * - * @param models a list of user models - * @return true if update is successful - * @since 1.2.0 - */ - boolean updateUserModels(Collection<UserModel> 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 - */ - boolean updateUserModel(String username, UserModel model); - - /** - * Deletes the user object from the user service. - * - * @param model - * @return true if successful - */ - boolean deleteUserModel(UserModel model); - - /** - * Delete the user object with the specified username - * - * @param username - * @return true if successful - */ - boolean deleteUser(String username); - - /** - * Returns the list of all users available to the login service. - * - * @return list of all usernames - */ - List<String> getAllUsernames(); - - /** - * Returns the list of all users available to the login service. - * - * @return list of all users - * @since 0.8.0 - */ - List<UserModel> getAllUsers(); - - /** - * Returns the list of all teams available to the login service. - * - * @return list of all teams - * @since 0.8.0 - */ - List<String> getAllTeamNames(); - - /** - * Returns the list of all teams available to the login service. - * - * @return list of all teams - * @since 0.8.0 - */ - List<TeamModel> getAllTeams(); - - /** - * 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 - */ - List<String> getTeamNamesForRepositoryRole(String role); - - /** - * Retrieve the team object for the specified team name. - * - * @param teamname - * @return a team object or null - * @since 0.8.0 - */ - TeamModel getTeamModel(String teamname); - - /** - * Updates/writes a complete team object. - * - * @param model - * @return true if update is successful - * @since 0.8.0 - */ - boolean updateTeamModel(TeamModel model); - - /** - * Updates/writes all specified team objects. - * - * @param models a list of team models - * @return true if update is successful - * @since 1.2.0 - */ - boolean updateTeamModels(Collection<TeamModel> 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 - */ - boolean updateTeamModel(String teamname, TeamModel model); - - /** - * Deletes the team object from the user service. - * - * @param model - * @return true if successful - * @since 0.8.0 - */ - boolean deleteTeamModel(TeamModel model); - - /** - * Delete the team object with the specified teamname - * - * @param teamname - * @return true if successful - * @since 0.8.0 - */ - boolean deleteTeam(String 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 - */ - List<String> getUsernamesForRepositoryRole(String role); - - /** - * Renames a repository role. - * - * @param oldRole - * @param newRole - * @return true if successful - */ - boolean renameRepositoryRole(String oldRole, String newRole); - - /** - * Removes a repository role from all users. - * - * @param role - * @return true if successful - */ - boolean deleteRepositoryRole(String role); }
\ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/SessionManager.java b/src/main/java/com/gitblit/manager/SessionManager.java deleted file mode 100644 index 6a85da89..00000000 --- a/src/main/java/com/gitblit/manager/SessionManager.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * 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.nio.charset.Charset; -import java.security.Principal; -import java.text.MessageFormat; -import java.util.List; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.wicket.RequestCycle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.gitblit.Constants; -import com.gitblit.Constants.AuthenticationType; -import com.gitblit.IStoredSettings; -import com.gitblit.Keys; -import com.gitblit.models.UserModel; -import com.gitblit.utils.Base64; -import com.gitblit.utils.HttpUtils; -import com.gitblit.utils.StringUtils; -import com.gitblit.utils.X509Utils.X509Metadata; -import com.gitblit.wicket.GitBlitWebSession; - -/** - * The session manager handles user login & logout. - * - * @author James Moger - * - */ -public class SessionManager implements ISessionManager { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final IStoredSettings settings; - - private final IRuntimeManager runtimeManager; - - private final IUserManager userManager; - - public SessionManager( - IRuntimeManager runtimeManager, - IUserManager userManager) { - - this.settings = runtimeManager.getSettings(); - this.runtimeManager = runtimeManager; - this.userManager = userManager; - } - - @Override - public SessionManager start() { - List<String> services = settings.getStrings("realm.authenticationServices"); - for (String service : services) { - // TODO populate authentication services here - } - return this; - } - - @Override - public SessionManager stop() { - return this; - } - - /** - * Authenticate a user based on HTTP request parameters. - * - * Authentication by X509Certificate is tried first and then by cookie. - * - * @param httpRequest - * @return a user object or null - */ - @Override - public UserModel authenticate(HttpServletRequest httpRequest) { - return authenticate(httpRequest, false); - } - - /** - * Authenticate a user based on HTTP request parameters. - * - * Authentication by X509Certificate, servlet container principal, cookie, - * and BASIC header. - * - * @param httpRequest - * @param requiresCertificate - * @return a user object or null - */ - @Override - public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { - // try to authenticate by certificate - boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true); - String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]); - UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids); - if (model != null) { - // grab real user model and preserve certificate serial number - UserModel user = userManager.getUserModel(model.username); - X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); - if (user != null) { - flagWicketSession(AuthenticationType.CERTIFICATE); - logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}", - user.username, metadata.serialNumber, httpRequest.getRemoteAddr())); - return user; - } else { - logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}", - model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); - } - } - - if (requiresCertificate) { - // caller requires client certificate authentication (e.g. git servlet) - return null; - } - - // try to authenticate by servlet container principal - Principal principal = httpRequest.getUserPrincipal(); - if (principal != null) { - String username = principal.getName(); - if (!StringUtils.isEmpty(username)) { - boolean internalAccount = isInternalAccount(username); - UserModel user = userManager.getUserModel(username); - if (user != null) { - // existing user - flagWicketSession(AuthenticationType.CONTAINER); - logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", - user.username, httpRequest.getRemoteAddr())); - return user; - } else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false) - && !internalAccount) { - // auto-create user from an authenticated container principal - user = new UserModel(username.toLowerCase()); - user.displayName = username; - user.password = Constants.EXTERNAL_ACCOUNT; - userManager.updateUserModel(user); - flagWicketSession(AuthenticationType.CONTAINER); - logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", - user.username, httpRequest.getRemoteAddr())); - return user; - } else if (!internalAccount) { - logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", - principal.getName(), httpRequest.getRemoteAddr())); - } - } - } - - // try to authenticate by cookie - if (userManager.supportsCookies()) { - UserModel user = authenticate(httpRequest.getCookies()); - if (user != null) { - flagWicketSession(AuthenticationType.COOKIE); - logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}", - user.username, httpRequest.getRemoteAddr())); - return user; - } - } - - // try to authenticate by BASIC - final String authorization = httpRequest.getHeader("Authorization"); - if (authorization != null && authorization.startsWith("Basic")) { - // Authorization: Basic base64credentials - String base64Credentials = authorization.substring("Basic".length()).trim(); - String credentials = new String(Base64.decode(base64Credentials), - Charset.forName("UTF-8")); - // credentials = username:password - final String[] values = credentials.split(":", 2); - - if (values.length == 2) { - String username = values[0]; - char[] password = values[1].toCharArray(); - UserModel user = authenticate(username, password); - if (user != null) { - flagWicketSession(AuthenticationType.CREDENTIALS); - logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}", - user.username, httpRequest.getRemoteAddr())); - return user; - } else { - logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", - username, httpRequest.getRemoteAddr())); - } - } - } - return null; - } - - /** - * Authenticate a user based on their cookie. - * - * @param cookies - * @return a user object or null - */ - protected UserModel authenticate(Cookie[] cookies) { - if (userManager.supportsCookies()) { - if (cookies != null && cookies.length > 0) { - for (Cookie cookie : cookies) { - if (cookie.getName().equals(Constants.NAME)) { - String value = cookie.getValue(); - return userManager.authenticate(value.toCharArray()); - } - } - } - } - return null; - } - - protected void flagWicketSession(AuthenticationType authenticationType) { - RequestCycle requestCycle = RequestCycle.get(); - if (requestCycle != null) { - // flag the Wicket session, if this is a Wicket request - GitBlitWebSession session = GitBlitWebSession.get(); - session.authenticationType = authenticationType; - } - } - - /** - * Authenticate a user based on a username and password. - * - * @see IUserService.authenticate(String, char[]) - * @param username - * @param password - * @return a user object or null - */ - @Override - public UserModel authenticate(String username, char[] password) { - if (StringUtils.isEmpty(username)) { - // can not authenticate empty username - return null; - } - - String usernameDecoded = StringUtils.decodeUsername(username); - String pw = new String(password); - if (StringUtils.isEmpty(pw)) { - // can not authenticate empty password - return null; - } - // check to see if this is the federation user -// if (canFederate()) { -// if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) { -// List<String> tokens = getFederationTokens(); -// if (tokens.contains(pw)) { -// return getFederationUser(); -// } -// } -// } - - UserModel user = userManager.authenticate(usernameDecoded, password); - - // try registered external authentication providers - if (user == null) { -// for (AuthenticationService service : authenticationServices) { -// if (service instanceof UsernamePasswordAuthenticationService) { -// user = service.authenticate(usernameDecoded, password); -// if (user != null) { -// // user authenticated -// user.accountType = service.getAccountType(); -// return user; -// } -// } -// } - } - return user; - } - - /** - * Sets a cookie for the specified user. - * - * @param response - * @param user - */ - @Override - public void setCookie(HttpServletResponse response, UserModel user) { - GitBlitWebSession session = GitBlitWebSession.get(); - boolean standardLogin = session.authenticationType.isStandard(); - - if (userManager.supportsCookies() && standardLogin) { - Cookie userCookie; - if (user == null) { - // clear cookie for logout - userCookie = new Cookie(Constants.NAME, ""); - } else { - // set cookie for login - String cookie = userManager.getCookie(user); - if (StringUtils.isEmpty(cookie)) { - // create empty cookie - userCookie = new Cookie(Constants.NAME, ""); - } else { - // create real cookie - userCookie = new Cookie(Constants.NAME, cookie); - userCookie.setMaxAge(Integer.MAX_VALUE); - } - } - userCookie.setPath("/"); - response.addCookie(userCookie); - } - } - - /** - * Logout a user. - * - * @param user - */ - @Override - public void logout(HttpServletResponse response, UserModel user) { - setCookie(response, null); - userManager.logout(user); - } - - /** - * Returns true if the username represents an internal account - * - * @param username - * @return true if the specified username represents an internal account - */ - protected boolean isInternalAccount(String username) { - return !StringUtils.isEmpty(username) - && (username.equalsIgnoreCase(Constants.FEDERATION_USER) - || username.equalsIgnoreCase(UserModel.ANONYMOUS.username)); - } - -// protected UserModel getFederationUser() { -// // the federation user is an administrator -// UserModel federationUser = new UserModel(Constants.FEDERATION_USER); -// federationUser.canAdmin = true; -// return federationUser; -// } -} diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java index 90b9d1e2..3ca62e23 100644 --- a/src/main/java/com/gitblit/manager/UserManager.java +++ b/src/main/java/com/gitblit/manager/UserManager.java @@ -20,20 +20,19 @@ import java.io.IOException; 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.models.TeamModel; import com.gitblit.models.UserModel; -import com.gitblit.utils.DeepCopier; import com.gitblit.utils.StringUtils; /** @@ -50,37 +49,68 @@ public class UserManager implements IUserManager { private final IRuntimeManager runtimeManager; + private final Map<String, String> legacyBackingServices; + private IUserService userService; public UserManager(IRuntimeManager runtimeManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; + + // 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("UserService: " + userService.toString()); + logger.info(userService.toString()); this.userService = userService; this.userService.setup(runtimeManager); } @Override + 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("#################################################################"); + logger.warn(" Key '{}' is obsolete!", realmKey); + logger.warn(" Please set '{}={}'", Keys.realm.userService, settings.getString(realmKey, "${baseFolder}/users.conf")); + logger.warn("#################################################################"); + 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 (Throwable t) { + // typical file path configuration + File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf"); + service = createUserService(realmFile); + } } setUserService(service); } @@ -90,7 +120,7 @@ public class UserManager implements IUserManager { 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); } @@ -118,74 +148,6 @@ public class UserManager implements IUserManager { 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. * @@ -198,47 +160,18 @@ public class UserManager implements IUserManager { } /** - * 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); - } - - /** * Retrieve the user object for the specified username. * * @param username @@ -251,7 +184,6 @@ public class UserManager implements IUserManager { } String usernameDecoded = StringUtils.decodeUsername(username); UserModel user = userService.getUserModel(usernameDecoded); - setAccountType(user); return user; } @@ -290,32 +222,7 @@ public class UserManager implements IUserManager { */ @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; + return userService.updateUserModel(username, model); } /** @@ -364,9 +271,6 @@ public class UserManager implements IUserManager { @Override public List<UserModel> getAllUsers() { List<UserModel> users = userService.getAllUsers(); - for (UserModel user : users) { - setAccountType(user); - } return users; } @@ -378,7 +282,8 @@ public class UserManager implements IUserManager { */ @Override public List<String> getAllTeamNames() { - return userService.getAllTeamNames(); + List<String> teams = userService.getAllTeamNames(); + return teams; } /** @@ -404,7 +309,8 @@ public class UserManager implements IUserManager { */ @Override public List<String> getTeamNamesForRepositoryRole(String role) { - return userService.getTeamNamesForRepositoryRole(role); + List<String> teams = userService.getTeamNamesForRepositoryRole(role); + return teams; } /** @@ -416,7 +322,8 @@ public class UserManager implements IUserManager { */ @Override public TeamModel getTeamModel(String teamname) { - return userService.getTeamModel(teamname); + TeamModel team = userService.getTeamModel(teamname); + return team; } /** @@ -456,14 +363,6 @@ public class UserManager implements IUserManager { */ @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); } @@ -527,16 +426,4 @@ public class UserManager implements IUserManager { 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(); - } - } - } } |