/// <summary> /// Finalizes the <paramref name="result"/> based on the individual <see cref="RuleResult"/> values contained in <paramref name="resultsCollector"/> /// </summary> /// <param name="result">The result to update</param> /// <param name="resultsCollector">The set of <see cref="RuleResult"/> values used to determine the compound outcome</param> /// <returns></returns> protected ComposedRuleResult FinalizeResult(ComposedRuleResult result, List <RuleResult> resultsCollector) { // set all the rule results on the compound result result.Results = resultsCollector.ToArray(); // account for success if an entry doesn't have a given field result.Success = resultsCollector.Count < 1 || resultsCollector.All(r => r.Success); if (!result.Success) { // we propose the last proposed action, matching the original logic result.ProposedAction = result.Results.Where(r => !r.Success).Last().ProposedAction; // we propose the final result's updated value where success is false, matching the original logic result.ProposedValue = result.Results.Where(r => !r.Success).Last().UpdatedValue; } return(result); }
/// <summary> /// Creates a consistent base <see cref="ComposedRuleResult"/> instance with default values /// </summary> /// <param name="entry">The entry for which we are creating a result</param> /// <param name="isValuePresent">Allows calling code to know if the <paramref name="entry"/> has a value for the given <see cref="AttributeName"/></param> /// <returns></returns> protected ComposedRuleResult InitResult(SearchResultEntry entry, out bool isValuePresent) { isValuePresent = false; var result = new ComposedRuleResult() { AttributeName = this.AttributeName, EntityDistinguishedName = entry.Attributes[StringLiterals.DistinguishedName][0].ToString(), ObjectType = ComposedRule.GetObjectType(entry), OriginalValue = null, ProposedAction = ActionType.None }; if (entry.Attributes.Contains(this.AttributeName)) { isValuePresent = true; result.OriginalValue = entry.Attributes[this.AttributeName][0].ToString(); } return(result); }
/// <summary> /// Executes a rule collection against the supplied connection /// </summary> /// <param name="collection">The rule collection to execute</param> /// <param name="connection">The connection used to retrieve entries for processing</param> /// <returns><see cref="RuleCollectionResult"/> or null if canceled</returns> private RuleCollectionResult ExecuteRuleCollection(RuleCollection collection, LdapConnection connection) { // these count all the totals for the connection against which this RuleCollection is being run var stopWatch = new Stopwatch(); long skipCount = 0; long entryCount = 0; long duplicateCount = 0; long errorCount = 0; this.OnStatusUpdate?.Invoke(StringLiterals.LdapConnectionEstablishing); var searchRequest = collection.CreateSearchRequest(); this.OnStatusUpdate?.Invoke(StringLiterals.LdapConnectionEstablished); var errors = new List <ComposedRuleResult>(); this.OnStatusUpdate?.Invoke(StringLiterals.BeginningQuery); while (true) { var searchResponse = (SearchResponse)connection.SendRequest(searchRequest); // verify support for paged results if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is PageResultResponseControl)) { this.OnStatusUpdate?.Invoke(StringLiterals.CannotPageResultSet); throw new InvalidOperationException(StringLiterals.CannotPageResultSet); } foreach (SearchResultEntry entry in searchResponse.Entries) { if (this.CancellationPending) { return(null); } if (collection.Skip(entry)) { skipCount++; continue; } // this tracks the number of entries we have processed and not skipped entryCount++; foreach (var composedRule in collection.Rules) { // run each composed rule which can produce multiple results var results = composedRule.Execute(entry); for (var i = 0; i < results.Length; i++) { var result = results[i]; if (!result.Success) { errorCount++; if (result.Results.Any(r => (r.ErrorTypeFlags & ErrorType.Duplicate) != 0)) { duplicateCount++; if (result.ProposedAction == ActionType.Edit) { // Add original LDAP entry with the same value. var originalEntry = DuplicateStore.GetOriginalSearchResultEntry(result.AttributeName, result.OriginalValue); var additionalResult = new ComposedRuleResult { AttributeName = result.AttributeName, EntityDistinguishedName = originalEntry.DistinguishedName, EntityCommonName = originalEntry.Attributes[StringLiterals.Cn][0].ToString(), ObjectType = ComposedRule.GetObjectType(entry), OriginalValue = result.OriginalValue, ProposedAction = result.ProposedAction, ProposedValue = result.ProposedValue, Results = new RuleResult[] { new RuleResult(false) { ErrorTypeFlags = ErrorType.Duplicate, ProposedAction = result.ProposedAction, UpdatedValue = result.OriginalValue } }, Success = result.Success }; errors.Add(additionalResult); } } errors.Add(result); } } } } // handle paging var cookie = searchResponse.Controls.OfType <PageResultResponseControl>().First().Cookie; // if this is true, there are no more pages to request if (cookie.Length == 0) { break; } searchRequest.Controls.OfType <PageResultRequestControl>().First().Cookie = cookie; } // we are all done, stop tracking time stopWatch.Stop(); return(new RuleCollectionResult { TotalDuplicates = duplicateCount, TotalErrors = errorCount, TotalFound = skipCount + entryCount, TotalSkips = skipCount, TotalProcessed = entryCount, Elapsed = stopWatch.Elapsed, Errors = errors.ToArray() }); }