Browse Source

Refactor user services and separate authentication (issue-281)

Change-Id: I336e005e02623fc5e11a4f8b4408bea5465a43fd
tags/v1.4.0
James Moger 10 years ago
parent
commit
04a98505a4
61 changed files with 2567 additions and 3532 deletions
  1. 3
    1
      releases.moxie
  2. 20
    78
      src/main/distrib/data/gitblit.properties
  3. 19
    183
      src/main/java/com/gitblit/ConfigUserService.java
  4. 10
    1
      src/main/java/com/gitblit/Constants.java
  5. 9
    9
      src/main/java/com/gitblit/DaggerModule.java
  6. 27
    28
      src/main/java/com/gitblit/GitBlit.java
  7. 0
    325
      src/main/java/com/gitblit/GitblitUserService.java
  8. 2
    91
      src/main/java/com/gitblit/IUserService.java
  9. 182
    0
      src/main/java/com/gitblit/auth/AuthenticationProvider.java
  10. 67
    152
      src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java
  11. 508
    530
      src/main/java/com/gitblit/auth/LdapAuthProvider.java
  12. 126
    143
      src/main/java/com/gitblit/auth/PAMAuthProvider.java
  13. 186
    203
      src/main/java/com/gitblit/auth/RedmineAuthProvider.java
  14. 17
    28
      src/main/java/com/gitblit/auth/SalesforceAuthProvider.java
  15. 177
    195
      src/main/java/com/gitblit/auth/WindowsAuthProvider.java
  16. 0
    1
      src/main/java/com/gitblit/client/EditTeamDialog.java
  17. 3
    13
      src/main/java/com/gitblit/client/EditUserDialog.java
  18. 5
    5
      src/main/java/com/gitblit/git/GitblitUploadPackFactory.java
  19. 511
    0
      src/main/java/com/gitblit/manager/AuthenticationManager.java
  20. 0
    7
      src/main/java/com/gitblit/manager/GitblitManager.java
  21. 42
    1
      src/main/java/com/gitblit/manager/IAuthenticationManager.java
  22. 2
    259
      src/main/java/com/gitblit/manager/IUserManager.java
  23. 0
    340
      src/main/java/com/gitblit/manager/SessionManager.java
  24. 54
    167
      src/main/java/com/gitblit/manager/UserManager.java
  25. 0
    8
      src/main/java/com/gitblit/models/ServerSettings.java
  26. 7
    0
      src/main/java/com/gitblit/models/TeamModel.java
  27. 3
    3
      src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java
  28. 5
    5
      src/main/java/com/gitblit/servlet/AuthenticationFilter.java
  29. 3
    3
      src/main/java/com/gitblit/servlet/DownloadZipFilter.java
  30. 5
    5
      src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java
  31. 3
    3
      src/main/java/com/gitblit/servlet/GitFilter.java
  32. 2
    2
      src/main/java/com/gitblit/servlet/GitblitContext.java
  33. 3
    3
      src/main/java/com/gitblit/servlet/PagesFilter.java
  34. 3
    3
      src/main/java/com/gitblit/servlet/RpcFilter.java
  35. 5
    5
      src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java
  36. 3
    3
      src/main/java/com/gitblit/servlet/SyndicationFilter.java
  37. 6
    6
      src/main/java/com/gitblit/wicket/GitBlitWebApp.java
  38. 2
    2
      src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
  39. 1
    1
      src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
  40. 5
    9
      src/main/java/com/gitblit/wicket/pages/EditUserPage.java
  41. 1
    1
      src/main/java/com/gitblit/wicket/pages/LogoutPage.java
  42. 3
    3
      src/main/java/com/gitblit/wicket/pages/RootPage.java
  43. 2
    2
      src/main/java/com/gitblit/wicket/pages/SessionPage.java
  44. 1
    1
      src/main/java/com/gitblit/wicket/panels/TeamsPanel.java
  45. 1
    2
      src/main/java/com/gitblit/wicket/panels/UsersPanel.java
  46. 9
    14
      src/site/setup_authentication.mkd
  47. 2
    4
      src/test/config/test-users.conf
  48. 2
    2
      src/test/java/com/gitblit/tests/GitBlitSuite.java
  49. 1
    1
      src/test/java/com/gitblit/tests/GitBlitTest.java
  50. 3
    3
      src/test/java/com/gitblit/tests/GitblitUnitTest.java
  51. 365
    0
      src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java
  52. 0
    569
      src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java
  53. 32
    37
      src/test/java/com/gitblit/tests/LdapAuthenticationTest.java
  54. 65
    0
      src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java
  55. 0
    66
      src/test/java/com/gitblit/tests/RedmineUserServiceTest.java
  56. 1
    6
      src/test/java/com/gitblit/tests/UserServiceTest.java
  57. 0
    0
      src/test/resources/htpasswd/htpasswd-user.in
  58. 0
    0
      src/test/resources/htpasswd/htpasswd.in
  59. 0
    0
      src/test/resources/htpasswd/users.conf.in
  60. 0
    0
      src/test/resources/ldap/sampledata.ldif
  61. 53
    0
      src/test/resources/ldap/users.conf

+ 3
- 1
releases.moxie View File

