/// <summary>
        /// Enqueue changes.
        /// </summary>
        /// <param name="entries">Changes to process.</param>
        public void AddToQueue(List <SearchResultEntry> entries)
        {
            if ((entries != null) && entries.Any())
            {
                lock (mQueueLock)
                {
                    // Add items to query
                    Queue.AddRange(entries);

                    // Sort query by uSNChanged and process users first
                    Queue.Sort((a, b) =>
                    {
                        if (LdapHelper.IsUser(a) ^ LdapHelper.IsUser(b))
                        {
                            return(LdapHelper.IsUser(a) ? -1 : 1);
                        }

                        return(LdapHelper.GetUsnChanged(a).CompareTo(LdapHelper.GetUsnChanged(b)));
                    });

                    // Start sending changes
                    if (DispatchAllowed && !DispatchRunning)
                    {
                        DispatchRunning = true;
                        Task.Factory.StartNew(SendChanges, TaskCreationOptions.LongRunning);
                    }
                }
            }
        }
        /// <summary>
        /// Handle single user.
        /// </summary>
        /// <param name="entry">User LDAP object.</param>
        private void HandleUser(SearchResultEntry entry)
        {
            // Create CMS object from LDAP object
            var user = new User(
                LdapHelper.GetObjectGuid(entry),
                LdapHelper.GetAttributeString(entry.Attributes["name"], true),
                LdapHelper.IsUserEnabled(entry),
                UserBindings.Select(k => new KeyValuePair <string, string>(k.Cms, LdapHelper.GetAttributeString(entry.Attributes[k.Ldap]))).ToList());

            // Find existing object in LDAP replica
            var existing = Replica.Users.FirstOrDefault(u => u.Guid == user.Guid);

            if (LdapHelper.IsDeleted(entry))
            {
                if (existing != null)
                {
                    // Remove user
                    Sender.RemoveUser(existing);
                    Replica.Users.Remove(existing);
                }
            }
            else if (existing != null)
            {
                // Check if any attribute has changed
                var userXml = Sender.GetUser(user.Guid);

                if (!string.IsNullOrEmpty(userXml))
                {
                    bool userChanged =
                        User.InternalBindings.Any(
                            b =>
                            RestHelper.GetAttributeFromReponse(userXml, b.Value) !=
                            ((b.Key == "userAccountControl" ? LdapHelper.IsUserEnabled(entry).ToString().ToLowerInvariant() : LdapHelper.GetAttributeString(entry.Attributes[b.Key], b.Key == "name")) ?? string.Empty));
                    userChanged |=
                        UserBindings.Any(
                            b =>
                            RestHelper.GetAttributeFromReponse(userXml, b.Cms) !=
                            (LdapHelper.GetAttributeString(entry.Attributes[b.Ldap]) ?? string.Empty));

                    if (userChanged)
                    {
                        // Modify user
                        Sender.ModifyUser(user);
                    }
                }
            }
            else
            {
                // Add user
                long?userId = Sender.AddUser(user);

                if (userId != null)
                {
                    user.Id = userId.Value;
                    user.DistinguishedName = entry.DistinguishedName;
                    Replica.Users.Add(user);
                }
            }
        }
        /// <summary>
        /// Send changes to CMS one-by-one.
        /// </summary>
        private void SendChanges()
        {
            // Re-enumerate queue in each iteration
            while (Queue.Any() && DispatchAllowed)
            {
                lock (mQueueLock)
                {
                    try
                    {
                        if (Replica == null)
                        {
                            LoadDirecotryReplica();
                        }

                        // Process first entry
                        var entry = Queue.First();

                        if (entry == null)
                        {
                            // Remove all nulls
                            Queue.RemoveAll(e => e == null);
                            continue;
                        }

                        // Handle incoming change
                        if (LdapHelper.IsUser(entry))
                        {
                            HandleUser(entry);
                        }
                        else if (LdapHelper.IsGroup(entry))
                        {
                            HandleGroup(entry);
                        }

                        Queue.Remove(entry);

                        // Set actual uSNChanged attribute
                        long newUsn = LdapHelper.GetUsnChanged(entry);
                        if ((Replica != null) && (Replica.HighestUsnChanged < newUsn))
                        {
                            Replica.HighestUsnChanged = newUsn;
                        }

                        SaveDirecotryReplica();
                    }
                    catch (Exception ex)
                    {
                        LogError("Exception occurred when processing object.", ex);
                    }
                }
            }

            DispatchRunning = false;
        }
        /// <summary>
        /// Handle single role.
        /// </summary>
        /// <param name="entry">Group LDAP object.</param>
        private void HandleGroup(SearchResultEntry entry)
        {
            // Create CMS object from LDAP object
            var role = new Role(LdapHelper.GetObjectGuid(entry),
                                LdapHelper.GetAttributeString(entry.Attributes["sAMAccountName"], true),
                                LdapHelper.GetAttributeString(entry.Attributes["displayName"]),
                                GroupBindings.Select(k => new KeyValuePair <string, string>(k.Cms, LdapHelper.GetAttributeString(entry.Attributes[k.Ldap]))).ToList());

            var existing = Replica.Groups.FirstOrDefault(g => g.Guid == role.Guid);

            List <User> currentMembers = (existing == null) ? new List <User>() : Replica.Bindings.Where(b => b.RoleId == existing.Id).SelectMany(b => Replica.Users.Where(u => u.Id == b.UserId)).ToList();
            List <User> newMembers     = LdapHelper.GetGroupMembers(entry).SelectMany(d => Replica.Users.Where(u => string.Equals(u.DistinguishedName, d, StringComparison.InvariantCultureIgnoreCase))).ToList();

            if (LdapHelper.IsDeleted(entry))
            {
                if (existing != null)
                {
                    // Delete role
                    Sender.RemoveRole(existing);
                    Replica.Groups.Remove(existing);
                }
            }
            else
            {
                if (existing != null)
                {
                    role.Id = existing.Id;

                    // Check if any attribute has changed
                    var roleXml = Sender.GetRole(role.Id);

                    if (!string.IsNullOrEmpty(roleXml))
                    {
                        bool roleChanged =
                            Role.InternalBindings.Any(
                                b =>
                                RestHelper.GetAttributeFromReponse(roleXml, b.Value) !=
                                (LdapHelper.GetAttributeString(entry.Attributes[b.Key], b.Key == "sAMAccountName") ?? string.Empty));
                        roleChanged |=
                            GroupBindings.Any(
                                b =>
                                RestHelper.GetAttributeFromReponse(roleXml, b.Cms) !=
                                (LdapHelper.GetAttributeString(entry.Attributes[b.Ldap]) ?? string.Empty));

                        if (roleChanged)
                        {
                            // Modify role
                            Sender.ModifyRole(role);
                        }
                    }
                }
                else
                {
                    // Add role
                    long?roleId = Sender.AddRole(role);

                    if (roleId != null)
                    {
                        role.Id = roleId.Value;
                        Replica.Groups.Add(role);
                    }
                }

                // Add members
                var addedMembers = newMembers.Where(m => currentMembers.All(c => c.Guid != m.Guid)).ToList();
                foreach (var member in addedMembers)
                {
                    var userroleId = Sender.AddUserToRole(member.Id, role.Id);

                    if (userroleId != null)
                    {
                        Replica.Bindings.Add(new UserRoleBinding(member.Id, role.Id)
                        {
                            Id = userroleId.Value
                        });
                    }
                }

                // Remove members
                var removedMembers =
                    currentMembers.Where(m => newMembers.All(c => c.Guid != m.Guid))
                    .SelectMany(m => Replica.Bindings.Where(b => (b.RoleId == role.Id) && (b.UserId == m.Id)))
                    .ToList();
                foreach (var member in removedMembers)
                {
                    Sender.RemoveUserFromRole(member.Id);
                    Replica.Bindings.Remove(member);
                }
            }
        }
