From a502d96a860456ec5e8c96761db70f7cabb74751 Mon Sep 17 00:00:00 2001 From: Paul Martin <paul@paulsputer.com> Date: Sat, 30 Apr 2016 04:19:14 -0400 Subject: [PATCH] Merge pull request #1073 from gitblit/1062-DocEditorUpdates --- src/main/java/com/gitblit/auth/LdapAuthProvider.java | 233 +++++++++++++++++++++++++++++++++++---------------------- 1 files changed, 143 insertions(+), 90 deletions(-) diff --git a/src/main/java/com/gitblit/auth/LdapAuthProvider.java b/src/main/java/com/gitblit/auth/LdapAuthProvider.java index b208459..cc772e7 100644 --- a/src/main/java/com/gitblit/auth/LdapAuthProvider.java +++ b/src/main/java/com/gitblit/auth/LdapAuthProvider.java @@ -19,6 +19,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.security.GeneralSecurityException; +import java.text.MessageFormat; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -26,10 +27,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import com.gitblit.Constants; import com.gitblit.Constants.AccountType; +import com.gitblit.Constants.Role; import com.gitblit.Keys; import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider; import com.gitblit.models.TeamModel; @@ -60,109 +61,123 @@ */ public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider { - private final AtomicLong lastLdapUserSync = new AtomicLong(0L); + private final ScheduledExecutorService scheduledExecutorService; public LdapAuthProvider() { super("ldap"); + + scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); } - private long getSynchronizationPeriodInMilliseconds(String name) { - final String cacheDuration = settings.getString(name, "2 MINUTES"); + private long getSynchronizationPeriodInMilliseconds() { + String period = settings.getString(Keys.realm.ldap.syncPeriod, null); + if (StringUtils.isEmpty(period)) { + period = settings.getString("realm.ldap.ldapCachePeriod", null); + if (StringUtils.isEmpty(period)) { + period = "5 MINUTES"; + } else { + logger.warn("realm.ldap.ldapCachePeriod is obsolete!"); + logger.warn(MessageFormat.format("Please set {0}={1} in gitblit.properties!", Keys.realm.ldap.syncPeriod, period)); + settings.overrideSetting(Keys.realm.ldap.syncPeriod, period); + } + } + try { - final String[] s = cacheDuration.split(" ", 2); + final String[] s = period.split(" ", 2); long duration = Math.abs(Long.parseLong(s[0])); TimeUnit timeUnit = TimeUnit.valueOf(s[1]); return timeUnit.toMillis(duration); } catch (RuntimeException ex) { - throw new IllegalArgumentException(name + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'"); + throw new IllegalArgumentException(Keys.realm.ldap.syncPeriod + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'"); } } @Override public void setup() { - synchronizeLdapUsers(); - configureLdapSyncService(); - } - - public void synchronizeWithLdapService() { - synchronizeLdapUsers(); + configureSyncService(); } - protected synchronized void synchronizeLdapUsers() { - final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.enable, false); - if (enabled) { - if (System.currentTimeMillis() > (lastLdapUserSync.get() + getSynchronizationPeriodInMilliseconds(Keys.realm.ldap.ldapCachePeriod))) { - logger.info("Synchronizing with LDAP @ " + settings.getRequiredString(Keys.realm.ldap.server)); - final boolean deleteRemovedLdapUsers = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.removeDeleted, true); - LDAPConnection ldapConnection = getLdapConnection(); - if (ldapConnection != null) { - try { - String accountBase = settings.getString(Keys.realm.ldap.accountBase, ""); - String uidAttribute = settings.getString(Keys.realm.ldap.uid, "uid"); - String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))"); - accountPattern = StringUtils.replace(accountPattern, "${username}", "*"); + @Override + public void stop() { + scheduledExecutorService.shutdownNow(); + } - SearchResult result = doSearch(ldapConnection, accountBase, accountPattern); - if (result != null && result.getEntryCount() > 0) { - final Map<String, UserModel> ldapUsers = new HashMap<String, UserModel>(); + public synchronized void sync() { + final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronize, false); + if (enabled) { + logger.info("Synchronizing with LDAP @ " + settings.getRequiredString(Keys.realm.ldap.server)); + final boolean deleteRemovedLdapUsers = settings.getBoolean(Keys.realm.ldap.removeDeletedUsers, true); + LDAPConnection ldapConnection = getLdapConnection(); + if (ldapConnection != null) { + try { + String accountBase = settings.getString(Keys.realm.ldap.accountBase, ""); + String uidAttribute = settings.getString(Keys.realm.ldap.uid, "uid"); + String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))"); + accountPattern = StringUtils.replace(accountPattern, "${username}", "*"); - for (SearchResultEntry loggingInUser : result.getSearchEntries()) { + SearchResult result = doSearch(ldapConnection, accountBase, accountPattern); + if (result != null && result.getEntryCount() > 0) { + final Map<String, UserModel> ldapUsers = new HashMap<String, UserModel>(); - final String username = loggingInUser.getAttribute(uidAttribute).getValue(); - logger.debug("LDAP synchronizing: " + username); + for (SearchResultEntry loggingInUser : result.getSearchEntries()) { + Attribute uid = loggingInUser.getAttribute(uidAttribute); + if (uid == null) { + logger.error("Can not synchronize with LDAP, missing \"{}\" attribute", uidAttribute); + continue; + } + final String username = uid.getValue(); + logger.debug("LDAP synchronizing: " + username); - UserModel user = userManager.getUserModel(username); - if (user == null) { - user = new UserModel(username); - } + UserModel user = userManager.getUserModel(username); + if (user == null) { + user = new UserModel(username); + } - if (!supportsTeamMembershipChanges()) { - getTeamsFromLdap(ldapConnection, username, loggingInUser, user); - } + if (!supportsTeamMembershipChanges()) { + getTeamsFromLdap(ldapConnection, username, loggingInUser, user); + } - // Get User Attributes - setUserAttributes(user, loggingInUser); + // Get User Attributes + setUserAttributes(user, loggingInUser); - // store in map - ldapUsers.put(username.toLowerCase(), user); - } + // store in map + ldapUsers.put(username.toLowerCase(), user); + } - if (deleteRemovedLdapUsers) { - logger.debug("detecting removed LDAP users..."); + if (deleteRemovedLdapUsers) { + logger.debug("detecting removed LDAP users..."); - for (UserModel userModel : userManager.getAllUsers()) { - if (Constants.EXTERNAL_ACCOUNT.equals(userModel.password)) { - if (!ldapUsers.containsKey(userModel.username)) { - logger.info("deleting removed LDAP user " + userModel.username + " from user service"); - userManager.deleteUser(userModel.username); - } - } - } - } + for (UserModel userModel : userManager.getAllUsers()) { + if (AccountType.LDAP == userModel.accountType) { + if (!ldapUsers.containsKey(userModel.username)) { + logger.info("deleting removed LDAP user " + userModel.username + " from user service"); + userManager.deleteUser(userModel.username); + } + } + } + } - userManager.updateUserModels(ldapUsers.values()); + userManager.updateUserModels(ldapUsers.values()); - if (!supportsTeamMembershipChanges()) { - final Map<String, TeamModel> userTeams = new HashMap<String, TeamModel>(); - for (UserModel user : ldapUsers.values()) { - for (TeamModel userTeam : user.teams) { - userTeams.put(userTeam.name, userTeam); - } - } - userManager.updateTeamModels(userTeams.values()); - } - } - if (!supportsTeamMembershipChanges()) { - getEmptyTeamsFromLdap(ldapConnection); - } - lastLdapUserSync.set(System.currentTimeMillis()); - } finally { - ldapConnection.close(); - } - } - } - } - } + if (!supportsTeamMembershipChanges()) { + final Map<String, TeamModel> userTeams = new HashMap<String, TeamModel>(); + for (UserModel user : ldapUsers.values()) { + for (TeamModel userTeam : user.teams) { + userTeams.put(userTeam.name, userTeam); + } + } + userManager.updateTeamModels(userTeams.values()); + } + } + if (!supportsTeamMembershipChanges()) { + getEmptyTeamsFromLdap(ldapConnection); + } + } finally { + ldapConnection.close(); + } + } + } + } private LDAPConnection getLdapConnection() { try { @@ -258,7 +273,6 @@ return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, "")); } - /** * If the LDAP server will maintain team memberships then LdapUserService * will not allow team membership changes. In this scenario all team @@ -270,6 +284,32 @@ @Override public boolean supportsTeamMembershipChanges() { return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false); + } + + @Override + public boolean supportsRoleChanges(UserModel user, Role role) { + if (Role.ADMIN == role) { + if (!supportsTeamMembershipChanges()) { + List<String> admins = settings.getStrings(Keys.realm.ldap.admins); + if (admins.contains(user.username)) { + return false; + } + } + } + return true; + } + + @Override + public boolean supportsRoleChanges(TeamModel team, Role role) { + if (Role.ADMIN == role) { + if (!supportsTeamMembershipChanges()) { + List<String> admins = settings.getStrings(Keys.realm.ldap.admins); + if (admins.contains("@" + team.name)) { + return false; + } + } + } + return true; } @Override @@ -284,6 +324,20 @@ LDAPConnection ldapConnection = getLdapConnection(); if (ldapConnection != null) { try { + boolean alreadyAuthenticated = false; + + String bindPattern = settings.getString(Keys.realm.ldap.bindpattern, ""); + if (!StringUtils.isEmpty(bindPattern)) { + try { + String bindUser = StringUtils.replace(bindPattern, "${username}", escapeLDAPSearchFilter(simpleUsername)); + ldapConnection.bind(bindUser, new String(password)); + + alreadyAuthenticated = true; + } catch (LDAPException e) { + return null; + } + } + // Find the logging in user's DN String accountBase = settings.getString(Keys.realm.ldap.accountBase, ""); String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))"); @@ -294,7 +348,7 @@ SearchResultEntry loggingInUser = result.getSearchEntries().get(0); String loggingInUserDN = loggingInUser.getDN(); - if (isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) { + if (alreadyAuthenticated || isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) { logger.debug("LDAP authenticated: " + username); UserModel user = null; @@ -399,6 +453,10 @@ Attribute attribute = userEntry.getAttribute(email); if (attribute != null && attribute.hasValue()) { user.emailAddress = attribute.getValue(); + } else { + // issue-456/ticket-134 + // allow LDAP to delete an email address + user.emailAddress = null; } } } @@ -439,6 +497,7 @@ } private void getEmptyTeamsFromLdap(LDAPConnection ldapConnection) { + logger.info("Start fetching empty teams from ldap."); String groupBase = settings.getString(Keys.realm.ldap.groupBase, ""); String groupMemberPattern = settings.getString(Keys.realm.ldap.groupEmptyMemberPattern, "(&(objectClass=group)(!(member=*)))"); @@ -448,7 +507,7 @@ SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i); if (!teamEntry.hasAttribute("member")) { String teamName = teamEntry.getAttribute("cn").getValue(); - + TeamModel teamModel = userManager.getTeamModel(teamName); if (teamModel == null) { teamModel = createTeamFromLdap(teamEntry); @@ -457,6 +516,7 @@ } } } + logger.info("Finished fetching empty teams from ldap."); } private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) { @@ -552,24 +612,17 @@ return sb.toString(); } - private void configureLdapSyncService() { - logger.info("Start configuring ldap sync service"); + private void configureSyncService() { LdapSyncService ldapSyncService = new LdapSyncService(settings, this); if (ldapSyncService.isReady()) { - long ldapSyncPeriod = getSynchronizationPeriodInMilliseconds(Keys.realm.ldap.synchronizeUsers.ldapSyncPeriod); - long ldapCachePeriod = getSynchronizationPeriodInMilliseconds(Keys.realm.ldap.synchronizeUsers.ldapSyncPeriod); - if (ldapSyncPeriod < ldapCachePeriod) { - ldapSyncPeriod = ldapCachePeriod; - } + long ldapSyncPeriod = getSynchronizationPeriodInMilliseconds(); int delay = 1; - ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + logger.info("Ldap sync service will update users and groups every {} minutes.", + TimeUnit.MILLISECONDS.toMinutes(ldapSyncPeriod)); scheduledExecutorService.scheduleAtFixedRate(ldapSyncService, delay, ldapSyncPeriod, TimeUnit.MILLISECONDS); - logger.info("Ldap sync service will update user and groups every {} minutes.", ldapSyncPeriod); - logger.info("Next scheduled ldap sync is in {} minutes", delay); } else { logger.info("Ldap sync service is disabled."); } - logger.info("Finished configuring ldap sync service"); } } -- Gitblit v1.9.1