@@ -20,6 +20,7 @@ r20: {
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)
@@ -58,11 +59,12 @@ r20: {
- { 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

+ 20
- 78
src/main/distrib/data/gitblit.properties View File

@@ -562,17 +562,8 @@ web.allowCookieAuthentication = true
web.projectsFile = ${baseFolder}/projects.conf
# Either the full path to a user config file (users.conf)
# OR the full path to a simple user properties file (users.properties)
# OR a fully qualified class name that implements the IUserService interface.
#
# Alternative user services:
# com.gitblit.LdapUserService
# com.gitblit.RedmineUserService
# com.gitblit.SalesforceUserService
# com.gitblit.WindowsUserService
# com.gitblit.PAMUserService
# com.gitblit.HtpasswdUserService
#
# Any custom user service implementation must have a public default constructor.
#
# SINCE 0.5.0
@@ -580,6 +571,25 @@ web.projectsFile = ${baseFolder}/projects.conf
# BASEFOLDER
realm.userService = ${baseFolder}/users.conf
# Ordered list of external authentication providers which will be used if
# authentication against the local user service fails.
#
# Valid providers are:
#
# htpasswd
# ldap
# pam
# redmine
# salesforce
# windows
# e.g. realm.authenticationProviders = htpasswd windows
#
# SINCE 1.4.0
# RESTART REQUIRED
# SPACE-DELIMITED
realm.authenticationProviders =
# How to store passwords.
# Valid values are plain, md5, or combined-md5. md5 is the hash of password.
# combined-md5 is the hash of username.toLowerCase()+password.
@@ -1331,15 +1341,6 @@ federation.sets =
# SINCE 1.3.0
realm.container.autoCreateAccounts = false
# The WindowsUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# RESTART REQUIRED
# BASEFOLDER
# SINCE 1.3.0
realm.windows.backingUserService = ${baseFolder}/users.conf
# Allow or prohibit Windows guest account logins
#
# SINCE 1.3.0
@@ -1357,30 +1358,12 @@ realm.windows.allowGuests = false
# SINCE 1.3.0
realm.windows.defaultDomain =
# The PAMUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# RESTART REQUIRED
# BASEFOLDER
# SINCE 1.3.1
realm.pam.backingUserService = ${baseFolder}/users.conf
# The PAM service name for authentication.
# default: system-auth
#
# SINCE 1.3.1
realm.pam.serviceName = system-auth
# The HtpasswdUserService must be backed by another user service for standard user
# and team management and attributes. This can be one of the local Gitblit user services.
# default: users.conf
#
# RESTART REQUIRED
# BASEFOLDER
# SINCE 1.3.2
realm.htpasswd.backingUserService = ${baseFolder}/users.conf
# The Apache htpasswd file that contains the users and passwords.
# default: ${baseFolder}/htpasswd
#
@@ -1389,30 +1372,6 @@ realm.htpasswd.backingUserService = ${baseFolder}/users.conf
# SINCE 1.3.2
realm.htpasswd.userfile = ${baseFolder}/htpasswd
# Determines how accounts are looked up upon login.
#
# If set to false, then authentication for local accounts is done against
# the backing user service.
# If set to true, then authentication will first be checked against the
# htpasswd store, even if the account appears as a local account in the
# backing user service. If the user is found in the htpasswd store, then
# an already existing local account will be turned into an external account.
# In this case an initial local password is never used and gets overwritten
# by the externally stored password upon login.
# default: false
#
# SINCE 1.3.2
realm.htpasswd.overrideLocalAuthentication = false
# The SalesforceUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# RESTART REQUIRED
# BASEFOLDER
# SINCE 1.3.0
realm.salesforce.backingUserService = ${baseFolder}/users.conf
# Restrict the Salesforce user to members of this org.
# default: 0 (i.e. do not check the Org ID)
#
@@ -1439,15 +1398,6 @@ realm.ldap.username = cn=Directory Manager
# SINCE 1.0.0
realm.ldap.password = password
# The LdapUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# SINCE 1.0.0
# RESTART REQUIRED
# BASEFOLDER
realm.ldap.backingUserService = ${baseFolder}/users.conf
# Delegate team membership control to LDAP.
#
# If true, team user memberships will be specified by LDAP groups. This will
@@ -1565,14 +1515,6 @@ realm.ldap.synchronizeUsers.removeDeleted = true
# For MS Active Directory this may be sAMAccountName
realm.ldap.uid = uid
# The RedmineUserService must be backed by another user service for standard user
# and team management.
# default: users.conf
#
# RESTART REQUIRED
# BASEFOLDER
realm.redmine.backingUserService = ${baseFolder}/users.conf
# URL of the Redmine.
realm.redmine.url = http://example.com/redmine
@@ -1638,7 +1580,7 @@ server.ajpPort = 0
#
# SINCE 1.4.0
# RESTART REQUIRED
server.redirectToHttpsPort = true
server.redirectToHttpsPort = false
# Specify the interface for Jetty to bind the standard connector.
# You may specify an ip or an empty value to bind to all interfaces.

+ 19
- 183
src/main/java/com/gitblit/ConfigUserService.java View File

@@ -96,6 +96,8 @@ public class ConfigUserService implements IUserService {
private static final String LOCALE = "locale";
private static final String ACCOUNTTYPE = "accountType";
private final File realmFile;
private final Logger logger = LoggerFactory.getLogger(ConfigUserService.class);
@@ -124,60 +126,6 @@ public class ConfigUserService implements IUserService {
public void setup(IRuntimeManager runtimeManager) {
}
/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsCredentialChanges() {
return true;
}
/**
* Does the user service support changes to user display name?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsDisplayNameChanges() {
return true;
}
/**
* Does the user service support changes to user email address?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsEmailAddressChanges() {
return true;
}
/**
* Does the user service support changes to team memberships?
*
* @return true or false
* @since 1.0.0
*/
@Override
public boolean supportsTeamMembershipChanges() {
return true;
}
/**
* Does the user service support cookie authentication?
*
* @return true or false
*/
@Override
public boolean supportsCookies() {
return true;
}
/**
* Returns the cookie value for the specified user.
*
@@ -197,13 +145,13 @@ public class ConfigUserService implements IUserService {
}
/**
* Authenticate a user based on their cookie.
* Gets the user object for the specified cookie.
*
* @param cookie
* @return a user object or null
*/
@Override
public synchronized UserModel authenticate(char[] cookie) {
public synchronized UserModel getUserModel(char[] cookie) {
String hash = new String(cookie);
if (StringUtils.isEmpty(hash)) {
return null;
@@ -222,49 +170,6 @@ public class ConfigUserService implements IUserService {
return model;
}
/**
* Authenticate a user based on a username and password.
*
* @param username
* @param password
* @return a user object or null
*/
@Override
public UserModel authenticate(String username, char[] password) {
UserModel returnedUser = null;
UserModel user = getUserModel(username);
if (user == null) {
return 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;
}
/**
* Logout a user.
*
* @param user
*/
@Override
public void logout(UserModel user) {
}
/**
* Retrieve the user object for the specified username.
*
@@ -357,6 +262,10 @@ public class ConfigUserService implements IUserService {
public synchronized boolean updateUserModel(String username, UserModel model) {
UserModel originalUser = null;
try {
if (!model.isLocalAccount()) {
// do not persist password
model.password = Constants.EXTERNAL_ACCOUNT;
}
read();
originalUser = users.remove(username.toLowerCase());
users.put(model.username.toLowerCase(), model);
@@ -504,45 +413,6 @@ public class ConfigUserService implements IUserService {
return list;
}
/**
* Sets the list of all teams who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @param teamnames
* @return true if successful
*/
@Override
public synchronized boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {
try {
Set<String> specifiedTeams = new HashSet<String>();
for (String teamname : teamnames) {
specifiedTeams.add(teamname.toLowerCase());
}
read();
// identify teams which require add or remove role
for (TeamModel team : teams.values()) {
// team has role, check against revised team list
if (specifiedTeams.contains(team.name.toLowerCase())) {
team.addRepositoryPermission(role);
} else {
// remove role from team
team.removeRepositoryPermission(role);
}
}
// persist changes
write();
return true;
} catch (Throwable t) {
logger.error(MessageFormat.format("Failed to set teams for role {0}!", role), t);
}
return false;
}
/**
* Retrieve the team object for the specified team name.
*
@@ -715,46 +585,6 @@ public class ConfigUserService implements IUserService {
return list;
}
/**
* Sets the list of all uses who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @param usernames
* @return true if successful
*/
@Override
@Deprecated
public synchronized boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {
try {
Set<String> specifiedUsers = new HashSet<String>();
for (String username : usernames) {
specifiedUsers.add(username.toLowerCase());
}
read();
// identify users which require add or remove role
for (UserModel user : users.values()) {
// user has role, check against revised user list
if (specifiedUsers.contains(user.username.toLowerCase())) {
user.addRepositoryPermission(role);
} else {
// remove role from user
user.removeRepositoryPermission(role);
}
}
// persist changes
write();
return true;
} catch (Throwable t) {
logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);
}
return false;
}
/**
* Renames a repository role.
*
@@ -846,6 +676,9 @@ public class ConfigUserService implements IUserService {
if (!StringUtils.isEmpty(model.emailAddress)) {
config.setString(USER, model.username, EMAILADDRESS, model.emailAddress);
}
if (model.accountType != null) {
config.setString(USER, model.username, ACCOUNTTYPE, model.accountType.name());
}
if (!StringUtils.isEmpty(model.organizationalUnit)) {
config.setString(USER, model.username, ORGANIZATIONALUNIT, model.organizationalUnit);
}
@@ -928,6 +761,9 @@ public class ConfigUserService implements IUserService {
roles.add(Constants.NO_ROLE);
}
config.setStringList(TEAM, model.name, ROLE, roles);
if (model.accountType != null) {
config.setString(TEAM, model.name, ACCOUNTTYPE, model.accountType.name());
}
if (!model.canAdmin) {
// write team permission for non-admin teams
@@ -1021,6 +857,10 @@ public class ConfigUserService implements IUserService {
user.password = config.getString(USER, username, PASSWORD);
user.displayName = config.getString(USER, username, DISPLAYNAME);
user.emailAddress = config.getString(USER, username, EMAILADDRESS);
user.accountType = AccountType.fromString(config.getString(USER, username, ACCOUNTTYPE));
if (Constants.EXTERNAL_ACCOUNT.equals(user.password) && user.accountType.isLocal()) {
user.accountType = null;
}
user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT);
user.organization = config.getString(USER, username, ORGANIZATION);
user.locality = config.getString(USER, username, LOCALITY);
@@ -1074,6 +914,7 @@ public class ConfigUserService implements IUserService {
team.canAdmin = roles.contains(Constants.ADMIN_ROLE);
team.canFork = roles.contains(Constants.FORK_ROLE);
team.canCreate = roles.contains(Constants.CREATE_ROLE);
team.accountType = AccountType.fromString(config.getString(TEAM, teamname, ACCOUNTTYPE));
if (!team.canAdmin) {
// non-admin team, read permissions
@@ -1112,9 +953,4 @@ public class ConfigUserService implements IUserService {
public String toString() {
return getClass().getSimpleName() + "(" + realmFile.getAbsolutePath() + ")";
}
@Override
public AccountType getAccountType() {
return AccountType.LOCAL;
}
}

+ 10
- 1
src/main/java/com/gitblit/Constants.java View File

@@ -499,7 +499,16 @@ public class Constants {
}
public static enum AccountType {
LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;
LOCAL, EXTERNAL, CONTAINER, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;
public static AccountType fromString(String value) {
for (AccountType type : AccountType.values()) {
if (type.name().equalsIgnoreCase(value)) {
return type;
}
}
return AccountType.LOCAL;
}
public boolean isLocal() {
return this == LOCAL;

+ 9
- 9
src/main/java/com/gitblit/DaggerModule.java View File

@@ -20,8 +20,10 @@ import javax.inject.Singleton;
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;
@@ -29,14 +31,12 @@ import com.gitblit.manager.IProjectManager;
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;
@@ -74,7 +74,7 @@ import dagger.Provides;
IRuntimeManager.class,
INotificationManager.class,
IUserManager.class,
ISessionManager.class,
IAuthenticationManager.class,
IRepositoryManager.class,
IProjectManager.class,
IGitblitManager.class,
@@ -122,11 +122,11 @@ public class DaggerModule {
return new UserManager(runtimeManager);
}

@Provides @Singleton ISessionManager provideSessionManager(
@Provides @Singleton IAuthenticationManager provideAuthenticationManager(
IRuntimeManager runtimeManager,
IUserManager userManager) {

return new SessionManager(
return new AuthenticationManager(
runtimeManager,
userManager);
}
@@ -179,7 +179,7 @@ public class DaggerModule {
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
@@ -189,7 +189,7 @@ public class DaggerModule {
runtimeManager,
notificationManager,
userManager,
sessionManager,
authenticationManager,
repositoryManager,
projectManager,
gitblitManager,
@@ -204,7 +204,7 @@ public class DaggerModule {
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
@@ -214,7 +214,7 @@ public class DaggerModule {
runtimeManager,
notificationManager,
userManager,
sessionManager,
authenticationManager,
repositoryManager,
projectManager,
gitblitManager,

+ 27
- 28
src/main/java/com/gitblit/GitBlit.java View File

@@ -29,13 +29,13 @@ import org.eclipse.jgit.lib.Repository;

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;
@@ -65,7 +65,7 @@ import com.gitblit.models.UserModel;
public class GitBlit implements IRuntimeManager,
INotificationManager,
IUserManager,
ISessionManager,
IAuthenticationManager,
IRepositoryManager,
IProjectManager,
IGitblitManager,
@@ -77,7 +77,7 @@ public class GitBlit implements IRuntimeManager,

private final IUserManager userManager;

private final ISessionManager sessionManager;
private final IAuthenticationManager authenticationManager;

private final IRepositoryManager repositoryManager;

@@ -91,7 +91,7 @@ public class GitBlit implements IRuntimeManager,
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
@@ -100,7 +100,7 @@ public class GitBlit implements IRuntimeManager,
this.runtimeManager = runtimeManager;
this.notificationManager = notificationManager;
this.userManager = userManager;
this.sessionManager = sessionManager;
this.authenticationManager = authenticationManager;
this.repositoryManager = repositoryManager;
this.projectManager = projectManager;
this.gitblitManager = gitblitManager;
@@ -239,55 +239,59 @@ public class GitBlit implements IRuntimeManager,

@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
@@ -320,11 +324,6 @@ public class GitBlit implements IRuntimeManager,
return userManager.getTeamModel(teamname);
}

@Override
public boolean supportsCookies() {
return userManager.supportsCookies();
}

@Override
public String getCookie(UserModel model) {
return userManager.getCookie(model);

+ 0
- 325
src/main/java/com/gitblit/GitblitUserService.java View File

@@ -1,325 +0,0 @@
/*
* Copyright 2011 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.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.AccountType;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.StringUtils;
/**
* This class wraps the default user service and is recommended as the starting
* point for custom user service implementations.
*
* This does seem a little convoluted, but the idea is to allow IUserService to
* evolve with new methods and implementations without breaking custom
* authentication implementations.
*
* The most common implementation of a custom IUserService is to only override
* authentication and then delegate all other functionality to one of Gitblit's
* user services. This class optimizes that use-case.
*
* Extending GitblitUserService allows for authentication customization without
* having to keep-up-with IUSerService API changes.
*
* @author James Moger
*
*/
public class GitblitUserService implements IUserService {
protected IUserService serviceImpl;
private final Logger logger = LoggerFactory.getLogger(GitblitUserService.class);
public GitblitUserService() {
}
@Override
public void setup(IRuntimeManager runtimeManager) {
File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
serviceImpl = createUserService(realmFile);
logger.info("GUS delegating to " + serviceImpl.toString());
}
protected IUserService createUserService(File realmFile) {
IUserService service = null;
if (realmFile.getName().toLowerCase().endsWith(".conf")) {
// v0.8.0+ config-based realm file
service = new ConfigUserService(realmFile);
}
assert service != null;
if (!realmFile.exists()) {
// Create the Administrator account for a new realm file
try {
realmFile.createNewFile();
} catch (IOException x) {
logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmFile), x);
}
UserModel admin = new UserModel("admin");
admin.password = "admin";
admin.canAdmin = true;
admin.excludeFromFederation = true;
service.updateUserModel(admin);
}
return service;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
@Override
public boolean supportsCredentialChanges() {
return serviceImpl.supportsCredentialChanges();
}
@Override
public boolean supportsDisplayNameChanges() {
return serviceImpl.supportsDisplayNameChanges();
}
@Override
public boolean supportsEmailAddressChanges() {
return serviceImpl.supportsEmailAddressChanges();
}
@Override
public boolean supportsTeamMembershipChanges() {
return serviceImpl.supportsTeamMembershipChanges();
}
@Override
public boolean supportsCookies() {
return serviceImpl.supportsCookies();
}
@Override
public String getCookie(UserModel model) {
return serviceImpl.getCookie(model);
}
/**
* Authenticate a user based on their cookie.
*
* @param cookie
* @return a user object or null
*/
@Override
public UserModel authenticate(char[] cookie) {
UserModel user = serviceImpl.authenticate(cookie);
setAccountType(user);
return user;
}
@Override
public UserModel authenticate(String username, char[] password) {
UserModel user = serviceImpl.authenticate(username, password);
setAccountType(user);
return user;
}
@Override
public void logout(UserModel user) {
serviceImpl.logout(user);
}
@Override
public UserModel getUserModel(String username) {
UserModel user = serviceImpl.getUserModel(username);
setAccountType(user);
return user;
}
@Override
public boolean updateUserModel(UserModel model) {
return serviceImpl.updateUserModel(model);
}
@Override
public boolean updateUserModels(Collection<UserModel> models) {
return serviceImpl.updateUserModels(models);
}
@Override
public boolean updateUserModel(String username, UserModel model) {
if (model.isLocalAccount() || supportsCredentialChanges()) {
if (!model.isLocalAccount() && !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 serviceImpl.updateUserModel(username, model);
}
if (model.username.equals(username)) {
// passwords are not persisted by the backing user service
model.password = null;
if (!model.isLocalAccount() && !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 serviceImpl.updateUserModel(username, model);
}
logger.error("Users can not be renamed!");
return false;
}
@Override
public boolean deleteUserModel(UserModel model) {
return serviceImpl.deleteUserModel(model);
}
@Override
public boolean deleteUser(String username) {
return serviceImpl.deleteUser(username);
}
@Override
public List<String> getAllUsernames() {
return serviceImpl.getAllUsernames();
}
@Override
public List<UserModel> getAllUsers() {
List<UserModel> users = serviceImpl.getAllUsers();
for (UserModel user : users) {
setAccountType(user);
}
return users;
}
@Override
public List<String> getAllTeamNames() {
return serviceImpl.getAllTeamNames();
}
@Override
public List<TeamModel> getAllTeams() {
return serviceImpl.getAllTeams();
}
@Override
public List<String> getTeamNamesForRepositoryRole(String role) {
return serviceImpl.getTeamNamesForRepositoryRole(role);
}
@Override
@Deprecated
public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) {
return serviceImpl.setTeamnamesForRepositoryRole(role, teamnames);
}
@Override
public TeamModel getTeamModel(String teamname) {
return serviceImpl.getTeamModel(teamname);
}
@Override
public boolean updateTeamModel(TeamModel model) {
return serviceImpl.updateTeamModel(model);
}
@Override
public boolean updateTeamModels(Collection<TeamModel> models) {
return serviceImpl.updateTeamModels(models);
}
@Override
public boolean updateTeamModel(String teamname, TeamModel model) {
if (!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 serviceImpl.updateTeamModel(teamname, model);
}
@Override
public boolean deleteTeamModel(TeamModel model) {
return serviceImpl.deleteTeamModel(model);
}
@Override
public boolean deleteTeam(String teamname) {
return serviceImpl.deleteTeam(teamname);
}
@Override
public List<String> getUsernamesForRepositoryRole(String role) {
return serviceImpl.getUsernamesForRepositoryRole(role);
}
@Override
@Deprecated
public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) {
return serviceImpl.setUsernamesForRepositoryRole(role, usernames);
}
@Override
public boolean renameRepositoryRole(String oldRole, String newRole) {
return serviceImpl.renameRepositoryRole(oldRole, newRole);
}
@Override
public boolean deleteRepositoryRole(String role) {
return serviceImpl.deleteRepositoryRole(role);
}
protected boolean isLocalAccount(String username) {
UserModel user = getUserModel(username);
return user != null && user.isLocalAccount();
}
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 = getAccountType();
}
}
}
@Override
public AccountType getAccountType() {
return AccountType.LOCAL;
}
}

+ 2
- 91
src/main/java/com/gitblit/IUserService.java View File

@@ -18,7 +18,6 @@ package com.gitblit;
import java.util.Collection;
import java.util.List;
import com.gitblit.Constants.AccountType;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
@@ -42,45 +41,6 @@ public interface IUserService {
*/
void setup(IRuntimeManager runtimeManager);
/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsCredentialChanges();
/**
* Does the user service support changes to user display name?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsDisplayNameChanges();
/**
* Does the user service support changes to user email address?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsEmailAddressChanges();
/**
* Does the user service support changes to team memberships?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsTeamMembershipChanges();
/**
* Does the user service support cookie authentication?
*
* @return true or false
*/
boolean supportsCookies();
/**
* Returns the cookie value for the specified user.
*
@@ -90,28 +50,12 @@ public interface IUserService {
String getCookie(UserModel model);
/**
* Authenticate a user based on their cookie.
* Retrieve a user object for the specified 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);
UserModel getUserModel(char[] cookie);
/**
* Retrieve the user object for the specified username.
@@ -208,19 +152,6 @@ public interface IUserService {
*/
List<String> getTeamNamesForRepositoryRole(String role);
/**
* Sets the list of all teams who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @param teamnames
* @return true if successful
* @since 0.8.0
*/
@Deprecated
boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames);
/**
* Retrieve the team object for the specified team name.
*
@@ -290,18 +221,6 @@ public interface IUserService {
*/
List<String> getUsernamesForRepositoryRole(String role);
/**
* Sets the list of all uses who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @param usernames
* @return true if successful
*/
@Deprecated
boolean setUsernamesForRepositoryRole(String role, List<String> usernames);
/**
* Renames a repository role.
*
@@ -319,14 +238,6 @@ public interface IUserService {
*/
boolean deleteRepositoryRole(String role);
/**
* Returns the account type for the user models.
*
* @return the account type
* @since 1.4.0
*/
AccountType getAccountType();
/**
* @See java.lang.Object.toString();
* @return string representation of the login service

+ 182
- 0
src/main/java/com/gitblit/auth/AuthenticationProvider.java View File

@@ -0,0 +1,182 @@
/*
* 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;
}
}
}

src/main/java/com/gitblit/HtpasswdUserService.java → src/main/java/com/gitblit/auth/HtpasswdAuthProvider.java View File

@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
package com.gitblit.auth;

import java.io.File;
import java.io.FileInputStream;
@@ -29,11 +29,11 @@ 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;
import com.gitblit.Constants.AccountType;
import com.gitblit.manager.IRuntimeManager;
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;
@@ -64,52 +64,25 @@ import com.gitblit.utils.StringUtils;
* @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";
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_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 boolean supportPlainTextPwd;

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;
}
public HtpasswdAuthProvider() {
super("htpasswd");
}



/**
* Setup the user service.
*
@@ -118,42 +91,40 @@ public class HtpasswdUserService extends GitblitUserService
* In addition the setup tries to read and parse the htpasswd file to be used
* for authentication.
*
* @param runtimeManager
* @since 1.4.0
* @param settings
* @since 0.7.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());

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);
}



/**
* 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()
{
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.
@@ -168,14 +139,7 @@ public class HtpasswdUserService extends GitblitUserService
* @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);
}


public UserModel authenticate(String username, char[] password) {
read();
String storedPwd = htUsers.get(username);
if (storedPwd != null) {
@@ -183,27 +147,27 @@ public class HtpasswdUserService extends GitblitUserService
final String passwd = new String(password);

// test Apache MD5 variant encrypted password
if ( storedPwd.startsWith("$apr1$") ) {
if ( storedPwd.equals(Md5Crypt.apr1Crypt(passwd, storedPwd)) ) {
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}") ) {
else if (storedPwd.startsWith("{SHA}")) {
String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(passwd));
if ( storedPwd.substring("{SHA}".length()).equals(passwd64) ) {
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)) ) {
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) ){
else if (supportPlaintextPwd() && storedPwd.equals(passwd)){
logger.debug("Clear text password matched for user '" + username + "'");
authenticated = true;
}
@@ -212,10 +176,13 @@ public class HtpasswdUserService extends GitblitUserService
if (authenticated) {
logger.debug("Htpasswd authenticated: " + username);

UserModel user = getUserModel(username);
if (user == null) {
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
@@ -228,7 +195,7 @@ public class HtpasswdUserService extends GitblitUserService
user.accountType = getAccountType();

// Push the looked up values to backing file
super.updateUserModel(user);
updateUser(user);

return user;
}
@@ -237,70 +204,29 @@ public class HtpasswdUserService extends GitblitUserService
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()
{
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);
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();
this.forceReload = true;
forceReload = true;
}

if (htpasswdFile.exists() && (forceReload || (htpasswdFile.lastModified() != lastModified))) {
forceReload = false;
lastModified = htpasswdFile.lastModified();
htUsers.clear();

@@ -309,53 +235,42 @@ public class HtpasswdUserService extends GitblitUserService
Scanner scanner = null;
try {
scanner = new Scanner(new FileInputStream(htpasswdFile));
while( scanner.hasNextLine()) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if ( !line.isEmpty() && !line.startsWith("#") ) {
if (!line.isEmpty() && !line.startsWith("#")) {
Matcher m = entry.matcher(line);
if ( m.matches() ) {
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();
} finally {
if (scanner != null) {
scanner.close();
}
}
}
}



private boolean supportPlaintextPwd()
{
return this.settings.getBoolean(KEY_SUPPORT_PLAINTEXT_PWD, SUPPORT_PLAINTEXT_PWD);
private boolean supportPlaintextPwd() {
return this.settings.getBoolean(KEY_SUPPORT_PLAINTEXT_PWD, supportPlainTextPwd);
}


private boolean supportCryptPwd()
{
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()
{
public int getNumberHtpasswdUsers() {
return this.htUsers.size();
}

@Override
public String toString() {
return getClass().getSimpleName() + "(" + ((htpasswdFile != null) ? htpasswdFile.getAbsolutePath() : "null") + ")";
}
}

src/main/java/com/gitblit/auth/LdapAuthProvider.java
File diff suppressed because it is too large
View File


src/main/java/com/gitblit/PAMUserService.java → src/main/java/com/gitblit/auth/PAMAuthProvider.java View File

@@ -1,143 +1,126 @@
/*
* 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 org.jvnet.libpam.PAM;
import org.jvnet.libpam.PAMException;
import org.jvnet.libpam.impl.CLibrary;
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 PAM user service for Linux/Unix/MacOSX.
*
* @author James Moger
*/
public class PAMUserService extends GitblitUserService {
private final Logger logger = LoggerFactory.getLogger(PAMUserService.class);
private IStoredSettings settings;
public PAMUserService() {
super();
}
@Override
public void setup(IRuntimeManager runtimeManager) {
this.settings = runtimeManager.getSettings();
String file = settings.getString(Keys.realm.pam.backingUserService, "${baseFolder}/users.conf");
File realmFile = runtimeManager.getFileOrFolder(file);
serviceImpl = createUserService(realmFile);
logger.info("PAM User Service backed by " + serviceImpl.toString());
// 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 User Service could not find a passwd database!");
} else if (!passwdFile.canRead()) {
logger.error("PAM User Service 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 (isLocalAccount(username)) {
// local account, bypass PAM authentication
return super.authenticate(username, 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 = 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
super.updateUserModel(user);
return user;
}
}
/*
* 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;
}
}

src/main/java/com/gitblit/RedmineUserService.java → src/main/java/com/gitblit/auth/RedmineAuthProvider.java View File

@@ -1,203 +1,186 @@
/*
* 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;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import org.apache.wicket.util.io.IOUtils;
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.ConnectionUtils;
import com.gitblit.utils.StringUtils;
import com.google.gson.Gson;
/**
* Implementation of an Redmine user service.<br>
* you can login to gitblit with Redmine user id and api key.
*/
public class RedmineUserService extends GitblitUserService {
private final Logger logger = LoggerFactory.getLogger(RedmineUserService.class);
private IStoredSettings settings;
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 RedmineUserService() {
super();
}
@Override
public void setup(IRuntimeManager runtimeManager) {
this.settings = runtimeManager.getSettings();
String file = settings.getString(Keys.realm.redmine.backingUserService, "${baseFolder}/users.conf");
File realmFile = runtimeManager.getFileOrFolder(file);
serviceImpl = createUserService(realmFile);
logger.info("Redmine User Service backed by " + serviceImpl.toString());
}
@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) {
if (isLocalAccount(username)) {
// local account, bypass Redmine authentication
return super.authenticate(username, 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 = 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
super.updateUserModel(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;
}
}
/*
* 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;
}
}

src/main/java/com/gitblit/SalesforceUserService.java → src/main/java/com/gitblit/auth/SalesforceAuthProvider.java View File

@@ -1,12 +1,9 @@
package com.gitblit;

import java.io.File;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package com.gitblit.auth;

import com.gitblit.Constants;
import com.gitblit.Constants.AccountType;
import com.gitblit.manager.IRuntimeManager;
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;
@@ -16,10 +13,11 @@ import com.sforce.soap.partner.PartnerConnection;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

public class SalesforceUserService extends GitblitUserService {
public class SalesforceAuthProvider extends UsernamePasswordAuthenticationProvider {

public static final Logger logger = LoggerFactory.getLogger(SalesforceUserService.class);
private IStoredSettings settings;
public SalesforceAuthProvider() {
super("salesforce");
}

@Override
public AccountType getAccountType() {
@@ -27,26 +25,11 @@ public class SalesforceUserService extends GitblitUserService {
}

@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());
public void setup() {
}

@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));
@@ -78,7 +61,7 @@ public class SalesforceUserService extends GitblitUserService {

UserModel user = null;
synchronized (this) {
user = getUserModel(simpleUsername);
user = userManager.getUserModel(simpleUsername);
if (user == null)
user = new UserModel(simpleUsername);

@@ -90,7 +73,7 @@ public class SalesforceUserService extends GitblitUserService {

setUserAttributes(user, info);

super.updateUserModel(user);
updateUser(user);
}

return user;
@@ -122,6 +105,7 @@ public class SalesforceUserService extends GitblitUserService {
return email.split("@")[0];
}


@Override
public boolean supportsCredentialChanges() {
return false;
@@ -136,4 +120,9 @@ public class SalesforceUserService extends GitblitUserService {
public boolean supportsEmailAddressChanges() {
return false;
}

@Override
public boolean supportsTeamMembershipChanges() {
return true;
}
}

src/main/java/com/gitblit/WindowsUserService.java → src/main/java/com/gitblit/auth/WindowsAuthProvider.java View File

@@ -1,195 +1,177 @@
/*
* 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.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.AccountType;
import com.gitblit.manager.IRuntimeManager;
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 user service.
*
* @author James Moger
*/
public class WindowsUserService extends GitblitUserService {
private final Logger logger = LoggerFactory.getLogger(WindowsUserService.class);
private IStoredSettings settings;
private IWindowsAuthProvider waffle;
public WindowsUserService() {
super();
}
@Override
public void setup(IRuntimeManager runtimeManager) {
this.settings = runtimeManager.getSettings();
String file = settings.getString(Keys.realm.windows.backingUserService, "${baseFolder}/users.conf");
File realmFile = runtimeManager.getFileOrFolder(file);
serviceImpl = createUserService(realmFile);
logger.info("Windows User Service backed by " + serviceImpl.toString());
waffle = new WindowsAuthProviderImpl();
IWindowsComputer computer = waffle.getCurrentComputer();
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) {
if (isLocalAccount(username)) {
// local account, bypass Windows authentication
return super.authenticate(username, 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 = 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
super.updateUserModel(user);
// cleanup resources
identity.dispose();
return user;
}
}
/*
* 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;
}
}

+ 0
- 1
src/main/java/com/gitblit/client/EditTeamDialog.java View File

@@ -146,7 +146,6 @@ public class EditTeamDialog extends JDialog {
final Insets _insets = new Insets(5, 5, 5, 5);
repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);
userPalette = new JPalette<String>();
userPalette.setEnabled(settings.supportsTeamMembershipChanges);
JPanel fieldsPanelTop = new JPanel(new BorderLayout());
fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);

+ 3
- 13
src/main/java/com/gitblit/client/EditUserDialog.java View File

@@ -161,18 +161,9 @@ public class EditUserDialog extends JDialog {
countryCodeField = new JTextField(anUser.countryCode == null ? "" : anUser.countryCode, 15);
// credentials are optionally controlled by 3rd-party authentication
usernameField.setEnabled(settings.supportsCredentialChanges);
passwordField.setEnabled(settings.supportsCredentialChanges);
confirmPasswordField.setEnabled(settings.supportsCredentialChanges);
displayNameField.setEnabled(settings.supportsDisplayNameChanges);
emailAddressField.setEnabled(settings.supportsEmailAddressChanges);
organizationalUnitField.setEnabled(settings.supportsDisplayNameChanges);
organizationField.setEnabled(settings.supportsDisplayNameChanges);
localityField.setEnabled(settings.supportsDisplayNameChanges);
stateProvinceField.setEnabled(settings.supportsDisplayNameChanges);
countryCodeField.setEnabled(settings.supportsDisplayNameChanges);
usernameField.setEnabled(anUser.isLocalAccount());
passwordField.setEnabled(anUser.isLocalAccount());
confirmPasswordField.setEnabled(anUser.isLocalAccount());
JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
fieldsPanel.add(newFieldPanel(Translation.get("gb.username"), usernameField));
@@ -196,7 +187,6 @@ public class EditUserDialog extends JDialog {
final Insets _insets = new Insets(5, 5, 5, 5);
repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);
teamsPalette = new JPalette<TeamModel>();
teamsPalette.setEnabled(settings.supportsTeamMembershipChanges);
JPanel fieldsPanelTop = new JPanel(new BorderLayout());
fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);

+ 5
- 5
src/main/java/com/gitblit/git/GitblitUploadPackFactory.java View File

@@ -23,7 +23,7 @@ import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
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;

/**
@@ -36,10 +36,10 @@ 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
@@ -51,7 +51,7 @@ public class GitblitUploadPackFactory<X> implements UploadPackFactory<X> {

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;
}

+ 511
- 0
src/main/java/com/gitblit/manager/AuthenticationManager.java View File

@@ -0,0 +1,511 @@
/*
* Copyright 2013 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.manager;

import java.nio.charset.Charset;
import java.security.Principal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.RequestCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants;
import com.gitblit.Constants.AccountType;
import com.gitblit.Constants.AuthenticationType;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.auth.AuthenticationProvider;
import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
import com.gitblit.auth.HtpasswdAuthProvider;
import com.gitblit.auth.LdapAuthProvider;
import com.gitblit.auth.PAMAuthProvider;
import com.gitblit.auth.RedmineAuthProvider;
import com.gitblit.auth.SalesforceAuthProvider;
import com.gitblit.auth.WindowsAuthProvider;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.Base64;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.X509Utils.X509Metadata;
import com.gitblit.wicket.GitBlitWebSession;

/**
* The authentication manager handles user login & logout.
*
* @author James Moger
*
*/
public class AuthenticationManager implements IAuthenticationManager {

private final Logger logger = LoggerFactory.getLogger(getClass());

private final IStoredSettings settings;

private final IRuntimeManager runtimeManager;

private final IUserManager userManager;

private final List<AuthenticationProvider> authenticationProviders;

private final Map<String, Class<? extends AuthenticationProvider>> providerNames;

private final Map<String, String> legacyRedirects;

public AuthenticationManager(
IRuntimeManager runtimeManager,
IUserManager userManager) {

this.settings = runtimeManager.getSettings();
this.runtimeManager = runtimeManager;
this.userManager = userManager;
this.authenticationProviders = new ArrayList<AuthenticationProvider>();

// map of shortcut provider names
providerNames = new HashMap<String, Class<? extends AuthenticationProvider>>();
providerNames.put("htpasswd", HtpasswdAuthProvider.class);
providerNames.put("ldap", LdapAuthProvider.class);
providerNames.put("pam", PAMAuthProvider.class);
providerNames.put("redmine", RedmineAuthProvider.class);
providerNames.put("salesforce", SalesforceAuthProvider.class);
providerNames.put("windows", WindowsAuthProvider.class);

// map of legacy external user services
legacyRedirects = new HashMap<String, String>();
legacyRedirects.put("com.gitblit.HtpasswdUserService", "htpasswd");
legacyRedirects.put("com.gitblit.LdapUserService", "ldap");
legacyRedirects.put("com.gitblit.PAMUserService", "pam");
legacyRedirects.put("com.gitblit.RedmineUserService", "redmine");
legacyRedirects.put("com.gitblit.SalesforceUserService", "salesforce");
legacyRedirects.put("com.gitblit.WindowsUserService", "windows");
}

@Override
public AuthenticationManager start() {
// automatically adjust legacy configurations
String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf");
if (legacyRedirects.containsKey(realm)) {
logger.warn("");
logger.warn("#################################################################");
logger.warn(" IUserService '{}' is obsolete!", realm);
logger.warn(" Please set '{}={}'", "realm.authenticationProviders", legacyRedirects.get(realm));
logger.warn("#################################################################");
logger.warn("");

// conditionally override specified authentication providers
if (StringUtils.isEmpty(settings.getString(Keys.realm.authenticationProviders, null))) {
settings.overrideSetting(Keys.realm.authenticationProviders, legacyRedirects.get(realm));
}
}

// instantiate and setup specified authentication providers
List<String> providers = settings.getStrings(Keys.realm.authenticationProviders);
if (providers.isEmpty()) {
logger.info("External authentication disabled.");
} else {
for (String provider : providers) {
try {
Class<?> authClass;
if (providerNames.containsKey(provider)) {
// map the name -> class
authClass = providerNames.get(provider);
} else {
// reflective lookup
authClass = Class.forName(provider);
}
logger.info("setting up {}", authClass.getName());
AuthenticationProvider authImpl = (AuthenticationProvider) authClass.newInstance();
authImpl.setup(runtimeManager, userManager);
authenticationProviders.add(authImpl);
} catch (Exception e) {
logger.error("", e);
}
}
}
return this;
}

@Override
public AuthenticationManager stop() {
return this;
}

/**
* Authenticate a user based on HTTP request parameters.
*
* Authentication by X509Certificate is tried first and then by cookie.
*
* @param httpRequest
* @return a user object or null
*/
@Override
public UserModel authenticate(HttpServletRequest httpRequest) {
return authenticate(httpRequest, false);
}

/**
* Authenticate a user based on HTTP request parameters.
*
* Authentication by servlet container principal, X509Certificate, cookie,
* and finally BASIC header.
*
* @param httpRequest
* @param requiresCertificate
* @return a user object or null
*/
@Override
public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
// try to authenticate by servlet container principal
if (!requiresCertificate) {
Principal principal = httpRequest.getUserPrincipal();
if (principal != null) {
String username = principal.getName();
if (!StringUtils.isEmpty(username)) {
boolean internalAccount = isInternalAccount(username);
UserModel user = userManager.getUserModel(username);
if (user != null) {
// existing user
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false)
&& !internalAccount) {
// auto-create user from an authenticated container principal
user = new UserModel(username.toLowerCase());
user.displayName = username;
user.password = Constants.EXTERNAL_ACCOUNT;
user.accountType = AccountType.CONTAINER;
userManager.updateUserModel(user);
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else if (!internalAccount) {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}",
principal.getName(), httpRequest.getRemoteAddr()));
}
}
}
}

// try to authenticate by certificate
boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);
String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids);
if (model != null) {
// grab real user model and preserve certificate serial number
UserModel user = userManager.getUserModel(model.username);
X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);
if (user != null) {
flagWicketSession(AuthenticationType.CERTIFICATE);
logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",
user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
return user;
} else {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}",
model.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
}
}

if (requiresCertificate) {
// caller requires client certificate authentication (e.g. git servlet)
return null;
}

// try to authenticate by cookie
UserModel user = authenticate(httpRequest.getCookies());
if (user != null) {
flagWicketSession(AuthenticationType.COOKIE);
logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
}

// try to authenticate by BASIC
final String authorization = httpRequest.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
String credentials = new String(Base64.decode(base64Credentials),
Charset.forName("UTF-8"));
// credentials = username:password
final String[] values = credentials.split(":", 2);

if (values.length == 2) {
String username = values[0];
char[] password = values[1].toCharArray();
user = authenticate(username, password);
if (user != null) {
flagWicketSession(AuthenticationType.CREDENTIALS);
logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else {
logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}",
username, httpRequest.getRemoteAddr()));
}
}
}
return null;
}