Example #5
0
        /// <summary>
        /// Asynchronous callback that processes changes from Active Directory.
        /// </summary>
        /// <param name="asyncResult">Result of permanens search</param>
        private void RunAsyncSearch(IAsyncResult asyncResult)
        {
            var results = new List <SearchResultEntry>();

            // Get changes
            if (!asyncResult.IsCompleted)
            {
                PartialResultsCollection partialResults = null;

                try
                {
                    partialResults = Connection.GetPartialResults(asyncResult);
                }
                catch (Exception e)
                {
                    LogError("Retrieving partial results from Active Directory asynchronous search failed.", e);
                }

                if (partialResults != null)
                {
                    // Add only users and groups
                    results.AddRange(partialResults.OfType <SearchResultEntry>().Where(p => LdapHelper.IsUser(p, PersonObjectCategory) || LdapHelper.IsGroup(p, GroupObjectCategory)));
                }
            }
            else
            {
                LogMessage("The change notification control unexpectedly ended the search.");

                mSearches.Remove(asyncResult);
                StartIncrementalSynchronization();
            }

            // Send changes to CMS
            Dispatcher.AddToQueue(results);
        }
Example #6
0
        /// <summary>
        /// Retrieves current state of LDAP database and reflects it to the CMS.
        /// </summary>
        public void Synchronize()
        {
            try
            {
                var request = new SearchRequest(DefaultNamingContext, "(&(|(objectClass=user)(objectClass=group))(usnchanged>=" + (Dispatcher.HighestUsnChanged + 1) + "))", SearchScope.Subtree, null);
                request.Controls.Add(new ShowDeletedControl());

                // Page result
                var prc = new PageResultRequestControl(5);
                request.Controls.Add(prc);
                var soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope);
                request.Controls.Add(soc);

                while (true)
                {
                    var searchResponse = (SearchResponse)Connection.SendRequest(request);

                    if (searchResponse != null)
                    {
                        // Find the returned page response control
                        foreach (DirectoryControl control in searchResponse.Controls)
                        {
                            if (control is PageResultResponseControl)
                            {
                                //update the cookie for next set
                                prc.Cookie = ((PageResultResponseControl)control).Cookie;
                                break;
                            }
                        }

                        Dispatcher.AddToQueue(searchResponse.Entries.Cast <SearchResultEntry>().Where(p => LdapHelper.IsUser(p, PersonObjectCategory) || LdapHelper.IsGroup(p, GroupObjectCategory)).ToList());

                        if (prc.Cookie.Length == 0)
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                LogError("Full synchronization failed.", ex);
            }
        }