Alfred Schmid
2014-01-31 eb1264b0944cfc0ac951c62239cc5a85428088e0
Basic implementation of feature for ldap user synchronization as
background service. Introduced configuration property to configure the
synchronization period.
2 files added
5 files modified
183 ■■■■■ changed files
src/main/distrib/data/gitblit.properties 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/auth/LdapAuthProvider.java 31 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/service/LdapSyncService.java 64 ●●●●● patch | view | raw | blame | history
src/test/config/test-users.conf 5 ●●●●● patch | view | raw | blame | history
src/test/java/com/gitblit/tests/LdapAuthenticationTest.java 43 ●●●●● patch | view | raw | blame | history
src/test/resources/ldap/adduser.ldif 11 ●●●●● patch | view | raw | blame | history
src/test/resources/ldap/users.conf 19 ●●●● patch | view | raw | blame | history
src/main/distrib/data/gitblit.properties
@@ -1508,6 +1508,16 @@
# If left blank, false is assumed
realm.ldap.synchronizeUsers.enable = false
# Defines the period to be used when synchronizing users from ldap. This is currently
# only used for LDAP user synchronization.
#
# Must be of the form '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'
# <long> is at least 5 Minutes if lower values are given the default is used.
# default: 5 MINUTES
#
# RESTART REQUIRED
realm.ldap.synchronizeUsers.ldapSyncPeriod = 5 MINUTES
# Defines whether to delete non-existent LDAP users from the backing user service
# during synchronization. depends on  realm.ldap.synchronizeUsers.enable = true
#
src/main/java/com/gitblit/auth/LdapAuthProvider.java
@@ -23,6 +23,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@@ -32,8 +34,10 @@
import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.service.LdapSyncService;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DereferencePolicy;
import com.unboundid.ldap.sdk.ExtendedResult;
@@ -57,7 +61,7 @@
 */
public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider {
    private AtomicLong lastLdapUserSync = new AtomicLong(0L);
    private final AtomicLong lastLdapUserSync = new AtomicLong(0L);
    public LdapAuthProvider() {
        super("ldap");
@@ -77,6 +81,11 @@
    @Override
    public void setup() {
        synchronizeLdapUsers();
        configureLdapSyncService();
    }
    public void synchronizeWithLdapService() {
        synchronizeLdapUsers();
    }
@@ -519,4 +528,24 @@
        }
        return sb.toString();
    }
    private void configureLdapSyncService() {
        logger.info("Start configuring ldap sync service");
        LdapSyncService ldapSyncService = new LdapSyncService(settings, this);
        if (ldapSyncService.isReady()) {
            int mins = TimeUtils.convertFrequencyToMinutes(settings.getString(Keys.realm.ldap.synchronizeUsers.ldapSyncPeriod, "5 mins"));
            if (mins < 5) {
                mins = 5;
            }
            int delay = 1;
            ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            scheduledExecutorService.scheduleAtFixedRate(ldapSyncService, delay, mins,  TimeUnit.MINUTES);
            logger.info("Ldap sync service will update user and groups every {} minutes.", mins);
            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");
    }
}
src/main/java/com/gitblit/service/LdapSyncService.java
New file
@@ -0,0 +1,64 @@
/*
 * 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.service;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.auth.LdapAuthProvider;
/**
 * @author Alfred Schmid
 *
 */