/**
* Authenticate a user based on their cookie.
*
* @param cookies
* @return a user object or null
*/
protected UserModel authenticate(Cookie[] cookies) {
if (settings.getBoolean(Keys.web.allowCookieAuthentication, true)) {
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(Constants.NAME)) {
String value = cookie.getValue();
return userManager.getUserModel(value.toCharArray());
}
}
}
}
return null;
}

protected void flagWicketSession(AuthenticationType authenticationType) {
RequestCycle requestCycle = RequestCycle.get();
if (requestCycle != null) {
// flag the Wicket session, if this is a Wicket request
GitBlitWebSession session = GitBlitWebSession.get();
session.authenticationType = authenticationType;
}
}

/**
* Authenticate a user based on a username and password.
*
* @see IUserService.authenticate(String, char[])
* @param username
* @param password
* @return a user object or null
*/
@Override
public UserModel authenticate(String username, char[] password) {
if (StringUtils.isEmpty(username)) {
// can not authenticate empty username
return null;
}

String usernameDecoded = StringUtils.decodeUsername(username);
String pw = new String(password);
if (StringUtils.isEmpty(pw)) {
// can not authenticate empty password
return null;
}
// check to see if this is the federation user
// if (canFederate()) {
// if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) {
// List<String> tokens = getFederationTokens();
// if (tokens.contains(pw)) {
// return getFederationUser();
// }
// }
// }

// try local authentication
UserModel user = userManager.getUserModel(usernameDecoded);
if (user != null) {
UserModel returnedUser = null;
if (user.password.startsWith(StringUtils.MD5_TYPE)) {
// password digest
String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));
if (user.password.equalsIgnoreCase(md5)) {
returnedUser = user;
}
} else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) {
// username+password digest
String md5 = StringUtils.COMBINED_MD5_TYPE
+ StringUtils.getMD5(username.toLowerCase() + new String(password));
if (user.password.equalsIgnoreCase(md5)) {
returnedUser = user;
}
} else if (user.password.equals(new String(password))) {
// plain-text password
returnedUser = user;
}
return returnedUser;
}

