changes:
- Gitblit now rejects pushes to mirror repositories (issue-5)
- Personal repository prefix (~) is now configurable (issue-265)
+ - Refactored user services and separated authentication into providers (issue-281)
- Reversed line links in blob view (issue-309)
- Dashboard and Activity pages now obey the web.generateActivityGraph setting (issue-310)
- Do not log passwords on failed authentication attempts (issue-316)
- { name: 'git.enableMirroring', defaultValue: 'false' }
- { name: 'git.defaultAccessRestriction', defaultValue: 'PUSH' }
- { name: 'git.mirrorPeriod', defaultValue: '30 mins' }
+ - { name: 'realm.authenticationProviders', defaultValue: ' ' }
- { name: 'web.commitMessageRenderer', defaultValue: 'plain' }
- { name: 'web.documents', defaultValue: 'readme home index changelog contributing submitting_patches copying license notice authors' }
- { name: 'web.showBranchGraph', defaultValue: 'true' }
- { name: 'web.summaryShowReadme', defaultValue: 'false' }
- - { name: 'server.redirectToHttpsPort', defaultValue: 'true' }
+ - { name: 'server.redirectToHttpsPort', defaultValue: 'false' }
contributors:
- James Moger
- Robin Rosenberg
web.projectsFile = ${baseFolder}/projects.conf\r
\r
# Either the full path to a user config file (users.conf)\r
-# OR the full path to a simple user properties file (users.properties)\r
# OR a fully qualified class name that implements the IUserService interface.\r
#\r
-# Alternative user services:\r
-# com.gitblit.LdapUserService\r
-# com.gitblit.RedmineUserService\r
-# com.gitblit.SalesforceUserService\r
-# com.gitblit.WindowsUserService\r
-# com.gitblit.PAMUserService\r
-# com.gitblit.HtpasswdUserService\r
-#\r
# Any custom user service implementation must have a public default constructor.\r
#\r
# SINCE 0.5.0\r
# BASEFOLDER\r
realm.userService = ${baseFolder}/users.conf\r
\r
+# Ordered list of external authentication providers which will be used if\r
+# authentication against the local user service fails.\r
+#\r
+# Valid providers are:\r
+#\r
+# htpasswd\r
+# ldap\r
+# pam\r
+# redmine\r
+# salesforce\r
+# windows\r
+\r
+# e.g. realm.authenticationProviders = htpasswd windows\r
+#\r
+# SINCE 1.4.0\r
+# RESTART REQUIRED\r
+# SPACE-DELIMITED\r
+realm.authenticationProviders =\r
+\r
# How to store passwords.\r
# Valid values are plain, md5, or combined-md5. md5 is the hash of password.\r
# combined-md5 is the hash of username.toLowerCase()+password.\r
# SINCE 1.3.0\r
realm.container.autoCreateAccounts = false\r
\r
-# The WindowsUserService must be backed by another user service for standard user\r
-# and team management.\r
-# default: users.conf\r
-#\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-# SINCE 1.3.0\r
-realm.windows.backingUserService = ${baseFolder}/users.conf\r
-\r
# Allow or prohibit Windows guest account logins\r
#\r
# SINCE 1.3.0\r
# SINCE 1.3.0\r
realm.windows.defaultDomain =\r
\r
-# The PAMUserService must be backed by another user service for standard user\r
-# and team management.\r
-# default: users.conf\r
-#\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-# SINCE 1.3.1\r
-realm.pam.backingUserService = ${baseFolder}/users.conf\r
-\r
# The PAM service name for authentication.\r
# default: system-auth\r
#\r
# SINCE 1.3.1\r
realm.pam.serviceName = system-auth\r
\r
-# The HtpasswdUserService must be backed by another user service for standard user\r
-# and team management and attributes. This can be one of the local Gitblit user services.\r
-# default: users.conf\r
-#\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-# SINCE 1.3.2\r
-realm.htpasswd.backingUserService = ${baseFolder}/users.conf\r
-\r
# The Apache htpasswd file that contains the users and passwords.\r
# default: ${baseFolder}/htpasswd\r
#\r
# SINCE 1.3.2\r
realm.htpasswd.userfile = ${baseFolder}/htpasswd\r
\r
-# Determines how accounts are looked up upon login.\r
-#\r
-# If set to false, then authentication for local accounts is done against\r
-# the backing user service.\r
-# If set to true, then authentication will first be checked against the\r
-# htpasswd store, even if the account appears as a local account in the\r
-# backing user service. If the user is found in the htpasswd store, then\r
-# an already existing local account will be turned into an external account.\r
-# In this case an initial local password is never used and gets overwritten\r
-# by the externally stored password upon login.\r
-# default: false\r
-#\r
-# SINCE 1.3.2\r
-realm.htpasswd.overrideLocalAuthentication = false\r
-\r
-# The SalesforceUserService must be backed by another user service for standard user\r
-# and team management.\r
-# default: users.conf\r
-#\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-# SINCE 1.3.0\r
-realm.salesforce.backingUserService = ${baseFolder}/users.conf\r
-\r
# Restrict the Salesforce user to members of this org.\r
# default: 0 (i.e. do not check the Org ID)\r
#\r
# SINCE 1.0.0\r
realm.ldap.password = password\r
\r
-# The LdapUserService must be backed by another user service for standard user\r
-# and team management.\r
-# default: users.conf\r
-#\r
-# SINCE 1.0.0\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-realm.ldap.backingUserService = ${baseFolder}/users.conf\r
-\r
# Delegate team membership control to LDAP.\r
#\r
# If true, team user memberships will be specified by LDAP groups. This will\r
# For MS Active Directory this may be sAMAccountName\r
realm.ldap.uid = uid\r
\r
-# The RedmineUserService must be backed by another user service for standard user\r
-# and team management.\r
-# default: users.conf\r
-#\r
-# RESTART REQUIRED\r
-# BASEFOLDER\r
-realm.redmine.backingUserService = ${baseFolder}/users.conf\r
-\r
# URL of the Redmine.\r
realm.redmine.url = http://example.com/redmine\r
\r
#\r
# SINCE 1.4.0\r
# RESTART REQUIRED\r
-server.redirectToHttpsPort = true\r
+server.redirectToHttpsPort = false\r
\r
# Specify the interface for Jetty to bind the standard connector.\r
# You may specify an ip or an empty value to bind to all interfaces.\r
\r
private static final String LOCALE = "locale";\r
\r
+ private static final String ACCOUNTTYPE = "accountType";\r
+\r
private final File realmFile;\r
\r
private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class);\r
public void setup(IRuntimeManager runtimeManager) {\r
}\r
\r
- /**\r
- * Does the user service support changes to credentials?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return true;\r
- }\r
-\r
- /**\r
- * Does the user service support changes to user display name?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return true;\r
- }\r
-\r
- /**\r
- * Does the user service support changes to user email address?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return true;\r
- }\r
-\r
- /**\r
- * Does the user service support changes to team memberships?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return true;\r
- }\r
-\r
- /**\r
- * Does the user service support cookie authentication?\r
- *\r
- * @return true or false\r
- */\r
- @Override\r
- public boolean supportsCookies() {\r
- return true;\r
- }\r
-\r
/**\r
* Returns the cookie value for the specified user.\r
*\r
}\r
\r
/**\r
- * Authenticate a user based on their cookie.\r
+ * Gets the user object for the specified cookie.\r
*\r
* @param cookie\r
* @return a user object or null\r
*/\r
@Override\r
- public synchronized UserModel authenticate(char[] cookie) {\r
+ public synchronized UserModel getUserModel(char[] cookie) {\r
String hash = new String(cookie);\r
if (StringUtils.isEmpty(hash)) {\r
return null;\r
return model;\r
}\r
\r
- /**\r
- * Authenticate a user based on a username and password.\r
- *\r
- * @param username\r
- * @param password\r
- * @return a user object or null\r
- */\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- UserModel returnedUser = null;\r
- UserModel user = getUserModel(username);\r
- if (user == null) {\r
- return null;\r
- }\r
- if (user.password.startsWith(StringUtils.MD5_TYPE)) {\r
- // password digest\r
- String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));\r
- if (user.password.equalsIgnoreCase(md5)) {\r
- returnedUser = user;\r
- }\r
- } else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) {\r
- // username+password digest\r
- String md5 = StringUtils.COMBINED_MD5_TYPE\r
- + StringUtils.getMD5(username.toLowerCase() + new String(password));\r
- if (user.password.equalsIgnoreCase(md5)) {\r
- returnedUser = user;\r
- }\r
- } else if (user.password.equals(new String(password))) {\r
- // plain-text password\r
- returnedUser = user;\r
- }\r
- return returnedUser;\r
- }\r
-\r
- /**\r
- * Logout a user.\r
- *\r
- * @param user\r
- */\r
- @Override\r
- public void logout(UserModel user) {\r
- }\r
-\r
/**\r
* Retrieve the user object for the specified username.\r
*\r
public synchronized boolean updateUserModel(String username, UserModel model) {\r
UserModel originalUser = null;\r
try {\r
+ if (!model.isLocalAccount()) {\r
+ // do not persist password\r
+ model.password = Constants.EXTERNAL_ACCOUNT;\r
+ }\r
read();\r
originalUser = users.remove(username.toLowerCase());\r
users.put(model.username.toLowerCase(), model);\r
return list;\r
}\r
\r
- /**\r
- * Sets the list of all teams who are allowed to bypass the access\r
- * restriction placed on the specified repository.\r
- *\r
- * @param role\r
- * the repository name\r
- * @param teamnames\r
- * @return true if successful\r
- */\r
- @Override\r
- public synchronized boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {\r
- try {\r
- Set<String> specifiedTeams = new HashSet<String>();\r
- for (String teamname : teamnames) {\r
- specifiedTeams.add(teamname.toLowerCase());\r
- }\r
-\r
- read();\r
-\r
- // identify teams which require add or remove role\r
- for (TeamModel team : teams.values()) {\r
- // team has role, check against revised team list\r
- if (specifiedTeams.contains(team.name.toLowerCase())) {\r
- team.addRepositoryPermission(role);\r
- } else {\r
- // remove role from team\r
- team.removeRepositoryPermission(role);\r
- }\r
- }\r
-\r
- // persist changes\r
- write();\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to set teams for role {0}!", role), t);\r
- }\r
- return false;\r
- }\r
-\r
/**\r
* Retrieve the team object for the specified team name.\r
*\r
return list;\r
}\r
\r
- /**\r
- * Sets the list of all uses who are allowed to bypass the access\r
- * restriction placed on the specified repository.\r
- *\r
- * @param role\r
- * the repository name\r
- * @param usernames\r
- * @return true if successful\r
- */\r
- @Override\r
- @Deprecated\r
- public synchronized boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {\r
- try {\r
- Set<String> specifiedUsers = new HashSet<String>();\r
- for (String username : usernames) {\r
- specifiedUsers.add(username.toLowerCase());\r
- }\r
-\r
- read();\r
-\r
- // identify users which require add or remove role\r
- for (UserModel user : users.values()) {\r
- // user has role, check against revised user list\r
- if (specifiedUsers.contains(user.username.toLowerCase())) {\r
- user.addRepositoryPermission(role);\r
- } else {\r
- // remove role from user\r
- user.removeRepositoryPermission(role);\r
- }\r
- }\r
-\r
- // persist changes\r
- write();\r
- return true;\r
- } catch (Throwable t) {\r
- logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);\r
- }\r
- return false;\r
- }\r
-\r
/**\r
* Renames a repository role.\r
*\r
if (!StringUtils.isEmpty(model.emailAddress)) {\r
config.setString(USER, model.username, EMAILADDRESS, model.emailAddress);\r
}\r
+ if (model.accountType != null) {\r
+ config.setString(USER, model.username, ACCOUNTTYPE, model.accountType.name());\r
+ }\r
if (!StringUtils.isEmpty(model.organizationalUnit)) {\r
config.setString(USER, model.username, ORGANIZATIONALUNIT, model.organizationalUnit);\r
}\r
roles.add(Constants.NO_ROLE);\r
}\r
config.setStringList(TEAM, model.name, ROLE, roles);\r
+ if (model.accountType != null) {\r
+ config.setString(TEAM, model.name, ACCOUNTTYPE, model.accountType.name());\r
+ }\r
\r
if (!model.canAdmin) {\r
// write team permission for non-admin teams\r
user.password = config.getString(USER, username, PASSWORD);\r
user.displayName = config.getString(USER, username, DISPLAYNAME);\r
user.emailAddress = config.getString(USER, username, EMAILADDRESS);\r
+ user.accountType = AccountType.fromString(config.getString(USER, username, ACCOUNTTYPE));\r
+ if (Constants.EXTERNAL_ACCOUNT.equals(user.password) && user.accountType.isLocal()) {\r
+ user.accountType = null;\r
+ }\r
user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT);\r
user.organization = config.getString(USER, username, ORGANIZATION);\r
user.locality = config.getString(USER, username, LOCALITY);\r
team.canAdmin = roles.contains(Constants.ADMIN_ROLE);\r
team.canFork = roles.contains(Constants.FORK_ROLE);\r
team.canCreate = roles.contains(Constants.CREATE_ROLE);\r
+ team.accountType = AccountType.fromString(config.getString(TEAM, teamname, ACCOUNTTYPE));\r
\r
if (!team.canAdmin) {\r
// non-admin team, read permissions\r
public String toString() {\r
return getClass().getSimpleName() + "(" + realmFile.getAbsolutePath() + ")";\r
}\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.LOCAL;\r
- }\r
}\r
}\r
\r
public static enum AccountType {\r
- LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;\r
+ LOCAL, EXTERNAL, CONTAINER, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;\r
+\r
+ public static AccountType fromString(String value) {\r
+ for (AccountType type : AccountType.values()) {\r
+ if (type.name().equalsIgnoreCase(value)) {\r
+ return type;\r
+ }\r
+ }\r
+ return AccountType.LOCAL;\r
+ }\r
\r
public boolean isLocal() {\r
return this == LOCAL;\r
import org.apache.wicket.protocol.http.WebApplication;
import com.gitblit.git.GitServlet;
+import com.gitblit.manager.AuthenticationManager;
import com.gitblit.manager.FederationManager;
import com.gitblit.manager.GitblitManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IFederationManager;
import com.gitblit.manager.IGitblitManager;
import com.gitblit.manager.INotificationManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.IServicesManager;
-import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.manager.NotificationManager;
import com.gitblit.manager.ProjectManager;
import com.gitblit.manager.RepositoryManager;
import com.gitblit.manager.RuntimeManager;
import com.gitblit.manager.ServicesManager;
-import com.gitblit.manager.SessionManager;
import com.gitblit.manager.UserManager;
import com.gitblit.servlet.BranchGraphServlet;
import com.gitblit.servlet.DownloadZipFilter;
IRuntimeManager.class,
INotificationManager.class,
IUserManager.class,
- ISessionManager.class,
+ IAuthenticationManager.class,
IRepositoryManager.class,
IProjectManager.class,
IGitblitManager.class,
return new UserManager(runtimeManager);
}
- @Provides @Singleton ISessionManager provideSessionManager(
+ @Provides @Singleton IAuthenticationManager provideAuthenticationManager(
IRuntimeManager runtimeManager,
IUserManager userManager) {
- return new SessionManager(
+ return new AuthenticationManager(
runtimeManager,
userManager);
}
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
- ISessionManager sessionManager,
+ IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
runtimeManager,
notificationManager,
userManager,
- sessionManager,
+ authenticationManager,
repositoryManager,
projectManager,
gitblitManager,
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
- ISessionManager sessionManager,
+ IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
runtimeManager,
notificationManager,
userManager,
- sessionManager,
+ authenticationManager,
repositoryManager,
projectManager,
gitblitManager,
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IFederationManager;
import com.gitblit.manager.IGitblitManager;
import com.gitblit.manager.INotificationManager;
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
public class GitBlit implements IRuntimeManager,
INotificationManager,
IUserManager,
- ISessionManager,
+ IAuthenticationManager,
IRepositoryManager,
IProjectManager,
IGitblitManager,
private final IUserManager userManager;
- private final ISessionManager sessionManager;
+ private final IAuthenticationManager authenticationManager;
private final IRepositoryManager repositoryManager;
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
- ISessionManager sessionManager,
+ IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
this.runtimeManager = runtimeManager;
this.notificationManager = notificationManager;
this.userManager = userManager;
- this.sessionManager = sessionManager;
+ this.authenticationManager = authenticationManager;
this.repositoryManager = repositoryManager;
this.projectManager = projectManager;
this.gitblitManager = gitblitManager;
@Override
public UserModel authenticate(String username, char[] password) {
- return sessionManager.authenticate(username, password);
+ return authenticationManager.authenticate(username, password);
}
@Override
public UserModel authenticate(HttpServletRequest httpRequest) {
- return sessionManager.authenticate(httpRequest, false);
+ return authenticationManager.authenticate(httpRequest, false);
}
@Override
public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
- return sessionManager.authenticate(httpRequest, requiresCertificate);
+ return authenticationManager.authenticate(httpRequest, requiresCertificate);
}
@Override
public void setCookie(HttpServletResponse response, UserModel user) {
- sessionManager.setCookie(response, user);
+ authenticationManager.setCookie(response, user);
}
@Override
public void logout(HttpServletResponse response, UserModel user) {
- sessionManager.logout(response, user);
- }
-
- /*
- * USER MANAGER
- */
-
- @Override
- public boolean supportsAddUser() {
- return userManager.supportsAddUser();
+ authenticationManager.logout(response, user);
}
@Override
public boolean supportsCredentialChanges(UserModel user) {
- return userManager.supportsCredentialChanges(user);
+ return authenticationManager.supportsCredentialChanges(user);
}
@Override
public boolean supportsDisplayNameChanges(UserModel user) {
- return userManager.supportsDisplayNameChanges(user);
+ return authenticationManager.supportsDisplayNameChanges(user);
}
@Override
public boolean supportsEmailAddressChanges(UserModel user) {
- return userManager.supportsEmailAddressChanges(user);
+ return authenticationManager.supportsEmailAddressChanges(user);
}
@Override
public boolean supportsTeamMembershipChanges(UserModel user) {
- return userManager.supportsTeamMembershipChanges(user);
+ return authenticationManager.supportsTeamMembershipChanges(user);
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges(TeamModel team) {
+ return authenticationManager.supportsTeamMembershipChanges(team);
+ }
+
+ /*
+ * USER MANAGER
+ */
+
+ @Override
+ public void setup(IRuntimeManager runtimeManager) {
}
@Override
return userManager.getTeamModel(teamname);
}
- @Override
- public boolean supportsCookies() {
- return userManager.supportsCookies();
- }
-
@Override
public String getCookie(UserModel model) {
return userManager.getCookie(model);
+++ /dev/null
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-import java.util.Collection;\r
-import java.util.List;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccountType;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.TeamModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.DeepCopier;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * This class wraps the default user service and is recommended as the starting\r
- * point for custom user service implementations.\r
- *\r
- * This does seem a little convoluted, but the idea is to allow IUserService to\r
- * evolve with new methods and implementations without breaking custom\r
- * authentication implementations.\r
- *\r
- * The most common implementation of a custom IUserService is to only override\r
- * authentication and then delegate all other functionality to one of Gitblit's\r
- * user services. This class optimizes that use-case.\r
- *\r
- * Extending GitblitUserService allows for authentication customization without\r
- * having to keep-up-with IUSerService API changes.\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class GitblitUserService implements IUserService {\r
-\r
- protected IUserService serviceImpl;\r
-\r
- private final Logger logger = LoggerFactory.getLogger(GitblitUserService.class);\r
-\r
- public GitblitUserService() {\r
- }\r
-\r
- @Override\r
- public void setup(IRuntimeManager runtimeManager) {\r
- File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");\r
- serviceImpl = createUserService(realmFile);\r
- logger.info("GUS delegating to " + serviceImpl.toString());\r
- }\r
-\r
- protected IUserService createUserService(File realmFile) {\r
- IUserService service = null;\r
- if (realmFile.getName().toLowerCase().endsWith(".conf")) {\r
- // v0.8.0+ config-based realm file\r
- service = new ConfigUserService(realmFile);\r
- }\r
-\r
- assert service != null;\r
-\r
- if (!realmFile.exists()) {\r
- // Create the Administrator account for a new realm file\r
- try {\r
- realmFile.createNewFile();\r
- } catch (IOException x) {\r
- logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmFile), x);\r
- }\r
- UserModel admin = new UserModel("admin");\r
- admin.password = "admin";\r
- admin.canAdmin = true;\r
- admin.excludeFromFederation = true;\r
- service.updateUserModel(admin);\r
- }\r
-\r
- return service;\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- return getClass().getSimpleName();\r
- }\r
-\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return serviceImpl.supportsCredentialChanges();\r
- }\r
-\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return serviceImpl.supportsDisplayNameChanges();\r
- }\r
-\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return serviceImpl.supportsEmailAddressChanges();\r
- }\r
-\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return serviceImpl.supportsTeamMembershipChanges();\r
- }\r
-\r
- @Override\r
- public boolean supportsCookies() {\r
- return serviceImpl.supportsCookies();\r
- }\r
-\r
- @Override\r
- public String getCookie(UserModel model) {\r
- return serviceImpl.getCookie(model);\r
- }\r
-\r
- /**\r
- * Authenticate a user based on their cookie.\r
- *\r
- * @param cookie\r
- * @return a user object or null\r
- */\r
- @Override\r
- public UserModel authenticate(char[] cookie) {\r
- UserModel user = serviceImpl.authenticate(cookie);\r
- setAccountType(user);\r
- return user;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- UserModel user = serviceImpl.authenticate(username, password);\r
- setAccountType(user);\r
- return user;\r
- }\r
-\r
- @Override\r
- public void logout(UserModel user) {\r
- serviceImpl.logout(user);\r
- }\r
-\r
- @Override\r
- public UserModel getUserModel(String username) {\r
- UserModel user = serviceImpl.getUserModel(username);\r
- setAccountType(user);\r
- return user;\r
- }\r
-\r
- @Override\r
- public boolean updateUserModel(UserModel model) {\r
- return serviceImpl.updateUserModel(model);\r
- }\r
-\r
- @Override\r
- public boolean updateUserModels(Collection<UserModel> models) {\r
- return serviceImpl.updateUserModels(models);\r
- }\r
-\r
- @Override\r
- public boolean updateUserModel(String username, UserModel model) {\r
- if (model.isLocalAccount() || supportsCredentialChanges()) {\r
- if (!model.isLocalAccount() && !supportsTeamMembershipChanges()) {\r
- // teams are externally controlled - copy from original model\r
- UserModel existingModel = getUserModel(username);\r
-\r
- model = DeepCopier.copy(model);\r
- model.teams.clear();\r
- model.teams.addAll(existingModel.teams);\r
- }\r
- return serviceImpl.updateUserModel(username, model);\r
- }\r
- if (model.username.equals(username)) {\r
- // passwords are not persisted by the backing user service\r
- model.password = null;\r
- if (!model.isLocalAccount() && !supportsTeamMembershipChanges()) {\r
- // teams are externally controlled- copy from original model\r
- UserModel existingModel = getUserModel(username);\r
-\r
- model = DeepCopier.copy(model);\r
- model.teams.clear();\r
- model.teams.addAll(existingModel.teams);\r
- }\r
- return serviceImpl.updateUserModel(username, model);\r
- }\r
- logger.error("Users can not be renamed!");\r
- return false;\r
- }\r
- @Override\r
- public boolean deleteUserModel(UserModel model) {\r
- return serviceImpl.deleteUserModel(model);\r
- }\r
-\r
- @Override\r
- public boolean deleteUser(String username) {\r
- return serviceImpl.deleteUser(username);\r
- }\r
-\r
- @Override\r
- public List<String> getAllUsernames() {\r
- return serviceImpl.getAllUsernames();\r
- }\r
-\r
- @Override\r
- public List<UserModel> getAllUsers() {\r
- List<UserModel> users = serviceImpl.getAllUsers();\r
- for (UserModel user : users) {\r
- setAccountType(user);\r
- }\r
- return users;\r
- }\r
-\r
- @Override\r
- public List<String> getAllTeamNames() {\r
- return serviceImpl.getAllTeamNames();\r
- }\r
-\r
- @Override\r
- public List<TeamModel> getAllTeams() {\r
- return serviceImpl.getAllTeams();\r
- }\r
-\r
- @Override\r
- public List<String> getTeamNamesForRepositoryRole(String role) {\r
- return serviceImpl.getTeamNamesForRepositoryRole(role);\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {\r
- return serviceImpl.setTeamnamesForRepositoryRole(role, teamnames);\r
- }\r
-\r
- @Override\r
- public TeamModel getTeamModel(String teamname) {\r
- return serviceImpl.getTeamModel(teamname);\r
- }\r
-\r
- @Override\r
- public boolean updateTeamModel(TeamModel model) {\r
- return serviceImpl.updateTeamModel(model);\r
- }\r
-\r
- @Override\r
- public boolean updateTeamModels(Collection<TeamModel> models) {\r
- return serviceImpl.updateTeamModels(models);\r
- }\r
-\r
- @Override\r
- public boolean updateTeamModel(String teamname, TeamModel model) {\r
- if (!supportsTeamMembershipChanges()) {\r
- // teams are externally controlled - copy from original model\r
- TeamModel existingModel = getTeamModel(teamname);\r
-\r
- model = DeepCopier.copy(model);\r
- model.users.clear();\r
- model.users.addAll(existingModel.users);\r
- }\r
- return serviceImpl.updateTeamModel(teamname, model);\r
- }\r
-\r
- @Override\r
- public boolean deleteTeamModel(TeamModel model) {\r
- return serviceImpl.deleteTeamModel(model);\r
- }\r
-\r
- @Override\r
- public boolean deleteTeam(String teamname) {\r
- return serviceImpl.deleteTeam(teamname);\r
- }\r
-\r
- @Override\r
- public List<String> getUsernamesForRepositoryRole(String role) {\r
- return serviceImpl.getUsernamesForRepositoryRole(role);\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {\r
- return serviceImpl.setUsernamesForRepositoryRole(role, usernames);\r
- }\r
-\r
- @Override\r
- public boolean renameRepositoryRole(String oldRole, String newRole) {\r
- return serviceImpl.renameRepositoryRole(oldRole, newRole);\r
- }\r
-\r
- @Override\r
- public boolean deleteRepositoryRole(String role) {\r
- return serviceImpl.deleteRepositoryRole(role);\r
- }\r
-\r
- protected boolean isLocalAccount(String username) {\r
- UserModel user = getUserModel(username);\r
- return user != null && user.isLocalAccount();\r
- }\r
-\r
- protected void setAccountType(UserModel user) {\r
- if (user != null) {\r
- if (!StringUtils.isEmpty(user.password)\r
- && !Constants.EXTERNAL_ACCOUNT.equalsIgnoreCase(user.password)\r
- && !"StoredInLDAP".equalsIgnoreCase(user.password)) {\r
- user.accountType = AccountType.LOCAL;\r
- } else {\r
- user.accountType = getAccountType();\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.LOCAL;\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright 2013 Florian Zschocke
- * 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;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.text.MessageFormat;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.digest.Crypt;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.codec.digest.Md5Crypt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants.AccountType;
-import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.StringUtils;
-
-
-/**
- * Implementation of a user service using an Apache htpasswd file for authentication.
- *
- * This user service implement custom authentication using entries in a file created
- * by the 'htpasswd' program of an Apache web server. All possible output
- * options of the 'htpasswd' program version 2.2 are supported:
- * plain text (only on Windows and Netware),
- * glibc crypt() (not on Windows and NetWare),
- * Apache MD5 (apr1),
- * unsalted SHA-1.
- *
- * Configuration options:
- * realm.htpasswd.backingUserService - Specify the backing user service that is used
- * to keep the user data other than the password.
- * The default is '${baseFolder}/users.conf'.
- * realm.htpasswd.userfile - The text file with the htpasswd entries to be used for
- * authentication.
- * The default is '${baseFolder}/htpasswd'.
- * realm.htpasswd.overrideLocalAuthentication - Specify if local accounts are overwritten
- * when authentication matches for an
- * external account.
- *
- * @author Florian Zschocke
- *
- */
-public class HtpasswdUserService extends GitblitUserService
-{
-
- private static final String KEY_BACKING_US = Keys.realm.htpasswd.backingUserService;
- private static final String DEFAULT_BACKING_US = "${baseFolder}/users.conf";
-
- private static final String KEY_HTPASSWD_FILE = Keys.realm.htpasswd.userfile;
- private static final String DEFAULT_HTPASSWD_FILE = "${baseFolder}/htpasswd";
-
- private static final String KEY_OVERRIDE_LOCALAUTH = Keys.realm.htpasswd.overrideLocalAuthentication;
- private static final boolean DEFAULT_OVERRIDE_LOCALAUTH = true;
-
- private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
-
- private final boolean SUPPORT_PLAINTEXT_PWD;
-
- private IRuntimeManager runtimeManager;
- private IStoredSettings settings;
- private File htpasswdFile;
-
-
- private final Logger logger = LoggerFactory.getLogger(HtpasswdUserService.class);
-
- private final Map<String, String> htUsers = new ConcurrentHashMap<String, String>();
-
- private volatile long lastModified;
-
- private volatile boolean forceReload;
-
-
-
- public HtpasswdUserService()
- {
- super();
-
- String os = System.getProperty("os.name").toLowerCase();
- if (os.startsWith("windows") || os.startsWith("netware")) {
- SUPPORT_PLAINTEXT_PWD = true;
- }
- else {
- SUPPORT_PLAINTEXT_PWD = false;
- }
- }
-
-
-
- /**
- * Setup the user service.
- *
- * The HtpasswdUserService extends the GitblitUserService and is thus
- * backed by the available user services provided by the GitblitUserService.
- * In addition the setup tries to read and parse the htpasswd file to be used
- * for authentication.
- *
- * @param runtimeManager
- * @since 1.4.0
- */
- @Override
- public void setup(IRuntimeManager runtimeManager)
- {
- this.runtimeManager = runtimeManager;
- this.settings = runtimeManager.getSettings();
-
- // This is done in two steps in order to avoid calling GitBlit.getFileOrFolder(String, String) which will segfault for unit tests.
- String file = settings.getString(KEY_BACKING_US, DEFAULT_BACKING_US);
- File realmFile = runtimeManager.getFileOrFolder(file);
- serviceImpl = createUserService(realmFile);
- logger.info("Htpasswd User Service backed by " + serviceImpl.toString());
-
- read();
-
- logger.debug("Read " + htUsers.size() + " users from htpasswd file: " + this.htpasswdFile);
- }
-
-
-
- /**
- * For now, credentials are defined in the htpasswd file and can not be manipulated
- * from Gitblit.
- *
- * @return false
- * @since 1.0.0
- */
- @Override
- public boolean supportsCredentialChanges()
- {
- return false;
- }
-
-
-
- /**
- * Authenticate a user based on a username and password.
- *
- * If the account is determined to be a local account, authentication
- * will be done against the locally stored password.
- * Otherwise, the configured htpasswd file is read. All current output options
- * of htpasswd are supported: clear text, crypt(), Apache MD5 and unsalted SHA-1.
- *
- * @param username
- * @param password
- * @return a user object or null
- */
- @Override
- public UserModel authenticate(String username, char[] password)
- {
- if (isLocalAccount(username)) {
- // local account, bypass htpasswd authentication
- return super.authenticate(username, password);
- }
-
-
- read();
- String storedPwd = htUsers.get(username);
- if (storedPwd != null) {
- boolean authenticated = false;
- final String passwd = new String(password);
-
- // test Apache MD5 variant encrypted password
- if ( storedPwd.startsWith("$apr1$") ) {
- if ( storedPwd.equals(Md5Crypt.apr1Crypt(passwd, storedPwd)) ) {
- logger.debug("Apache MD5 encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- }
- // test unsalted SHA password
- else if ( storedPwd.startsWith("{SHA}") ) {
- String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(passwd));
- if ( storedPwd.substring("{SHA}".length()).equals(passwd64) ) {
- logger.debug("Unsalted SHA-1 encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- }
- // test libc crypt() encoded password
- else if ( supportCryptPwd() && storedPwd.equals(Crypt.crypt(passwd, storedPwd)) ) {
- logger.debug("Libc crypt encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- // test clear text
- else if ( supportPlaintextPwd() && storedPwd.equals(passwd) ){
- logger.debug("Clear text password matched for user '" + username + "'");
- authenticated = true;
- }
-
-
- if (authenticated) {
- logger.debug("Htpasswd authenticated: " + username);
-
- UserModel user = getUserModel(username);
- if (user == null) {
- // create user object for new authenticated user
- user = new UserModel(username);
- }
-
- // create a user cookie
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
- user.cookie = StringUtils.getSHA1(user.username + passwd);
- }
-
- // Set user attributes, hide password from backing user service.
- user.password = Constants.EXTERNAL_ACCOUNT;
- user.accountType = getAccountType();
-
- // Push the looked up values to backing file
- super.updateUserModel(user);
-
- return user;
- }
- }
-
- return null;
- }
-
-
-
- /**
- * Determine if the account is to be treated as a local account.
- *
- * This influences authentication. A local account will be authenticated
- * by the backing user service while an external account will be handled
- * by this user service.
- * <br/>
- * The decision also depends on the setting of the key
- * realm.htpasswd.overrideLocalAuthentication.
- * If it is set to true, then passwords will first be checked against the
- * htpasswd store. If an account exists and is marked as local in the backing
- * user service, that setting will be overwritten by the result. This
- * means that an account that looks local to the backing user service will
- * be turned into an external account upon valid login of a user that has
- * an entry in the htpasswd file.
- * If the key is set to false, then it is determined if the account is local
- * according to the logic of the GitblitUserService.
- */
- @Override
- protected boolean isLocalAccount(String username)
- {
- if ( settings.getBoolean(KEY_OVERRIDE_LOCALAUTH, DEFAULT_OVERRIDE_LOCALAUTH) ) {
- read();
- if ( htUsers.containsKey(username) ) return false;
- }
- return super.isLocalAccount(username);
- }
-
-
-
- /**
- * Get the account type used for this user service.
- *
- * @return AccountType.HTPASSWD
- */
- @Override
- public AccountType getAccountType()
- {
- return AccountType.HTPASSWD;
- }
-
-
-
- private String htpasswdFilePath = null;
- /**
- * Reads the realm file and rebuilds the in-memory lookup tables.
- */
- protected synchronized void read()
- {
-
- // This is done in two steps in order to avoid calling GitBlit.getFileOrFolder(String, String) which will segfault for unit tests.
- String file = settings.getString(KEY_HTPASSWD_FILE, DEFAULT_HTPASSWD_FILE);
- if ( !file.equals(htpasswdFilePath) ) {
- // The htpasswd file setting changed. Rediscover the file.
- this.htpasswdFilePath = file;
- this.htpasswdFile = runtimeManager.getFileOrFolder(file);
- this.htUsers.clear();
- this.forceReload = true;
- }
-
- if (htpasswdFile.exists() && (forceReload || (htpasswdFile.lastModified() != lastModified))) {
- forceReload = false;
- lastModified = htpasswdFile.lastModified();
- htUsers.clear();
-
- Pattern entry = Pattern.compile("^([^:]+):(.+)");
-
- Scanner scanner = null;
- try {
- scanner = new Scanner(new FileInputStream(htpasswdFile));
- while( scanner.hasNextLine()) {
- String line = scanner.nextLine().trim();
- if ( !line.isEmpty() && !line.startsWith("#") ) {
- Matcher m = entry.matcher(line);
- if ( m.matches() ) {
- htUsers.put(m.group(1), m.group(2));
- }
- }
- }
- } catch (Exception e) {
- logger.error(MessageFormat.format("Failed to read {0}", htpasswdFile), e);
- }
- finally {
- if (scanner != null) scanner.close();
- }
- }
- }
-
-
-
- private boolean supportPlaintextPwd()
- {
- return this.settings.getBoolean(KEY_SUPPORT_PLAINTEXT_PWD, SUPPORT_PLAINTEXT_PWD);
- }
-
-
- private boolean supportCryptPwd()
- {
- return !supportPlaintextPwd();
- }
-
-
-
- @Override
- public String toString()
- {
- return getClass().getSimpleName() + "(" + ((htpasswdFile != null) ? htpasswdFile.getAbsolutePath() : "null") + ")";
- }
-
-
-
-
- /*
- * Method only used for unit tests. Return number of users read from htpasswd file.
- */
- public int getNumberHtpasswdUsers()
- {
- return this.htUsers.size();
- }
-}
import java.util.Collection;\r
import java.util.List;\r
\r
-import com.gitblit.Constants.AccountType;\r
import com.gitblit.manager.IRuntimeManager;\r
import com.gitblit.models.TeamModel;\r
import com.gitblit.models.UserModel;\r
*/\r
void setup(IRuntimeManager runtimeManager);\r
\r
- /**\r
- * Does the user service support changes to credentials?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- boolean supportsCredentialChanges();\r
-\r
- /**\r
- * Does the user service support changes to user display name?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- boolean supportsDisplayNameChanges();\r
-\r
- /**\r
- * Does the user service support changes to user email address?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- boolean supportsEmailAddressChanges();\r
-\r
- /**\r
- * Does the user service support changes to team memberships?\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- boolean supportsTeamMembershipChanges();\r
-\r
- /**\r
- * Does the user service support cookie authentication?\r
- *\r
- * @return true or false\r
- */\r
- boolean supportsCookies();\r
-\r
/**\r
* Returns the cookie value for the specified user.\r
*\r
String getCookie(UserModel model);\r
\r
/**\r
- * Authenticate a user based on their cookie.\r
+ * Retrieve a user object for the specified cookie.\r
*\r
* @param cookie\r
* @return a user object or null\r
*/\r
- UserModel authenticate(char[] cookie);\r
-\r
- /**\r
- * Authenticate a user based on a username and password.\r
- *\r
- * @param username\r
- * @param password\r
- * @return a user object or null\r
- */\r
- UserModel authenticate(String username, char[] password);\r
-\r
- /**\r
- * Logout a user.\r
- *\r
- * @param user\r
- */\r
- void logout(UserModel user);\r
+ UserModel getUserModel(char[] cookie);\r
\r
/**\r
* Retrieve the user object for the specified username.\r
*/\r
List<String> getTeamNamesForRepositoryRole(String role);\r
\r
- /**\r
- * Sets the list of all teams who are allowed to bypass the access\r
- * restriction placed on the specified repository.\r
- *\r
- * @param role\r
- * the repository name\r
- * @param teamnames\r
- * @return true if successful\r
- * @since 0.8.0\r
- */\r
- @Deprecated\r
- boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames);\r
-\r
/**\r
* Retrieve the team object for the specified team name.\r
*\r
*/\r
List<String> getUsernamesForRepositoryRole(String role);\r
\r
- /**\r
- * Sets the list of all uses who are allowed to bypass the access\r
- * restriction placed on the specified repository.\r
- *\r
- * @param role\r
- * the repository name\r
- * @param usernames\r
- * @return true if successful\r
- */\r
- @Deprecated\r
- boolean setUsernamesForRepositoryRole(String role, List<String> usernames);\r
-\r
/**\r
* Renames a repository role.\r
*\r
*/\r
boolean deleteRepositoryRole(String role);\r
\r
- /**\r
- * Returns the account type for the user models.\r
- *\r
- * @return the account type\r
- * @since 1.4.0\r
- */\r
- AccountType getAccountType();\r
-\r
/**\r
* @See java.lang.Object.toString();\r
* @return string representation of the login service\r
+++ /dev/null
-/*\r
- * Copyright 2012 John Crygier\r
- * Copyright 2012 gitblit.com\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.net.URI;\r
-import java.net.URISyntaxException;\r
-import java.security.GeneralSecurityException;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.concurrent.TimeUnit;\r
-import java.util.concurrent.atomic.AtomicLong;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccountType;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.TeamModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.unboundid.ldap.sdk.Attribute;\r
-import com.unboundid.ldap.sdk.DereferencePolicy;\r
-import com.unboundid.ldap.sdk.ExtendedResult;\r
-import com.unboundid.ldap.sdk.LDAPConnection;\r
-import com.unboundid.ldap.sdk.LDAPException;\r
-import com.unboundid.ldap.sdk.LDAPSearchException;\r
-import com.unboundid.ldap.sdk.ResultCode;\r
-import com.unboundid.ldap.sdk.SearchRequest;\r
-import com.unboundid.ldap.sdk.SearchResult;\r
-import com.unboundid.ldap.sdk.SearchResultEntry;\r
-import com.unboundid.ldap.sdk.SearchScope;\r
-import com.unboundid.ldap.sdk.SimpleBindRequest;\r
-import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;\r
-import com.unboundid.util.ssl.SSLUtil;\r
-import com.unboundid.util.ssl.TrustAllTrustManager;\r
-\r
-/**\r
- * Implementation of an LDAP user service.\r
- *\r
- * @author John Crygier\r
- */\r
-public class LdapUserService extends GitblitUserService {\r
-\r
- public static final Logger logger = LoggerFactory.getLogger(LdapUserService.class);\r
-\r
- private IStoredSettings settings;\r
- private AtomicLong lastLdapUserSync = new AtomicLong(0L);\r
-\r
- public LdapUserService() {\r
- super();\r
- }\r
-\r
- private long getSynchronizationPeriod() {\r
- final String cacheDuration = settings.getString(Keys.realm.ldap.ldapCachePeriod, "2 MINUTES");\r
- try {\r
- final String[] s = cacheDuration.split(" ", 2);\r
- long duration = Long.parseLong(s[0]);\r
- TimeUnit timeUnit = TimeUnit.valueOf(s[1]);\r
- return timeUnit.toMillis(duration);\r
- } catch (RuntimeException ex) {\r
- throw new IllegalArgumentException(Keys.realm.ldap.ldapCachePeriod + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'");\r
- }\r
- }\r
-\r
- @Override\r
- public void setup(IRuntimeManager runtimeManager) {\r
- this.settings = runtimeManager.getSettings();\r
- String file = settings.getString(Keys.realm.ldap.backingUserService, "${baseFolder}/users.conf");\r
- File realmFile = runtimeManager.getFileOrFolder(file);\r
-\r
- serviceImpl = createUserService(realmFile);\r
- logger.info("LDAP User Service backed by " + serviceImpl.toString());\r
-\r
- synchronizeLdapUsers();\r
- }\r
-\r
- protected synchronized void synchronizeLdapUsers() {\r
- final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.enable, false);\r
- if (enabled) {\r
- if (System.currentTimeMillis() > (lastLdapUserSync.get() + getSynchronizationPeriod())) {\r
- logger.info("Synchronizing with LDAP @ " + settings.getRequiredString(Keys.realm.ldap.server));\r
- final boolean deleteRemovedLdapUsers = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.removeDeleted, true);\r
- LDAPConnection ldapConnection = getLdapConnection();\r
- if (ldapConnection != null) {\r
- try {\r
- String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");\r
- String uidAttribute = settings.getString(Keys.realm.ldap.uid, "uid");\r
- String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");\r
- accountPattern = StringUtils.replace(accountPattern, "${username}", "*");\r
-\r
- SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);\r
- if (result != null && result.getEntryCount() > 0) {\r
- final Map<String, UserModel> ldapUsers = new HashMap<String, UserModel>();\r
-\r
- for (SearchResultEntry loggingInUser : result.getSearchEntries()) {\r
-\r
- final String username = loggingInUser.getAttribute(uidAttribute).getValue();\r
- logger.debug("LDAP synchronizing: " + username);\r
-\r
- UserModel user = getUserModel(username);\r
- if (user == null) {\r
- user = new UserModel(username);\r
- }\r
-\r
- if (!supportsTeamMembershipChanges())\r
- getTeamsFromLdap(ldapConnection, username, loggingInUser, user);\r
-\r
- // Get User Attributes\r
- setUserAttributes(user, loggingInUser);\r
-\r
- // store in map\r
- ldapUsers.put(username.toLowerCase(), user);\r
- }\r
-\r
- if (deleteRemovedLdapUsers) {\r
- logger.debug("detecting removed LDAP users...");\r
-\r
- for (UserModel userModel : super.getAllUsers()) {\r
- if (Constants.EXTERNAL_ACCOUNT.equals(userModel.password)) {\r
- if (! ldapUsers.containsKey(userModel.username)) {\r
- logger.info("deleting removed LDAP user " + userModel.username + " from backing user service");\r
- super.deleteUser(userModel.username);\r
- }\r
- }\r
- }\r
- }\r
-\r
- super.updateUserModels(ldapUsers.values());\r
-\r
- if (!supportsTeamMembershipChanges()) {\r
- final Map<String, TeamModel> userTeams = new HashMap<String, TeamModel>();\r
- for (UserModel user : ldapUsers.values()) {\r
- for (TeamModel userTeam : user.teams) {\r
- userTeams.put(userTeam.name, userTeam);\r
- }\r
- }\r
- updateTeamModels(userTeams.values());\r
- }\r
- }\r
- lastLdapUserSync.set(System.currentTimeMillis());\r
- } finally {\r
- ldapConnection.close();\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- private LDAPConnection getLdapConnection() {\r
- try {\r
- \r
- URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));\r
- String ldapHost = ldapUrl.getHost();\r
- int ldapPort = ldapUrl.getPort();\r
- String bindUserName = settings.getString(Keys.realm.ldap.username, "");\r
- String bindPassword = settings.getString(Keys.realm.ldap.password, "");\r
-\r
- \r
- LDAPConnection conn;\r
- if (ldapUrl.getScheme().equalsIgnoreCase("ldaps")) { // SSL\r
- SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());\r
- conn = new LDAPConnection(sslUtil.createSSLSocketFactory());\r
- } else if (ldapUrl.getScheme().equalsIgnoreCase("ldap") || ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) { // no encryption or StartTLS\r
- conn = new LDAPConnection();\r
- } else {\r
- logger.error("Unsupported LDAP URL scheme: " + ldapUrl.getScheme());\r
- return null;\r
- }\r
- \r
- conn.connect(ldapHost, ldapPort);\r
- \r
- if (ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {\r
- SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());\r
- ExtendedResult extendedResult = conn.processExtendedOperation(\r
- new StartTLSExtendedRequest(sslUtil.createSSLContext()));\r
- if (extendedResult.getResultCode() != ResultCode.SUCCESS) {\r
- throw new LDAPException(extendedResult.getResultCode());\r
- }\r
- }\r
-\r
- if ( ! StringUtils.isEmpty(bindUserName) || ! StringUtils.isEmpty(bindPassword)) {\r
- conn.bind(new SimpleBindRequest(bindUserName, bindPassword));\r
- }\r
- \r
- return conn;\r
-\r
- } catch (URISyntaxException e) {\r
- logger.error("Bad LDAP URL, should be in the form: ldap(s|+tls)://<server>:<port>", e);\r
- } catch (GeneralSecurityException e) {\r
- logger.error("Unable to create SSL Connection", e);\r
- } catch (LDAPException e) {\r
- logger.error("Error Connecting to LDAP", e);\r
- }\r
-\r
- return null;\r
- }\r
-\r
- /**\r
- * Credentials are defined in the LDAP server and can not be manipulated\r
- * from Gitblit.\r
- *\r
- * @return false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return false;\r
- }\r
-\r
- /**\r
- * If no displayName pattern is defined then Gitblit can manage the display name.\r
- *\r
- * @return true if Gitblit can manage the user display name\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.displayName, ""));\r
- }\r
-\r
- /**\r
- * If no email pattern is defined then Gitblit can manage the email address.\r
- *\r
- * @return true if Gitblit can manage the user email address\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, ""));\r
- }\r
-\r
-\r
- /**\r
- * If the LDAP server will maintain team memberships then LdapUserService\r
- * will not allow team membership changes. In this scenario all team\r
- * changes must be made on the LDAP server by the LDAP administrator.\r
- *\r
- * @return true or false\r
- * @since 1.0.0\r
- */\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false);\r
- }\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.LDAP;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- if (isLocalAccount(username)) {\r
- // local account, bypass LDAP authentication\r
- return super.authenticate(username, password);\r
- }\r
-\r
- String simpleUsername = getSimpleUsername(username);\r
-\r
- LDAPConnection ldapConnection = getLdapConnection();\r
- if (ldapConnection != null) {\r
- try {\r
- // Find the logging in user's DN\r
- String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");\r
- String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");\r
- accountPattern = StringUtils.replace(accountPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));\r
-\r
- SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);\r
- if (result != null && result.getEntryCount() == 1) {\r
- SearchResultEntry loggingInUser = result.getSearchEntries().get(0);\r
- String loggingInUserDN = loggingInUser.getDN();\r
-\r
- if (isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) {\r
- logger.debug("LDAP authenticated: " + username);\r
-\r
- UserModel user = null;\r
- synchronized (this) {\r
- user = getUserModel(simpleUsername);\r
- if (user == null) // create user object for new authenticated user\r
- user = new UserModel(simpleUsername);\r
-\r
- // create a user cookie\r
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {\r
- user.cookie = StringUtils.getSHA1(user.username + new String(password));\r
- }\r
-\r
- if (!supportsTeamMembershipChanges())\r
- getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);\r
-\r
- // Get User Attributes\r
- setUserAttributes(user, loggingInUser);\r
-\r
- // Push the ldap looked up values to backing file\r
- super.updateUserModel(user);\r
- if (!supportsTeamMembershipChanges()) {\r
- for (TeamModel userTeam : user.teams)\r
- updateTeamModel(userTeam);\r
- }\r
- }\r
-\r
- return user;\r
- }\r
- }\r
- } finally {\r
- ldapConnection.close();\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Set the admin attribute from team memberships retrieved from LDAP.\r
- * If we are not storing teams in LDAP and/or we have not defined any\r
- * administrator teams, then do not change the admin flag.\r
- *\r
- * @param user\r
- */\r
- private void setAdminAttribute(UserModel user) {\r
- if (!supportsTeamMembershipChanges()) {\r
- List<String> admins = settings.getStrings(Keys.realm.ldap.admins);\r
- // if we have defined administrative teams, then set admin flag\r
- // otherwise leave admin flag unchanged\r
- if (!ArrayUtils.isEmpty(admins)) {\r
- user.canAdmin = false;\r
- for (String admin : admins) {\r
- if (admin.startsWith("@")) { // Team\r
- if (user.getTeam(admin.substring(1)) != null)\r
- user.canAdmin = true;\r
- } else\r
- if (user.getName().equalsIgnoreCase(admin))\r
- user.canAdmin = true;\r
- }\r
- }\r
- }\r
- }\r
-\r
- private void setUserAttributes(UserModel user, SearchResultEntry userEntry) {\r
- // Is this user an admin?\r
- setAdminAttribute(user);\r
-\r
- // Don't want visibility into the real password, make up a dummy\r
- user.password = Constants.EXTERNAL_ACCOUNT;\r
- user.accountType = getAccountType();\r
-\r
- // Get full name Attribute\r
- String displayName = settings.getString(Keys.realm.ldap.displayName, "");\r
- if (!StringUtils.isEmpty(displayName)) {\r
- // Replace embedded ${} with attributes\r
- if (displayName.contains("${")) {\r
- for (Attribute userAttribute : userEntry.getAttributes())\r
- displayName = StringUtils.replace(displayName, "${" + userAttribute.getName() + "}", userAttribute.getValue());\r
-\r
- user.displayName = displayName;\r
- } else {\r
- Attribute attribute = userEntry.getAttribute(displayName);\r
- if (attribute != null && attribute.hasValue()) {\r
- user.displayName = attribute.getValue();\r
- }\r
- }\r
- }\r
-\r
- // Get email address Attribute\r
- String email = settings.getString(Keys.realm.ldap.email, "");\r
- if (!StringUtils.isEmpty(email)) {\r
- if (email.contains("${")) {\r
- for (Attribute userAttribute : userEntry.getAttributes())\r
- email = StringUtils.replace(email, "${" + userAttribute.getName() + "}", userAttribute.getValue());\r
-\r
- user.emailAddress = email;\r
- } else {\r
- Attribute attribute = userEntry.getAttribute(email);\r
- if (attribute != null && attribute.hasValue()) {\r
- user.emailAddress = attribute.getValue();\r
- }\r
- }\r
- }\r
- }\r
-\r
- private void getTeamsFromLdap(LDAPConnection ldapConnection, String simpleUsername, SearchResultEntry loggingInUser, UserModel user) {\r
- String loggingInUserDN = loggingInUser.getDN();\r
-\r
- user.teams.clear(); // Clear the users team memberships - we're going to get them from LDAP\r
- String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");\r
- String groupMemberPattern = settings.getString(Keys.realm.ldap.groupMemberPattern, "(&(objectClass=group)(member=${dn}))");\r
-\r
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", escapeLDAPSearchFilter(loggingInUserDN));\r
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));\r
-\r
- // Fill in attributes into groupMemberPattern\r
- for (Attribute userAttribute : loggingInUser.getAttributes())\r
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", escapeLDAPSearchFilter(userAttribute.getValue()));\r
-\r
- SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, true, groupMemberPattern, Arrays.asList("cn"));\r
- if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {\r
- for (int i = 0; i < teamMembershipResult.getEntryCount(); i++) {\r
- SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i);\r
- String teamName = teamEntry.getAttribute("cn").getValue();\r
-\r
- TeamModel teamModel = getTeamModel(teamName);\r
- if (teamModel == null)\r
- teamModel = createTeamFromLdap(teamEntry);\r
-\r
- user.teams.add(teamModel);\r
- teamModel.addUser(user.getName());\r
- }\r
- }\r
- }\r
-\r
- private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) {\r
- TeamModel answer = new TeamModel(teamEntry.getAttributeValue("cn"));\r
- // potentially retrieve other attributes here in the future\r
-\r
- return answer;\r
- }\r
-\r
- private SearchResult doSearch(LDAPConnection ldapConnection, String base, String filter) {\r
- try {\r
- return ldapConnection.search(base, SearchScope.SUB, filter);\r
- } catch (LDAPSearchException e) {\r
- logger.error("Problem Searching LDAP", e);\r
-\r
- return null;\r
- }\r
- }\r
- \r
- private SearchResult doSearch(LDAPConnection ldapConnection, String base, boolean dereferenceAliases, String filter, List<String> attributes) {\r
- try {\r
- SearchRequest searchRequest = new SearchRequest(base, SearchScope.SUB, filter);\r
- if ( dereferenceAliases ) {\r
- searchRequest.setDerefPolicy(DereferencePolicy.SEARCHING);\r
- }\r
- if (attributes != null) {\r
- searchRequest.setAttributes(attributes);\r
- }\r
- return ldapConnection.search(searchRequest);\r
-\r
- } catch (LDAPSearchException e) {\r
- logger.error("Problem Searching LDAP", e);\r
-\r
- return null;\r
- } catch (LDAPException e) {\r
- logger.error("Problem creating LDAP search", e);\r
- return null;\r
- }\r
- }\r
- \r
- private boolean isAuthenticated(LDAPConnection ldapConnection, String userDn, String password) {\r
- try {\r
- // Binding will stop any LDAP-Injection Attacks since the searched-for user needs to bind to that DN\r
- ldapConnection.bind(userDn, password);\r
- return true;\r
- } catch (LDAPException e) {\r
- logger.error("Error authenticating user", e);\r
- return false;\r
- }\r
- }\r
-\r
- @Override\r
- public List<String> getAllUsernames() {\r
- synchronizeLdapUsers();\r
- return super.getAllUsernames();\r
- }\r
-\r
- @Override\r
- public List<UserModel> getAllUsers() {\r
- synchronizeLdapUsers();\r
- return super.getAllUsers();\r
- }\r
-\r
- /**\r
- * Returns a simple username without any domain prefixes.\r
- *\r
- * @param username\r
- * @return a simple username\r
- */\r
- protected String getSimpleUsername(String username) {\r
- int lastSlash = username.lastIndexOf('\\');\r
- if (lastSlash > -1) {\r
- username = username.substring(lastSlash + 1);\r
- }\r
-\r
- return username;\r
- }\r
-\r
- // From: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java\r
- public static final String escapeLDAPSearchFilter(String filter) {\r
- StringBuilder sb = new StringBuilder();\r
- for (int i = 0; i < filter.length(); i++) {\r
- char curChar = filter.charAt(i);\r
- switch (curChar) {\r
- case '\\':\r
- sb.append("\\5c");\r
- break;\r
- case '*':\r
- sb.append("\\2a");\r
- break;\r
- case '(':\r
- sb.append("\\28");\r
- break;\r
- case ')':\r
- sb.append("\\29");\r
- break;\r
- case '\u0000':\r
- sb.append("\\00");\r
- break;\r
- default:\r
- sb.append(curChar);\r
- }\r
- }\r
- return sb.toString();\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2013 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-\r
-import org.jvnet.libpam.PAM;\r
-import org.jvnet.libpam.PAMException;\r
-import org.jvnet.libpam.impl.CLibrary;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccountType;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-/**\r
- * Implementation of a PAM user service for Linux/Unix/MacOSX.\r
- *\r
- * @author James Moger\r
- */\r
-public class PAMUserService extends GitblitUserService {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(PAMUserService.class);\r
-\r
- private IStoredSettings settings;\r
-\r
- public PAMUserService() {\r
- super();\r
- }\r
-\r
- @Override\r
- public void setup(IRuntimeManager runtimeManager) {\r
- this.settings = runtimeManager.getSettings();\r
-\r
- String file = settings.getString(Keys.realm.pam.backingUserService, "${baseFolder}/users.conf");\r
- File realmFile = runtimeManager.getFileOrFolder(file);\r
-\r
- serviceImpl = createUserService(realmFile);\r
- logger.info("PAM User Service backed by " + serviceImpl.toString());\r
-\r
- // Try to identify the passwd database\r
- String [] files = { "/etc/shadow", "/etc/master.passwd" };\r
- File passwdFile = null;\r
- for (String name : files) {\r
- File f = new File(name);\r
- if (f.exists()) {\r
- passwdFile = f;\r
- break;\r
- }\r
- }\r
- if (passwdFile == null) {\r
- logger.error("PAM User Service could not find a passwd database!");\r
- } else if (!passwdFile.canRead()) {\r
- logger.error("PAM User Service can not read passwd database {}! PAM authentications may fail!", passwdFile);\r
- }\r
- }\r
-\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.PAM;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- if (isLocalAccount(username)) {\r
- // local account, bypass PAM authentication\r
- return super.authenticate(username, password);\r
- }\r
-\r
- if (CLibrary.libc.getpwnam(username) == null) {\r
- logger.warn("Can not get PAM passwd for " + username);\r
- return null;\r
- }\r
-\r
- PAM pam = null;\r
- try {\r
- String serviceName = settings.getString(Keys.realm.pam.serviceName, "system-auth");\r
- pam = new PAM(serviceName);\r
- pam.authenticate(username, new String(password));\r
- } catch (PAMException e) {\r
- logger.error(e.getMessage());\r
- return null;\r
- } finally {\r
- pam.dispose();\r
- }\r
-\r
- UserModel user = getUserModel(username);\r
- if (user == null) // create user object for new authenticated user\r
- user = new UserModel(username.toLowerCase());\r
-\r
- // create a user cookie\r
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {\r
- user.cookie = StringUtils.getSHA1(user.username + new String(password));\r
- }\r
-\r
- // update user attributes from UnixUser\r
- user.accountType = getAccountType();\r
- user.password = Constants.EXTERNAL_ACCOUNT;\r
-\r
- // TODO consider mapping PAM groups to teams\r
-\r
- // push the changes to the backing user service\r
- super.updateUserModel(user);\r
-\r
- return user;\r
- }\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2012 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.InputStreamReader;\r
-import java.net.HttpURLConnection;\r
-\r
-import org.apache.wicket.util.io.IOUtils;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccountType;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.ConnectionUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.google.gson.Gson;\r
-\r
-/**\r
- * Implementation of an Redmine user service.<br>\r
- * you can login to gitblit with Redmine user id and api key.\r
- */\r
-public class RedmineUserService extends GitblitUserService {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(RedmineUserService.class);\r
-\r
- private IStoredSettings settings;\r
-\r
- private String testingJson;\r
-\r
- private class RedmineCurrent {\r
- private class RedmineUser {\r
- public String login;\r
- public String firstname;\r
- public String lastname;\r
- public String mail;\r
- }\r
-\r
- public RedmineUser user;\r
- }\r
-\r
- public RedmineUserService() {\r
- super();\r
- }\r
-\r
- @Override\r
- public void setup(IRuntimeManager runtimeManager) {\r
- this.settings = runtimeManager.getSettings();\r
-\r
- String file = settings.getString(Keys.realm.redmine.backingUserService, "${baseFolder}/users.conf");\r
- File realmFile = runtimeManager.getFileOrFolder(file);\r
-\r
- serviceImpl = createUserService(realmFile);\r
- logger.info("Redmine User Service backed by " + serviceImpl.toString());\r
- }\r
-\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.REDMINE;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- if (isLocalAccount(username)) {\r
- // local account, bypass Redmine authentication\r
- return super.authenticate(username, password);\r
- }\r
-\r
- String jsonString = null;\r
- try {\r
- // first attempt by username/password\r
- jsonString = getCurrentUserAsJson(username, password);\r
- } catch (Exception e1) {\r
- logger.warn("Failed to authenticate via username/password against Redmine");\r
- try {\r
- // second attempt is by apikey\r
- jsonString = getCurrentUserAsJson(null, password);\r
- username = null;\r
- } catch (Exception e2) {\r
- logger.error("Failed to authenticate via apikey against Redmine", e2);\r
- return null;\r
- }\r
- }\r
-\r
- if (StringUtils.isEmpty(jsonString)) {\r
- logger.error("Received empty authentication response from Redmine");\r
- return null;\r
- }\r
-\r
- RedmineCurrent current = null;\r
- try {\r
- current = new Gson().fromJson(jsonString, RedmineCurrent.class);\r
- } catch (Exception e) {\r
- logger.error("Failed to deserialize Redmine json response: " + jsonString, e);\r
- return null;\r
- }\r
-\r
- if (StringUtils.isEmpty(username)) {\r
- // if the username has been reset because of apikey authentication\r
- // then use the email address of the user. this is the original\r
- // behavior as contributed by github/mallowlabs\r
- username = current.user.mail;\r
- }\r
-\r
- UserModel user = getUserModel(username);\r
- if (user == null) // create user object for new authenticated user\r
- user = new UserModel(username.toLowerCase());\r
-\r
- // create a user cookie\r
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {\r
- user.cookie = StringUtils.getSHA1(user.username + new String(password));\r
- }\r
-\r
- // update user attributes from Redmine\r
- user.accountType = getAccountType();\r
- user.displayName = current.user.firstname + " " + current.user.lastname;\r
- user.emailAddress = current.user.mail;\r
- user.password = Constants.EXTERNAL_ACCOUNT;\r
- if (!StringUtils.isEmpty(current.user.login)) {\r
- // only admin users can get login name\r
- // evidently this is an undocumented behavior of Redmine\r
- user.canAdmin = true;\r
- }\r
-\r
- // TODO consider Redmine group mapping for team membership\r
- // http://www.redmine.org/projects/redmine/wiki/Rest_Users\r
-\r
- // push the changes to the backing user service\r
- super.updateUserModel(user);\r
-\r
- return user;\r
- }\r
-\r
- private String getCurrentUserAsJson(String username, char [] password) throws IOException {\r
- if (testingJson != null) { // for testing\r
- return testingJson;\r
- }\r
-\r
- String url = this.settings.getString(Keys.realm.redmine.url, "");\r
- if (!url.endsWith("/")) {\r
- url = url.concat("/");\r
- }\r
- HttpURLConnection http;\r
- if (username == null) {\r
- // apikey authentication\r
- String apiKey = String.valueOf(password);\r
- String apiUrl = url + "users/current.json?key=" + apiKey;\r
- http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, null, null);\r
- } else {\r
- // username/password BASIC authentication\r
- String apiUrl = url + "users/current.json";\r
- http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, username, password);\r
- }\r
- http.setRequestMethod("GET");\r
- http.connect();\r
- InputStreamReader reader = new InputStreamReader(http.getInputStream());\r
- return IOUtils.toString(reader);\r
- }\r
-\r
- /**\r
- * set json response. do NOT invoke from production code.\r
- * @param json json\r
- */\r
- public void setTestingCurrentUserAsJson(String json) {\r
- this.testingJson = json;\r
- }\r
-}\r
+++ /dev/null
-package com.gitblit;
-
-import java.io.File;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants.AccountType;
-import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.StringUtils;
-import com.sforce.soap.partner.Connector;
-import com.sforce.soap.partner.GetUserInfoResult;
-import com.sforce.soap.partner.PartnerConnection;
-import com.sforce.ws.ConnectionException;
-import com.sforce.ws.ConnectorConfig;
-
-public class SalesforceUserService extends GitblitUserService {
-
- public static final Logger logger = LoggerFactory.getLogger(SalesforceUserService.class);
- private IStoredSettings settings;
-
- @Override
- public AccountType getAccountType() {
- return AccountType.SALESFORCE;
- }
-
- @Override
- public void setup(IRuntimeManager runtimeManager) {
- this.settings = runtimeManager.getSettings();
- String file = settings.getString(
- Keys.realm.salesforce.backingUserService,
- "${baseFolder}/users.conf");
- File realmFile = runtimeManager.getFileOrFolder(file);
-
- serviceImpl = createUserService(realmFile);
-
- logger.info("Salesforce User Service backed by "
- + serviceImpl.toString());
- }
-
- @Override
- public UserModel authenticate(String username, char[] password) {
- if (isLocalAccount(username)) {
- // local account, bypass Salesforce authentication
- return super.authenticate(username, password);
- }
-
- ConnectorConfig config = new ConnectorConfig();
- config.setUsername(username);
- config.setPassword(new String(password));
-
- try {
- PartnerConnection connection = Connector.newConnection(config);
-
- GetUserInfoResult info = connection.getUserInfo();
-
- String org = settings.getString(Keys.realm.salesforce.orgId, "0")
- .trim();
-
- if (!org.equals("0")) {
- if (!org.equals(info.getOrganizationId())) {
- logger.warn("Access attempted by user of an invalid org: "
- + info.getUserName() + ", org: "
- + info.getOrganizationName() + "("
- + info.getOrganizationId() + ")");
-
- return null;
- }
- }
-
- logger.info("Authenticated user " + info.getUserName()
- + " using org " + info.getOrganizationName() + "("
- + info.getOrganizationId() + ")");
-
- String simpleUsername = getSimpleUsername(info);
-
- UserModel user = null;
- synchronized (this) {
- user = getUserModel(simpleUsername);
- if (user == null)
- user = new UserModel(simpleUsername);
-
- if (StringUtils.isEmpty(user.cookie)
- && !ArrayUtils.isEmpty(password)) {
- user.cookie = StringUtils.getSHA1(user.username
- + new String(password));
- }
-
- setUserAttributes(user, info);
-
- super.updateUserModel(user);
- }
-
- return user;
- } catch (ConnectionException e) {
- logger.error("Failed to authenticate", e);
- }
-
- return null;
- }
-
- private void setUserAttributes(UserModel user, GetUserInfoResult info) {
- // Don't want visibility into the real password, make up a dummy
- user.password = Constants.EXTERNAL_ACCOUNT;
- user.accountType = getAccountType();
-
- // Get full name Attribute
- user.displayName = info.getUserFullName();
-
- // Get email address Attribute
- user.emailAddress = info.getUserEmail();
- }
-
- /**
- * Simple user name is the first part of the email address.
- */
- private String getSimpleUsername(GetUserInfoResult info) {
- String email = info.getUserEmail();
-
- return email.split("@")[0];
- }
-
- @Override
- public boolean supportsCredentialChanges() {
- return false;
- }
-
- @Override
- public boolean supportsDisplayNameChanges() {
- return false;
- }
-
- @Override
- public boolean supportsEmailAddressChanges() {
- return false;
- }
-}
+++ /dev/null
-/*\r
- * Copyright 2013 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import waffle.windows.auth.IWindowsAccount;\r
-import waffle.windows.auth.IWindowsAuthProvider;\r
-import waffle.windows.auth.IWindowsComputer;\r
-import waffle.windows.auth.IWindowsIdentity;\r
-import waffle.windows.auth.impl.WindowsAuthProviderImpl;\r
-\r
-import com.gitblit.Constants.AccountType;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.StringUtils;\r
-import com.sun.jna.platform.win32.Win32Exception;\r
-\r
-/**\r
- * Implementation of a Windows user service.\r
- *\r
- * @author James Moger\r
- */\r
-public class WindowsUserService extends GitblitUserService {\r
-\r
- private final Logger logger = LoggerFactory.getLogger(WindowsUserService.class);\r
-\r
- private IStoredSettings settings;\r
-\r
- private IWindowsAuthProvider waffle;\r
-\r
- public WindowsUserService() {\r
- super();\r
- }\r
-\r
- @Override\r
- public void setup(IRuntimeManager runtimeManager) {\r
- this.settings = runtimeManager.getSettings();\r
-\r
- String file = settings.getString(Keys.realm.windows.backingUserService, "${baseFolder}/users.conf");\r
- File realmFile = runtimeManager.getFileOrFolder(file);\r
-\r
- serviceImpl = createUserService(realmFile);\r
- logger.info("Windows User Service backed by " + serviceImpl.toString());\r
-\r
- waffle = new WindowsAuthProviderImpl();\r
- IWindowsComputer computer = waffle.getCurrentComputer();\r
- logger.info(" name = " + computer.getComputerName());\r
- logger.info(" status = " + describeJoinStatus(computer.getJoinStatus()));\r
- logger.info(" memberOf = " + computer.getMemberOf());\r
- //logger.info(" groups = " + Arrays.asList(computer.getGroups()));\r
- }\r
-\r
- protected String describeJoinStatus(String value) {\r
- if ("NetSetupUnknownStatus".equals(value)) {\r
- return "unknown";\r
- } else if ("NetSetupUnjoined".equals(value)) {\r
- return "not joined";\r
- } else if ("NetSetupWorkgroupName".equals(value)) {\r
- return "joined to a workgroup";\r
- } else if ("NetSetupDomainName".equals(value)) {\r
- return "joined to a domain";\r
- }\r
- return value;\r
- }\r
-\r
- @Override\r
- public boolean supportsCredentialChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsDisplayNameChanges() {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean supportsEmailAddressChanges() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public boolean supportsTeamMembershipChanges() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public AccountType getAccountType() {\r
- return AccountType.WINDOWS;\r
- }\r
-\r
- @Override\r
- public UserModel authenticate(String username, char[] password) {\r
- if (isLocalAccount(username)) {\r
- // local account, bypass Windows authentication\r
- return super.authenticate(username, password);\r
- }\r
-\r
- String defaultDomain = settings.getString(Keys.realm.windows.defaultDomain, null);\r
- if (StringUtils.isEmpty(defaultDomain)) {\r
- // ensure that default domain is null\r
- defaultDomain = null;\r
- }\r
-\r
- if (defaultDomain != null) {\r
- // sanitize username\r
- if (username.startsWith(defaultDomain + "\\")) {\r
- // strip default domain from domain\ username\r
- username = username.substring(defaultDomain.length() + 1);\r
- } else if (username.endsWith("@" + defaultDomain)) {\r
- // strip default domain from username@domain\r
- username = username.substring(0, username.lastIndexOf('@'));\r
- }\r
- }\r
-\r
- IWindowsIdentity identity = null;\r
- try {\r
- if (username.indexOf('@') > -1 || username.indexOf('\\') > -1) {\r
- // manually specified domain\r
- identity = waffle.logonUser(username, new String(password));\r
- } else {\r
- // no domain specified, use default domain\r
- identity = waffle.logonDomainUser(username, defaultDomain, new String(password));\r
- }\r
- } catch (Win32Exception e) {\r
- logger.error(e.getMessage());\r
- return null;\r
- }\r
-\r
- if (identity.isGuest() && !settings.getBoolean(Keys.realm.windows.allowGuests, false)) {\r
- logger.warn("Guest account access is disabled");\r
- identity.dispose();\r
- return null;\r
- }\r
-\r
- UserModel user = getUserModel(username);\r
- if (user == null) // create user object for new authenticated user\r
- user = new UserModel(username.toLowerCase());\r
-\r
- // create a user cookie\r
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {\r
- user.cookie = StringUtils.getSHA1(user.username + new String(password));\r
- }\r
-\r
- // update user attributes from Windows identity\r
- user.accountType = getAccountType();\r
- String fqn = identity.getFqn();\r
- if (fqn.indexOf('\\') > -1) {\r
- user.displayName = fqn.substring(fqn.lastIndexOf('\\') + 1);\r
- } else {\r
- user.displayName = fqn;\r
- }\r
- user.password = Constants.EXTERNAL_ACCOUNT;\r
-\r
- Set<String> groupNames = new TreeSet<String>();\r
- for (IWindowsAccount group : identity.getGroups()) {\r
- groupNames.add(group.getFqn());\r
- }\r
-\r
- if (groupNames.contains("BUILTIN\\Administrators")) {\r
- // local administrator\r
- user.canAdmin = true;\r
- }\r
-\r
- // TODO consider mapping Windows groups to teams\r
-\r
- // push the changes to the backing user service\r
- super.updateUserModel(user);\r
-\r
-\r
- // cleanup resources\r
- identity.dispose();\r
-\r
- return user;\r
- }\r
-}\r
--- /dev/null
+/*
+ * 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.auth;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants.AccountType;
+import com.gitblit.IStoredSettings;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+
+public abstract class AuthenticationProvider {
+
+ public static NullProvider NULL_PROVIDER = new NullProvider();
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected final String serviceName;
+
+ protected File baseFolder;
+
+ protected IStoredSettings settings;
+
+ protected IRuntimeManager runtimeManager;
+
+ protected IUserManager userManager;
+
+ protected AuthenticationProvider(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ /**
+ * Returns the file object for the specified configuration key.
+ *
+ * @return the file
+ */
+ public File getFileOrFolder(String key, String defaultFileOrFolder) {
+ return runtimeManager.getFileOrFolder(key, defaultFileOrFolder);
+ }
+
+ public final void setup(IRuntimeManager runtimeManager, IUserManager userManager) {
+ this.baseFolder = runtimeManager.getBaseFolder();
+ this.settings = runtimeManager.getSettings();
+ this.runtimeManager = runtimeManager;
+ this.userManager = userManager;
+ setup();
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ protected void updateUser(UserModel userModel) {
+ // TODO implement user model change detection
+ // account for new user and revised user
+
+ // username
+ // displayname
+ // email address
+ // cookie
+
+ userManager.updateUserModel(userModel);
+ }
+
+ protected void updateTeam(TeamModel teamModel) {
+ // TODO implement team model change detection
+ // account for new team and revised team
+
+ // memberships
+
+ userManager.updateTeamModel(teamModel);
+ }
+
+ public abstract void setup();
+
+ public abstract UserModel authenticate(String username, char[] password);
+
+ public abstract AccountType getAccountType();
+
+ /**
+ * Does the user service support changes to credentials?
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ public abstract boolean supportsCredentialChanges();
+
+ /**
+ * Returns true if the user's display name can be changed.
+ *
+ * @param user
+ * @return true if the user service supports display name changes
+ */
+ public abstract boolean supportsDisplayNameChanges();
+
+ /**
+ * Returns true if the user's email address can be changed.
+ *
+ * @param user
+ * @return true if the user service supports email address changes
+ */
+ public abstract boolean supportsEmailAddressChanges();
+
+ /**
+ * Returns true if the user's team memberships can be changed.
+ *
+ * @param user
+ * @return true if the user service supports team membership changes
+ */
+ public abstract boolean supportsTeamMembershipChanges();
+
+ @Override
+ public String toString() {
+ return getServiceName() + " (" + getClass().getName() + ")";
+ }
+
+ public abstract static class UsernamePasswordAuthenticationProvider extends AuthenticationProvider {
+ protected UsernamePasswordAuthenticationProvider(String serviceName) {
+ super(serviceName);
+ }
+ }
+
+ public static class NullProvider extends AuthenticationProvider {
+
+ protected NullProvider() {
+ super("NULL");
+ }
+
+ @Override
+ public void setup() {
+
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ return null;
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.LOCAL;
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return false;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright 2013 Florian Zschocke
+ * 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.auth;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.Crypt;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.codec.digest.Md5Crypt;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+
+
+/**
+ * Implementation of a user service using an Apache htpasswd file for authentication.
+ *
+ * This user service implement custom authentication using entries in a file created
+ * by the 'htpasswd' program of an Apache web server. All possible output
+ * options of the 'htpasswd' program version 2.2 are supported:
+ * plain text (only on Windows and Netware),
+ * glibc crypt() (not on Windows and NetWare),
+ * Apache MD5 (apr1),
+ * unsalted SHA-1.
+ *
+ * Configuration options:
+ * realm.htpasswd.backingUserService - Specify the backing user service that is used
+ * to keep the user data other than the password.
+ * The default is '${baseFolder}/users.conf'.
+ * realm.htpasswd.userfile - The text file with the htpasswd entries to be used for
+ * authentication.
+ * The default is '${baseFolder}/htpasswd'.
+ * realm.htpasswd.overrideLocalAuthentication - Specify if local accounts are overwritten
+ * when authentication matches for an
+ * external account.
+ *
+ * @author Florian Zschocke
+ *
+ */
+public class HtpasswdAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ private static final String KEY_HTPASSWD_FILE = Keys.realm.htpasswd.userfile;
+ private static final String DEFAULT_HTPASSWD_FILE = "${baseFolder}/htpasswd";
+
+ private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
+
+ private boolean supportPlainTextPwd;
+
+ private File htpasswdFile;
+
+ private final Map<String, String> htUsers = new ConcurrentHashMap<String, String>();
+
+ private volatile long lastModified;
+
+ public HtpasswdAuthProvider() {
+ super("htpasswd");
+ }
+
+ /**
+ * Setup the user service.
+ *
+ * The HtpasswdUserService extends the GitblitUserService and is thus
+ * backed by the available user services provided by the GitblitUserService.
+ * In addition the setup tries to read and parse the htpasswd file to be used
+ * for authentication.
+ *
+ * @param settings
+ * @since 0.7.0
+ */
+ @Override
+ public void setup() {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.startsWith("windows") || os.startsWith("netware")) {
+ supportPlainTextPwd = true;
+ } else {
+ supportPlainTextPwd = false;
+ }
+ read();
+ logger.debug("Read " + htUsers.size() + " users from htpasswd file: " + this.htpasswdFile);
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return true;
+ }
+
+ /**
+ * Authenticate a user based on a username and password.
+ *
+ * If the account is determined to be a local account, authentication
+ * will be done against the locally stored password.
+ * Otherwise, the configured htpasswd file is read. All current output options
+ * of htpasswd are supported: clear text, crypt(), Apache MD5 and unsalted SHA-1.
+ *
+ * @param username
+ * @param password
+ * @return a user object or null
+ */
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ read();
+ String storedPwd = htUsers.get(username);
+ if (storedPwd != null) {
+ boolean authenticated = false;
+ final String passwd = new String(password);
+
+ // test Apache MD5 variant encrypted password
+ if (storedPwd.startsWith("$apr1$")) {
+ if (storedPwd.equals(Md5Crypt.apr1Crypt(passwd, storedPwd))) {
+ logger.debug("Apache MD5 encoded password matched for user '" + username + "'");
+ authenticated = true;
+ }
+ }
+ // test unsalted SHA password
+ else if (storedPwd.startsWith("{SHA}")) {
+ String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(passwd));
+ if (storedPwd.substring("{SHA}".length()).equals(passwd64)) {
+ logger.debug("Unsalted SHA-1 encoded password matched for user '" + username + "'");
+ authenticated = true;
+ }
+ }
+ // test libc crypt() encoded password
+ else if (supportCryptPwd() && storedPwd.equals(Crypt.crypt(passwd, storedPwd))) {
+ logger.debug("Libc crypt encoded password matched for user '" + username + "'");
+ authenticated = true;
+ }
+ // test clear text
+ else if (supportPlaintextPwd() && storedPwd.equals(passwd)){
+ logger.debug("Clear text password matched for user '" + username + "'");
+ authenticated = true;
+ }
+
+
+ if (authenticated) {
+ logger.debug("Htpasswd authenticated: " + username);
+
+ UserModel curr = userManager.getUserModel(username);
+ UserModel user;
+ if (curr == null) {
+ // create user object for new authenticated user
+ user = new UserModel(username);
+ } else {
+ user = curr;
+ }
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + passwd);
+ }
+
+ // Set user attributes, hide password from backing user service.
+ user.password = Constants.EXTERNAL_ACCOUNT;
+ user.accountType = getAccountType();
+
+ // Push the looked up values to backing file
+ updateUser(user);
+
+ return user;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the account type used for this user service.
+ *
+ * @return AccountType.HTPASSWD
+ */
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.HTPASSWD;
+ }
+
+ /**
+ * Reads the realm file and rebuilds the in-memory lookup tables.
+ */
+ protected synchronized void read() {
+ boolean forceReload = false;
+ File file = getFileOrFolder(KEY_HTPASSWD_FILE, DEFAULT_HTPASSWD_FILE);
+ if (!file.equals(htpasswdFile)) {
+ this.htpasswdFile = file;
+ this.htUsers.clear();
+ forceReload = true;
+ }
+
+ if (htpasswdFile.exists() && (forceReload || (htpasswdFile.lastModified() != lastModified))) {
+ lastModified = htpasswdFile.lastModified();
+ htUsers.clear();
+
+ Pattern entry = Pattern.compile("^([^:]+):(.+)");
+
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(new FileInputStream(htpasswdFile));
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine().trim();
+ if (!line.isEmpty() && !line.startsWith("#")) {
+ Matcher m = entry.matcher(line);
+ if (m.matches()) {
+ htUsers.put(m.group(1), m.group(2));
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(MessageFormat.format("Failed to read {0}", htpasswdFile), e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ }
+ }
+
+ private boolean supportPlaintextPwd() {
+ return this.settings.getBoolean(KEY_SUPPORT_PLAINTEXT_PWD, supportPlainTextPwd);
+ }
+
+ private boolean supportCryptPwd() {
+ return !supportPlaintextPwd();
+ }
+
+ /*
+ * Method only used for unit tests. Return number of users read from htpasswd file.
+ */
+ public int getNumberHtpasswdUsers() {
+ return this.htUsers.size();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + ((htpasswdFile != null) ? htpasswdFile.getAbsolutePath() : "null") + ")";
+ }
+}
--- /dev/null
+/*
+ * Copyright 2012 John Crygier
+ * Copyright 2012 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.auth;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.unboundid.ldap.sdk.Attribute;
+import com.unboundid.ldap.sdk.DereferencePolicy;
+import com.unboundid.ldap.sdk.ExtendedResult;
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+import com.unboundid.ldap.sdk.LDAPSearchException;
+import com.unboundid.ldap.sdk.ResultCode;
+import com.unboundid.ldap.sdk.SearchRequest;
+import com.unboundid.ldap.sdk.SearchResult;
+import com.unboundid.ldap.sdk.SearchResultEntry;
+import com.unboundid.ldap.sdk.SearchScope;
+import com.unboundid.ldap.sdk.SimpleBindRequest;
+import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
+import com.unboundid.util.ssl.SSLUtil;
+import com.unboundid.util.ssl.TrustAllTrustManager;
+
+/**
+ * Implementation of an LDAP user service.
+ *
+ * @author John Crygier
+ */
+public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ private AtomicLong lastLdapUserSync = new AtomicLong(0L);
+
+ public LdapAuthProvider() {
+ super("ldap");
+ }
+
+ private long getSynchronizationPeriod() {
+ final String cacheDuration = settings.getString(Keys.realm.ldap.ldapCachePeriod, "2 MINUTES");
+ try {
+ final String[] s = cacheDuration.split(" ", 2);
+ long duration = Long.parseLong(s[0]);
+ TimeUnit timeUnit = TimeUnit.valueOf(s[1]);
+ return timeUnit.toMillis(duration);
+ } catch (RuntimeException ex) {
+ throw new IllegalArgumentException(Keys.realm.ldap.ldapCachePeriod + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'");
+ }
+ }
+
+ @Override
+ public void setup() {
+ synchronizeLdapUsers();
+ }
+
+ protected synchronized void synchronizeLdapUsers() {
+ final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.enable, false);
+ if (enabled) {
+ if (System.currentTimeMillis() > (lastLdapUserSync.get() + getSynchronizationPeriod())) {
+ logger.info("Synchronizing with LDAP @ " + settings.getRequiredString(Keys.realm.ldap.server));
+ final boolean deleteRemovedLdapUsers = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.removeDeleted, true);
+ LDAPConnection ldapConnection = getLdapConnection();
+ if (ldapConnection != null) {
+ try {
+ String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
+ String uidAttribute = settings.getString(Keys.realm.ldap.uid, "uid");
+ String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
+ accountPattern = StringUtils.replace(accountPattern, "${username}", "*");
+
+ SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
+ if (result != null && result.getEntryCount() > 0) {
+ final Map<String, UserModel> ldapUsers = new HashMap<String, UserModel>();
+
+ for (SearchResultEntry loggingInUser : result.getSearchEntries()) {
+
+ final String username = loggingInUser.getAttribute(uidAttribute).getValue();
+ logger.debug("LDAP synchronizing: " + username);
+
+ UserModel user = userManager.getUserModel(username);
+ if (user == null) {
+ user = new UserModel(username);
+ }
+
+ if (!supportsTeamMembershipChanges()) {
+ getTeamsFromLdap(ldapConnection, username, loggingInUser, user);
+ }
+
+ // Get User Attributes
+ setUserAttributes(user, loggingInUser);
+
+ // store in map
+ ldapUsers.put(username.toLowerCase(), user);
+ }
+
+ if (deleteRemovedLdapUsers) {
+ logger.debug("detecting removed LDAP users...");
+
+ for (UserModel userModel : userManager.getAllUsers()) {
+ if (Constants.EXTERNAL_ACCOUNT.equals(userModel.password)) {
+ if (!ldapUsers.containsKey(userModel.username)) {
+ logger.info("deleting removed LDAP user " + userModel.username + " from user service");
+ userManager.deleteUser(userModel.username);
+ }
+ }
+ }
+ }
+
+ userManager.updateUserModels(ldapUsers.values());
+
+ if (!supportsTeamMembershipChanges()) {
+ final Map<String, TeamModel> userTeams = new HashMap<String, TeamModel>();
+ for (UserModel user : ldapUsers.values()) {
+ for (TeamModel userTeam : user.teams) {
+ userTeams.put(userTeam.name, userTeam);
+ }
+ }
+ userManager.updateTeamModels(userTeams.values());
+ }
+ }
+ lastLdapUserSync.set(System.currentTimeMillis());
+ } finally {
+ ldapConnection.close();
+ }
+ }
+ }
+ }
+ }
+
+ private LDAPConnection getLdapConnection() {
+ try {
+
+ URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));
+ String ldapHost = ldapUrl.getHost();
+ int ldapPort = ldapUrl.getPort();
+ String bindUserName = settings.getString(Keys.realm.ldap.username, "");
+ String bindPassword = settings.getString(Keys.realm.ldap.password, "");
+
+
+ LDAPConnection conn;
+ if (ldapUrl.getScheme().equalsIgnoreCase("ldaps")) {
+ // SSL
+ SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
+ conn = new LDAPConnection(sslUtil.createSSLSocketFactory());
+ } else if (ldapUrl.getScheme().equalsIgnoreCase("ldap") || ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
+ // no encryption or StartTLS
+ conn = new LDAPConnection();
+ } else {
+ logger.error("Unsupported LDAP URL scheme: " + ldapUrl.getScheme());
+ return null;
+ }
+
+ conn.connect(ldapHost, ldapPort);
+
+ if (ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
+ SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
+ ExtendedResult extendedResult = conn.processExtendedOperation(
+ new StartTLSExtendedRequest(sslUtil.createSSLContext()));
+ if (extendedResult.getResultCode() != ResultCode.SUCCESS) {
+ throw new LDAPException(extendedResult.getResultCode());
+ }
+ }
+
+ if (!StringUtils.isEmpty(bindUserName) || !StringUtils.isEmpty(bindPassword)) {
+ conn.bind(new SimpleBindRequest(bindUserName, bindPassword));
+ }
+
+ return conn;
+
+ } catch (URISyntaxException e) {
+ logger.error("Bad LDAP URL, should be in the form: ldap(s|+tls)://<server>:<port>", e);
+ } catch (GeneralSecurityException e) {
+ logger.error("Unable to create SSL Connection", e);
+ } catch (LDAPException e) {
+ logger.error("Error Connecting to LDAP", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Credentials are defined in the LDAP server and can not be manipulated
+ * from Gitblit.
+ *
+ * @return false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ /**
+ * If no displayName pattern is defined then Gitblit can manage the display name.
+ *
+ * @return true if Gitblit can manage the user display name
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.displayName, ""));
+ }
+
+ /**
+ * If no email pattern is defined then Gitblit can manage the email address.
+ *
+ * @return true if Gitblit can manage the user email address
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, ""));
+ }
+
+
+ /**
+ * If the LDAP server will maintain team memberships then LdapUserService
+ * will not allow team membership changes. In this scenario all team
+ * changes must be made on the LDAP server by the LDAP administrator.
+ *
+ * @return true or false
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false);
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.LDAP;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ String simpleUsername = getSimpleUsername(username);
+
+ LDAPConnection ldapConnection = getLdapConnection();
+ if (ldapConnection != null) {
+ try {
+ // Find the logging in user's DN
+ String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
+ String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
+ accountPattern = StringUtils.replace(accountPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
+
+ SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
+ if (result != null && result.getEntryCount() == 1) {
+ SearchResultEntry loggingInUser = result.getSearchEntries().get(0);
+ String loggingInUserDN = loggingInUser.getDN();
+
+ if (isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) {
+ logger.debug("LDAP authenticated: " + username);
+
+ UserModel user = null;
+ synchronized (this) {
+ user = userManager.getUserModel(simpleUsername);
+ if (user == null) // create user object for new authenticated user
+ user = new UserModel(simpleUsername);
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ }
+
+ if (!supportsTeamMembershipChanges())
+ getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
+
+ // Get User Attributes
+ setUserAttributes(user, loggingInUser);
+
+ // Push the ldap looked up values to backing file
+ updateUser(user);
+
+ if (!supportsTeamMembershipChanges()) {
+ for (TeamModel userTeam : user.teams)
+ updateTeam(userTeam);
+ }
+ }
+
+ return user;
+ }
+ }
+ } finally {
+ ldapConnection.close();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the admin attribute from team memberships retrieved from LDAP.
+ * If we are not storing teams in LDAP and/or we have not defined any
+ * administrator teams, then do not change the admin flag.
+ *
+ * @param user
+ */
+ private void setAdminAttribute(UserModel user) {
+ if (!supportsTeamMembershipChanges()) {
+ List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
+ // if we have defined administrative teams, then set admin flag
+ // otherwise leave admin flag unchanged
+ if (!ArrayUtils.isEmpty(admins)) {
+ user.canAdmin = false;
+ for (String admin : admins) {
+ if (admin.startsWith("@")) { // Team
+ if (user.getTeam(admin.substring(1)) != null)
+ user.canAdmin = true;
+ } else
+ if (user.getName().equalsIgnoreCase(admin))
+ user.canAdmin = true;
+ }
+ }
+ }
+ }
+
+ private void setUserAttributes(UserModel user, SearchResultEntry userEntry) {
+ // Is this user an admin?
+ setAdminAttribute(user);
+
+ // Don't want visibility into the real password, make up a dummy
+ user.password = Constants.EXTERNAL_ACCOUNT;
+ user.accountType = getAccountType();
+
+ // Get full name Attribute
+ String displayName = settings.getString(Keys.realm.ldap.displayName, "");
+ if (!StringUtils.isEmpty(displayName)) {
+ // Replace embedded ${} with attributes
+ if (displayName.contains("${")) {
+ for (Attribute userAttribute : userEntry.getAttributes())
+ displayName = StringUtils.replace(displayName, "${" + userAttribute.getName() + "}", userAttribute.getValue());
+
+ user.displayName = displayName;
+ } else {
+ Attribute attribute = userEntry.getAttribute(displayName);
+ if (attribute != null && attribute.hasValue()) {
+ user.displayName = attribute.getValue();
+ }
+ }
+ }
+
+ // Get email address Attribute
+ String email = settings.getString(Keys.realm.ldap.email, "");
+ if (!StringUtils.isEmpty(email)) {
+ if (email.contains("${")) {
+ for (Attribute userAttribute : userEntry.getAttributes())
+ email = StringUtils.replace(email, "${" + userAttribute.getName() + "}", userAttribute.getValue());
+
+ user.emailAddress = email;
+ } else {
+ Attribute attribute = userEntry.getAttribute(email);
+ if (attribute != null && attribute.hasValue()) {
+ user.emailAddress = attribute.getValue();
+ }
+ }
+ }
+ }
+
+ private void getTeamsFromLdap(LDAPConnection ldapConnection, String simpleUsername, SearchResultEntry loggingInUser, UserModel user) {
+ String loggingInUserDN = loggingInUser.getDN();
+
+ user.teams.clear(); // Clear the users team memberships - we're going to get them from LDAP
+ String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");
+ String groupMemberPattern = settings.getString(Keys.realm.ldap.groupMemberPattern, "(&(objectClass=group)(member=${dn}))");
+
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", escapeLDAPSearchFilter(loggingInUserDN));
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
+
+ // Fill in attributes into groupMemberPattern
+ for (Attribute userAttribute : loggingInUser.getAttributes()) {
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", escapeLDAPSearchFilter(userAttribute.getValue()));
+ }
+
+ SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, true, groupMemberPattern, Arrays.asList("cn"));
+ if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {
+ for (int i = 0; i < teamMembershipResult.getEntryCount(); i++) {
+ SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i);
+ String teamName = teamEntry.getAttribute("cn").getValue();
+
+ TeamModel teamModel = userManager.getTeamModel(teamName);
+ if (teamModel == null) {
+ teamModel = createTeamFromLdap(teamEntry);
+ }
+
+ user.teams.add(teamModel);
+ teamModel.addUser(user.getName());
+ }
+ }
+ }
+
+ private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) {
+ TeamModel answer = new TeamModel(teamEntry.getAttributeValue("cn"));
+ answer.accountType = getAccountType();
+ // potentially retrieve other attributes here in the future
+
+ return answer;
+ }
+
+ private SearchResult doSearch(LDAPConnection ldapConnection, String base, String filter) {
+ try {
+ return ldapConnection.search(base, SearchScope.SUB, filter);
+ } catch (LDAPSearchException e) {
+ logger.error("Problem Searching LDAP", e);
+
+ return null;
+ }
+ }
+
+ private SearchResult doSearch(LDAPConnection ldapConnection, String base, boolean dereferenceAliases, String filter, List<String> attributes) {
+ try {
+ SearchRequest searchRequest = new SearchRequest(base, SearchScope.SUB, filter);
+ if (dereferenceAliases) {
+ searchRequest.setDerefPolicy(DereferencePolicy.SEARCHING);
+ }
+ if (attributes != null) {
+ searchRequest.setAttributes(attributes);
+ }
+ return ldapConnection.search(searchRequest);
+
+ } catch (LDAPSearchException e) {
+ logger.error("Problem Searching LDAP", e);
+
+ return null;
+ } catch (LDAPException e) {
+ logger.error("Problem creating LDAP search", e);
+ return null;
+ }
+ }
+
+ private boolean isAuthenticated(LDAPConnection ldapConnection, String userDn, String password) {
+ try {
+ // Binding will stop any LDAP-Injection Attacks since the searched-for user needs to bind to that DN
+ ldapConnection.bind(userDn, password);
+ return true;
+ } catch (LDAPException e) {
+ logger.error("Error authenticating user", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns a simple username without any domain prefixes.
+ *
+ * @param username
+ * @return a simple username
+ */
+ protected String getSimpleUsername(String username) {
+ int lastSlash = username.lastIndexOf('\\');
+ if (lastSlash > -1) {
+ username = username.substring(lastSlash + 1);
+ }
+
+ return username;
+ }
+
+ // From: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java
+ public static final String escapeLDAPSearchFilter(String filter) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < filter.length(); i++) {
+ char curChar = filter.charAt(i);
+ switch (curChar) {
+ case '\\':
+ sb.append("\\5c");
+ break;
+ case '*':
+ sb.append("\\2a");
+ break;
+ case '(':
+ sb.append("\\28");
+ break;
+ case ')':
+ sb.append("\\29");
+ break;
+ case '\u0000':
+ sb.append("\\00");
+ break;
+ default:
+ sb.append(curChar);
+ }
+ }
+ return sb.toString();
+ }
+}
--- /dev/null
+/*
+ * 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.auth;
+
+import java.io.File;
+
+import org.jvnet.libpam.PAM;
+import org.jvnet.libpam.PAMException;
+import org.jvnet.libpam.impl.CLibrary;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Implementation of PAM authentication for Linux/Unix/MacOSX.
+ *
+ * @author James Moger
+ */
+public class PAMAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ public PAMAuthProvider() {
+ super("pam");
+ }
+
+ @Override
+ public void setup() {
+ // Try to identify the passwd database
+ String [] files = { "/etc/shadow", "/etc/master.passwd" };
+ File passwdFile = null;
+ for (String name : files) {
+ File f = new File(name);
+ if (f.exists()) {
+ passwdFile = f;
+ break;
+ }
+ }
+ if (passwdFile == null) {
+ logger.error("PAM Authentication could not find a passwd database!");
+ } else if (!passwdFile.canRead()) {
+ logger.error("PAM Authentication can not read passwd database {}! PAM authentications may fail!", passwdFile);
+ }
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return true;
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.PAM;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ if (CLibrary.libc.getpwnam(username) == null) {
+ logger.warn("Can not get PAM passwd for " + username);
+ return null;
+ }
+
+ PAM pam = null;
+ try {
+ String serviceName = settings.getString(Keys.realm.pam.serviceName, "system-auth");
+ pam = new PAM(serviceName);
+ pam.authenticate(username, new String(password));
+ } catch (PAMException e) {
+ logger.error(e.getMessage());
+ return null;
+ } finally {
+ pam.dispose();
+ }
+
+ UserModel user = userManager.getUserModel(username);
+ if (user == null) // create user object for new authenticated user
+ user = new UserModel(username.toLowerCase());
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ }
+
+ // update user attributes from UnixUser
+ user.accountType = getAccountType();
+ user.password = Constants.EXTERNAL_ACCOUNT;
+
+ // TODO consider mapping PAM groups to teams
+
+ // push the changes to the backing user service
+ updateUser(user);
+
+ return user;
+ }
+}
--- /dev/null
+/*
+ * Copyright 2012 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.auth;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+
+import org.apache.wicket.util.io.IOUtils;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.ConnectionUtils;
+import com.gitblit.utils.StringUtils;
+import com.google.gson.Gson;
+
+/**
+ * Implementation of Redmine authentication.<br>
+ * you can login to gitblit with Redmine user id and api key.
+ */
+public class RedmineAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ private String testingJson;
+
+ private class RedmineCurrent {
+ private class RedmineUser {
+ public String login;
+ public String firstname;
+ public String lastname;
+ public String mail;
+ }
+
+ public RedmineUser user;
+ }
+
+ public RedmineAuthProvider() {
+ super("redmine");
+ }
+
+ @Override
+ public void setup() {
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return false;
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.REDMINE;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ String jsonString = null;
+ try {
+ // first attempt by username/password
+ jsonString = getCurrentUserAsJson(username, password);
+ } catch (Exception e1) {
+ logger.warn("Failed to authenticate via username/password against Redmine");
+ try {
+ // second attempt is by apikey
+ jsonString = getCurrentUserAsJson(null, password);
+ username = null;
+ } catch (Exception e2) {
+ logger.error("Failed to authenticate via apikey against Redmine", e2);
+ return null;
+ }
+ }
+
+ if (StringUtils.isEmpty(jsonString)) {
+ logger.error("Received empty authentication response from Redmine");
+ return null;
+ }
+
+ RedmineCurrent current = null;
+ try {
+ current = new Gson().fromJson(jsonString, RedmineCurrent.class);
+ } catch (Exception e) {
+ logger.error("Failed to deserialize Redmine json response: " + jsonString, e);
+ return null;
+ }
+
+ if (StringUtils.isEmpty(username)) {
+ // if the username has been reset because of apikey authentication
+ // then use the email address of the user. this is the original
+ // behavior as contributed by github/mallowlabs
+ username = current.user.mail;
+ }
+
+ UserModel user = userManager.getUserModel(username);
+ if (user == null) // create user object for new authenticated user
+ user = new UserModel(username.toLowerCase());
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ }
+
+ // update user attributes from Redmine
+ user.accountType = getAccountType();
+ user.displayName = current.user.firstname + " " + current.user.lastname;
+ user.emailAddress = current.user.mail;
+ user.password = Constants.EXTERNAL_ACCOUNT;
+ if (!StringUtils.isEmpty(current.user.login)) {
+ // only admin users can get login name
+ // evidently this is an undocumented behavior of Redmine
+ user.canAdmin = true;
+ }
+
+ // TODO consider Redmine group mapping for team membership
+ // http://www.redmine.org/projects/redmine/wiki/Rest_Users
+
+ // push the changes to the backing user service
+ updateUser(user);
+
+ return user;
+ }
+
+ private String getCurrentUserAsJson(String username, char [] password) throws IOException {
+ if (testingJson != null) { // for testing
+ return testingJson;
+ }
+
+ String url = this.settings.getString(Keys.realm.redmine.url, "");
+ if (!url.endsWith("/")) {
+ url = url.concat("/");
+ }
+ HttpURLConnection http;
+ if (username == null) {
+ // apikey authentication
+ String apiKey = String.valueOf(password);
+ String apiUrl = url + "users/current.json?key=" + apiKey;
+ http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, null, null);
+ } else {
+ // username/password BASIC authentication
+ String apiUrl = url + "users/current.json";
+ http = (HttpURLConnection) ConnectionUtils.openConnection(apiUrl, username, password);
+ }
+ http.setRequestMethod("GET");
+ http.connect();
+ InputStreamReader reader = new InputStreamReader(http.getInputStream());
+ return IOUtils.toString(reader);
+ }
+
+ /**
+ * set json response. do NOT invoke from production code.
+ * @param json json
+ */
+ public void setTestingCurrentUserAsJson(String json) {
+ this.testingJson = json;
+ }
+}
--- /dev/null
+package com.gitblit.auth;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.sforce.soap.partner.Connector;
+import com.sforce.soap.partner.GetUserInfoResult;
+import com.sforce.soap.partner.PartnerConnection;
+import com.sforce.ws.ConnectionException;
+import com.sforce.ws.ConnectorConfig;
+
+public class SalesforceAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ public SalesforceAuthProvider() {
+ super("salesforce");
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.SALESFORCE;
+ }
+
+ @Override
+ public void setup() {
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ ConnectorConfig config = new ConnectorConfig();
+ config.setUsername(username);
+ config.setPassword(new String(password));
+
+ try {
+ PartnerConnection connection = Connector.newConnection(config);
+
+ GetUserInfoResult info = connection.getUserInfo();
+
+ String org = settings.getString(Keys.realm.salesforce.orgId, "0")
+ .trim();
+
+ if (!org.equals("0")) {
+ if (!org.equals(info.getOrganizationId())) {
+ logger.warn("Access attempted by user of an invalid org: "
+ + info.getUserName() + ", org: "
+ + info.getOrganizationName() + "("
+ + info.getOrganizationId() + ")");
+
+ return null;
+ }
+ }
+
+ logger.info("Authenticated user " + info.getUserName()
+ + " using org " + info.getOrganizationName() + "("
+ + info.getOrganizationId() + ")");
+
+ String simpleUsername = getSimpleUsername(info);
+
+ UserModel user = null;
+ synchronized (this) {
+ user = userManager.getUserModel(simpleUsername);
+ if (user == null)
+ user = new UserModel(simpleUsername);
+
+ if (StringUtils.isEmpty(user.cookie)
+ && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username
+ + new String(password));
+ }
+
+ setUserAttributes(user, info);
+
+ updateUser(user);
+ }
+
+ return user;
+ } catch (ConnectionException e) {
+ logger.error("Failed to authenticate", e);
+ }
+
+ return null;
+ }
+
+ private void setUserAttributes(UserModel user, GetUserInfoResult info) {
+ // Don't want visibility into the real password, make up a dummy
+ user.password = Constants.EXTERNAL_ACCOUNT;
+ user.accountType = getAccountType();
+
+ // Get full name Attribute
+ user.displayName = info.getUserFullName();
+
+ // Get email address Attribute
+ user.emailAddress = info.getUserEmail();
+ }
+
+ /**
+ * Simple user name is the first part of the email address.
+ */
+ private String getSimpleUsername(GetUserInfoResult info) {
+ String email = info.getUserEmail();
+
+ return email.split("@")[0];
+ }
+
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return true;
+ }
+}
--- /dev/null
+/*
+ * 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.auth;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+import waffle.windows.auth.IWindowsAccount;
+import waffle.windows.auth.IWindowsAuthProvider;
+import waffle.windows.auth.IWindowsComputer;
+import waffle.windows.auth.IWindowsIdentity;
+import waffle.windows.auth.impl.WindowsAuthProviderImpl;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Keys;
+import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.sun.jna.platform.win32.Win32Exception;
+
+/**
+ * Implementation of a Windows authentication provider.
+ *
+ * @author James Moger
+ */
+public class WindowsAuthProvider extends UsernamePasswordAuthenticationProvider {
+
+ private IWindowsAuthProvider waffle;
+
+ public WindowsAuthProvider() {
+ super("windows");
+ }
+
+ @Override
+ public void setup() {
+
+ waffle = new WindowsAuthProviderImpl();
+ IWindowsComputer computer = waffle.getCurrentComputer();
+ logger.info("Windows Authentication Provider");
+ logger.info(" name = " + computer.getComputerName());
+ logger.info(" status = " + describeJoinStatus(computer.getJoinStatus()));
+ logger.info(" memberOf = " + computer.getMemberOf());
+ //logger.info(" groups = " + Arrays.asList(computer.getGroups()));
+ }
+
+ protected String describeJoinStatus(String value) {
+ if ("NetSetupUnknownStatus".equals(value)) {
+ return "unknown";
+ } else if ("NetSetupUnjoined".equals(value)) {
+ return "not joined";
+ } else if ("NetSetupWorkgroupName".equals(value)) {
+ return "joined to a workgroup";
+ } else if ("NetSetupDomainName".equals(value)) {
+ return "joined to a domain";
+ }
+ return value;
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return true;
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.WINDOWS;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password) {
+ String defaultDomain = settings.getString(Keys.realm.windows.defaultDomain, null);
+ if (StringUtils.isEmpty(defaultDomain)) {
+ // ensure that default domain is null
+ defaultDomain = null;
+ }
+
+ if (defaultDomain != null) {
+ // sanitize username
+ if (username.startsWith(defaultDomain + "\\")) {
+ // strip default domain from domain\ username
+ username = username.substring(defaultDomain.length() + 1);
+ } else if (username.endsWith("@" + defaultDomain)) {
+ // strip default domain from username@domain
+ username = username.substring(0, username.lastIndexOf('@'));
+ }
+ }
+
+ IWindowsIdentity identity = null;
+ try {
+ if (username.indexOf('@') > -1 || username.indexOf('\\') > -1) {
+ // manually specified domain
+ identity = waffle.logonUser(username, new String(password));
+ } else {
+ // no domain specified, use default domain
+ identity = waffle.logonDomainUser(username, defaultDomain, new String(password));
+ }
+ } catch (Win32Exception e) {
+ logger.error(e.getMessage());
+ return null;
+ }
+
+ if (identity.isGuest() && !settings.getBoolean(Keys.realm.windows.allowGuests, false)) {
+ logger.warn("Guest account access is disabled");
+ identity.dispose();
+ return null;
+ }
+
+ UserModel user = userManager.getUserModel(username);
+ if (user == null) // create user object for new authenticated user
+ user = new UserModel(username.toLowerCase());
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ }
+
+ // update user attributes from Windows identity
+ user.accountType = getAccountType();
+ String fqn = identity.getFqn();
+ if (fqn.indexOf('\\') > -1) {
+ user.displayName = fqn.substring(fqn.lastIndexOf('\\') + 1);
+ } else {
+ user.displayName = fqn;
+ }
+ user.password = Constants.EXTERNAL_ACCOUNT;
+
+ Set<String> groupNames = new TreeSet<String>();
+ for (IWindowsAccount group : identity.getGroups()) {
+ groupNames.add(group.getFqn());
+ }
+
+ if (groupNames.contains("BUILTIN\\Administrators")) {
+ // local administrator
+ user.canAdmin = true;
+ }
+
+ // TODO consider mapping Windows groups to teams
+
+ // push the changes to the backing user service
+ updateUser(user);
+
+ // cleanup resources
+ identity.dispose();
+
+ return user;
+ }
+}
final Insets _insets = new Insets(5, 5, 5, 5);\r
repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);\r
userPalette = new JPalette<String>();\r
- userPalette.setEnabled(settings.supportsTeamMembershipChanges);\r
\r
JPanel fieldsPanelTop = new JPanel(new BorderLayout());\r
fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);\r
countryCodeField = new JTextField(anUser.countryCode == null ? "" : anUser.countryCode, 15);\r
\r
// credentials are optionally controlled by 3rd-party authentication\r
- usernameField.setEnabled(settings.supportsCredentialChanges);\r
- passwordField.setEnabled(settings.supportsCredentialChanges);\r
- confirmPasswordField.setEnabled(settings.supportsCredentialChanges);\r
-\r
- displayNameField.setEnabled(settings.supportsDisplayNameChanges);\r
- emailAddressField.setEnabled(settings.supportsEmailAddressChanges);\r
-\r
- organizationalUnitField.setEnabled(settings.supportsDisplayNameChanges);\r
- organizationField.setEnabled(settings.supportsDisplayNameChanges);\r
- localityField.setEnabled(settings.supportsDisplayNameChanges);\r
- stateProvinceField.setEnabled(settings.supportsDisplayNameChanges);\r
- countryCodeField.setEnabled(settings.supportsDisplayNameChanges);\r
+ usernameField.setEnabled(anUser.isLocalAccount());\r
+ passwordField.setEnabled(anUser.isLocalAccount());\r
+ confirmPasswordField.setEnabled(anUser.isLocalAccount());\r
\r
JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));\r
fieldsPanel.add(newFieldPanel(Translation.get("gb.username"), usernameField));\r
final Insets _insets = new Insets(5, 5, 5, 5);\r
repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);\r
teamsPalette = new JPalette<TeamModel>();\r
- teamsPalette.setEnabled(settings.supportsTeamMembershipChanges);\r
\r
JPanel fieldsPanelTop = new JPanel(new BorderLayout());\r
fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);\r
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
-import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.UserModel;
/**
*/
public class GitblitUploadPackFactory<X> implements UploadPackFactory<X> {
- private final ISessionManager sessionManager;
+ private final IAuthenticationManager authenticationManager;
- public GitblitUploadPackFactory(ISessionManager sessionManager) {
- this.sessionManager = sessionManager;
+ public GitblitUploadPackFactory(IAuthenticationManager authenticationManager) {
+ this.authenticationManager = authenticationManager;
}
@Override
if (req instanceof HttpServletRequest) {
// http/https request may or may not be authenticated
- user = sessionManager.authenticate((HttpServletRequest) req);
+ user = authenticationManager.authenticate((HttpServletRequest) req);
if (user == null) {
user = UserModel.ANONYMOUS;
}
--- /dev/null
+/*
+ * 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;
+// }
+}
* @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
--- /dev/null
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+
+public interface IAuthenticationManager extends IManager {
+
+ /**
+ * 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
+ */
+ UserModel authenticate(HttpServletRequest httpRequest);
+
+ /**
+ * 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
+ */
+ UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate);
+
+ /**
+ * Authenticate a user based on a username and password.
+ *
+ * @see IUserService.authenticate(String, char[])
+ * @param username
+ * @param password
+ * @return a user object or null
+ */
+ UserModel authenticate(String username, char[] password);
+
+ /**
+ * Sets a cookie for the specified user.
+ *
+ * @param response
+ * @param user
+ */
+ void setCookie(HttpServletResponse response, UserModel user);
+
+ /**
+ * Logout a user.
+ *
+ * @param user
+ */
+ 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
+++ /dev/null
-/*
- * 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 javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.gitblit.models.UserModel;
-
-public interface ISessionManager extends IManager {
-
- /**
- * 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
- */
- UserModel authenticate(HttpServletRequest httpRequest);
-
- /**
- * 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
- */
- UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate);
-
- /**
- * Authenticate a user based on a username and password.
- *
- * @see IUserService.authenticate(String, char[])
- * @param username
- * @param password
- * @return a user object or null
- */
- UserModel authenticate(String username, char[] password);
-
- /**
- * Sets a cookie for the specified user.
- *
- * @param response
- * @param user
- */
- void setCookie(HttpServletResponse response, UserModel user);
-
- /**
- * Logout a user.
- *
- * @param user
- */
- void logout(HttpServletResponse response, UserModel user);
-
-}
\ No newline at end of file
*/
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
+++ /dev/null
-/*
- * 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;
-// }
-}
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;
/**
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);
}
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);
}
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.
*
}
/**
- * 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.
*
}
String usernameDecoded = StringUtils.decodeUsername(username);
UserModel user = userService.getUserModel(usernameDecoded);
- setAccountType(user);
return user;
}
*/
@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);
}
/**
@Override
public List<UserModel> getAllUsers() {
List<UserModel> users = userService.getAllUsers();
- for (UserModel user : users) {
- setAccountType(user);
- }
return users;
}
*/
@Override
public List<String> getAllTeamNames() {
- return userService.getAllTeamNames();
+ List<String> teams = userService.getAllTeamNames();
+ return teams;
}
/**
*/
@Override
public List<String> getTeamNamesForRepositoryRole(String role) {
- return userService.getTeamNamesForRepositoryRole(role);
+ List<String> teams = userService.getTeamNamesForRepositoryRole(role);
+ return teams;
}
/**
*/
@Override
public TeamModel getTeamModel(String teamname) {
- return userService.getTeamModel(teamname);
+ TeamModel team = userService.getTeamModel(teamname);
+ return team;
}
/**
*/
@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);
}
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();
- }
- }
- }
}
\r
public List<String> pushScripts;\r
\r
- public boolean supportsCredentialChanges;\r
-\r
- public boolean supportsDisplayNameChanges;\r
-\r
- public boolean supportsEmailAddressChanges;\r
-\r
- public boolean supportsTeamMembershipChanges;\r
-\r
public ServerSettings() {\r
settings = new TreeMap<String, SettingModel>();\r
}\r
\r
import com.gitblit.Constants.AccessPermission;\r
import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.Constants.AccountType;\r
import com.gitblit.Constants.PermissionType;\r
import com.gitblit.Constants.RegistrantType;\r
import com.gitblit.Constants.Unused;\r
public boolean canAdmin;\r
public boolean canFork;\r
public boolean canCreate;\r
+ public AccountType accountType;\r
public final Set<String> users = new HashSet<String>();\r
// retained for backwards-compatibility with RPC clients\r
@Deprecated\r
\r
public TeamModel(String name) {\r
this.name = name;\r
+ this.accountType = AccountType.LOCAL;\r
}\r
\r
/**\r
}\r
}\r
\r
+ public boolean isLocalTeam() {\r
+ return accountType.isLocal();\r
+ }\r
+\r
@Override\r
public String toString() {\r
return name;\r
\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.StringUtils;\r
\r
protected AccessRestrictionFilter(\r
IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager) {\r
- super(sessionManager);\r
+ super(authenticationManager);\r
this.runtimeManager = runtimeManager;\r
this.repositoryManager = repositoryManager;\r
}\r
import org.slf4j.LoggerFactory;\r
\r
import com.gitblit.Constants;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.DeepCopier;\r
import com.gitblit.utils.StringUtils;\r
\r
protected transient Logger logger = LoggerFactory.getLogger(getClass());\r
\r
- protected final ISessionManager sessionManager;\r
+ protected final IAuthenticationManager authenticationManager;\r
\r
- protected AuthenticationFilter(ISessionManager sessionManager) {\r
- this.sessionManager = sessionManager;\r
+ protected AuthenticationFilter(IAuthenticationManager authenticationManager) {\r
+ this.authenticationManager = authenticationManager;\r
}\r
\r
/**\r
* @return user\r
*/\r
protected UserModel getUser(HttpServletRequest httpRequest) {\r
- UserModel user = sessionManager.authenticate(httpRequest, requiresClientCertificate());\r
+ UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate());\r
return user;\r
}\r
\r
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
\r
@Inject\r
public DownloadZipFilter(\r
IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager) {\r
\r
- super(runtimeManager, sessionManager, repositoryManager);\r
+ super(runtimeManager, authenticationManager, repositoryManager);\r
}\r
\r
/**\r
import com.gitblit.Keys;
import com.gitblit.Keys.web;
import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.UserModel;
/**
private final IStoredSettings settings;
- private final ISessionManager sessionManager;
+ private final IAuthenticationManager authenticationManager;
@Inject
public EnforceAuthenticationFilter(
IRuntimeManager runtimeManager,
- ISessionManager sessionManager) {
+ IAuthenticationManager authenticationManager) {
super();
this.settings = runtimeManager.getSettings();
- this.sessionManager = sessionManager;
+ this.authenticationManager = authenticationManager;
}
/*
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
- UserModel user = sessionManager.authenticate(httpRequest);
+ UserModel user = authenticationManager.authenticate(httpRequest);
if (mustForceAuth && (user == null)) {
// not authenticated, enforce now:
import com.gitblit.Keys.git;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.StringUtils;\r
@Inject\r
public GitFilter(\r
IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager) {\r
\r
- super(runtimeManager, sessionManager, repositoryManager);\r
+ super(runtimeManager, authenticationManager, repositoryManager);\r
this.settings = runtimeManager.getSettings();\r
}\r
\r
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.IServicesManager;
-import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.utils.ContainerUtils;
import com.gitblit.utils.StringUtils;
// start all other managers
startManager(injector, INotificationManager.class);
startManager(injector, IUserManager.class);
- startManager(injector, ISessionManager.class);
+ startManager(injector, IAuthenticationManager.class);
startManager(injector, IRepositoryManager.class);
startManager(injector, IProjectManager.class);
startManager(injector, IGitblitManager.class);
import com.gitblit.Constants.AccessRestrictionType;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
\r
\r
@Inject\r
public PagesFilter(IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager) {\r
\r
- super(runtimeManager, sessionManager, repositoryManager);\r
+ super(runtimeManager, authenticationManager, repositoryManager);\r
}\r
\r
/**\r
import com.gitblit.IStoredSettings;\r
import com.gitblit.Keys;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.UserModel;\r
\r
/**\r
@Inject\r
public RpcFilter(\r
IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager) {\r
+ IAuthenticationManager authenticationManager) {\r
\r
- super(sessionManager);\r
+ super(authenticationManager);\r
this.settings = runtimeManager.getSettings();\r
this.runtimeManager = runtimeManager;\r
}\r
import com.gitblit.Keys.fanout;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.manager.IUserManager;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
\r
private final IUserManager userManager;\r
\r
- private final ISessionManager sessionManager;\r
+ private final IAuthenticationManager authenticationManager;\r
\r
private final IRepositoryManager repositoryManager;\r
\r
public SparkleShareInviteServlet(\r
IRuntimeManager runtimeManager,\r
IUserManager userManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager) {\r
\r
super();\r
this.settings = runtimeManager.getSettings();\r
this.userManager = userManager;\r
- this.sessionManager = sessionManager;\r
+ this.authenticationManager = authenticationManager;\r
this.repositoryManager = repositoryManager;\r
}\r
\r
}\r
UserModel user;\r
if (StringUtils.isEmpty(username)) {\r
- user = sessionManager.authenticate(request);\r
+ user = authenticationManager.authenticate(request);\r
} else {\r
user = userManager.getUserModel(username);\r
}\r
import com.gitblit.manager.IProjectManager;\r
import com.gitblit.manager.IRepositoryManager;\r
import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.ISessionManager;\r
+import com.gitblit.manager.IAuthenticationManager;\r
import com.gitblit.models.ProjectModel;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.UserModel;\r
@Inject\r
public SyndicationFilter(\r
IRuntimeManager runtimeManager,\r
- ISessionManager sessionManager,\r
+ IAuthenticationManager authenticationManager,\r
IRepositoryManager repositoryManager,\r
IProjectManager projectManager) {\r
\r
- super(sessionManager);\r
+ super(authenticationManager);\r
this.runtimeManager = runtimeManager;\r
this.repositoryManager = repositoryManager;\r
this.projectManager = projectManager;\r
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.pages.ActivityPage;
private final IUserManager userManager;
- private final ISessionManager sessionManager;
+ private final IAuthenticationManager authenticationManager;
private final IRepositoryManager repositoryManager;
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
- ISessionManager sessionManager,
+ IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
this.runtimeManager = runtimeManager;
this.notificationManager = notificationManager;
this.userManager = userManager;
- this.sessionManager = sessionManager;
+ this.authenticationManager = authenticationManager;
this.repositoryManager = repositoryManager;
this.projectManager = projectManager;
this.gitblitManager = gitblitManager;
return userManager;
}
- public ISessionManager session() {
- return sessionManager;
+ public IAuthenticationManager authentication() {
+ return authenticationManager;
}
public IRepositoryManager repositories() {
}\r
\r
UserModel user = GitBlitWebSession.get().getUser();\r
- if (!app().users().supportsCredentialChanges(user)) {\r
+ if (!app().authentication().supportsCredentialChanges(user)) {\r
error(MessageFormat.format(getString("gb.userServiceDoesNotPermitPasswordChanges"),\r
app().settings().getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);\r
}\r
app().gitblit().updateUserModel(user.username, user, false);\r
if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {\r
WebResponse response = (WebResponse) getRequestCycle().getResponse();\r
- app().session().setCookie(response.getHttpServletResponse(), user);\r
+ app().authentication().setCookie(response.getHttpServletResponse(), user);\r
}\r
} catch (GitBlitException e) {\r
error(e.getMessage());\r
form.add(new SimpleAttributeModifier("autocomplete", "off"));\r
\r
// not all user services support manipulating team memberships\r
- boolean editMemberships = app().users().supportsTeamMembershipChanges(null);\r
+ boolean editMemberships = app().authentication().supportsTeamMembershipChanges(teamModel);\r
\r
// field names reflective match TeamModel fields\r
form.add(new TextField<String>("name"));\r
public EditUserPage() {\r
// create constructor\r
super();\r
- if (!app().users().supportsAddUser()) {\r
- error(MessageFormat.format(getString("gb.userServiceDoesNotPermitAddUser"),\r
- app().settings().getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);\r
- }\r
isCreate = true;\r
setupPage(new UserModel(""));\r
setStatelessHint(false);\r
}\r
boolean rename = !StringUtils.isEmpty(oldName)\r
&& !oldName.equalsIgnoreCase(username);\r
- if (app().users().supportsCredentialChanges(userModel)) {\r
+ if (app().authentication().supportsCredentialChanges(userModel)) {\r
if (!userModel.password.equals(confirmPassword.getObject())) {\r
error(getString("gb.passwordsDoNotMatch"));\r
return;\r
form.add(new SimpleAttributeModifier("autocomplete", "off"));\r
\r
// not all user services support manipulating username and password\r
- boolean editCredentials = app().users().supportsCredentialChanges(userModel);\r
+ boolean editCredentials = app().authentication().supportsCredentialChanges(userModel);\r
\r
// not all user services support manipulating display name\r
- boolean editDisplayName = app().users().supportsDisplayNameChanges(userModel);\r
+ boolean editDisplayName = app().authentication().supportsDisplayNameChanges(userModel);\r
\r
// not all user services support manipulating email address\r
- boolean editEmailAddress = app().users().supportsEmailAddressChanges(userModel);\r
+ boolean editEmailAddress = app().authentication().supportsEmailAddressChanges(userModel);\r
\r
// not all user services support manipulating team memberships\r
- boolean editTeams = app().users().supportsTeamMembershipChanges(userModel);\r
+ boolean editTeams = app().authentication().supportsTeamMembershipChanges(userModel);\r
\r
// field names reflective match UserModel fields\r
form.add(new TextField<String>("username").setEnabled(editCredentials));\r
super();\r
GitBlitWebSession session = GitBlitWebSession.get();\r
UserModel user = session.getUser();\r
- app().session().logout(((WebResponse) getResponse()).getHttpServletResponse(), user);\r
+ app().authentication().logout(((WebResponse) getResponse()).getHttpServletResponse(), user);\r
session.invalidate();\r
\r
/*\r
// Set Cookie\r
if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {\r
WebResponse response = (WebResponse) getRequestCycle().getResponse();\r
- app().session().setCookie(response.getHttpServletResponse(), user);\r
+ app().authentication().setCookie(response.getHttpServletResponse(), user);\r
}\r
\r
if (!session.continueRequest()) {\r
String username = RootPage.this.username.getObject();\r
char[] password = RootPage.this.password.getObject().toCharArray();\r
\r
- UserModel user = app().session().authenticate(username, password);\r
+ UserModel user = app().authentication().authenticate(username, password);\r
if (user == null) {\r
error(getString("gb.invalidUsernameOrPassword"));\r
} else if (user.username.equals(Constants.FEDERATION_USER)) {\r
\r
GitBlitWebSession session = GitBlitWebSession.get();\r
UserModel user = session.getUser();\r
- boolean editCredentials = app().users().supportsCredentialChanges(user);\r
+ boolean editCredentials = app().authentication().supportsCredentialChanges(user);\r
boolean standardLogin = session.authenticationType.isStandard();\r
\r
if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {\r
// try to authenticate by servlet request\r
HttpServletRequest httpRequest = ((WebRequest) getRequestCycle().getRequest())\r
.getHttpServletRequest();\r
- UserModel user = app().session().authenticate(httpRequest);\r
+ UserModel user = app().authentication().authenticate(httpRequest);\r
\r
// Login the user\r
if (user != null) {\r
\r
// Set Cookie\r
WebResponse response = (WebResponse) getRequestCycle().getResponse();\r
- app().session().setCookie(response.getHttpServletResponse(), user);\r
+ app().authentication().setCookie(response.getHttpServletResponse(), user);\r
\r
session.continueRequest();\r
}\r
\r
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);\r
adminLinks.add(new BookmarkablePageLink<Void>("newTeam", EditTeamPage.class));\r
- add(adminLinks.setVisible(showAdmin && app().users().supportsTeamMembershipChanges(null)));\r
+ add(adminLinks.setVisible(showAdmin));\r
\r
final List<TeamModel> teams = app().users().getAllTeams();\r
DataView<TeamModel> teamsView = new DataView<TeamModel>("teamRow",\r
super(wicketId);\r
\r
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);\r
- adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class)\r
- .setVisible(app().users().supportsAddUser()));\r
+ adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class));\r
add(adminLinks.setVisible(showAdmin));\r
\r
final List<UserModel> users = app().users().getAllUsers();\r
### LDAP Authentication\r
*SINCE 1.0.0*\r
\r
-LDAP can be used to authenticate Users and optionally control Team memberships. When properly configured, Gitblit will delegate authentication to your LDAP server and will cache some user information in the usual users file (.conf or .properties).\r
+LDAP can be used to authenticate Users and optionally control Team memberships. When properly configured, Gitblit will delegate authentication to your LDAP server and will cache some user information in the usual users.conf file.\r
\r
When using the LDAP User Service, new user accounts can not be manually created from Gitblit. Gitblit user accounts are automatically created for new users on their first succesful authentication through Gitblit against the LDAP server. It is also important to note that the LDAP User Service does not retrieve or store user passwords nor does it implement any LDAP-write functionality.\r
\r
-To use the *LdapUserService* set *realm.userService=com.gitblit.LdapUserService* in your `gitblit.properties` file or your `web.xml` file and then configure the *realm.ldap* settings appropriately for your LDAP environment.\r
+To use the *LdapUserService* set *realm.authenticationProviders=ldap* in your `gitblit.properties` file and then configure the *realm.ldap* settings appropriately for your LDAP environment.\r
\r
#### Example LDAP Layout\r
![block diagram](ldapSample.png "LDAP Sample")\r
<th>realm.ldap.password</th><td>password</td>\r
<td>The credentials that will log into the LDAP server</td>\r
</tr>\r
-<tr>\r
- <th>realm.ldap.backingUserService</th><td>users.conf</td>\r
- <td>Where to store all information that is used by Gitblit. All information will be synced here upon user login.</td>\r
-</tr>\r
<tr>\r
<th>realm.ldap.maintainTeams</th><td>true</td>\r
<td>Are team memberships maintained in LDAP (<em>true</em>) or manually in Gitblit (<em>false</em>).</td>\r
\r
Windows authentication is based on the use of Waffle and JNA. It is known to work properly for authenticating against the local Windows machine, but it is unclear if it works properly with a domain controller and Active Directory. To use this service, your Gitblit server must be installed on a Windows machine.\r
\r
- realm.userService = com.gitblit.WindowsUserService\r
+ realm.authenticationProviders = windows\r
realm.windows.defaultDomain =\r
\r
### PAM Authentication\r
\r
PAM authentication is based on the use of libpam4j and JNA. To use this service, your Gitblit server must be installed on a Linux/Unix/MacOSX machine and the user that Gitblit runs-as must have root permissions.\r
\r
- realm.userService = com.gitblit.PAMUserService\r
+ realm.authenticationProviders = pam\r
realm.pam.serviceName = system-auth\r
\r
### Htpasswd Authentication\r
\r
Htpasswd authentication allows you to maintain your user credentials in an Apache htpasswd file thay may be shared with other htpasswd-capable servers.\r
\r
- realm.userService = com.gitblit.HtpasswdUserService\r
+ realm.authenticationProviders = htpasswd\r
realm.htpasswd.userFile = /path/to/htpasswd\r
\r
### Redmine Authentication\r
\r
You may authenticate your users against a Redmine installation as long as your Redmine install has properly enabled [API authentication](http://www.redmine.org/projects/redmine/wiki/Rest_Api#Authentication). This user service only supports user authentication; it does not support team creation based on Redmine groups. Redmine administrators will also be Gitblit administrators.\r
\r
- realm.userService = com.gitblit.RedmineUserService\r
+ realm.authenticationProviders = redmine\r
realm.redmine.url = http://example.com/redmine\r
\r
### Salesforce.com Authentication\r
\r
You may authenticate your users against Salesforce.com. You can require that user's belong to a particular organization by specifying a non-zero organization id.\r
\r
- realm.userService = com.gitblit.SalesforceUserService\r
+ realm.authenticationProviders = salesforce\r
realm.salesforce.orgId = 0\r
\r
### Container Authentication\r
\r
This is the simplest choice where you implement custom authentication and delegate all other standard user and team operations to one of Gitblit's user service implementations. This choice insulates your customization from changes in User and Team model classes and additional API that may be added to IUserService.\r
\r
-Please subclass [com.gitblit.GitblitUserService](https://github.com/gitblit/gitblit/blob/master/src/main/java/com/gitblit/GitblitUserService.java) and override the *setup()* and *authenticate()* methods. \r
-Make sure to set the *serviceImpl* field in your *setup()* method.\r
+Please subclass [com.gitblit.auth.AuthenticationProvider](https://github.com/gitblit/gitblit/blob/master/src/main/java/com/gitblit/auth/AuthenticationProvider.java).\r
\r
-You may use your subclass by specifying its fully qualified classname in the *realm.userService* setting.\r
+You may use your subclass by specifying its fully qualified classname in the *realm.authenticationProviders* setting.\r
\r
Your subclass must be on Gitblit's classpath and must have a public default constructor. \r
\r
[user "admin"]
password = admin
cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
+ accountType = LOCAL
role = "#admin"
role = "#notfederated"
-[user "sampleuser"]
- password = sampleuser
- cookie = 6e07ed42149fc166206319faffdfba2e2ec82e43
- role = "#none"
[team "admins"]
role = "#none"
+ accountType = LOCAL
user = admin
@RunWith(Suite.class)\r
@SuiteClasses({ ArrayUtilsTest.class, FileUtilsTest.class, TimeUtilsTest.class,\r
StringUtilsTest.class, Base64Test.class, JsonUtilsTest.class, ByteFormatTest.class,\r
- ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapUserServiceTest.class,\r
+ ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapAuthenticationTest.class,\r
MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,\r
DiffUtilsTest.class, MetricUtilsTest.class, X509UtilsTest.class,\r
GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class,\r
GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,\r
- FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdUserServiceTest.class,\r
+ FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,\r
ModelUtilsTest.class, JnaUtilsTest.class })\r
public class GitBlitSuite {\r
\r
\r
@Test\r
public void testAuthentication() throws Exception {\r
- assertTrue(session().authenticate("admin", "admin".toCharArray()) != null);\r
+ assertTrue(authentication().authenticate("admin", "admin".toCharArray()) != null);\r
}\r
\r
@Test\r
import com.gitblit.manager.IProjectManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.servlet.GitblitContext;
return GitblitContext.getManager(IUserManager.class);
}
- public static ISessionManager session() {
- return GitblitContext.getManager(ISessionManager.class);
+ public static IAuthenticationManager authentication() {
+ return GitblitContext.getManager(IAuthenticationManager.class);
}
public static IRepositoryManager repositories() {
--- /dev/null
+/*
+ * 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.tests;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.auth.HtpasswdAuthProvider;
+import com.gitblit.manager.RuntimeManager;
+import com.gitblit.manager.UserManager;
+import com.gitblit.models.UserModel;
+import com.gitblit.tests.mock.MemorySettings;
+
+/**
+ * Test the Htpasswd user service.
+ *
+ */
+public class HtpasswdAuthenticationTest extends GitblitUnitTest {
+
+ private static final String RESOURCE_DIR = "src/test/resources/htpasswd/";
+ private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
+
+ private static final int NUM_USERS_HTPASSWD = 10;
+
+ private static final MemorySettings MS = new MemorySettings(new HashMap<String, Object>());
+
+ private HtpasswdAuthProvider htpasswd;
+
+
+ private MemorySettings getSettings(String userfile, String groupfile, Boolean overrideLA)
+ {
+ MS.put("realm.userService", RESOURCE_DIR + "users.conf");
+ MS.put("realm.htpasswd.userfile", (userfile == null) ? (RESOURCE_DIR + "htpasswd") : userfile);
+ MS.put("realm.htpasswd.groupfile", (groupfile == null) ? (RESOURCE_DIR + "htgroup") : groupfile);
+ MS.put("realm.htpasswd.overrideLocalAuthentication", (overrideLA == null) ? "false" : overrideLA.toString());
+ // Default to keep test the same on all platforms.
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
+
+ return MS;
+ }
+
+ private MemorySettings getSettings()
+ {
+ return getSettings(null, null, null);
+ }
+
+ private void setupUS()
+ {
+ htpasswd = newHtpasswdAuthentication(getSettings());
+ }
+
+ private HtpasswdAuthProvider newHtpasswdAuthentication(IStoredSettings settings) {
+ RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start();
+ UserManager users = new UserManager(runtime).start();
+ HtpasswdAuthProvider htpasswd = new HtpasswdAuthProvider();
+ htpasswd.setup(runtime, users);
+ return htpasswd;
+ }
+
+
+ private void copyInFiles() throws IOException
+ {
+ File dir = new File(RESOURCE_DIR);
+ FilenameFilter filter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String file) {
+ return file.endsWith(".in");
+ }
+ };
+ for (File inf : dir.listFiles(filter)) {
+ File dest = new File(inf.getParent(), inf.getName().substring(0, inf.getName().length() - 3));
+ FileUtils.copyFile(inf, dest);
+ }
+ }
+
+
+ private void deleteGeneratedFiles()
+ {
+ File dir = new File(RESOURCE_DIR);
+ FilenameFilter filter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String file) {
+ return !(file.endsWith(".in"));
+ }
+ };
+ for (File file : dir.listFiles(filter)) {
+ file.delete();
+ }
+ }
+
+
+ @Before
+ public void setup() throws IOException
+ {
+ copyInFiles();
+ setupUS();
+ }
+
+
+ @After
+ public void tearDown()
+ {
+ deleteGeneratedFiles();
+ }
+
+
+
+ @Test
+ public void testSetup() throws IOException
+ {
+ assertEquals(NUM_USERS_HTPASSWD, htpasswd.getNumberHtpasswdUsers());
+ }
+
+
+ @Test
+ public void testAuthenticate()
+ {
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
+ UserModel user = htpasswd.authenticate("user1", "pass1".toCharArray());
+ assertNotNull(user);
+ assertEquals("user1", user.username);
+
+ user = htpasswd.authenticate("user2", "pass2".toCharArray());
+ assertNotNull(user);
+ assertEquals("user2", user.username);
+
+ // Test different encryptions
+ user = htpasswd.authenticate("plain", "passWord".toCharArray());
+ assertNotNull(user);
+ assertEquals("plain", user.username);
+
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
+ user = htpasswd.authenticate("crypt", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("crypt", user.username);
+
+ user = htpasswd.authenticate("md5", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("md5", user.username);
+
+ user = htpasswd.authenticate("sha", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("sha", user.username);
+
+
+ // Test leading and trailing whitespace
+ user = htpasswd.authenticate("trailing", "whitespace".toCharArray());
+ assertNotNull(user);
+ assertEquals("trailing", user.username);
+
+ user = htpasswd.authenticate("tabbed", "frontAndBack".toCharArray());
+ assertNotNull(user);
+ assertEquals("tabbed", user.username);
+
+ user = htpasswd.authenticate("leading", "whitespace".toCharArray());
+ assertNotNull(user);
+ assertEquals("leading", user.username);
+ }
+
+
+ @Test
+ public void testAttributes()
+ {
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
+ UserModel user = htpasswd.authenticate("user1", "pass1".toCharArray());
+ assertNotNull(user);
+ assertEquals("El Capitan", user.displayName);
+ assertEquals("cheffe@example.com", user.emailAddress);
+ assertTrue(user.canAdmin);
+
+ user = htpasswd.authenticate("user2", "pass2".toCharArray());
+ assertNotNull(user);
+ assertEquals("User Two", user.displayName);
+ assertTrue(user.canCreate);
+ assertTrue(user.canFork);
+ }
+
+
+ @Test
+ public void testAuthenticateDenied()
+ {
+ UserModel user = null;
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
+ user = htpasswd.authenticate("user1", "".toCharArray());
+ assertNull("User 'user1' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("user1", "pass2".toCharArray());
+ assertNull("User 'user1' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("user2", "lalala".toCharArray());
+ assertNull("User 'user2' falsely authenticated.", user);
+
+
+ user = htpasswd.authenticate("user3", "disabled".toCharArray());
+ assertNull("User 'user3' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("user4", "disabled".toCharArray());
+ assertNull("User 'user4' falsely authenticated.", user);
+
+
+ user = htpasswd.authenticate("plain", "text".toCharArray());
+ assertNull("User 'plain' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("plain", "password".toCharArray());
+ assertNull("User 'plain' falsely authenticated.", user);
+
+
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
+
+ user = htpasswd.authenticate("crypt", "".toCharArray());
+ assertNull("User 'cyrpt' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("crypt", "passwd".toCharArray());
+ assertNull("User 'crypt' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("md5", "".toCharArray());
+ assertNull("User 'md5' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("md5", "pwd".toCharArray());
+ assertNull("User 'md5' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("sha", "".toCharArray());
+ assertNull("User 'sha' falsely authenticated.", user);
+
+ user = htpasswd.authenticate("sha", "letmein".toCharArray());
+ assertNull("User 'sha' falsely authenticated.", user);
+
+
+ user = htpasswd.authenticate(" tabbed", "frontAndBack".toCharArray());
+ assertNull("User 'tabbed' falsely authenticated.", user);
+
+ user = htpasswd.authenticate(" leading", "whitespace".toCharArray());
+ assertNull("User 'leading' falsely authenticated.", user);
+ }
+
+
+ @Test
+ public void testCleartextIntrusion()
+ {
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
+ assertNull(htpasswd.authenticate("md5", "$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0".toCharArray()));
+ assertNull(htpasswd.authenticate("sha", "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=".toCharArray()));
+
+ assertNull(htpasswd.authenticate("user1", "#externalAccount".toCharArray()));
+
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
+ assertNull(htpasswd.authenticate("md5", "$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0".toCharArray()));
+ assertNull(htpasswd.authenticate("sha", "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=".toCharArray()));
+
+ assertNull(htpasswd.authenticate("user1", "#externalAccount".toCharArray()));
+ }
+
+
+ @Test
+ public void testCryptVsPlaintext()
+ {
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
+ assertNull(htpasswd.authenticate("crypt", "6TmlbxqZ2kBIA".toCharArray()));
+ assertNotNull(htpasswd.authenticate("crypt", "password".toCharArray()));
+
+ MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
+ assertNotNull(htpasswd.authenticate("crypt", "6TmlbxqZ2kBIA".toCharArray()));
+ assertNull(htpasswd.authenticate("crypt", "password".toCharArray()));
+ }
+
+ @Test
+ public void testChangeHtpasswdFile()
+ {
+ UserModel user;
+
+ // User default set up.
+ user = htpasswd.authenticate("md5", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("md5", user.username);
+
+ user = htpasswd.authenticate("sha", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("sha", user.username);
+
+ user = htpasswd.authenticate("blueone", "GoBlue!".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("bluetwo", "YayBlue!".toCharArray());
+ assertNull(user);
+
+
+ // Switch to different htpasswd file.
+ getSettings(RESOURCE_DIR + "htpasswd-user", null, null);
+
+ user = htpasswd.authenticate("md5", "password".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("sha", "password".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("blueone", "GoBlue!".toCharArray());
+ assertNotNull(user);
+ assertEquals("blueone", user.username);
+
+ user = htpasswd.authenticate("bluetwo", "YayBlue!".toCharArray());
+ assertNotNull(user);
+ assertEquals("bluetwo", user.username);
+ }
+
+
+ @Test
+ public void testChangeHtpasswdFileNotExisting()
+ {
+ UserModel user;
+
+ // User default set up.
+ user = htpasswd.authenticate("md5", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("md5", user.username);
+
+ user = htpasswd.authenticate("sha", "password".toCharArray());
+ assertNotNull(user);
+ assertEquals("sha", user.username);
+
+ user = htpasswd.authenticate("blueone", "GoBlue!".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("bluetwo", "YayBlue!".toCharArray());
+ assertNull(user);
+
+
+ // Switch to different htpasswd file that doesn't exist.
+ // Currently we stop working with old users upon this change.
+ getSettings(RESOURCE_DIR + "no-such-file", null, null);
+
+ user = htpasswd.authenticate("md5", "password".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("sha", "password".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("blueone", "GoBlue!".toCharArray());
+ assertNull(user);
+
+ user = htpasswd.authenticate("bluetwo", "YayBlue!".toCharArray());
+ assertNull(user);
+ }
+
+}
+++ /dev/null
-/*
- * 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.tests;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.apache.commons.io.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.gitblit.HtpasswdUserService;
-import com.gitblit.models.UserModel;
-import com.gitblit.tests.mock.MemorySettings;
-import com.gitblit.tests.mock.MockRuntimeManager;
-import com.gitblit.utils.StringUtils;
-
-/**
- * Test the Htpasswd user service.
- *
- */
-public class HtpasswdUserServiceTest extends GitblitUnitTest {
-
- private static final String RESOURCE_DIR = "src/test/resources/htpasswdUSTest/";
- private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
-
- private static final int NUM_USERS_HTPASSWD = 10;
-
- private static final MemorySettings MS = new MemorySettings(new HashMap<String, Object>());
-
- private HtpasswdUserService htpwdUserService;
-
-
- private MemorySettings getSettings( String userfile, String groupfile, Boolean overrideLA)
- {
- MS.put("realm.htpasswd.backingUserService", RESOURCE_DIR + "users.conf");
- MS.put("realm.htpasswd.userfile", (userfile == null) ? (RESOURCE_DIR+"htpasswd") : userfile);
- MS.put("realm.htpasswd.groupfile", (groupfile == null) ? (RESOURCE_DIR+"htgroup") : groupfile);
- MS.put("realm.htpasswd.overrideLocalAuthentication", (overrideLA == null) ? "false" : overrideLA.toString());
- // Default to keep test the same on all platforms.
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
-
- return MS;
- }
-
- private MemorySettings getSettings()
- {
- return getSettings(null, null, null);
- }
-
- private MemorySettings getSettings(boolean overrideLA)
- {
- return getSettings(null, null, new Boolean(overrideLA));
- }
-
-
- private void setupUS()
- {
- htpwdUserService = new HtpasswdUserService();
- htpwdUserService.setup(new MockRuntimeManager(getSettings()));
- }
-
- private void setupUS(boolean overrideLA)
- {
- htpwdUserService = new HtpasswdUserService();
- htpwdUserService.setup(new MockRuntimeManager(getSettings(overrideLA)));
- }
-
-
- private void copyInFiles() throws IOException
- {
- File dir = new File(RESOURCE_DIR);
- FilenameFilter filter = new FilenameFilter() {
- @Override
- public boolean accept(File dir, String file) {
- return file.endsWith(".in");
- }
- };
- for (File inf : dir.listFiles(filter)) {
- File dest = new File(inf.getParent(), inf.getName().substring(0, inf.getName().length()-3));
- FileUtils.copyFile(inf, dest);
- }
- }
-
-
- private void deleteGeneratedFiles()
- {
- File dir = new File(RESOURCE_DIR);
- FilenameFilter filter = new FilenameFilter() {
- @Override
- public boolean accept(File dir, String file) {
- return !(file.endsWith(".in"));
- }
- };
- for (File file : dir.listFiles(filter)) {
- file.delete();
- }
- }
-
-
- @Before
- public void setup() throws IOException
- {
- copyInFiles();
- setupUS();
- }
-
-
- @After
- public void tearDown()
- {
- deleteGeneratedFiles();
- }
-
-
-
- @Test
- public void testSetup() throws IOException
- {
- assertEquals(NUM_USERS_HTPASSWD, htpwdUserService.getNumberHtpasswdUsers());
- }
-
-
- @Test
- public void testAuthenticate()
- {
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
- UserModel user = htpwdUserService.authenticate("user1", "pass1".toCharArray());
- assertNotNull(user);
- assertEquals("user1", user.username);
-
- user = htpwdUserService.authenticate("user2", "pass2".toCharArray());
- assertNotNull(user);
- assertEquals("user2", user.username);
-
- // Test different encryptions
- user = htpwdUserService.authenticate("plain", "passWord".toCharArray());
- assertNotNull(user);
- assertEquals("plain", user.username);
-
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
- user = htpwdUserService.authenticate("crypt", "password".toCharArray());
- assertNotNull(user);
- assertEquals("crypt", user.username);
-
- user = htpwdUserService.authenticate("md5", "password".toCharArray());
- assertNotNull(user);
- assertEquals("md5", user.username);
-
- user = htpwdUserService.authenticate("sha", "password".toCharArray());
- assertNotNull(user);
- assertEquals("sha", user.username);
-
-
- // Test leading and trailing whitespace
- user = htpwdUserService.authenticate("trailing", "whitespace".toCharArray());
- assertNotNull(user);
- assertEquals("trailing", user.username);
-
- user = htpwdUserService.authenticate("tabbed", "frontAndBack".toCharArray());
- assertNotNull(user);
- assertEquals("tabbed", user.username);
-
- user = htpwdUserService.authenticate("leading", "whitespace".toCharArray());
- assertNotNull(user);
- assertEquals("leading", user.username);
-
-
- // Test local account
- user = htpwdUserService.authenticate("admin", "admin".toCharArray());
- assertNotNull(user);
- assertEquals("admin", user.username);
- }
-
-
- @Test
- public void testAttributes()
- {
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
- UserModel user = htpwdUserService.authenticate("user1", "pass1".toCharArray());
- assertNotNull(user);
- assertEquals("El Capitan", user.displayName);
- assertEquals("cheffe@example.com", user.emailAddress);
- assertTrue(user.canAdmin);
-
- user = htpwdUserService.authenticate("user2", "pass2".toCharArray());
- assertNotNull(user);
- assertEquals("User Two", user.displayName);
- assertTrue(user.canCreate);
- assertTrue(user.canFork);
-
-
- user = htpwdUserService.authenticate("admin", "admin".toCharArray());
- assertNotNull(user);
- assertTrue(user.canAdmin);
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("Local User", user.displayName);
- assertFalse(user.canCreate);
- assertFalse(user.canFork);
- assertFalse(user.canAdmin);
- }
-
-
- @Test
- public void testAuthenticateDenied()
- {
- UserModel user = null;
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
- user = htpwdUserService.authenticate("user1", "".toCharArray());
- assertNull("User 'user1' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("user1", "pass2".toCharArray());
- assertNull("User 'user1' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("user2", "lalala".toCharArray());
- assertNull("User 'user2' falsely authenticated.", user);
-
-
- user = htpwdUserService.authenticate("user3", "disabled".toCharArray());
- assertNull("User 'user3' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("user4", "disabled".toCharArray());
- assertNull("User 'user4' falsely authenticated.", user);
-
-
- user = htpwdUserService.authenticate("plain", "text".toCharArray());
- assertNull("User 'plain' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("plain", "password".toCharArray());
- assertNull("User 'plain' falsely authenticated.", user);
-
-
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
-
- user = htpwdUserService.authenticate("crypt", "".toCharArray());
- assertNull("User 'cyrpt' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("crypt", "passwd".toCharArray());
- assertNull("User 'crypt' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("md5", "".toCharArray());
- assertNull("User 'md5' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("md5", "pwd".toCharArray());
- assertNull("User 'md5' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("sha", "".toCharArray());
- assertNull("User 'sha' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate("sha", "letmein".toCharArray());
- assertNull("User 'sha' falsely authenticated.", user);
-
-
- user = htpwdUserService.authenticate(" tabbed", "frontAndBack".toCharArray());
- assertNull("User 'tabbed' falsely authenticated.", user);
-
- user = htpwdUserService.authenticate(" leading", "whitespace".toCharArray());
- assertNull("User 'leading' falsely authenticated.", user);
- }
-
-
- @Test
- public void testNewLocalAccount()
- {
- UserModel newUser = new UserModel("newlocal");
- newUser.displayName = "Local User 2";
- newUser.password = StringUtils.MD5_TYPE + StringUtils.getMD5("localPwd2");
- assertTrue("Failed to add local account.", htpwdUserService.updateUserModel(newUser));
-
- UserModel localAccount = htpwdUserService.authenticate(newUser.username, "localPwd2".toCharArray());
- assertNotNull(localAccount);
- assertEquals(newUser, localAccount);
-
- localAccount = htpwdUserService.authenticate(newUser.username, "localPwd2".toCharArray());
- assertNotNull(localAccount);
- assertEquals(newUser, localAccount);
-
- assertTrue("Failed to delete local account.", htpwdUserService.deleteUser(localAccount.username));
- assertNull(htpwdUserService.authenticate(newUser.username, "localPwd2".toCharArray()));
- }
-
-
- @Test
- public void testCleartextIntrusion()
- {
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
- assertNull(htpwdUserService.authenticate("md5", "$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0".toCharArray()));
- assertNull(htpwdUserService.authenticate("sha", "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=".toCharArray()));
-
- assertNull(htpwdUserService.authenticate("user1", "#externalAccount".toCharArray()));
-
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
- assertNull(htpwdUserService.authenticate("md5", "$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0".toCharArray()));
- assertNull(htpwdUserService.authenticate("sha", "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=".toCharArray()));
-
- assertNull(htpwdUserService.authenticate("user1", "#externalAccount".toCharArray()));
- }
-
-
- @Test
- public void testCryptVsPlaintext()
- {
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "false");
- assertNull(htpwdUserService.authenticate("crypt", "6TmlbxqZ2kBIA".toCharArray()));
- assertNotNull(htpwdUserService.authenticate("crypt", "password".toCharArray()));
-
- MS.put(KEY_SUPPORT_PLAINTEXT_PWD, "true");
- assertNotNull(htpwdUserService.authenticate("crypt", "6TmlbxqZ2kBIA".toCharArray()));
- assertNull(htpwdUserService.authenticate("crypt", "password".toCharArray()));
- }
-
-
- /*
- * Test case: User exists in user.conf with a local password and in htpasswd with an external password.
- * If overrideLocalAuthentication is false, the local account takes precedence and is never updated.
- */
- @Test
- public void testPreparedAccountPreferLocal() throws IOException
- {
- setupUS(false);
-
- UserModel user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
-
- deleteGeneratedFiles();
- copyInFiles();
- setupUS(false);
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
- }
-
-
- /*
- * Test case: User exists in user.conf with a local password and in htpasswd with an external password.
- * If overrideLocalAuthentication is true, the external account takes precedence,
- * the initial local password is never used and discarded.
- */
- @Test
- public void testPreparedAccountPreferExternal() throws IOException
- {
- setupUS(true);
-
- UserModel user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
-
- deleteGeneratedFiles();
- copyInFiles();
- setupUS(true);
-
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
- // Make sure no authentication by using the string constant for external accounts is possible.
- user = htpwdUserService.authenticate("leaderred", "#externalAccount".toCharArray());
- assertNull(user);
- }
-
-
- /*
- * Test case: User exists in user.conf with a local password and in htpasswd with an external password.
- * If overrideLocalAuthentication is true, the external account takes precedence,
- * the initial local password is never used and discarded.
- */
- @Test
- public void testPreparedAccountChangeSetting() throws IOException
- {
- getSettings(false);
-
- UserModel user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
-
- getSettings(true);
-
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
- // Make sure no authentication by using the string constant for external accounts is possible.
- user = htpwdUserService.authenticate("leaderred", "#externalAccount".toCharArray());
- assertNull(user);
-
-
- getSettings(false);
- // The preference is now back to local accounts but since the prepared account got switched
- // to an external account, it will stay this way.
-
- user = htpwdUserService.authenticate("leaderred", "localPassword".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("leaderred", "externalPassword".toCharArray());
- assertNotNull(user);
- assertEquals("leaderred", user.getName());
-
- user = htpwdUserService.authenticate("staylocal", "localUser".toCharArray());
- assertNotNull(user);
- assertEquals("staylocal", user.getName());
-
- // Make sure no authentication by using the string constant for external accounts is possible.
- user = htpwdUserService.authenticate("leaderred", "#externalAccount".toCharArray());
- assertNull(user);
- }
-
-
- @Test
- public void testChangeHtpasswdFile()
- {
- UserModel user;
-
- // User default set up.
- user = htpwdUserService.authenticate("md5", "password".toCharArray());
- assertNotNull(user);
- assertEquals("md5", user.username);
-
- user = htpwdUserService.authenticate("sha", "password".toCharArray());
- assertNotNull(user);
- assertEquals("sha", user.username);
-
- user = htpwdUserService.authenticate("blueone", "GoBlue!".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("bluetwo", "YayBlue!".toCharArray());
- assertNull(user);
-
-
- // Switch to different htpasswd file.
- getSettings(RESOURCE_DIR + "htpasswd-user", null, null);
-
- user = htpwdUserService.authenticate("md5", "password".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("sha", "password".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("blueone", "GoBlue!".toCharArray());
- assertNotNull(user);
- assertEquals("blueone", user.username);
-
- user = htpwdUserService.authenticate("bluetwo", "YayBlue!".toCharArray());
- assertNotNull(user);
- assertEquals("bluetwo", user.username);
- }
-
-
- @Test
- public void testChangeHtpasswdFileNotExisting()
- {
- UserModel user;
-
- // User default set up.
- user = htpwdUserService.authenticate("md5", "password".toCharArray());
- assertNotNull(user);
- assertEquals("md5", user.username);
-
- user = htpwdUserService.authenticate("sha", "password".toCharArray());
- assertNotNull(user);
- assertEquals("sha", user.username);
-
- user = htpwdUserService.authenticate("blueone", "GoBlue!".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("bluetwo", "YayBlue!".toCharArray());
- assertNull(user);
-
-
- // Switch to different htpasswd file that doesn't exist.
- // Currently we stop working with old users upon this change.
- getSettings(RESOURCE_DIR + "no-such-file", null, null);
-
- user = htpwdUserService.authenticate("md5", "password".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("sha", "password".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("blueone", "GoBlue!".toCharArray());
- assertNull(user);
-
- user = htpwdUserService.authenticate("bluetwo", "YayBlue!".toCharArray());
- assertNull(user);
- }
-
-}
--- /dev/null
+/*
+ * Copyright 2012 John Crygier
+ * Copyright 2012 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.tests;
+
+import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.auth.LdapAuthProvider;
+import com.gitblit.manager.RuntimeManager;
+import com.gitblit.manager.UserManager;
+import com.gitblit.models.UserModel;
+import com.gitblit.tests.mock.MemorySettings;
+import com.unboundid.ldap.listener.InMemoryDirectoryServer;
+import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
+import com.unboundid.ldap.listener.InMemoryListenerConfig;
+import com.unboundid.ldif.LDIFReader;
+
+/**
+ * An Integration test for LDAP that tests going against an in-memory UnboundID
+ * LDAP server.
+ *
+ * @author jcrygier
+ *
+ */
+public class LdapAuthenticationTest extends GitblitUnitTest {
+
+ private static final String RESOURCE_DIR = "src/test/resources/ldap/";
+
+ private LdapAuthProvider ldap;
+
+ static int ldapPort = 1389;
+
+ @BeforeClass
+ public static void createInMemoryLdapServer() throws Exception {
+ InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=MyDomain");
+ config.addAdditionalBindCredentials("cn=Directory Manager", "password");
+ config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", ldapPort));
+ config.setSchema(null);
+
+ InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
+ ds.importFromLDIF(true, new LDIFReader(new FileInputStream(RESOURCE_DIR + "sampledata.ldif")));
+ ds.startListening();
+ }
+
+ @Before
+ public void newLdapAuthentication() {
+ ldap = newLdapAuthentication(getSettings());
+ }
+
+ public LdapAuthProvider newLdapAuthentication(IStoredSettings settings) {
+ RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start();
+ UserManager users = new UserManager(runtime).start();
+ LdapAuthProvider ldap = new LdapAuthProvider();
+ ldap.setup(runtime, users);
+ return ldap;
+ }
+
+ private MemorySettings getSettings() {
+ Map<String, Object> backingMap = new HashMap<String, Object>();
+ backingMap.put("realm.userService", RESOURCE_DIR + "users.conf");
+ backingMap.put("realm.ldap.server", "ldap://localhost:" + ldapPort);
+ backingMap.put("realm.ldap.domain", "");
+ backingMap.put("realm.ldap.username", "cn=Directory Manager");
+ backingMap.put("realm.ldap.password", "password");
+ backingMap.put("realm.ldap.backingUserService", "users.conf");
+ backingMap.put("realm.ldap.maintainTeams", "true");
+ backingMap.put("realm.ldap.accountBase", "OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain");
+ backingMap.put("realm.ldap.accountPattern", "(&(objectClass=person)(sAMAccountName=${username}))");
+ backingMap.put("realm.ldap.groupBase", "OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain");
+ backingMap.put("realm.ldap.groupPattern", "(&(objectClass=group)(member=${dn}))");
+ backingMap.put("realm.ldap.admins", "UserThree @Git_Admins \"@Git Admins\"");
+ backingMap.put("realm.ldap.displayName", "displayName");
+ backingMap.put("realm.ldap.email", "email");
+
+ MemorySettings ms = new MemorySettings(backingMap);
+ return ms;
+ }
+
+ @Test
+ public void testAuthenticate() {
+ UserModel userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
+ assertNotNull(userOneModel);
+ assertNotNull(userOneModel.getTeam("git_admins"));
+ assertNotNull(userOneModel.getTeam("git_users"));
+ assertTrue(userOneModel.canAdmin);
+
+ UserModel userOneModelFailedAuth = ldap.authenticate("UserOne", "userTwoPassword".toCharArray());
+ assertNull(userOneModelFailedAuth);
+
+ UserModel userTwoModel = ldap.authenticate("UserTwo", "userTwoPassword".toCharArray());
+ assertNotNull(userTwoModel);
+ assertNotNull(userTwoModel.getTeam("git_users"));
+ assertNull(userTwoModel.getTeam("git_admins"));
+ assertNotNull(userTwoModel.getTeam("git admins"));
+ assertTrue(userTwoModel.canAdmin);
+
+ UserModel userThreeModel = ldap.authenticate("UserThree", "userThreePassword".toCharArray());
+ assertNotNull(userThreeModel);
+ assertNotNull(userThreeModel.getTeam("git_users"));
+ assertNull(userThreeModel.getTeam("git_admins"));
+ assertTrue(userThreeModel.canAdmin);
+ }
+
+ @Test
+ public void testDisplayName() {
+ UserModel userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
+ assertNotNull(userOneModel);
+ assertEquals("User One", userOneModel.displayName);
+
+ // Test more complicated scenarios - concat
+ MemorySettings ms = getSettings();
+ ms.put("realm.ldap.displayName", "${personalTitle}. ${givenName} ${surname}");
+ ldap = newLdapAuthentication(ms);
+
+ userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
+ assertNotNull(userOneModel);
+ assertEquals("Mr. User One", userOneModel.displayName);
+ }
+
+ @Test
+ public void testEmail() {
+ UserModel userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
+ assertNotNull(userOneModel);
+ assertEquals("userone@gitblit.com", userOneModel.emailAddress);
+
+ // Test more complicated scenarios - concat
+ MemorySettings ms = getSettings();
+ ms.put("realm.ldap.email", "${givenName}.${surname}@gitblit.com");
+ ldap = newLdapAuthentication(ms);
+
+ userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
+ assertNotNull(userOneModel);
+ assertEquals("User.One@gitblit.com", userOneModel.emailAddress);
+ }
+
+ @Test
+ public void testLdapInjection() {
+ // Inject so "(&(objectClass=person)(sAMAccountName=${username}))" becomes "(&(objectClass=person)(sAMAccountName=*)(userPassword=userOnePassword))"
+ // Thus searching by password
+
+ UserModel userOneModel = ldap.authenticate("*)(userPassword=userOnePassword", "userOnePassword".toCharArray());
+ assertNull(userOneModel);
+ }
+
+}
+++ /dev/null
-/*
- * Copyright 2012 John Crygier
- * Copyright 2012 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.tests;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import com.gitblit.LdapUserService;
-import com.gitblit.models.UserModel;
-import com.gitblit.tests.mock.MemorySettings;
-import com.gitblit.tests.mock.MockRuntimeManager;
-import com.gitblit.utils.StringUtils;
-import com.unboundid.ldap.listener.InMemoryDirectoryServer;
-import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
-import com.unboundid.ldap.listener.InMemoryListenerConfig;
-import com.unboundid.ldif.LDIFReader;
-
-/**
- * An Integration test for LDAP that tests going against an in-memory UnboundID
- * LDAP server.
- *
- * @author jcrygier
- *
- */
-public class LdapUserServiceTest extends GitblitUnitTest {
-
- private LdapUserService ldapUserService;
-
- static int ldapPort = 1389;
-
- @BeforeClass
- public static void createInMemoryLdapServer() throws Exception {
- InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=MyDomain");
- config.addAdditionalBindCredentials("cn=Directory Manager", "password");
- config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", ldapPort));
- config.setSchema(null);
-
- InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
- ds.importFromLDIF(true, new LDIFReader(LdapUserServiceTest.class.getResourceAsStream("resources/ldapUserServiceSampleData.ldif")));
- ds.startListening();
- }
-
- @Before
- public void createLdapUserService() {
- ldapUserService = new LdapUserService();
- ldapUserService.setup(new MockRuntimeManager(getSettings()));
- }
-
- private MemorySettings getSettings() {
- Map<String, Object> backingMap = new HashMap<String, Object>();
- backingMap.put("realm.ldap.server", "ldap://localhost:" + ldapPort);
- backingMap.put("realm.ldap.domain", "");
- backingMap.put("realm.ldap.username", "cn=Directory Manager");
- backingMap.put("realm.ldap.password", "password");
- backingMap.put("realm.ldap.backingUserService", "users.conf");
- backingMap.put("realm.ldap.maintainTeams", "true");
- backingMap.put("realm.ldap.accountBase", "OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain");
- backingMap.put("realm.ldap.accountPattern", "(&(objectClass=person)(sAMAccountName=${username}))");
- backingMap.put("realm.ldap.groupBase", "OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain");
- backingMap.put("realm.ldap.groupPattern", "(&(objectClass=group)(member=${dn}))");
- backingMap.put("realm.ldap.admins", "UserThree @Git_Admins \"@Git Admins\"");
- backingMap.put("realm.ldap.displayName", "displayName");
- backingMap.put("realm.ldap.email", "email");
-
- MemorySettings ms = new MemorySettings(backingMap);
- return ms;
- }
-
- @Test
- public void testAuthenticate() {
- UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
- assertNotNull(userOneModel);
- assertNotNull(userOneModel.getTeam("git_admins"));
- assertNotNull(userOneModel.getTeam("git_users"));
- assertTrue(userOneModel.canAdmin);
-
- UserModel userOneModelFailedAuth = ldapUserService.authenticate("UserOne", "userTwoPassword".toCharArray());
- assertNull(userOneModelFailedAuth);
-
- UserModel userTwoModel = ldapUserService.authenticate("UserTwo", "userTwoPassword".toCharArray());
- assertNotNull(userTwoModel);
- assertNotNull(userTwoModel.getTeam("git_users"));
- assertNull(userTwoModel.getTeam("git_admins"));
- assertNotNull(userTwoModel.getTeam("git admins"));
- assertTrue(userTwoModel.canAdmin);
-
- UserModel userThreeModel = ldapUserService.authenticate("UserThree", "userThreePassword".toCharArray());
- assertNotNull(userThreeModel);
- assertNotNull(userThreeModel.getTeam("git_users"));
- assertNull(userThreeModel.getTeam("git_admins"));
- assertTrue(userThreeModel.canAdmin);
- }
-
- @Test
- public void testDisplayName() {
- UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
- assertNotNull(userOneModel);
- assertEquals("User One", userOneModel.displayName);
-
- // Test more complicated scenarios - concat
- MemorySettings ms = getSettings();
- ms.put("realm.ldap.displayName", "${personalTitle}. ${givenName} ${surname}");
- ldapUserService = new LdapUserService();
- ldapUserService.setup(new MockRuntimeManager(ms));
-
- userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
- assertNotNull(userOneModel);
- assertEquals("Mr. User One", userOneModel.displayName);
- }
-
- @Test
- public void testEmail() {
- UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
- assertNotNull(userOneModel);
- assertEquals("userone@gitblit.com", userOneModel.emailAddress);
-
- // Test more complicated scenarios - concat
- MemorySettings ms = getSettings();
- ms.put("realm.ldap.email", "${givenName}.${surname}@gitblit.com");
- ldapUserService = new LdapUserService();
- ldapUserService.setup(new MockRuntimeManager(ms));
-
- userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
- assertNotNull(userOneModel);
- assertEquals("User.One@gitblit.com", userOneModel.emailAddress);
- }
-
- @Test
- public void testLdapInjection() {
- // Inject so "(&(objectClass=person)(sAMAccountName=${username}))" becomes "(&(objectClass=person)(sAMAccountName=*)(userPassword=userOnePassword))"
- // Thus searching by password
-
- UserModel userOneModel = ldapUserService.authenticate("*)(userPassword=userOnePassword", "userOnePassword".toCharArray());
- assertNull(userOneModel);
- }
-
- @Test
- public void testLocalAccount() {
- UserModel localAccount = new UserModel("bruce");
- localAccount.displayName = "Bruce Campbell";
- localAccount.password = StringUtils.MD5_TYPE + StringUtils.getMD5("gimmesomesugar");
- ldapUserService.deleteUser(localAccount.username);
- assertTrue("Failed to add local account",
- ldapUserService.updateUserModel(localAccount));
- assertEquals("Accounts are not equal!",
- localAccount,
- ldapUserService.authenticate(localAccount.username, "gimmesomesugar".toCharArray()));
- assertTrue("Failed to delete local account!",
- ldapUserService.deleteUser(localAccount.username));
- }
-
-}
--- /dev/null
+package com.gitblit.tests;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.auth.RedmineAuthProvider;
+import com.gitblit.manager.RuntimeManager;
+import com.gitblit.manager.UserManager;
+import com.gitblit.models.UserModel;
+import com.gitblit.tests.mock.MemorySettings;
+
+public class RedmineAuthenticationTest extends GitblitUnitTest {
+
+ private static final String JSON = "{\"user\":{\"created_on\":\"2011-03-28T00:41:29Z\",\"lastname\":\"foo\","
+ + "\"last_login_on\":\"2012-09-06T23:59:26Z\",\"firstname\":\"baz\","
+ + "\"id\":4,\"login\":\"RedmineUserId\",\"mail\":\"baz@example.com\"}}";
+
+ private static final String NOT_ADMIN_JSON = "{\"user\":{\"lastname\":\"foo\","
+ + "\"last_login_on\":\"2012-09-08T13:59:01Z\",\"created_on\":\"2009-03-17T14:25:50Z\","
+ + "\"mail\":\"baz@example.com\",\"id\":5,\"firstname\":\"baz\"}}";
+
+ MemorySettings getSettings() {
+ return new MemorySettings(new HashMap<String, Object>());
+ }
+
+ RedmineAuthProvider newRedmineAuthentication(IStoredSettings settings) {
+ RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start();
+ UserManager users = new UserManager(runtime).start();
+ RedmineAuthProvider redmine = new RedmineAuthProvider();
+ redmine.setup(runtime, users);
+ return redmine;
+ }
+
+ RedmineAuthProvider newRedmineAuthentication() {
+ return newRedmineAuthentication(getSettings());
+ }
+
+ @Test
+ public void testAuthenticate() throws Exception {
+ RedmineAuthProvider redmine = newRedmineAuthentication();
+ redmine.setTestingCurrentUserAsJson(JSON);
+ UserModel userModel = redmine.authenticate("RedmineAdminId", "RedmineAPIKey".toCharArray());
+ assertThat(userModel.getName(), is("redmineadminid"));
+ assertThat(userModel.getDisplayName(), is("baz foo"));
+ assertThat(userModel.emailAddress, is("baz@example.com"));
+ assertNotNull(userModel.cookie);
+ assertThat(userModel.canAdmin, is(true));
+ }
+
+ @Test
+ public void testAuthenticateNotAdminUser() throws Exception {
+ RedmineAuthProvider redmine = newRedmineAuthentication();
+ redmine.setTestingCurrentUserAsJson(NOT_ADMIN_JSON);
+ UserModel userModel = redmine.authenticate("RedmineUserId", "RedmineAPIKey".toCharArray());
+ assertThat(userModel.getName(), is("redmineuserid"));
+ assertThat(userModel.getDisplayName(), is("baz foo"));
+ assertThat(userModel.emailAddress, is("baz@example.com"));
+ assertNotNull(userModel.cookie);
+ assertThat(userModel.canAdmin, is(false));
+ }
+}
+++ /dev/null
-package com.gitblit.tests;\r
-\r
-import static org.hamcrest.CoreMatchers.is;\r
-\r
-import org.junit.Test;\r
-\r
-import com.gitblit.RedmineUserService;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.tests.mock.MockRuntimeManager;\r
-import com.gitblit.utils.StringUtils;\r
-\r
-public class RedmineUserServiceTest extends GitblitUnitTest {\r
-\r
- private static final String JSON = "{\"user\":{\"created_on\":\"2011-03-28T00:41:29Z\",\"lastname\":\"foo\","\r
- + "\"last_login_on\":\"2012-09-06T23:59:26Z\",\"firstname\":\"baz\","\r
- + "\"id\":4,\"login\":\"RedmineUserId\",\"mail\":\"baz@example.com\"}}";\r
-\r
- private static final String NOT_ADMIN_JSON = "{\"user\":{\"lastname\":\"foo\","\r
- + "\"last_login_on\":\"2012-09-08T13:59:01Z\",\"created_on\":\"2009-03-17T14:25:50Z\","\r
- + "\"mail\":\"baz@example.com\",\"id\":5,\"firstname\":\"baz\"}}";\r
-\r
- @Test\r
- public void testAuthenticate() throws Exception {\r
- RedmineUserService redmineUserService = new RedmineUserService();\r
- redmineUserService.setup(new MockRuntimeManager());\r
- redmineUserService.setTestingCurrentUserAsJson(JSON);\r
- UserModel userModel = redmineUserService.authenticate("RedmineAdminId", "RedmineAPIKey".toCharArray());\r
- assertThat(userModel.getName(), is("redmineadminid"));\r
- assertThat(userModel.getDisplayName(), is("baz foo"));\r
- assertThat(userModel.emailAddress, is("baz@example.com"));\r
- assertNotNull(userModel.cookie);\r
- assertThat(userModel.canAdmin, is(true));\r
- }\r
-\r
- @Test\r
- public void testAuthenticateNotAdminUser() throws Exception {\r
- RedmineUserService redmineUserService = new RedmineUserService();\r
- redmineUserService.setup(new MockRuntimeManager());\r
- redmineUserService.setTestingCurrentUserAsJson(NOT_ADMIN_JSON);\r
- UserModel userModel = redmineUserService.authenticate("RedmineUserId", "RedmineAPIKey".toCharArray());\r
- assertThat(userModel.getName(), is("redmineuserid"));\r
- assertThat(userModel.getDisplayName(), is("baz foo"));\r
- assertThat(userModel.emailAddress, is("baz@example.com"));\r
- assertNotNull(userModel.cookie);\r
- assertThat(userModel.canAdmin, is(false));\r
- }\r
-\r
- @Test\r
- public void testLocalAccount() {\r
- RedmineUserService redmineUserService = new RedmineUserService();\r
- redmineUserService.setup(new MockRuntimeManager());\r
-\r
- UserModel localAccount = new UserModel("bruce");\r
- localAccount.displayName = "Bruce Campbell";\r
- localAccount.password = StringUtils.MD5_TYPE + StringUtils.getMD5("gimmesomesugar");\r
- redmineUserService.deleteUser(localAccount.username);\r
- assertTrue("Failed to add local account",\r
- redmineUserService.updateUserModel(localAccount));\r
- assertEquals("Accounts are not equal!",\r
- localAccount,\r
- redmineUserService.authenticate(localAccount.username, "gimmesomesugar".toCharArray()));\r
- assertTrue("Failed to delete local account!",\r
- redmineUserService.deleteUser(localAccount.username));\r
- }\r
-\r
-}\r
assertTrue(newUser.hasRepositoryPermission("repo2"));\r
assertTrue(newUser.hasRepositoryPermission("sub/repo3"));\r
\r
- // confirm authentication of test user\r
- UserModel testUser = service.authenticate("test", "testPassword".toCharArray());\r
- assertEquals("test", testUser.username);\r
- assertEquals("testPassword", testUser.password);\r
-\r
// delete a repository role and confirm role removal from test user\r
service.deleteRepositoryRole("repo2");\r
- testUser = service.getUserModel("test");\r
+ UserModel testUser = service.getUserModel("test");\r
assertEquals(2, testUser.permissions.size());\r
\r
// delete garbage user and confirm user count\r
+++ /dev/null
-dn: DC=MyDomain
-dc: MyDomain
-objectClass: top
-objectClass: domain
-
-dn: OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: MyOrganization
-
-dn: OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: UserControl
-
-dn: OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: Groups
-
-dn: CN=Git_Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: group
-cn: Git_Admins
-sAMAccountName: Git_Admins
-member: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: CN=Git Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: group
-cn: Git Admins
-sAMAccountName: Git_Admins_With_Space
-member: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: group
-cn: Git_Users
-sAMAccountName: Git_Users
-member: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-member: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-member: CN=UserThree,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-member: CN=UserFour,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: Users
-
-dn: OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: US
-
-dn: OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: top
-objectClass: organizationalUnit
-ou: Canada
-
-dn: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: user
-objectClass: person
-sAMAccountName: UserOne
-userPassword: userOnePassword
-displayName: User One
-givenName: User
-surname: One
-personalTitle: Mr
-email: userone@gitblit.com
-memberOf: CN=Git_Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: user
-objectClass: person
-sAMAccountName: UserTwo
-userPassword: userTwoPassword
-displayName: User Two
-givenName: User
-surname: Two
-personalTitle: Mr
-email: usertwo@gitblit.com
-memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-memberOf: CN=Git Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: CN=UserThree,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: user
-objectClass: person
-sAMAccountName: UserThree
-userPassword: userThreePassword
-displayName: User Three
-givenName: User
-surname: Three
-personalTitle: Mrs
-email: userthree@gitblit.com
-memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
-
-dn: CN=UserFour,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
-objectClass: user
-objectClass: person
-sAMAccountName: UserFour
-userPassword: userFourPassword
-displayName: User Four
-givenName: User
-surname: Four
-personalTitle: Miss
-email: userfour@gitblit.com
-memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
\ No newline at end of file
--- /dev/null
+# User database
+
+# htpasswd generated entries
+
+# Plaintext
+redone:Yonder
+
+# Unix crypt() "GoRed!"
+redtwo:RMghf6oG.QwAs
+
+ # Apache MD5 "GoBlue!"
+blueone:$apr1$phRTn/7N$237Owfhw5wZTdTyP9NPvC1
+
+# SHA1 "YayBlue!"
+bluetwo:{SHA}ITMvZI9OU5+Rx324C4jpf+MHAL8=
--- /dev/null
+# User database
+
+user1:pass1
+user2:pass2
+
+# "externalPassword"
+leaderred:{SHA}2VZsTsVQYmWAMfQUjNAScpaAlJI=
+
+#user3:disabled
+ # user4:disabled
+
+# htpasswd generated entries
+
+# Plaintext
+plain:passWord
+
+# Unix crypt() "password"
+crypt:6TmlbxqZ2kBIA
+
+ # Apache MD5 "password"
+md5:$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0
+
+
+# SHA1 "password"
+sha:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
+
+
+trailing:.dAxRAQiOOlN.
+
+ tabbed:$apr1$Is7zctsH$CMAXrGkgACQKgRYuQ5vHq.
+ leading:$apr1$O1nQtxjE$8gN15gMeuF3W1Nr8Yz/6J.
--- /dev/null
+[user "admin"]
+ password = admin
+ cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
+ role = "#admin"
+ role = "#notfederated"
+[user "user1"]
+ password = "#externalAccount"
+ cookie = 6c7d13cf0aa43054d0fb620546e3a4d79e3d3e89
+ displayName = El Capitan
+ emailAddress = cheffe@example.com
+ role = "#admin"
+[user "user2"]
+ password = "#externalAccount"
+ cookie = d15eabb3a83c44a05ccbdaf3bf5fd1402d971e99
+ displayName = User Two
+ role = "#create"
+ role = "#fork"
+[user "staylocal"]
+ password = localUser
+ cookie = 0a99767e0259dc06ccae5ee6349177be289968f3
+ displayName = Local User
+ role = "#none"
+[user "leaderRed"]
+ password = localPassword
+ displayName = Red Leader
+ role = "#create"
+++ /dev/null
-# User database
-
-# htpasswd generated entries
-
-# Plaintext
-redone:Yonder
-
-# Unix crypt() "GoRed!"
-redtwo:RMghf6oG.QwAs
-
- # Apache MD5 "GoBlue!"
-blueone:$apr1$phRTn/7N$237Owfhw5wZTdTyP9NPvC1
-
-# SHA1 "YayBlue!"
-bluetwo:{SHA}ITMvZI9OU5+Rx324C4jpf+MHAL8=
+++ /dev/null
-# User database
-
-user1:pass1
-user2:pass2
-
-# "externalPassword"
-leaderred:{SHA}2VZsTsVQYmWAMfQUjNAScpaAlJI=
-
-#user3:disabled
- # user4:disabled
-
-# htpasswd generated entries
-
-# Plaintext
-plain:passWord
-
-# Unix crypt() "password"
-crypt:6TmlbxqZ2kBIA
-
- # Apache MD5 "password"
-md5:$apr1$qAGGNfli$sAn14mn.WKId/3EQS7KSX0
-
-
-# SHA1 "password"
-sha:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
-
-
-trailing:.dAxRAQiOOlN.
-
- tabbed:$apr1$Is7zctsH$CMAXrGkgACQKgRYuQ5vHq.
- leading:$apr1$O1nQtxjE$8gN15gMeuF3W1Nr8Yz/6J.
+++ /dev/null
-[user "admin"]
- password = admin
- cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
- role = "#admin"
- role = "#notfederated"
-[user "user1"]
- password = "#externalAccount"
- cookie = 6c7d13cf0aa43054d0fb620546e3a4d79e3d3e89
- displayName = El Capitan
- emailAddress = cheffe@example.com
- role = "#admin"
-[user "user2"]
- password = "#externalAccount"
- cookie = d15eabb3a83c44a05ccbdaf3bf5fd1402d971e99
- displayName = User Two
- role = "#create"
- role = "#fork"
-[user "staylocal"]
- password = localUser
- cookie = 0a99767e0259dc06ccae5ee6349177be289968f3
- displayName = Local User
- role = "#none"
-[user "leaderRed"]
- password = localPassword
- displayName = Red Leader
- role = "#create"
--- /dev/null
+dn: DC=MyDomain
+dc: MyDomain
+objectClass: top
+objectClass: domain
+
+dn: OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: MyOrganization
+
+dn: OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: UserControl
+
+dn: OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: Groups
+
+dn: CN=Git_Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: group
+cn: Git_Admins
+sAMAccountName: Git_Admins
+member: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: CN=Git Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: group
+cn: Git Admins
+sAMAccountName: Git_Admins_With_Space
+member: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: group
+cn: Git_Users
+sAMAccountName: Git_Users
+member: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+member: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+member: CN=UserThree,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+member: CN=UserFour,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: Users
+
+dn: OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: US
+
+dn: OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: top
+objectClass: organizationalUnit
+ou: Canada
+
+dn: CN=UserOne,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: user
+objectClass: person
+sAMAccountName: UserOne
+userPassword: userOnePassword
+displayName: User One
+givenName: User
+surname: One
+personalTitle: Mr
+email: userone@gitblit.com
+memberOf: CN=Git_Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: CN=UserTwo,OU=US,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: user
+objectClass: person
+sAMAccountName: UserTwo
+userPassword: userTwoPassword
+displayName: User Two
+givenName: User
+surname: Two
+personalTitle: Mr
+email: usertwo@gitblit.com
+memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+memberOf: CN=Git Admins,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: CN=UserThree,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: user
+objectClass: person
+sAMAccountName: UserThree
+userPassword: userThreePassword
+displayName: User Three
+givenName: User
+surname: Three
+personalTitle: Mrs
+email: userthree@gitblit.com
+memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
+
+dn: CN=UserFour,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
+objectClass: user
+objectClass: person
+sAMAccountName: UserFour
+userPassword: userFourPassword
+displayName: User Four
+givenName: User
+surname: Four
+personalTitle: Miss
+email: userfour@gitblit.com
+memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
\ No newline at end of file
--- /dev/null
+[user "admin"]
+ password = admin
+ cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
+ accountType = LOCAL
+ role = "#admin"
+ role = "#notfederated"
+[user "userthree"]
+ password = "#externalAccount"
+ cookie = d7d3894fc517612aa6c595555b6e1ab8e147e597
+ displayName = User Three
+ emailAddress = userthree@gitblit.com
+ accountType = LDAP
+ role = "#admin"
+[user "userone"]
+ password = "#externalAccount"
+ cookie = c97cd38e50858cd0b389ec61b18fb9a89b4da54c
+ displayName = Mr. User One
+ emailAddress = userone@gitblit.com
+ accountType = LDAP
+ role = "#admin"
+[user "usertwo"]
+ password = "#externalAccount"
+ cookie = 498ca9bd2841d39050fa45d1d737b9f9f767858d
+ displayName = User Two
+ emailAddress = usertwo@gitblit.com
+ accountType = LDAP
+ role = "#admin"
+[user "basic"]
+ password = MD5:f17aaabc20bfe045075927934fed52d2
+ cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
+ accountType = LOCAL
+ role = "#fork"
+ repository = RW:~repocreator/shb.git
+ repository = V:test/gitective.git
+[user "repocreator"]
+ password = MD5:b77e53bb561c47368d133b22e285f60b
+ cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
+ accountType = LOCAL
+ role = "#create"
+[team "Git_Admins"]
+ role = "#none"
+ accountType = LOCAL
+ user = userone
+[team "Git_Users"]
+ role = "#none"
+ accountType = LOCAL
+ user = userone
+ user = usertwo
+ user = userthree
+[team "Git Admins"]
+ role = "#none"
+ accountType = LOCAL
+ user = usertwo