public final class LdapSyncService implements Runnable {
    private final Logger logger = LoggerFactory.getLogger(LdapSyncService.class);
    private final IStoredSettings settings;
    private final LdapAuthProvider ldapAuthProvider;
    private final AtomicBoolean running = new AtomicBoolean(false);
    public LdapSyncService(IStoredSettings settings, LdapAuthProvider ldapAuthProvider) {
        this.settings = settings;
        this.ldapAuthProvider = ldapAuthProvider;
    }
    /**
     *
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        logger.info("Starting user and group sync with ldap service");
        if (!running.getAndSet(true)) {
            ldapAuthProvider.synchronizeWithLdapService();
            running.getAndSet(false);
        }
        logger.info("Finished user and group sync with ldap service");
    }
    public boolean isReady() {
        return settings.getBoolean(Keys.realm.ldap.synchronizeUsers.enable, false);
    }
}
src/test/config/test-users.conf
@@ -4,6 +4,11 @@
    accountType = LOCAL
    role = "#admin"
    role = "#notfederated"
[user "sampleuser"]
    password = sampleuser
    cookie = 6e07ed42149fc166206319faffdfba2e2ec82e43
    accountType = LOCAL
    role = "#none"
[team "admins"]
    role = "#none"
    accountType = LOCAL
src/test/java/com/gitblit/tests/LdapAuthenticationTest.java
@@ -24,8 +24,10 @@
import org.junit.BeforeClass;
import org.junit.Test;
import com.gitblit.Constants.AccountType;
import com.gitblit.IStoredSettings;
import com.gitblit.auth.LdapAuthProvider;
import com.gitblit.manager.IUserManager;
import com.gitblit.manager.RuntimeManager;
import com.gitblit.manager.UserManager;
import com.gitblit.models.UserModel;
@@ -33,6 +35,8 @@
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldif.LDIFReader;
/**
@@ -50,6 +54,10 @@
    static int ldapPort = 1389;
    private static InMemoryDirectoryServer ds;
    private IUserManager userManager;
    @BeforeClass
    public static void createInMemoryLdapServer() throws Exception {
        InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=MyDomain");
@@ -57,7 +65,7 @@
        config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", ldapPort));
        config.setSchema(null);
        InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
        ds = new InMemoryDirectoryServer(config);
        ds.importFromLDIF(true, new LDIFReader(new FileInputStream(RESOURCE_DIR + "sampledata.ldif")));
        ds.startListening();
    }
@@ -69,9 +77,9 @@
    public LdapAuthProvider newLdapAuthentication(IStoredSettings settings) {
        RuntimeManager runtime = new RuntimeManager(settings, GitBlitSuite.BASEFOLDER).start();
        UserManager users = new UserManager(runtime).start();
        userManager = new UserManager(runtime).start();
        LdapAuthProvider ldap = new LdapAuthProvider();
        ldap.setup(runtime, users);
        ldap.setup(runtime, userManager);
        return ldap;
    }
@@ -91,6 +99,9 @@
        backingMap.put("realm.ldap.admins", "UserThree @Git_Admins \"@Git Admins\"");
        backingMap.put("realm.ldap.displayName", "displayName");
        backingMap.put("realm.ldap.email", "email");
        backingMap.put("realm.ldap.synchronizeUsers.enable", "true");
        backingMap.put("realm.ldap.uid", "sAMAccountName");
        backingMap.put("realm.ldap.ldapCachePeriod", "0 MINUTES");
        MemorySettings ms = new MemorySettings(backingMap);
        return ms;
@@ -162,4 +173,30 @@
        assertNull(userOneModel);
    }
    @Test
    public void checkIfSevenUsersLoadedFromLdap() throws Exception {
        SearchResult searchResult = ds.search("OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain", SearchScope.SUB, "objectClass=person");
        assertEquals("Number of ldap users in gitblit user model", searchResult.getEntryCount(), countLdapUsersInUserManager());
    }
    @Test
    public void addingUserInLdapShouldUpdateGitBlitUsersAndGroups() throws Exception {
        ds.addEntries(LDIFReader.readEntries(RESOURCE_DIR + "adduser.ldif"));
        for(String user : userManager.getAllUsernames()) {
            System.out.println(user);
        }
        ldap.synchronizeWithLdapService();
        assertEquals("Number of ldap users in gitblit user model", 5, countLdapUsersInUserManager());
    }
    private int countLdapUsersInUserManager() {
        int ldapAccountCount = 0;
        for (UserModel userModel : userManager.getAllUsers()) {
            if (AccountType.LDAP.equals(userModel.accountType)) {
                ldapAccountCount++;
            }
        }
        return ldapAccountCount;
    }
}
src/test/resources/ldap/adduser.ldif
New file
@@ -0,0 +1,11 @@
dn: CN=UserFive,OU=Canada,OU=Users,OU=UserControl,OU=MyOrganization,DC=MyDomain
objectClass: user
objectClass: person
sAMAccountName: UserFive
userPassword: userFivePassword
displayName: User Five
givenName: User
surname: Five
personalTitle: Miss
email: userfive@gitblit.com
memberOf: CN=Git_Users,OU=Groups,OU=UserControl,OU=MyOrganization,DC=MyDomain
src/test/resources/ldap/users.conf
@@ -7,10 +7,17 @@
[user "userthree"]
    password = "#externalAccount"
    cookie = d7d3894fc517612aa6c595555b6e1ab8e147e597
    displayName = User Three
    displayName = Mrs. User Three
    emailAddress = userthree@gitblit.com
    accountType = LDAP
    role = "#admin"
[user "userfive"]
    password = "#externalAccount"
    cookie = 220bafef069b8b399b2597644015b6b0f4667982
    displayName = Miss. User Five
    emailAddress = userfive@gitblit.com
    accountType = LDAP
    role = "#none"
[user "userone"]
    password = "#externalAccount"
    cookie = c97cd38e50858cd0b389ec61b18fb9a89b4da54c
@@ -21,7 +28,7 @@
[user "usertwo"]
    password = "#externalAccount"
    cookie = 498ca9bd2841d39050fa45d1d737b9f9f767858d
    displayName = User Two
    displayName = Mr. User Two
    emailAddress = usertwo@gitblit.com
    accountType = LDAP
    role = "#admin"
@@ -37,6 +44,13 @@
    cookie = dd94709528bb1c83d08f3088d4043f4742891f4f
    accountType = LOCAL
    role = "#create"
[user "userfour"]
    password = "#externalAccount"
    cookie = d4fca47ebc70f0495947f8b6158ac6edb546e2fe
    displayName = Miss. User Four
    emailAddress = userfour@gitblit.com
    accountType = LDAP
    role = "#none"
[team "Git_Admins"]
    role = "#none"
    accountType = LOCAL
@@ -47,6 +61,7 @@
    user = userone
    user = usertwo
    user = userthree
    user = userfour
[team "Git Admins"]
    role = "#none"
    accountType = LOCAL