// try registered external authentication providers
if (user == null) {
for (AuthenticationProvider provider : authenticationProviders) {
if (provider instanceof UsernamePasswordAuthenticationProvider) {
user = provider.authenticate(usernameDecoded, password);
if (user != null) {
// user authenticated
user.accountType = provider.getAccountType();
return user;
}
}
}
}
return user;
}

/**
* Sets a cookie for the specified user.
*
* @param response
* @param user
*/
@Override
public void setCookie(HttpServletResponse response, UserModel user) {
if (settings.getBoolean(Keys.web.allowCookieAuthentication, true)) {
GitBlitWebSession session = GitBlitWebSession.get();
boolean standardLogin = session.authenticationType.isStandard();

if (standardLogin) {
Cookie userCookie;
if (user == null) {
// clear cookie for logout
userCookie = new Cookie(Constants.NAME, "");
} else {
// set cookie for login
String cookie = userManager.getCookie(user);
if (StringUtils.isEmpty(cookie)) {
// create empty cookie
userCookie = new Cookie(Constants.NAME, "");
} else {
// create real cookie
userCookie = new Cookie(Constants.NAME, cookie);
userCookie.setMaxAge(Integer.MAX_VALUE);
}
}
userCookie.setPath("/");
response.addCookie(userCookie);
}
}
}

/**
* Logout a user.
*
* @param user
*/
@Override
public void logout(HttpServletResponse response, UserModel user) {
setCookie(response, null);
}

/**
* Returns true if the user's credentials can be changed.
*
* @param user
* @return true if the user service supports credential changes
*/
@Override
public boolean supportsCredentialChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || findProvider(user).supportsCredentialChanges();
}

/**
* Returns true if the user's display name can be changed.
*
* @param user
* @return true if the user service supports display name changes
*/
@Override
public boolean supportsDisplayNameChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || findProvider(user).supportsDisplayNameChanges();
}

/**
* Returns true if the user's email address can be changed.
*
* @param user
* @return true if the user service supports email address changes
*/
@Override
public boolean supportsEmailAddressChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || findProvider(user).supportsEmailAddressChanges();
}

/**
* Returns true if the user's team memberships can be changed.
*
* @param user
* @return true if the user service supports team membership changes
*/
@Override
public boolean supportsTeamMembershipChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || findProvider(user).supportsTeamMembershipChanges();
}

/**
* Returns true if the team memberships can be changed.
*
* @param user
* @return true if the team membership can be changed
*/
@Override
public boolean supportsTeamMembershipChanges(TeamModel team) {
return (team != null && team.isLocalTeam()) || findProvider(team).supportsTeamMembershipChanges();
}

protected AuthenticationProvider findProvider(UserModel user) {
for (AuthenticationProvider provider : authenticationProviders) {
if (provider.getAccountType().equals(user.accountType)) {
return provider;
}
}
return AuthenticationProvider.NULL_PROVIDER;
}

protected AuthenticationProvider findProvider(TeamModel team) {
for (AuthenticationProvider provider : authenticationProviders) {
if (provider.getAccountType().equals(team.accountType)) {
return provider;
}
}
return AuthenticationProvider.NULL_PROVIDER;
}

/**
* Returns true if the username represents an internal account
*
* @param username
* @return true if the specified username represents an internal account
*/
protected boolean isInternalAccount(String username) {
return !StringUtils.isEmpty(username)
&& (username.equalsIgnoreCase(Constants.FEDERATION_USER)
|| username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
}

// protected UserModel getFederationUser() {
// // the federation user is an administrator
// UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
// federationUser.canAdmin = true;
// return federationUser;
// }
}

+ 0
- 7
src/main/java/com/gitblit/manager/GitblitManager.java View File

@@ -101,13 +101,6 @@ public class GitblitManager implements IGitblitManager {
* @return Map<String, SettingModel>
*/
private void loadSettingModels(ServerSettings settingsModel) {
// this entire "supports" concept will go away with user service refactoring
UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT);
externalUser.password = Constants.EXTERNAL_ACCOUNT;
settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser);
settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser);
settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser);
settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser);
try {
// Read bundled Gitblit properties to extract setting descriptions.
// This copy is pristine and only used for populating the setting

src/main/java/com/gitblit/manager/ISessionManager.java → src/main/java/com/gitblit/manager/IAuthenticationManager.java View File

@@ -18,9 +18,10 @@ package com.gitblit.manager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;

public interface ISessionManager extends IManager {
public interface IAuthenticationManager extends IManager {

/**
* Authenticate a user based on HTTP request parameters.
@@ -69,4 +70,44 @@ public interface ISessionManager extends IManager {
*/
void logout(HttpServletResponse response, UserModel user);

/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsCredentialChanges(UserModel user);

/**
* Returns true if the user's display name can be changed.
*
* @param user
* @return true if the user service supports display name changes
*/
boolean supportsDisplayNameChanges(UserModel user);

/**
* Returns true if the user's email address can be changed.
*
* @param user
* @return true if the user service supports email address changes
*/
boolean supportsEmailAddressChanges(UserModel user);

/**
* Returns true if the user's team memberships can be changed.
*
* @param user
* @return true if the user service supports team membership changes
*/
boolean supportsTeamMembershipChanges(UserModel user);

/**
* Returns true if the team memberships can be changed.
*
* @param user
* @return true if the team memberships can be changed
*/
boolean supportsTeamMembershipChanges(TeamModel team);

}

+ 2
- 259
src/main/java/com/gitblit/manager/IUserManager.java View File

@@ -15,266 +15,9 @@
*/
package com.gitblit.manager;

import java.util.Collection;
import java.util.List;
import com.gitblit.IUserService;

import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
public interface IUserManager extends IManager, IUserService {

public interface IUserManager extends IManager {

boolean supportsAddUser();

/**
* Does the user service support changes to credentials?
*
* @return true or false
* @since 1.0.0
*/
boolean supportsCredentialChanges(UserModel user);

/**
* Returns true if the user's display name can be changed.
*
* @param user
* @return true if the user service supports display name changes
*/
boolean supportsDisplayNameChanges(UserModel user);

/**
* Returns true if the user's email address can be changed.
*
* @param user
* @return true if the user service supports email address changes
*/
boolean supportsEmailAddressChanges(UserModel user);

/**
* Returns true if the user's team memberships can be changed.
*
* @param user
* @return true if the user service supports team membership changes
*/
boolean supportsTeamMembershipChanges(UserModel user);

/**
* Does the user service support cookie authentication?
*
* @return true or false
*/
boolean supportsCookies();

/**
* Returns the cookie value for the specified user.
*
* @param model
* @return cookie value
*/
String getCookie(UserModel model);

/**
* Authenticate a user based on their cookie.
*
* @param cookie
* @return a user object or null
*/
UserModel authenticate(char[] cookie);

/**
* Authenticate a user based on a username and password.
*
* @param username
* @param password
* @return a user object or null
*/
UserModel authenticate(String username, char[] password);

/**
* Logout a user.
*
* @param user
*/
void logout(UserModel user);

/**
* Retrieve the user object for the specified username.
*
* @param username
* @return a user object or null
*/
UserModel getUserModel(String username);

/**
* Updates/writes a complete user object.
*
* @param model
* @return true if update is successful
*/
boolean updateUserModel(UserModel model);

/**
* Updates/writes all specified user objects.
*
* @param models a list of user models
* @return true if update is successful
* @since 1.2.0
*/
boolean updateUserModels(Collection<UserModel> models);

/**
* Adds/updates a user object keyed by username. This method allows for
* renaming a user.
*
* @param username
* the old username
* @param model
* the user object to use for username
* @return true if update is successful
*/
boolean updateUserModel(String username, UserModel model);

/**
* Deletes the user object from the user service.
*
* @param model
* @return true if successful
*/
boolean deleteUserModel(UserModel model);

/**
* Delete the user object with the specified username
*
* @param username
* @return true if successful
*/
boolean deleteUser(String username);

/**
* Returns the list of all users available to the login service.
*
* @return list of all usernames
*/
List<String> getAllUsernames();

/**
* Returns the list of all users available to the login service.
*
* @return list of all users
* @since 0.8.0
*/
List<UserModel> getAllUsers();

/**
* Returns the list of all teams available to the login service.
*
* @return list of all teams
* @since 0.8.0
*/
List<String> getAllTeamNames();

/**
* Returns the list of all teams available to the login service.
*
* @return list of all teams
* @since 0.8.0
*/
List<TeamModel> getAllTeams();

/**
* Returns the list of all users who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @return list of all usernames that can bypass the access restriction
* @since 0.8.0
*/
List<String> getTeamNamesForRepositoryRole(String role);

/**
* Retrieve the team object for the specified team name.
*
* @param teamname
* @return a team object or null
* @since 0.8.0
*/
TeamModel getTeamModel(String teamname);

/**
* Updates/writes a complete team object.
*
* @param model
* @return true if update is successful
* @since 0.8.0
*/
boolean updateTeamModel(TeamModel model);

/**
* Updates/writes all specified team objects.
*
* @param models a list of team models
* @return true if update is successful
* @since 1.2.0
*/
boolean updateTeamModels(Collection<TeamModel> models);

/**
* Updates/writes and replaces a complete team object keyed by teamname.
* This method allows for renaming a team.
*
* @param teamname
* the old teamname
* @param model
* the team object to use for teamname
* @return true if update is successful
* @since 0.8.0
*/
boolean updateTeamModel(String teamname, TeamModel model);

/**
* Deletes the team object from the user service.
*
* @param model
* @return true if successful
* @since 0.8.0
*/
boolean deleteTeamModel(TeamModel model);

/**
* Delete the team object with the specified teamname
*
* @param teamname
* @return true if successful
* @since 0.8.0
*/
boolean deleteTeam(String teamname);

/**
* Returns the list of all users who are allowed to bypass the access
* restriction placed on the specified repository.
*
* @param role
* the repository name
* @return list of all usernames that can bypass the access restriction
* @since 0.8.0
*/
List<String> getUsernamesForRepositoryRole(String role);

/**
* Renames a repository role.
*
* @param oldRole
* @param newRole
* @return true if successful
*/
boolean renameRepositoryRole(String oldRole, String newRole);

/**
* Removes a repository role from all users.
*
* @param role
* @return true if successful
*/
boolean deleteRepositoryRole(String role);

}

+ 0
- 340
src/main/java/com/gitblit/manager/SessionManager.java View File

@@ -1,340 +0,0 @@
/*
* Copyright 2013 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.manager;

import java.nio.charset.Charset;
import java.security.Principal;
import java.text.MessageFormat;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.RequestCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants;
import com.gitblit.Constants.AuthenticationType;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.models.UserModel;
import com.gitblit.utils.Base64;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.X509Utils.X509Metadata;
import com.gitblit.wicket.GitBlitWebSession;

/**
* The session manager handles user login & logout.
*
* @author James Moger
*
*/
public class SessionManager implements ISessionManager {

private final Logger logger = LoggerFactory.getLogger(getClass());

private final IStoredSettings settings;

private final IRuntimeManager runtimeManager;

private final IUserManager userManager;

public SessionManager(
IRuntimeManager runtimeManager,
IUserManager userManager) {

this.settings = runtimeManager.getSettings();
this.runtimeManager = runtimeManager;
this.userManager = userManager;
}

@Override
public SessionManager start() {
List<String> services = settings.getStrings("realm.authenticationServices");
for (String service : services) {
// TODO populate authentication services here
}
return this;
}

@Override
public SessionManager stop() {
return this;
}

/**
* Authenticate a user based on HTTP request parameters.
*
* Authentication by X509Certificate is tried first and then by cookie.
*
* @param httpRequest
* @return a user object or null
*/
@Override
public UserModel authenticate(HttpServletRequest httpRequest) {
return authenticate(httpRequest, false);
}

/**
* Authenticate a user based on HTTP request parameters.
*
* Authentication by X509Certificate, servlet container principal, cookie,
* and BASIC header.
*
* @param httpRequest
* @param requiresCertificate
* @return a user object or null
*/
@Override
public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
// try to authenticate by certificate
boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);
String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids);
if (model != null) {
// grab real user model and preserve certificate serial number
UserModel user = userManager.getUserModel(model.username);
X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);
if (user != null) {
flagWicketSession(AuthenticationType.CERTIFICATE);
logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",
user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
return user;
} else {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}",
model.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
}
}

if (requiresCertificate) {
// caller requires client certificate authentication (e.g. git servlet)
return null;
}

// try to authenticate by servlet container principal
Principal principal = httpRequest.getUserPrincipal();
if (principal != null) {
String username = principal.getName();
if (!StringUtils.isEmpty(username)) {
boolean internalAccount = isInternalAccount(username);
UserModel user = userManager.getUserModel(username);
if (user != null) {
// existing user
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false)
&& !internalAccount) {
// auto-create user from an authenticated container principal
user = new UserModel(username.toLowerCase());
user.displayName = username;
user.password = Constants.EXTERNAL_ACCOUNT;
userManager.updateUserModel(user);
flagWicketSession(AuthenticationType.CONTAINER);
logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else if (!internalAccount) {
logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}",
principal.getName(), httpRequest.getRemoteAddr()));
}
}
}

// try to authenticate by cookie
if (userManager.supportsCookies()) {
UserModel user = authenticate(httpRequest.getCookies());
if (user != null) {
flagWicketSession(AuthenticationType.COOKIE);
logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
}
}

// try to authenticate by BASIC
final String authorization = httpRequest.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
String credentials = new String(Base64.decode(base64Credentials),
Charset.forName("UTF-8"));
// credentials = username:password
final String[] values = credentials.split(":", 2);

if (values.length == 2) {
String username = values[0];
char[] password = values[1].toCharArray();
UserModel user = authenticate(username, password);
if (user != null) {
flagWicketSession(AuthenticationType.CREDENTIALS);
logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
} else {
logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}",
username, httpRequest.getRemoteAddr()));
}
}
}
return null;
}

/**
* Authenticate a user based on their cookie.
*
* @param cookies
* @return a user object or null
*/
protected UserModel authenticate(Cookie[] cookies) {
if (userManager.supportsCookies()) {
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(Constants.NAME)) {
String value = cookie.getValue();
return userManager.authenticate(value.toCharArray());
}
}
}
}
return null;
}

protected void flagWicketSession(AuthenticationType authenticationType) {
RequestCycle requestCycle = RequestCycle.get();
if (requestCycle != null) {
// flag the Wicket session, if this is a Wicket request
GitBlitWebSession session = GitBlitWebSession.get();
session.authenticationType = authenticationType;
}
}

/**
* Authenticate a user based on a username and password.
*
* @see IUserService.authenticate(String, char[])
* @param username
* @param password
* @return a user object or null
*/
@Override
public UserModel authenticate(String username, char[] password) {
if (StringUtils.isEmpty(username)) {
// can not authenticate empty username
return null;
}

String usernameDecoded = StringUtils.decodeUsername(username);
String pw = new String(password);
if (StringUtils.isEmpty(pw)) {
// can not authenticate empty password
return null;
}
// check to see if this is the federation user
// if (canFederate()) {
// if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) {
// List<String> tokens = getFederationTokens();
// if (tokens.contains(pw)) {
// return getFederationUser();
// }
// }
// }

UserModel user = userManager.authenticate(usernameDecoded, password);

// try registered external authentication providers
if (user == null) {
// for (AuthenticationService service : authenticationServices) {
// if (service instanceof UsernamePasswordAuthenticationService) {
// user = service.authenticate(usernameDecoded, password);
// if (user != null) {
// // user authenticated
// user.accountType = service.getAccountType();
// return user;
// }
// }
// }
}
return user;
}

/**
* Sets a cookie for the specified user.
*
* @param response
* @param user
*/
@Override
public void setCookie(HttpServletResponse response, UserModel user) {
GitBlitWebSession session = GitBlitWebSession.get();
boolean standardLogin = session.authenticationType.isStandard();

if (userManager.supportsCookies() && standardLogin) {
Cookie userCookie;
if (user == null) {
// clear cookie for logout
userCookie = new Cookie(Constants.NAME, "");
} else {
// set cookie for login
String cookie = userManager.getCookie(user);
if (StringUtils.isEmpty(cookie)) {
// create empty cookie
userCookie = new Cookie(Constants.NAME, "");
} else {
// create real cookie
userCookie = new Cookie(Constants.NAME, cookie);
userCookie.setMaxAge(Integer.MAX_VALUE);
}
}
userCookie.setPath("/");
response.addCookie(userCookie);
}
}

/**
* Logout a user.
*
* @param user
*/
@Override
public void logout(HttpServletResponse response, UserModel user) {
setCookie(response, null);
userManager.logout(user);
}

/**
* Returns true if the username represents an internal account
*
* @param username
* @return true if the specified username represents an internal account
*/
protected boolean isInternalAccount(String username) {
return !StringUtils.isEmpty(username)
&& (username.equalsIgnoreCase(Constants.FEDERATION_USER)
|| username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
}

// protected UserModel getFederationUser() {
// // the federation user is an administrator
// UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
// federationUser.canAdmin = true;
// return federationUser;
// }
}

+ 54
- 167
src/main/java/com/gitblit/manager/UserManager.java View File

@@ -20,20 +20,19 @@ import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.ConfigUserService;
import com.gitblit.Constants;
import com.gitblit.Constants.AccountType;
import com.gitblit.IStoredSettings;
import com.gitblit.IUserService;
import com.gitblit.Keys;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.StringUtils;

/**
@@ -50,37 +49,68 @@ public class UserManager implements IUserManager {

private final IRuntimeManager runtimeManager;

private final Map<String, String> legacyBackingServices;

private IUserService userService;

public UserManager(IRuntimeManager runtimeManager) {
this.settings = runtimeManager.getSettings();
this.runtimeManager = runtimeManager;

// map of legacy realm backing user services
legacyBackingServices = new HashMap<String, String>();
legacyBackingServices.put("com.gitblit.HtpasswdUserService", "realm.htpasswd.backingUserService");
legacyBackingServices.put("com.gitblit.LdapUserService", "realm.ldap.backingUserService");
legacyBackingServices.put("com.gitblit.PAMUserService", "realm.pam.backingUserService");
legacyBackingServices.put("com.gitblit.RedmineUserService", "realm.redmine.backingUserService");
legacyBackingServices.put("com.gitblit.SalesforceUserService", "realm.salesforce.backingUserService");
legacyBackingServices.put("com.gitblit.WindowsUserService", "realm.windows.backingUserService");
}

/**
* Set the user service. The user service authenticates local users and is
* responsible for persisting and retrieving users and teams.
* Set the user service. The user service authenticates *local* users and is
* responsible for persisting and retrieving all users and all teams.
*
* @param userService
*/
public void setUserService(IUserService userService) {
logger.info("UserService: " + userService.toString());
logger.info(userService.toString());
this.userService = userService;
this.userService.setup(runtimeManager);
}

@Override
public void setup(IRuntimeManager runtimeManager) {
// NOOP
}

@Override
public UserManager start() {
if (this.userService == null) {
String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties");
String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf");
IUserService service = null;
try {
// check to see if this "file" is a login service class
Class<?> realmClass = Class.forName(realm);
service = (IUserService) realmClass.newInstance();
} catch (Throwable t) {
File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
if (legacyBackingServices.containsKey(realm)) {
// create the user service from the legacy config
String realmKey = legacyBackingServices.get(realm);
logger.warn("");
logger.warn("#################################################################");
logger.warn(" Key '{}' is obsolete!", realmKey);
logger.warn(" Please set '{}={}'", Keys.realm.userService, settings.getString(realmKey, "${baseFolder}/users.conf"));
logger.warn("#################################################################");
logger.warn("");
File realmFile = runtimeManager.getFileOrFolder(realmKey, "${baseFolder}/users.conf");
service = createUserService(realmFile);
} else {
// either a file path OR a custom user service
try {
// check to see if this "file" is a custom user service class
Class<?> realmClass = Class.forName(realm);
service = (IUserService) realmClass.newInstance();
} catch (Throwable t) {
// typical file path configuration
File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
service = createUserService(realmFile);
}
}
setUserService(service);
}
@@ -90,7 +120,7 @@ public class UserManager implements IUserManager {
protected IUserService createUserService(File realmFile) {
IUserService service = null;
if (realmFile.getName().toLowerCase().endsWith(".conf")) {
// v0.8.0+ config-based realm file
// config-based realm file
service = new ConfigUserService(realmFile);
}

@@ -118,74 +148,6 @@ public class UserManager implements IUserManager {
return this;
}

@Override
public boolean supportsAddUser() {
return supportsCredentialChanges(new UserModel(""));
}

/**
* Returns true if the user's credentials can be changed.
*
* @param user
* @return true if the user service supports credential changes
*/
@Override
public boolean supportsCredentialChanges(UserModel user) {
if (user == null) {
return false;
} else if (AccountType.LOCAL.equals(user.accountType)) {
// local account, we can change credentials
return true;
} else {
// external account, ask user service
return userService.supportsCredentialChanges();
}
}

/**
* Returns true if the user's display name can be changed.
*
* @param user
* @return true if the user service supports display name changes
*/
@Override
public boolean supportsDisplayNameChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || userService.supportsDisplayNameChanges();
}

/**
* Returns true if the user's email address can be changed.
*
* @param user
* @return true if the user service supports email address changes
*/
@Override
public boolean supportsEmailAddressChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || userService.supportsEmailAddressChanges();
}

/**
* Returns true if the user's team memberships can be changed.
*
* @param user
* @return true if the user service supports team membership changes
*/
@Override
public boolean supportsTeamMembershipChanges(UserModel user) {
return (user != null && user.isLocalAccount()) || userService.supportsTeamMembershipChanges();
}

/**
* Allow to understand if GitBlit supports and is configured to allow
* cookie-based authentication.
*
* @return status of Cookie authentication enablement.
*/
@Override
public boolean supportsCookies() {
return settings.getBoolean(Keys.web.allowCookieAuthentication, true) && userService.supportsCookies();
}

/**
* Returns the cookie value for the specified user.
*
@@ -198,46 +160,17 @@ public class UserManager implements IUserManager {
}

/**
* Authenticate a user based on a username and password.
*
* @param username
* @param password
* @return a user object or null
*/

@Override
public UserModel authenticate(String username, char[] password) {
UserModel user = userService.authenticate(username, password);
setAccountType(user);
return user;
}

/**
* Authenticate a user based on their cookie.
* Retrieve the user object for the specified cookie.
*
* @param cookie
* @return a user object or null
*/
@Override
public UserModel authenticate(char[] cookie) {
UserModel user = userService.authenticate(cookie);
setAccountType(user);
public UserModel getUserModel(char[] cookie) {
UserModel user = userService.getUserModel(cookie);
return user;
}

/**
* Logout a user.
*
* @param user
*/
@Override
public void logout(UserModel user) {
if (userService == null) {
return;
}
userService.logout(user);
}

/**
* Retrieve the user object for the specified username.
*
@@ -251,7 +184,6 @@ public class UserManager implements IUserManager {
}
String usernameDecoded = StringUtils.decodeUsername(username);
UserModel user = userService.getUserModel(usernameDecoded);
setAccountType(user);
return user;
}

@@ -290,32 +222,7 @@ public class UserManager implements IUserManager {
*/
@Override
public boolean updateUserModel(String username, UserModel model) {
if (model.isLocalAccount() || userService.supportsCredentialChanges()) {
if (!model.isLocalAccount() && !userService.supportsTeamMembershipChanges()) {
// teams are externally controlled - copy from original model
UserModel existingModel = getUserModel(username);

model = DeepCopier.copy(model);
model.teams.clear();
model.teams.addAll(existingModel.teams);
}
return userService.updateUserModel(username, model);
}
if (model.username.equals(username)) {
// passwords are not persisted by the backing user service
model.password = null;
if (!model.isLocalAccount() && !userService.supportsTeamMembershipChanges()) {
// teams are externally controlled- copy from original model
UserModel existingModel = getUserModel(username);

model = DeepCopier.copy(model);
model.teams.clear();
model.teams.addAll(existingModel.teams);
}
return userService.updateUserModel(username, model);
}
logger.error("Users can not be renamed!");
return false;
return userService.updateUserModel(username, model);
}

/**
@@ -364,9 +271,6 @@ public class UserManager implements IUserManager {
@Override
public List<UserModel> getAllUsers() {
List<UserModel> users = userService.getAllUsers();
for (UserModel user : users) {
setAccountType(user);
}
return users;
}

@@ -378,7 +282,8 @@ public class UserManager implements IUserManager {
*/
@Override
public List<String> getAllTeamNames() {
return userService.getAllTeamNames();
List<String> teams = userService.getAllTeamNames();
return teams;
}

/**
@@ -404,7 +309,8 @@ public class UserManager implements IUserManager {
*/
@Override
public List<String> getTeamNamesForRepositoryRole(String role) {
return userService.getTeamNamesForRepositoryRole(role);
List<String> teams = userService.getTeamNamesForRepositoryRole(role);
return teams;
}

/**
@@ -416,7 +322,8 @@ public class UserManager implements IUserManager {
*/
@Override
public TeamModel getTeamModel(String teamname) {
return userService.getTeamModel(teamname);
TeamModel team = userService.getTeamModel(teamname);
return team;
}

/**
@@ -456,14 +363,6 @@ public class UserManager implements IUserManager {
*/
@Override
public boolean updateTeamModel(String teamname, TeamModel model) {
if (!userService.supportsTeamMembershipChanges()) {
// teams are externally controlled - copy from original model
TeamModel existingModel = getTeamModel(teamname);

model = DeepCopier.copy(model);
model.users.clear();
model.users.addAll(existingModel.users);
}
return userService.updateTeamModel(teamname, model);
}

@@ -527,16 +426,4 @@ public class UserManager implements IUserManager {
public boolean deleteRepositoryRole(String role) {
return userService.deleteRepositoryRole(role);
}

protected void setAccountType(UserModel user) {
if (user != null) {
if (!StringUtils.isEmpty(user.password)
&& !Constants.EXTERNAL_ACCOUNT.equalsIgnoreCase(user.password)
&& !"StoredInLDAP".equalsIgnoreCase(user.password)) {
user.accountType = AccountType.LOCAL;
} else {
user.accountType = userService.getAccountType();
}
}
}
}

+ 0
- 8
src/main/java/com/gitblit/models/ServerSettings.java View File

@@ -37,14 +37,6 @@ public class ServerSettings implements Serializable {
public List<String> pushScripts;
public boolean supportsCredentialChanges;
public boolean supportsDisplayNameChanges;
public boolean supportsEmailAddressChanges;
public boolean supportsTeamMembershipChanges;
public ServerSettings() {
settings = new TreeMap<String, SettingModel>();
}

+ 7
- 0
src/main/java/com/gitblit/models/TeamModel.java View File

@@ -27,6 +27,7 @@ import java.util.Set;
import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AccountType;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.Constants.Unused;
@@ -48,6 +49,7 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
public boolean canAdmin;
public boolean canFork;
public boolean canCreate;
public AccountType accountType;
public final Set<String> users = new HashSet<String>();
// retained for backwards-compatibility with RPC clients
@Deprecated
@@ -59,6 +61,7 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
public TeamModel(String name) {
this.name = name;
this.accountType = AccountType.LOCAL;
}
/**
@@ -358,6 +361,10 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
}
}
public boolean isLocalTeam() {
return accountType.isLocal();
}
@Override
public String toString() {
return name;

+ 3
- 3
src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java View File

@@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
@@ -54,9 +54,9 @@ public abstract class AccessRestrictionFilter extends AuthenticationFilter {
protected AccessRestrictionFilter(
IRuntimeManager runtimeManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager) {
super(sessionManager);
super(authenticationManager);
this.runtimeManager = runtimeManager;
this.repositoryManager = repositoryManager;
}

+ 5
- 5
src/main/java/com/gitblit/servlet/AuthenticationFilter.java View File

@@ -36,7 +36,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.UserModel;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.StringUtils;
@@ -58,10 +58,10 @@ public abstract class AuthenticationFilter implements Filter {
protected transient Logger logger = LoggerFactory.getLogger(getClass());
protected final ISessionManager sessionManager;
protected final IAuthenticationManager authenticationManager;
protected AuthenticationFilter(ISessionManager sessionManager) {
this.sessionManager = sessionManager;
protected AuthenticationFilter(IAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
@@ -108,7 +108,7 @@ public abstract class AuthenticationFilter implements Filter {
* @return user
*/
protected UserModel getUser(HttpServletRequest httpRequest) {
UserModel user = sessionManager.authenticate(httpRequest, requiresClientCertificate());
UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate());
return user;
}

+ 3
- 3
src/main/java/com/gitblit/servlet/DownloadZipFilter.java View File

@@ -21,7 +21,7 @@ import javax.inject.Singleton;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -39,10 +39,10 @@ public class DownloadZipFilter extends AccessRestrictionFilter {
@Inject
public DownloadZipFilter(
IRuntimeManager runtimeManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager) {
super(runtimeManager, sessionManager, repositoryManager);
super(runtimeManager, authenticationManager, repositoryManager);
}
/**

+ 5
- 5
src/main/java/com/gitblit/servlet/EnforceAuthenticationFilter.java View File

@@ -36,7 +36,7 @@ import com.gitblit.IStoredSettings;
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;

/**
@@ -54,16 +54,16 @@ public class EnforceAuthenticationFilter implements Filter {

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;
}

/*
@@ -86,7 +86,7 @@ public class EnforceAuthenticationFilter implements Filter {

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:

+ 3
- 3
src/main/java/com/gitblit/servlet/GitFilter.java View File

@@ -29,7 +29,7 @@ import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Keys.git;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
@@ -57,10 +57,10 @@ public class GitFilter extends AccessRestrictionFilter {
@Inject
public GitFilter(
IRuntimeManager runtimeManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager) {
super(runtimeManager, sessionManager, repositoryManager);
super(runtimeManager, authenticationManager, repositoryManager);
this.settings = runtimeManager.getSettings();
}

+ 2
- 2
src/main/java/com/gitblit/servlet/GitblitContext.java View File

@@ -49,7 +49,7 @@ import com.gitblit.manager.IProjectManager;
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;
@@ -170,7 +170,7 @@ public class GitblitContext extends DaggerContextListener {
// 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);

+ 3
- 3
src/main/java/com/gitblit/servlet/PagesFilter.java View File

@@ -23,7 +23,7 @@ import org.eclipse.jgit.lib.Repository;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -39,10 +39,10 @@ public class PagesFilter extends AccessRestrictionFilter {
@Inject
public PagesFilter(IRuntimeManager runtimeManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager) {
super(runtimeManager, sessionManager, repositoryManager);
super(runtimeManager, authenticationManager, repositoryManager);
}
/**

+ 3
- 3
src/main/java/com/gitblit/servlet/RpcFilter.java View File

@@ -31,7 +31,7 @@ import com.gitblit.Constants.RpcRequest;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.UserModel;
/**
@@ -57,9 +57,9 @@ public class RpcFilter extends AuthenticationFilter {
@Inject
public RpcFilter(
IRuntimeManager runtimeManager,
ISessionManager sessionManager) {
IAuthenticationManager authenticationManager) {
super(sessionManager);
super(authenticationManager);
this.settings = runtimeManager.getSettings();
this.runtimeManager = runtimeManager;
}

+ 5
- 5
src/main/java/com/gitblit/servlet/SparkleShareInviteServlet.java View File

@@ -31,7 +31,7 @@ import com.gitblit.Keys;
import com.gitblit.Keys.fanout;
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.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -52,7 +52,7 @@ public class SparkleShareInviteServlet extends HttpServlet {
private final IUserManager userManager;
private final ISessionManager sessionManager;
private final IAuthenticationManager authenticationManager;
private final IRepositoryManager repositoryManager;
@@ -60,13 +60,13 @@ public class SparkleShareInviteServlet extends HttpServlet {
public SparkleShareInviteServlet(
IRuntimeManager runtimeManager,
IUserManager userManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager) {
super();
this.settings = runtimeManager.getSettings();
this.userManager = userManager;
this.sessionManager = sessionManager;
this.authenticationManager = authenticationManager;
this.repositoryManager = repositoryManager;
}
@@ -106,7 +106,7 @@ public class SparkleShareInviteServlet extends HttpServlet {
}
UserModel user;
if (StringUtils.isEmpty(username)) {
user = sessionManager.authenticate(request);
user = authenticationManager.authenticate(request);
} else {
user = userManager.getUserModel(username);
}

+ 3
- 3
src/main/java/com/gitblit/servlet/SyndicationFilter.java View File

@@ -31,7 +31,7 @@ import com.gitblit.Constants.AccessRestrictionType;
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.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
@@ -54,11 +54,11 @@ public class SyndicationFilter extends AuthenticationFilter {
@Inject
public SyndicationFilter(
IRuntimeManager runtimeManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager) {
super(sessionManager);
super(authenticationManager);
this.runtimeManager = runtimeManager;
this.repositoryManager = repositoryManager;
this.projectManager = projectManager;

+ 6
- 6
src/main/java/com/gitblit/wicket/GitBlitWebApp.java View File

@@ -36,7 +36,7 @@ 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.IAuthenticationManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.pages.ActivityPage;
@@ -89,7 +89,7 @@ public class GitBlitWebApp extends WebApplication {

private final IUserManager userManager;

private final ISessionManager sessionManager;
private final IAuthenticationManager authenticationManager;

private final IRepositoryManager repositoryManager;

@@ -103,7 +103,7 @@ public class GitBlitWebApp extends WebApplication {
IRuntimeManager runtimeManager,
INotificationManager notificationManager,
IUserManager userManager,
ISessionManager sessionManager,
IAuthenticationManager authenticationManager,
IRepositoryManager repositoryManager,
IProjectManager projectManager,
IGitblitManager gitblitManager,
@@ -114,7 +114,7 @@ public class GitBlitWebApp extends WebApplication {
this.runtimeManager = runtimeManager;
this.notificationManager = notificationManager;
this.userManager = userManager;
this.sessionManager = sessionManager;
this.authenticationManager = authenticationManager;
this.repositoryManager = repositoryManager;
this.projectManager = projectManager;
this.gitblitManager = gitblitManager;
@@ -267,8 +267,8 @@ public class GitBlitWebApp extends WebApplication {
return userManager;
}

public ISessionManager session() {
return sessionManager;
public IAuthenticationManager authentication() {
return authenticationManager;
}

public IRepositoryManager repositories() {

+ 2
- 2
src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java View File

@@ -51,7 +51,7 @@ public class ChangePasswordPage extends RootSubPage {
}
UserModel user = GitBlitWebSession.get().getUser();
if (!app().users().supportsCredentialChanges(user)) {
if (!app().authentication().supportsCredentialChanges(user)) {
error(MessageFormat.format(getString("gb.userServiceDoesNotPermitPasswordChanges"),
app().settings().getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);
}
@@ -100,7 +100,7 @@ public class ChangePasswordPage extends RootSubPage {
app().gitblit().updateUserModel(user.username, user, false);
if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
WebResponse response = (WebResponse) getRequestCycle().getResponse();
app().session().setCookie(response.getHttpServletResponse(), user);
app().authentication().setCookie(response.getHttpServletResponse(), user);
}
} catch (GitBlitException e) {
error(e.getMessage());

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/EditTeamPage.java View File

@@ -216,7 +216,7 @@ public class EditTeamPage extends RootSubPage {
form.add(new SimpleAttributeModifier("autocomplete", "off"));
// not all user services support manipulating team memberships
boolean editMemberships = app().users().supportsTeamMembershipChanges(null);
boolean editMemberships = app().authentication().supportsTeamMembershipChanges(teamModel);
// field names reflective match TeamModel fields
form.add(new TextField<String>("name"));

+ 5
- 9
src/main/java/com/gitblit/wicket/pages/EditUserPage.java View File

@@ -54,10 +54,6 @@ public class EditUserPage extends RootSubPage {
public EditUserPage() {
// create constructor
super();
if (!app().users().supportsAddUser()) {
error(MessageFormat.format(getString("gb.userServiceDoesNotPermitAddUser"),
app().settings().getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);
}
isCreate = true;
setupPage(new UserModel(""));
setStatelessHint(false);
@@ -138,7 +134,7 @@ public class EditUserPage extends RootSubPage {
}
boolean rename = !StringUtils.isEmpty(oldName)
&& !oldName.equalsIgnoreCase(username);
if (app().users().supportsCredentialChanges(userModel)) {
if (app().authentication().supportsCredentialChanges(userModel)) {
if (!userModel.password.equals(confirmPassword.getObject())) {
error(getString("gb.passwordsDoNotMatch"));
return;
@@ -214,16 +210,16 @@ public class EditUserPage extends RootSubPage {
form.add(new SimpleAttributeModifier("autocomplete", "off"));
// not all user services support manipulating username and password
boolean editCredentials = app().users().supportsCredentialChanges(userModel);
boolean editCredentials = app().authentication().supportsCredentialChanges(userModel);
// not all user services support manipulating display name
boolean editDisplayName = app().users().supportsDisplayNameChanges(userModel);
boolean editDisplayName = app().authentication().supportsDisplayNameChanges(userModel);
// not all user services support manipulating email address
boolean editEmailAddress = app().users().supportsEmailAddressChanges(userModel);
boolean editEmailAddress = app().authentication().supportsEmailAddressChanges(userModel);
// not all user services support manipulating team memberships
boolean editTeams = app().users().supportsTeamMembershipChanges(userModel);
boolean editTeams = app().authentication().supportsTeamMembershipChanges(userModel);
// field names reflective match UserModel fields
form.add(new TextField<String>("username").setEnabled(editCredentials));

+ 1
- 1
src/main/java/com/gitblit/wicket/pages/LogoutPage.java View File

@@ -27,7 +27,7 @@ public class LogoutPage extends BasePage {
super();
GitBlitWebSession session = GitBlitWebSession.get();
UserModel user = session.getUser();
app().session().logout(((WebResponse) getResponse()).getHttpServletResponse(), user);
app().authentication().logout(((WebResponse) getResponse()).getHttpServletResponse(), user);
session.invalidate();
/*

+ 3
- 3
src/main/java/com/gitblit/wicket/pages/RootPage.java View File

@@ -252,7 +252,7 @@ public abstract class RootPage extends BasePage {
// Set Cookie
if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
WebResponse response = (WebResponse) getRequestCycle().getResponse();
app().session().setCookie(response.getHttpServletResponse(), user);
app().authentication().setCookie(response.getHttpServletResponse(), user);
}
if (!session.continueRequest()) {
@@ -536,7 +536,7 @@ public abstract class RootPage extends BasePage {
String username = RootPage.this.username.getObject();
char[] password = RootPage.this.password.getObject().toCharArray();
UserModel user = app().session().authenticate(username, password);
UserModel user = app().authentication().authenticate(username, password);
if (user == null) {
error(getString("gb.invalidUsernameOrPassword"));
} else if (user.username.equals(Constants.FEDERATION_USER)) {
@@ -572,7 +572,7 @@ public abstract class RootPage extends BasePage {
GitBlitWebSession session = GitBlitWebSession.get();
UserModel user = session.getUser();
boolean editCredentials = app().users().supportsCredentialChanges(user);
boolean editCredentials = app().authentication().supportsCredentialChanges(user);
boolean standardLogin = session.authenticationType.isStandard();
if (app().settings().getBoolean(Keys.web.allowGravatar, true)) {

+ 2
- 2
src/main/java/com/gitblit/wicket/pages/SessionPage.java View File

@@ -60,7 +60,7 @@ public abstract class SessionPage extends WebPage {
// try to authenticate by servlet request
HttpServletRequest httpRequest = ((WebRequest) getRequestCycle().getRequest())
.getHttpServletRequest();
UserModel user = app().session().authenticate(httpRequest);
UserModel user = app().authentication().authenticate(httpRequest);
// Login the user
if (user != null) {
@@ -70,7 +70,7 @@ public abstract class SessionPage extends WebPage {
// Set Cookie
WebResponse response = (WebResponse) getRequestCycle().getResponse();
app().session().setCookie(response.getHttpServletResponse(), user);
app().authentication().setCookie(response.getHttpServletResponse(), user);
session.continueRequest();
}

+ 1
- 1
src/main/java/com/gitblit/wicket/panels/TeamsPanel.java View File

@@ -39,7 +39,7 @@ public class TeamsPanel extends BasePanel {
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
adminLinks.add(new BookmarkablePageLink<Void>("newTeam", EditTeamPage.class));
add(adminLinks.setVisible(showAdmin && app().users().supportsTeamMembershipChanges(null)));
add(adminLinks.setVisible(showAdmin));
final List<TeamModel> teams = app().users().getAllTeams();
DataView<TeamModel> teamsView = new DataView<TeamModel>("teamRow",

+ 1
- 2
src/main/java/com/gitblit/wicket/panels/UsersPanel.java View File

@@ -39,8 +39,7 @@ public class UsersPanel extends BasePanel {
super(wicketId);
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class)
.setVisible(app().users().supportsAddUser()));
adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class));
add(adminLinks.setVisible(showAdmin));
final List<UserModel> users = app().users().getAllUsers();

+ 9
- 14
src/site/setup_authentication.mkd View File

@@ -15,11 +15,11 @@ Gitblit supports additional authentication mechanisms aside from it's internal o
### LDAP Authentication
*SINCE 1.0.0*
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).
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.
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.
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.
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.
#### Example LDAP Layout
![block diagram](ldapSample.png "LDAP Sample")
@@ -46,10 +46,6 @@ The following are the settings required to configure Gitblit to authenticate aga
<th>realm.ldap.password</th><td>password</td>
<td>The credentials that will log into the LDAP server</td>
</tr>
<tr>
<th>realm.ldap.backingUserService</th><td>users.conf</td>
<td>Where to store all information that is used by Gitblit. All information will be synced here upon user login.</td>
</tr>
<tr>
<th>realm.ldap.maintainTeams</th><td>true</td>
<td>Are team memberships maintained in LDAP (<em>true</em>) or manually in Gitblit (<em>false</em>).</td>
@@ -82,35 +78,35 @@ You can start Gitblit GO with an in-memory LDAP server by specifying the *--ldap
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.
realm.userService = com.gitblit.WindowsUserService
realm.authenticationProviders = windows
realm.windows.defaultDomain =
### PAM Authentication
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.
realm.userService = com.gitblit.PAMUserService
realm.authenticationProviders = pam
realm.pam.serviceName = system-auth
### Htpasswd Authentication
Htpasswd authentication allows you to maintain your user credentials in an Apache htpasswd file thay may be shared with other htpasswd-capable servers.
realm.userService = com.gitblit.HtpasswdUserService
realm.authenticationProviders = htpasswd
realm.htpasswd.userFile = /path/to/htpasswd
### Redmine Authentication
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.
realm.userService = com.gitblit.RedmineUserService
realm.authenticationProviders = redmine
realm.redmine.url = http://example.com/redmine
### Salesforce.com Authentication
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.
realm.userService = com.gitblit.SalesforceUserService
realm.authenticationProviders = salesforce
realm.salesforce.orgId = 0
### Container Authentication
@@ -123,10 +119,9 @@ If you are using the WAR variant and deploying into your own servlet container w
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.
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.
Make sure to set the *serviceImpl* field in your *setup()* method.
Please subclass [com.gitblit.auth.AuthenticationProvider](https://github.com/gitblit/gitblit/blob/master/src/main/java/com/gitblit/auth/AuthenticationProvider.java).
You may use your subclass by specifying its fully qualified classname in the *realm.userService* setting.
You may use your subclass by specifying its fully qualified classname in the *realm.authenticationProviders* setting.
Your subclass must be on Gitblit's classpath and must have a public default constructor.

+ 2
- 4
src/test/config/test-users.conf View File

@@ -1,12 +1,10 @@
[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

+ 2
- 2
src/test/java/com/gitblit/tests/GitBlitSuite.java View File

@@ -56,12 +56,12 @@ import com.gitblit.utils.JGitUtils;
@RunWith(Suite.class)
@SuiteClasses({ ArrayUtilsTest.class, FileUtilsTest.class, TimeUtilsTest.class,
StringUtilsTest.class, Base64Test.class, JsonUtilsTest.class, ByteFormatTest.class,
ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapUserServiceTest.class,
ObjectCacheTest.class, PermissionsTest.class, UserServiceTest.class, LdapAuthenticationTest.class,
MarkdownUtilsTest.class, JGitUtilsTest.class, SyndicationUtilsTest.class,
DiffUtilsTest.class, MetricUtilsTest.class, X509UtilsTest.class,
GitBlitTest.class, FederationTests.class, RpcTests.class, GitServletTest.class, GitDaemonTest.class,
GroovyScriptTest.class, LuceneExecutorTest.class, RepositoryModelTest.class,
FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdUserServiceTest.class,
FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,
ModelUtilsTest.class, JnaUtilsTest.class })
public class GitBlitSuite {

+ 1
- 1
src/test/java/com/gitblit/tests/GitBlitTest.java View File

@@ -172,7 +172,7 @@ public class GitBlitTest extends GitblitUnitTest {
@Test
public void testAuthentication() throws Exception {
assertTrue(session().authenticate("admin", "admin".toCharArray()) != null);
assertTrue(authentication().authenticate("admin", "admin".toCharArray()) != null);
}
@Test

+ 3
- 3
src/test/java/com/gitblit/tests/GitblitUnitTest.java View File

@@ -22,7 +22,7 @@ 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.IAuthenticationManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.servlet.GitblitContext;

@@ -45,8 +45,8 @@ public class GitblitUnitTest extends org.junit.Assert {
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() {

+ 365
- 0
src/test/java/com/gitblit/tests/HtpasswdAuthenticationTest.java View File

@@ -0,0 +1,365 @@
/*
* 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);
}

}

+ 0
- 569
src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java View File

@@ -1,569 +0,0 @@
/*
* Copyright 2013 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.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);
}

}

src/test/java/com/gitblit/tests/LdapUserServiceTest.java → src/test/java/com/gitblit/tests/LdapAuthenticationTest.java View File

@@ -16,6 +16,7 @@
*/
package com.gitblit.tests;

import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;

@@ -23,11 +24,12 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.gitblit.LdapUserService;
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.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;
@@ -40,9 +42,11 @@ import com.unboundid.ldif.LDIFReader;
* @author jcrygier
*
*/
public class LdapUserServiceTest extends GitblitUnitTest {
public class LdapAuthenticationTest extends GitblitUnitTest {

private LdapUserService ldapUserService;
private static final String RESOURCE_DIR = "src/test/resources/ldap/";

private LdapAuthProvider ldap;

static int ldapPort = 1389;

@@ -54,18 +58,26 @@ public class LdapUserServiceTest extends GitblitUnitTest {
config.setSchema(null);

InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.importFromLDIF(true, new LDIFReader(LdapUserServiceTest.class.getResourceAsStream("resources/ldapUserServiceSampleData.ldif")));
ds.importFromLDIF(true, new LDIFReader(new FileInputStream(RESOURCE_DIR + "sampledata.ldif")));
ds.startListening();
}

@Before
public void createLdapUserService() {
ldapUserService = new LdapUserService();
ldapUserService.setup(new MockRuntimeManager(getSettings()));
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");
@@ -86,23 +98,23 @@ public class LdapUserServiceTest extends GitblitUnitTest {

@Test
public void testAuthenticate() {
UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
UserModel userOneModel = ldap.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());
UserModel userOneModelFailedAuth = ldap.authenticate("UserOne", "userTwoPassword".toCharArray());
assertNull(userOneModelFailedAuth);

UserModel userTwoModel = ldapUserService.authenticate("UserTwo", "userTwoPassword".toCharArray());
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 = ldapUserService.authenticate("UserThree", "userThreePassword".toCharArray());
UserModel userThreeModel = ldap.authenticate("UserThree", "userThreePassword".toCharArray());
assertNotNull(userThreeModel);
assertNotNull(userThreeModel.getTeam("git_users"));
assertNull(userThreeModel.getTeam("git_admins"));
@@ -111,34 +123,32 @@ public class LdapUserServiceTest extends GitblitUnitTest {

@Test
public void testDisplayName() {
UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
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}");
ldapUserService = new LdapUserService();
ldapUserService.setup(new MockRuntimeManager(ms));
ldap = newLdapAuthentication(ms);

userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
assertNotNull(userOneModel);
assertEquals("Mr. User One", userOneModel.displayName);
}

@Test
public void testEmail() {
UserModel userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
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");
ldapUserService = new LdapUserService();
ldapUserService.setup(new MockRuntimeManager(ms));
ldap = newLdapAuthentication(ms);

userOneModel = ldapUserService.authenticate("UserOne", "userOnePassword".toCharArray());
userOneModel = ldap.authenticate("UserOne", "userOnePassword".toCharArray());
assertNotNull(userOneModel);
assertEquals("User.One@gitblit.com", userOneModel.emailAddress);
}
@@ -148,23 +158,8 @@ public class LdapUserServiceTest extends GitblitUnitTest {
// Inject so "(&(objectClass=person)(sAMAccountName=${username}))" becomes "(&(objectClass=person)(sAMAccountName=*)(userPassword=userOnePassword))"
// Thus searching by password

UserModel userOneModel = ldapUserService.authenticate("*)(userPassword=userOnePassword", "userOnePassword".toCharArray());
UserModel userOneModel = ldap.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));
}

}

+ 65
- 0
src/test/java/com/gitblit/tests/RedmineAuthenticationTest.java View File

@@ -0,0 +1,65 @@
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));
}
}

+ 0
- 66
src/test/java/com/gitblit/tests/RedmineUserServiceTest.java View File

@@ -1,66 +0,0 @@
package com.gitblit.tests;
import static org.hamcrest.CoreMatchers.is;
import org.junit.Test;
import com.gitblit.RedmineUserService;
import com.gitblit.models.UserModel;
import com.gitblit.tests.mock.MockRuntimeManager;
import com.gitblit.utils.StringUtils;
public class RedmineUserServiceTest 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\"}}";
@Test
public void testAuthenticate() throws Exception {
RedmineUserService redmineUserService = new RedmineUserService();
redmineUserService.setup(new MockRuntimeManager());
redmineUserService.setTestingCurrentUserAsJson(JSON);
UserModel userModel = redmineUserService.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 {
RedmineUserService redmineUserService = new RedmineUserService();
redmineUserService.setup(new MockRuntimeManager());
redmineUserService.setTestingCurrentUserAsJson(NOT_ADMIN_JSON);
UserModel userModel = redmineUserService.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));
}
@Test
public void testLocalAccount() {
RedmineUserService redmineUserService = new RedmineUserService();
redmineUserService.setup(new MockRuntimeManager());
UserModel localAccount = new UserModel("bruce");
localAccount.displayName = "Bruce Campbell";
localAccount.password = StringUtils.MD5_TYPE + StringUtils.getMD5("gimmesomesugar");
redmineUserService.deleteUser(localAccount.username);
assertTrue("Failed to add local account",
redmineUserService.updateUserModel(localAccount));
assertEquals("Accounts are not equal!",
localAccount,
redmineUserService.authenticate(localAccount.username, "gimmesomesugar".toCharArray()));
assertTrue("Failed to delete local account!",
redmineUserService.deleteUser(localAccount.username));
}
}

+ 1
- 6
src/test/java/com/gitblit/tests/UserServiceTest.java View File

@@ -85,14 +85,9 @@ public class UserServiceTest extends GitblitUnitTest {
assertTrue(newUser.hasRepositoryPermission("repo2"));
assertTrue(newUser.hasRepositoryPermission("sub/repo3"));
// confirm authentication of test user
UserModel testUser = service.authenticate("test", "testPassword".toCharArray());
assertEquals("test", testUser.username);
assertEquals("testPassword", testUser.password);
// delete a repository role and confirm role removal from test user
service.deleteRepositoryRole("repo2");
testUser = service.getUserModel("test");
UserModel testUser = service.getUserModel("test");
assertEquals(2, testUser.permissions.size());
// delete garbage user and confirm user count

src/test/resources/htpasswdUSTest/htpasswd-user.in → src/test/resources/htpasswd/htpasswd-user.in View File


src/test/resources/htpasswdUSTest/htpasswd.in → src/test/resources/htpasswd/htpasswd.in View File


src/test/resources/htpasswdUSTest/users.conf.in → src/test/resources/htpasswd/users.conf.in View File


src/test/java/com/gitblit/tests/resources/ldapUserServiceSampleData.ldif → src/test/resources/ldap/sampledata.ldif View File


+ 53
- 0
src/test/resources/ldap/users.conf View File

@@ -0,0 +1,53 @@
[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

Loading…
Cancel
Save